Skip to main content

agent_client_protocol/
jsonrpc.rs

1//! Core JSON-RPC server support.
2
3use agent_client_protocol_schema::v1::{
4    JsonRpcMessage as VersionedJsonRpcMessage, Notification as RpcNotification,
5    Request as RpcRequest, RequestId, Response as RpcResponse, SessionId,
6};
7
8// Types re-exported from crate root
9use serde::{Deserialize, Serialize};
10use std::any::TypeId;
11#[cfg(feature = "unstable_cancel_request")]
12use std::collections::HashMap;
13use std::fmt::Debug;
14use std::panic::Location;
15use std::pin::pin;
16use std::sync::Arc;
17#[cfg(feature = "unstable_cancel_request")]
18use std::sync::{
19    Mutex,
20    atomic::{AtomicBool, Ordering},
21};
22use uuid::Uuid;
23
24#[cfg(feature = "unstable_cancel_request")]
25use futures::FutureExt;
26use futures::channel::{mpsc, oneshot};
27use futures::future::{self, BoxFuture, Either};
28use futures::{AsyncRead, AsyncWrite, StreamExt};
29
30mod dynamic_handler;
31pub(crate) mod handlers;
32mod incoming_actor;
33mod outgoing_actor;
34mod protocol_compat;
35pub(crate) mod run;
36mod task_actor;
37mod transport_actor;
38
39use crate::jsonrpc::dynamic_handler::DynamicHandlerMessage;
40pub use crate::jsonrpc::handlers::NullHandler;
41use crate::jsonrpc::handlers::{ChainedHandler, NamedHandler};
42use crate::jsonrpc::handlers::{MessageHandler, NotificationHandler, RequestHandler};
43use crate::jsonrpc::outgoing_actor::{OutgoingMessageTx, send_raw_message};
44use crate::jsonrpc::protocol_compat::{ProtocolCompat, ProtocolMode};
45use crate::jsonrpc::run::SpawnedRun;
46use crate::jsonrpc::run::{ChainRun, NullRun, RunWithConnectionTo};
47use crate::jsonrpc::task_actor::{Task, TaskTx};
48use crate::mcp_server::McpServer;
49use crate::role::HasPeer;
50use crate::role::Role;
51use crate::{Agent, Client, ConnectTo, RoleId};
52
53/// Raw JSON-RPC message transported by [`Channel`].
54///
55/// This uses the JSON-RPC envelope types from `agent-client-protocol-schema`
56/// while keeping method params as raw, JSON-RPC-valid params at the transport boundary.
57#[derive(Debug, Clone)]
58pub enum RawJsonRpcMessage {
59    /// A JSON-RPC request with an id and expected response.
60    Request(RpcRequest<RawJsonRpcParams>),
61    /// A JSON-RPC notification without a response.
62    Notification(RpcNotification<RawJsonRpcParams>),
63    /// A JSON-RPC response to a prior request.
64    Response(RpcResponse<serde_json::Value>),
65}
66
67/// Raw JSON-RPC request or notification parameters.
68///
69/// JSON-RPC params, when present, must be either an array or an object.
70#[derive(Debug, Clone, PartialEq)]
71pub enum RawJsonRpcParams {
72    /// Positional JSON-RPC params.
73    Array(Vec<serde_json::Value>),
74    /// Named JSON-RPC params.
75    Object(serde_json::Map<String, serde_json::Value>),
76}
77
78impl RawJsonRpcParams {
79    /// Convert a JSON value into JSON-RPC params.
80    pub fn from_value(value: serde_json::Value) -> Result<Option<Self>, crate::Error> {
81        match value {
82            serde_json::Value::Null => Ok(None),
83            serde_json::Value::Array(array) => Ok(Some(Self::Array(array))),
84            serde_json::Value::Object(object) => Ok(Some(Self::Object(object))),
85            _ => {
86                Err(crate::Error::invalid_params()
87                    .data("JSON-RPC params must be an object or array"))
88            }
89        }
90    }
91
92    /// Convert params back into a JSON value.
93    #[must_use]
94    pub fn into_value(self) -> serde_json::Value {
95        match self {
96            Self::Array(array) => serde_json::Value::Array(array),
97            Self::Object(object) => serde_json::Value::Object(object),
98        }
99    }
100}
101
102impl Serialize for RawJsonRpcParams {
103    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104    where
105        S: serde::Serializer,
106    {
107        match self {
108            Self::Array(array) => array.serialize(serializer),
109            Self::Object(object) => object.serialize(serializer),
110        }
111    }
112}
113
114impl<'de> Deserialize<'de> for RawJsonRpcParams {
115    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
116    where
117        D: serde::Deserializer<'de>,
118    {
119        let value = serde_json::Value::deserialize(deserializer)?;
120        match value {
121            serde_json::Value::Array(array) => Ok(Self::Array(array)),
122            serde_json::Value::Object(object) => Ok(Self::Object(object)),
123            _ => Err(serde::de::Error::custom(
124                "JSON-RPC params must be an object or array",
125            )),
126        }
127    }
128}
129
130impl RawJsonRpcMessage {
131    /// Build a raw JSON-RPC request message.
132    pub fn request(
133        method: String,
134        params: serde_json::Value,
135        id: RequestId,
136    ) -> Result<Self, crate::Error> {
137        Ok(Self::Request(RpcRequest {
138            id,
139            method: Arc::from(method),
140            params: RawJsonRpcParams::from_value(params)?,
141        }))
142    }
143
144    /// Build a raw JSON-RPC notification message.
145    pub fn notification(method: String, params: serde_json::Value) -> Result<Self, crate::Error> {
146        Ok(Self::Notification(RpcNotification {
147            method: Arc::from(method),
148            params: RawJsonRpcParams::from_value(params)?,
149        }))
150    }
151
152    /// Build a raw JSON-RPC response message.
153    #[must_use]
154    pub fn response(id: RequestId, response: Result<serde_json::Value, crate::Error>) -> Self {
155        Self::Response(RpcResponse::new(id, response))
156    }
157
158    /// The response id, if this is a response.
159    #[must_use]
160    pub fn response_id(&self) -> Option<&RequestId> {
161        match self {
162            Self::Response(RpcResponse::Result { id, .. } | RpcResponse::Error { id, .. }) => {
163                Some(id)
164            }
165            Self::Request(_) | Self::Notification(_) => None,
166        }
167    }
168}
169
170impl Serialize for RawJsonRpcMessage {
171    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
172    where
173        S: serde::Serializer,
174    {
175        match self {
176            Self::Request(request) => {
177                VersionedJsonRpcMessage::wrap(request.clone()).serialize(serializer)
178            }
179            Self::Notification(notification) => {
180                VersionedJsonRpcMessage::wrap(notification.clone()).serialize(serializer)
181            }
182            Self::Response(response) => {
183                VersionedJsonRpcMessage::wrap(response.clone()).serialize(serializer)
184            }
185        }
186    }
187}
188
189impl<'de> Deserialize<'de> for RawJsonRpcMessage {
190    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
191    where
192        D: serde::Deserializer<'de>,
193    {
194        let value = serde_json::Value::deserialize(deserializer)?;
195        if value.get("method").is_some() {
196            if value.get("id").is_some() {
197                let request = serde_json::from_value::<
198                    VersionedJsonRpcMessage<RpcRequest<RawJsonRpcParams>>,
199                >(value)
200                .map_err(serde::de::Error::custom)?
201                .into_inner();
202                Ok(Self::Request(request))
203            } else {
204                let notification = serde_json::from_value::<
205                    VersionedJsonRpcMessage<RpcNotification<RawJsonRpcParams>>,
206                >(value)
207                .map_err(serde::de::Error::custom)?
208                .into_inner();
209                Ok(Self::Notification(notification))
210            }
211        } else if value.get("result").is_some() || value.get("error").is_some() {
212            let response = serde_json::from_value::<
213                VersionedJsonRpcMessage<RpcResponse<serde_json::Value>>,
214            >(value)
215            .map_err(serde::de::Error::custom)?
216            .into_inner();
217            Ok(Self::Response(response))
218        } else {
219            Err(serde::de::Error::custom("invalid JSON-RPC message"))
220        }
221    }
222}
223
224fn params_from_transport(params: Option<RawJsonRpcParams>) -> serde_json::Value {
225    params.map_or(serde_json::Value::Null, RawJsonRpcParams::into_value)
226}
227
228/// Handlers process incoming JSON-RPC messages on a connection.
229///
230/// When messages arrive, they flow through a chain of handlers. Each handler can
231/// either **claim** the message (handle it) or **decline** it (pass to the next handler).
232///
233/// # Message Flow
234///
235/// Messages flow through three layers of handlers in order:
236///
237/// ```text
238/// ┌─────────────────────────────────────────────────────────────────┐
239/// │                     Incoming Message                            │
240/// └─────────────────────────────────────────────────────────────────┘
241///                              │
242///                              ▼
243/// ┌─────────────────────────────────────────────────────────────────┐
244/// │  1. User Handlers (registered via on_receive_request, etc.)     │
245/// │     - Tried in registration order                               │
246/// │     - First handler to return Handled::Yes claims the message   │
247/// └─────────────────────────────────────────────────────────────────┘
248///                              │ Handled::No
249///                              ▼
250/// ┌─────────────────────────────────────────────────────────────────┐
251/// │  2. Dynamic Handlers (added at runtime)                         │
252/// │     - Used for session-specific message handling                │
253/// │     - Added via ConnectionTo::add_dynamic_handler             │
254/// └─────────────────────────────────────────────────────────────────┘
255///                              │ Handled::No
256///                              ▼
257/// ┌─────────────────────────────────────────────────────────────────┐
258/// │  3. Role Default Handler                                        │
259/// │     - Fallback based on the connection's Role                   │
260/// │     - Handles protocol-level messages (e.g., proxy forwarding)  │
261/// └─────────────────────────────────────────────────────────────────┘
262///                              │ Handled::No
263///                              ▼
264/// ┌─────────────────────────────────────────────────────────────────┐
265/// │  Unhandled: requests error, notifications ignored               │
266/// └─────────────────────────────────────────────────────────────────┘
267/// ```
268///
269/// # The `Handled` Return Value
270///
271/// Each handler returns [`Handled`] to indicate whether it processed the message:
272///
273/// - **`Handled::Yes`** - Message was handled. No further handlers are invoked.
274/// - **`Handled::No { message, retry }`** - Message was not handled. The message
275///   (possibly modified) is passed to the next handler in the chain.
276///
277/// For convenience, handlers can return `()` which is equivalent to `Handled::Yes`.
278///
279/// # The Retry Mechanism
280///
281/// The `retry` flag in `Handled::No` controls what happens when no handler claims a message:
282///
283/// - **`retry: false`** (default) - Send a "method not found" error
284///   response immediately for requests, or ignore notifications.
285/// - **`retry: true`** - Queue the message and retry it when new dynamic handlers are added.
286///
287/// This mechanism exists because of a timing issue with sessions: when a `session/new`
288/// response is being processed, the dynamic handler for that session hasn't been registered
289/// yet, but `session/update` notifications for that session may already be arriving.
290/// By setting `retry: true`, these early notifications are queued until the session's
291/// dynamic handler is added.
292///
293/// # Handler Registration
294///
295/// Most users register handlers using the builder methods on [`Builder`]:
296///
297/// ```
298/// # use agent_client_protocol::{Agent, Client, ConnectTo};
299/// # use agent_client_protocol::schema::v1::{AgentCapabilities, InitializeRequest, InitializeResponse};
300/// # use agent_client_protocol_test::StatusUpdate;
301/// # async fn example(transport: impl ConnectTo<Agent>) -> Result<(), agent_client_protocol::Error> {
302/// Agent.builder()
303///     .on_receive_request(async |req: InitializeRequest, responder, cx| {
304///         responder.respond(
305///             InitializeResponse::new(req.protocol_version)
306///                 .agent_capabilities(AgentCapabilities::new()),
307///         )
308///     }, agent_client_protocol::on_receive_request!())
309///     .on_receive_notification(async |notif: StatusUpdate, cx| {
310///         // Process notification
311///         Ok(())
312///     }, agent_client_protocol::on_receive_notification!())
313///     .connect_to(transport)
314///     .await?;
315/// # Ok(())
316/// # }
317/// ```
318///
319/// The type parameter on the closure determines which messages are dispatched to it.
320/// Messages that don't match the type are automatically passed to the next handler.
321///
322/// # Implementing Custom Handlers
323///
324/// For advanced use cases, you can implement `HandleMessageAs` directly:
325///
326/// ```ignore
327/// struct MyHandler;
328///
329/// impl HandleMessageAs<Agent> for MyHandler {
330///
331///     async fn handle_dispatch(
332///         &mut self,
333///         message: Dispatch,
334///         cx: ConnectionTo<Self::Role>,
335///     ) -> Result<Handled<Dispatch>, Error> {
336///         if message.method() == "my/custom/method" {
337///             // Handle it
338///             Ok(Handled::Yes)
339///         } else {
340///             // Pass to next handler
341///             Ok(Handled::No { message, retry: false })
342///         }
343///     }
344///
345///     fn describe_chain(&self) -> impl std::fmt::Debug {
346///         "MyHandler"
347///     }
348/// }
349/// ```
350///
351/// # Important: Handlers Must Not Block
352///
353/// The connection processes messages on a single async task. While a handler is running,
354/// no other messages can be processed. For expensive operations, use [`ConnectionTo::spawn`]
355/// to run work concurrently:
356///
357/// ```
358/// # use agent_client_protocol::{Client, Agent, ConnectTo};
359/// # use agent_client_protocol_test::{expensive_operation, ProcessComplete};
360/// # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
361/// # Client.builder().connect_with(transport, async |cx| {
362/// cx.spawn({
363///     let connection = cx.clone();
364///     async move {
365///         let result = expensive_operation("data").await?;
366///         connection.send_notification(ProcessComplete { result })?;
367///         Ok(())
368///     }
369/// })?;
370/// # Ok(())
371/// # }).await?;
372/// # Ok(())
373/// # }
374/// ```
375#[allow(async_fn_in_trait)]
376/// A handler for incoming JSON-RPC messages.
377///
378/// This trait is implemented by types that can process incoming messages on a connection.
379/// Handlers are registered with a [`Builder`] and are called in order until
380/// one claims the message.
381///
382/// The type parameter `R` is the role this handler plays - who I am.
383/// For an agent handler, `R = Agent` (I handle messages as an agent).
384/// For a client handler, `R = Client` (I handle messages as a client).
385pub trait HandleDispatchFrom<Counterpart: Role>: Send {
386    /// Attempt to claim an incoming message (request or notification).
387    ///
388    /// # Important: do not block
389    ///
390    /// The server will not process new messages until this handler returns.
391    /// You should avoid blocking in this callback unless you wish to block the server (e.g., for rate limiting).
392    /// The recommended approach to manage expensive operations is to the [`ConnectionTo::spawn`] method available on the message context.
393    ///
394    /// # Parameters
395    ///
396    /// * `message` - The incoming message to handle.
397    /// * `connection` - The connection, used to send messages and access connection state.
398    ///
399    /// # Returns
400    ///
401    /// * `Ok(Handled::Yes)` if the message was claimed. It will not be propagated further.
402    /// * `Ok(Handled::No(message))` if not; the (possibly changed) message will be passed to the remaining handlers.
403    /// * `Err` if an internal error occurs (this will bring down the server).
404    fn handle_dispatch_from(
405        &mut self,
406        message: Dispatch,
407        connection: ConnectionTo<Counterpart>,
408    ) -> impl Future<Output = Result<Handled<Dispatch>, crate::Error>> + Send;
409
410    /// Returns a debug description of the registered handlers for diagnostics.
411    fn describe_chain(&self) -> impl std::fmt::Debug;
412}
413
414impl<Counterpart: Role, H> HandleDispatchFrom<Counterpart> for &mut H
415where
416    H: HandleDispatchFrom<Counterpart>,
417{
418    fn handle_dispatch_from(
419        &mut self,
420        message: Dispatch,
421        cx: ConnectionTo<Counterpart>,
422    ) -> impl Future<Output = Result<Handled<Dispatch>, crate::Error>> + Send {
423        H::handle_dispatch_from(self, message, cx)
424    }
425
426    fn describe_chain(&self) -> impl std::fmt::Debug {
427        H::describe_chain(self)
428    }
429}
430
431/// A JSON-RPC connection that can act as either a server, client, or both.
432///
433/// [`Builder`] provides a builder-style API for creating JSON-RPC servers and clients.
434/// You start by calling `Role.builder()` (e.g., `Client.builder()`), then add message
435/// handlers, and finally drive the connection with either [`connect_to`](Builder::connect_to)
436/// or [`connect_with`](Builder::connect_with), providing a component implementation
437/// (e.g., [`ByteStreams`] for byte streams).
438///
439/// # JSON-RPC Primer
440///
441/// JSON-RPC 2.0 has two fundamental message types:
442///
443/// * **Requests** - Messages that expect a response. They have an `id` field that gets
444///   echoed back in the response so the sender can correlate them.
445/// * **Notifications** - Fire-and-forget messages with no `id` field. The sender doesn't
446///   expect or receive a response.
447///
448/// # Type-Driven Message Dispatch
449///
450/// The handler registration methods use Rust's type system to determine which messages
451/// to handle. The type parameter you provide controls what gets dispatched to your handler:
452///
453/// ## Single Message Types
454///
455/// The simplest case - handle one specific message type:
456///
457/// ```no_run
458/// # use agent_client_protocol_test::*;
459/// # use agent_client_protocol::schema::v1::{InitializeRequest, InitializeResponse, SessionNotification};
460/// # async fn example() -> Result<(), agent_client_protocol::Error> {
461/// # let connection = mock_connection();
462/// connection
463///     .on_receive_request(async |req: InitializeRequest, responder, cx| {
464///         // Handle only InitializeRequest messages
465///         responder.respond(InitializeResponse::make())
466///     }, agent_client_protocol::on_receive_request!())
467///     .on_receive_notification(async |notif: SessionNotification, cx| {
468///         // Handle only SessionUpdate notifications
469///         Ok(())
470///     }, agent_client_protocol::on_receive_notification!())
471/// # .connect_to(agent_client_protocol_test::MockTransport).await?;
472/// # Ok(())
473/// # }
474/// ```
475///
476/// ## Enum Message Types
477///
478/// You can also handle multiple related messages with a single handler by defining an enum
479/// that implements the appropriate trait ([`JsonRpcRequest`] or [`JsonRpcNotification`]):
480///
481/// ```no_run
482/// # use agent_client_protocol_test::*;
483/// # use agent_client_protocol::{JsonRpcRequest, JsonRpcMessage, UntypedMessage};
484/// # use agent_client_protocol::schema::v1::{InitializeRequest, InitializeResponse, PromptRequest, PromptResponse};
485/// # async fn example() -> Result<(), agent_client_protocol::Error> {
486/// # let connection = mock_connection();
487/// // Define an enum for multiple request types
488/// #[derive(Debug, Clone)]
489/// enum MyRequests {
490///     Initialize(InitializeRequest),
491///     Prompt(PromptRequest),
492/// }
493///
494/// // Implement JsonRpcRequest for your enum
495/// # impl JsonRpcMessage for MyRequests {
496/// #     fn matches_method(_method: &str) -> bool { false }
497/// #     fn method(&self) -> &str { "myRequests" }
498/// #     fn to_untyped_message(&self) -> Result<UntypedMessage, agent_client_protocol::Error> { todo!() }
499/// #     fn parse_message(_method: &str, _params: &impl serde::Serialize) -> Result<Self, agent_client_protocol::Error> { Err(agent_client_protocol::Error::method_not_found()) }
500/// # }
501/// impl JsonRpcRequest for MyRequests { type Response = serde_json::Value; }
502///
503/// // Handle all variants in one place
504/// connection.on_receive_request(async |req: MyRequests, responder, cx| {
505///     match req {
506///         MyRequests::Initialize(init) => { responder.respond(serde_json::json!({})) }
507///         MyRequests::Prompt(prompt) => { responder.respond(serde_json::json!({})) }
508///     }
509/// }, agent_client_protocol::on_receive_request!())
510/// # .connect_to(agent_client_protocol_test::MockTransport).await?;
511/// # Ok(())
512/// # }
513/// ```
514///
515/// ## Mixed Message Types
516///
517/// For enums containing both requests AND notifications, use [`on_receive_dispatch`](Self::on_receive_dispatch):
518///
519/// ```no_run
520/// # use agent_client_protocol_test::*;
521/// # use agent_client_protocol::Dispatch;
522/// # use agent_client_protocol::schema::v1::{InitializeRequest, InitializeResponse, SessionNotification};
523/// # async fn example() -> Result<(), agent_client_protocol::Error> {
524/// # let connection = mock_connection();
525/// // on_receive_dispatch receives Dispatch which can be either a request or notification
526/// connection.on_receive_dispatch(async |msg: Dispatch<InitializeRequest, SessionNotification>, _cx| {
527///     match msg {
528///         Dispatch::Request(req, responder) => {
529///             responder.respond(InitializeResponse::make())
530///         }
531///         Dispatch::Notification(notif) => {
532///             Ok(())
533///         }
534///         Dispatch::Response(result, router) => {
535///             // Forward response to its destination
536///             router.respond_with_result(result)
537///         }
538///     }
539/// }, agent_client_protocol::on_receive_dispatch!())
540/// # .connect_to(agent_client_protocol_test::MockTransport).await?;
541/// # Ok(())
542/// # }
543/// ```
544///
545/// # Handler Registration
546///
547/// Register handlers using these methods (listed from most common to most flexible):
548///
549/// * [`on_receive_request`](Self::on_receive_request) - Handle JSON-RPC requests (messages expecting responses)
550/// * [`on_receive_notification`](Self::on_receive_notification) - Handle JSON-RPC notifications (fire-and-forget)
551/// * [`on_receive_dispatch`](Self::on_receive_dispatch) - Handle enums containing both requests and notifications
552/// * [`with_handler`](Self::with_handler) - Low-level primitive for maximum flexibility
553///
554/// ## Handler Ordering
555///
556/// Handlers are tried in the order you register them. The first handler that claims a message
557/// (by matching its type) will process it. Subsequent handlers won't see that message:
558///
559/// ```no_run
560/// # use agent_client_protocol_test::*;
561/// # use agent_client_protocol::{Dispatch, UntypedMessage};
562/// # use agent_client_protocol::schema::v1::{InitializeRequest, InitializeResponse, PromptRequest, PromptResponse};
563/// # async fn example() -> Result<(), agent_client_protocol::Error> {
564/// # let connection = mock_connection();
565/// connection
566///     .on_receive_request(async |req: InitializeRequest, responder, cx| {
567///         // This runs first for InitializeRequest
568///         responder.respond(InitializeResponse::make())
569///     }, agent_client_protocol::on_receive_request!())
570///     .on_receive_request(async |req: PromptRequest, responder, cx| {
571///         // This runs first for PromptRequest
572///         responder.respond(PromptResponse::make())
573///     }, agent_client_protocol::on_receive_request!())
574///     .on_receive_dispatch(async |msg: Dispatch, cx| {
575///         // This runs for any message not handled above
576///         msg.respond_with_error(agent_client_protocol::util::internal_error("unknown method"), cx)
577///     }, agent_client_protocol::on_receive_dispatch!())
578/// # .connect_to(agent_client_protocol_test::MockTransport).await?;
579/// # Ok(())
580/// # }
581/// ```
582///
583/// # Event Loop and Concurrency
584///
585/// Understanding the event loop is critical for writing correct handlers.
586///
587/// ## The Event Loop
588///
589/// [`Builder`] runs all handler callbacks on a single async task - the event loop.
590/// While a handler is running, **the server cannot receive new messages**. This means
591/// any blocking or expensive work in your handlers will stall the entire connection.
592///
593/// To avoid blocking the event loop, use [`ConnectionTo::spawn`] to offload serious
594/// work to concurrent tasks:
595///
596/// ```no_run
597/// # use agent_client_protocol_test::*;
598/// # async fn example() -> Result<(), agent_client_protocol::Error> {
599/// # let connection = mock_connection();
600/// connection.on_receive_request(async |req: AnalyzeRequest, responder, cx| {
601///     // Clone cx for the spawned task
602///     cx.spawn({
603///         let connection = cx.clone();
604///         async move {
605///             let result = expensive_analysis(&req.data).await?;
606///             connection.send_notification(AnalysisComplete { result })?;
607///             Ok(())
608///         }
609///     })?;
610///
611///     // Respond immediately without blocking
612///     responder.respond(AnalysisStarted { job_id: 42 })
613/// }, agent_client_protocol::on_receive_request!())
614/// # .connect_to(agent_client_protocol_test::MockTransport).await?;
615/// # Ok(())
616/// # }
617/// ```
618///
619/// Note that the entire connection runs within one async task, so parallelism must be
620/// managed explicitly using [`spawn`](ConnectionTo::spawn).
621///
622/// ## The Connection Context
623///
624/// Handler callbacks receive a context object (`cx`) for interacting with the connection:
625///
626/// * **For request handlers** - [`Responder<R>`] provides [`respond`](Responder::respond)
627///   to send the response, plus methods to send other messages
628/// * **For notification handlers** - [`ConnectionTo`] provides methods to send messages
629///   and spawn tasks
630///
631/// Both context types support:
632/// * [`send_request`](ConnectionTo::send_request) - Send requests to the other side
633/// * [`send_notification`](ConnectionTo::send_notification) - Send notifications
634/// * [`spawn`](ConnectionTo::spawn) - Run tasks concurrently without blocking the event loop
635///
636/// The [`SentRequest`] returned by `send_request` provides methods like
637/// [`on_receiving_result`](SentRequest::on_receiving_result) that help you
638/// avoid accidentally blocking the event loop while waiting for responses.
639///
640/// # Driving the Connection
641///
642/// After adding handlers, you must drive the connection using one of two modes:
643///
644/// ## Server Mode: `connect_to()`
645///
646/// Use [`connect_to`](Self::connect_to) when you only need to respond to incoming messages:
647///
648/// ```no_run
649/// # use agent_client_protocol_test::*;
650/// # async fn example() -> Result<(), agent_client_protocol::Error> {
651/// # let connection = mock_connection();
652/// connection
653///     .on_receive_request(async |req: MyRequest, responder, cx| {
654///         responder.respond(MyResponse { status: "ok".into() })
655///     }, agent_client_protocol::on_receive_request!())
656///     .connect_to(MockTransport)  // Runs until connection closes or error occurs
657///     .await?;
658/// # Ok(())
659/// # }
660/// ```
661///
662/// The connection will process incoming messages and invoke your handlers until the
663/// connection is closed or an error occurs.
664///
665/// ## Client Mode: `connect_with()`
666///
667/// Use [`connect_with`](Self::connect_with) when you need to both handle incoming messages
668/// AND send your own requests/notifications:
669///
670/// ```no_run
671/// # use agent_client_protocol_test::*;
672/// # use agent_client_protocol::schema::v1::InitializeRequest;
673/// # async fn example() -> Result<(), agent_client_protocol::Error> {
674/// # let connection = mock_connection();
675/// connection
676///     .on_receive_request(async |req: MyRequest, responder, cx| {
677///         responder.respond(MyResponse { status: "ok".into() })
678///     }, agent_client_protocol::on_receive_request!())
679///     .connect_with(MockTransport, async |cx| {
680///         // You can send requests to the other side
681///         let response = cx.send_request(InitializeRequest::make())
682///             .block_task()
683///             .await?;
684///
685///         // And send notifications
686///         cx.send_notification(StatusUpdate { message: "ready".into() })?;
687///
688///         Ok(())
689///     })
690///     .await?;
691/// # Ok(())
692/// # }
693/// ```
694///
695/// The connection will serve incoming messages in the background while your client closure
696/// runs. When the closure returns, the connection shuts down.
697///
698/// # Example: Complete Agent
699///
700/// ```no_run
701/// # use agent_client_protocol::UntypedRole;
702/// # use agent_client_protocol::{Builder};
703/// # use agent_client_protocol::Stdio;
704/// # use agent_client_protocol::schema::v1::{InitializeRequest, InitializeResponse, PromptRequest, PromptResponse, SessionNotification};
705/// # async fn example() -> Result<(), agent_client_protocol::Error> {
706/// let transport = Stdio::new();
707///
708/// UntypedRole.builder()
709///     .name("my-agent")  // Optional: for debugging logs
710///     .on_receive_request(async |init: InitializeRequest, responder, cx| {
711///         let response: InitializeResponse = todo!();
712///         responder.respond(response)
713///     }, agent_client_protocol::on_receive_request!())
714///     .on_receive_request(async |prompt: PromptRequest, responder, cx| {
715///         // You can send notifications while processing a request
716///         let notif: SessionNotification = todo!();
717///         cx.send_notification(notif)?;
718///
719///         // Then respond to the request
720///         let response: PromptResponse = todo!();
721///         responder.respond(response)
722///     }, agent_client_protocol::on_receive_request!())
723///     .connect_to(transport)
724///     .await?;
725/// # Ok(())
726/// # }
727/// ```
728#[must_use]
729#[derive(Debug)]
730pub struct Builder<Host: Role, Handler = NullHandler, Runner = NullRun>
731where
732    Handler: HandleDispatchFrom<Host::Counterpart>,
733    Runner: RunWithConnectionTo<Host::Counterpart>,
734{
735    /// My role.
736    host: Host,
737
738    /// Name of the connection, used in tracing logs.
739    name: Option<String>,
740
741    /// Handler for incoming messages.
742    handler: Handler,
743
744    /// Responder for background tasks.
745    responder: Runner,
746
747    /// Protocol version mode for the public API and wire compatibility layer.
748    protocol_mode: ProtocolMode,
749}
750
751fn default_protocol_mode<Host: Role>() -> ProtocolMode {
752    let role = TypeId::of::<Host>();
753
754    if role == TypeId::of::<Agent>() {
755        ProtocolMode::v1_agent()
756    } else if role == TypeId::of::<Client>() {
757        ProtocolMode::v1_client()
758    } else {
759        ProtocolMode::disabled()
760    }
761}
762
763impl<Host: Role> Builder<Host, NullHandler, NullRun> {
764    /// Create a new connection builder for the given role.
765    /// This type follows a builder pattern; use other methods to configure and then invoke
766    /// [`Self::connect_to`] (to use as a server) or [`Self::connect_with`] to use as a client.
767    pub fn new(role: Host) -> Self {
768        Self {
769            host: role,
770            name: None,
771            handler: NullHandler,
772            responder: NullRun,
773            protocol_mode: default_protocol_mode::<Host>(),
774        }
775    }
776}
777
778impl<Host: Role, Handler> Builder<Host, Handler, NullRun>
779where
780    Handler: HandleDispatchFrom<Host::Counterpart>,
781{
782    /// Create a new connection builder with the given handler.
783    pub fn new_with(role: Host, handler: Handler) -> Self {
784        Self {
785            host: role,
786            name: None,
787            handler,
788            responder: NullRun,
789            protocol_mode: default_protocol_mode::<Host>(),
790        }
791    }
792}
793
794impl<
795    Host: Role,
796    Handler: HandleDispatchFrom<Host::Counterpart>,
797    Runner: RunWithConnectionTo<Host::Counterpart>,
798> Builder<Host, Handler, Runner>
799{
800    /// Set the "name" of this connection -- used only for debugging logs.
801    pub fn name(mut self, name: impl ToString) -> Self {
802        self.name = Some(name.to_string());
803        self
804    }
805
806    pub(crate) fn v1_agent(mut self) -> Self {
807        self.protocol_mode = ProtocolMode::v1_agent();
808        self
809    }
810
811    pub(crate) fn v1_client(mut self) -> Self {
812        self.protocol_mode = ProtocolMode::v1_client();
813        self
814    }
815
816    #[cfg(feature = "unstable_protocol_v2")]
817    pub(crate) fn v2_agent(mut self) -> Self {
818        self.protocol_mode = ProtocolMode::v2_agent();
819        self
820    }
821
822    #[cfg(feature = "unstable_protocol_v2")]
823    pub(crate) fn v2_client(mut self) -> Self {
824        self.protocol_mode = ProtocolMode::v2_client();
825        self
826    }
827
828    /// Merge another [`Builder`] into this one.
829    ///
830    /// Prefer [`Self::on_receive_request`] or [`Self::on_receive_notification`].
831    /// This is a low-level method that is not intended for general use.
832    pub fn with_connection_builder(
833        self,
834        other: Builder<
835            Host,
836            impl HandleDispatchFrom<Host::Counterpart>,
837            impl RunWithConnectionTo<Host::Counterpart>,
838        >,
839    ) -> Builder<
840        Host,
841        impl HandleDispatchFrom<Host::Counterpart>,
842        impl RunWithConnectionTo<Host::Counterpart>,
843    > {
844        let Builder {
845            name: other_name,
846            handler: other_handler,
847            responder: other_responder,
848            protocol_mode: other_protocol_mode,
849            host: _,
850        } = other;
851        Builder {
852            host: self.host,
853            name: self.name,
854            handler: ChainedHandler::new(
855                self.handler,
856                NamedHandler::new(other_name, other_handler),
857            ),
858            responder: ChainRun::new(self.responder, other_responder),
859            protocol_mode: self.protocol_mode.merge(other_protocol_mode),
860        }
861    }
862
863    /// Add a new [`HandleDispatchFrom`] to the chain.
864    ///
865    /// Prefer [`Self::on_receive_request`] or [`Self::on_receive_notification`].
866    /// This is a low-level method that is not intended for general use.
867    pub fn with_handler(
868        self,
869        handler: impl HandleDispatchFrom<Host::Counterpart>,
870    ) -> Builder<Host, impl HandleDispatchFrom<Host::Counterpart>, Runner> {
871        Builder {
872            host: self.host,
873            name: self.name,
874            handler: ChainedHandler::new(self.handler, handler),
875            responder: self.responder,
876            protocol_mode: self.protocol_mode,
877        }
878    }
879
880    /// Add a new [`RunWithConnectionTo`] to the chain.
881    pub fn with_responder<Run1>(
882        self,
883        responder: Run1,
884    ) -> Builder<Host, Handler, impl RunWithConnectionTo<Host::Counterpart>>
885    where
886        Run1: RunWithConnectionTo<Host::Counterpart>,
887    {
888        Builder {
889            host: self.host,
890            name: self.name,
891            handler: self.handler,
892            responder: ChainRun::new(self.responder, responder),
893            protocol_mode: self.protocol_mode,
894        }
895    }
896
897    /// Enqueue a task to run once the connection is actively serving traffic.
898    #[track_caller]
899    pub fn with_spawned<F, Fut>(
900        self,
901        task: F,
902    ) -> Builder<Host, Handler, impl RunWithConnectionTo<Host::Counterpart>>
903    where
904        F: FnOnce(ConnectionTo<Host::Counterpart>) -> Fut + Send,
905        Fut: Future<Output = Result<(), crate::Error>> + Send,
906    {
907        let location = Location::caller();
908        self.with_responder(SpawnedRun::new(location, task))
909    }
910
911    /// Register a handler for messages that can be either requests OR notifications.
912    ///
913    /// Use this when you want to handle an enum type that contains both request and
914    /// notification variants. Your handler receives a [`Dispatch<Req, Notif>`] which
915    /// is an enum with two variants:
916    ///
917    /// - `Dispatch::Request(request, responder)` - A request with its response context
918    /// - `Dispatch::Notification(notification)` - A notification
919    /// - `Dispatch::Response(result, router)` - A response to a request we sent
920    ///
921    /// # Example
922    ///
923    /// ```no_run
924    /// # use agent_client_protocol_test::*;
925    /// # use agent_client_protocol::Dispatch;
926    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
927    /// # let connection = mock_connection();
928    /// connection.on_receive_dispatch(async |message: Dispatch<MyRequest, StatusUpdate>, _cx| {
929    ///     match message {
930    ///         Dispatch::Request(req, responder) => {
931    ///             // Handle request and send response
932    ///             responder.respond(MyResponse { status: "ok".into() })
933    ///         }
934    ///         Dispatch::Notification(notif) => {
935    ///             // Handle notification (no response needed)
936    ///             Ok(())
937    ///         }
938    ///         Dispatch::Response(result, router) => {
939    ///             // Forward response to its destination
940    ///             router.respond_with_result(result)
941    ///         }
942    ///     }
943    /// }, agent_client_protocol::on_receive_dispatch!())
944    /// # .connect_to(agent_client_protocol_test::MockTransport).await?;
945    /// # Ok(())
946    /// # }
947    /// ```
948    ///
949    /// For most use cases, prefer [`on_receive_request`](Self::on_receive_request) or
950    /// [`on_receive_notification`](Self::on_receive_notification) which provide cleaner APIs
951    /// for handling requests or notifications separately.
952    ///
953    /// # Ordering
954    ///
955    /// This callback runs inside the dispatch loop and blocks further message processing
956    /// until it completes. See the [`ordering`](crate::concepts::ordering) module for details on
957    /// ordering guarantees and how to avoid deadlocks.
958    pub fn on_receive_dispatch<Req, Notif, F, T, ToFut>(
959        self,
960        op: F,
961        to_future_hack: ToFut,
962    ) -> Builder<Host, impl HandleDispatchFrom<Host::Counterpart>, Runner>
963    where
964        Host::Counterpart: HasPeer<Host::Counterpart>,
965        Req: JsonRpcRequest,
966        Notif: JsonRpcNotification,
967        F: AsyncFnMut(
968                Dispatch<Req, Notif>,
969                ConnectionTo<Host::Counterpart>,
970            ) -> Result<T, crate::Error>
971            + Send,
972        T: IntoHandled<Dispatch<Req, Notif>>,
973        ToFut: Fn(
974                &mut F,
975                Dispatch<Req, Notif>,
976                ConnectionTo<Host::Counterpart>,
977            ) -> crate::BoxFuture<'_, Result<T, crate::Error>>
978            + Send
979            + Sync,
980    {
981        let handler = MessageHandler::new(
982            self.host.counterpart(),
983            self.host.counterpart(),
984            op,
985            to_future_hack,
986        );
987        self.with_handler(handler)
988    }
989
990    /// Register a handler for JSON-RPC requests of type `Req`.
991    ///
992    /// Your handler receives two arguments:
993    /// 1. The request (type `Req`)
994    /// 2. A [`Responder<R, Req::Response>`] for sending the response
995    ///
996    /// The request context allows you to:
997    /// - Send the response with [`Responder::respond`]
998    /// - Send notifications to the client with [`ConnectionTo::send_notification`]
999    /// - Send requests to the client with [`ConnectionTo::send_request`]
1000    ///
1001    /// # Example
1002    ///
1003    /// ```ignore
1004    /// # use agent_client_protocol::UntypedRole;
1005    /// # use agent_client_protocol::{Builder};
1006    /// # use agent_client_protocol::schema::v1::{PromptRequest, PromptResponse, SessionNotification};
1007    /// # fn example<R: agent_client_protocol::Role>(connection: Builder<R, impl agent_client_protocol::HandleMessageAs<R>>) {
1008    /// connection.on_receive_request(async |request: PromptRequest, responder, cx| {
1009    ///     // Send a notification while processing
1010    ///     let notif: SessionNotification = todo!();
1011    ///     cx.send_notification(notif)?;
1012    ///
1013    ///     // Do some work...
1014    ///     let result = todo!("process the prompt");
1015    ///
1016    ///     // Send the response
1017    ///     let response: PromptResponse = todo!();
1018    ///     responder.respond(response)
1019    /// }, agent_client_protocol::on_receive_request!());
1020    /// # }
1021    /// ```
1022    ///
1023    /// # Type Parameter
1024    ///
1025    /// `Req` can be either a single request type or an enum of multiple request types.
1026    /// See the [type-driven dispatch](Self#type-driven-message-dispatch) section for details.
1027    ///
1028    /// # Ordering
1029    ///
1030    /// This callback runs inside the dispatch loop and blocks further message processing
1031    /// until it completes. See the [`ordering`](crate::concepts::ordering) module for details on
1032    /// ordering guarantees and how to avoid deadlocks.
1033    pub fn on_receive_request<Req: JsonRpcRequest, F, T, ToFut>(
1034        self,
1035        op: F,
1036        to_future_hack: ToFut,
1037    ) -> Builder<Host, impl HandleDispatchFrom<Host::Counterpart>, Runner>
1038    where
1039        Host::Counterpart: HasPeer<Host::Counterpart>,
1040        F: AsyncFnMut(
1041                Req,
1042                Responder<Req::Response>,
1043                ConnectionTo<Host::Counterpart>,
1044            ) -> Result<T, crate::Error>
1045            + Send,
1046        T: IntoHandled<(Req, Responder<Req::Response>)>,
1047        ToFut: Fn(
1048                &mut F,
1049                Req,
1050                Responder<Req::Response>,
1051                ConnectionTo<Host::Counterpart>,
1052            ) -> crate::BoxFuture<'_, Result<T, crate::Error>>
1053            + Send
1054            + Sync,
1055    {
1056        let handler = RequestHandler::new(
1057            self.host.counterpart(),
1058            self.host.counterpart(),
1059            op,
1060            to_future_hack,
1061        );
1062        self.with_handler(handler)
1063    }
1064
1065    /// Register a handler for JSON-RPC notifications of type `Notif`.
1066    ///
1067    /// Notifications are fire-and-forget messages that don't expect a response.
1068    /// Your handler receives:
1069    /// 1. The notification (type `Notif`)
1070    /// 2. A [`ConnectionTo<R>`] for sending messages to the other side
1071    ///
1072    /// Unlike request handlers, you cannot send a response (notifications don't have IDs),
1073    /// but you can still send your own requests and notifications using the context.
1074    ///
1075    /// # Example
1076    ///
1077    /// ```no_run
1078    /// # use agent_client_protocol_test::*;
1079    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
1080    /// # let connection = mock_connection();
1081    /// connection.on_receive_notification(async |notif: SessionUpdate, cx| {
1082    ///     // Process the notification
1083    ///     update_session_state(&notif)?;
1084    ///
1085    ///     // Optionally send a notification back
1086    ///     cx.send_notification(StatusUpdate {
1087    ///         message: "Acknowledged".into(),
1088    ///     })?;
1089    ///
1090    ///     Ok(())
1091    /// }, agent_client_protocol::on_receive_notification!())
1092    /// # .connect_to(agent_client_protocol_test::MockTransport).await?;
1093    /// # Ok(())
1094    /// # }
1095    /// ```
1096    ///
1097    /// # Type Parameter
1098    ///
1099    /// `Notif` can be either a single notification type or an enum of multiple notification types.
1100    /// See the [type-driven dispatch](Self#type-driven-message-dispatch) section for details.
1101    ///
1102    /// # Ordering
1103    ///
1104    /// This callback runs inside the dispatch loop and blocks further message processing
1105    /// until it completes. See the [`ordering`](crate::concepts::ordering) module for details on
1106    /// ordering guarantees and how to avoid deadlocks.
1107    pub fn on_receive_notification<Notif, F, T, ToFut>(
1108        self,
1109        op: F,
1110        to_future_hack: ToFut,
1111    ) -> Builder<Host, impl HandleDispatchFrom<Host::Counterpart>, Runner>
1112    where
1113        Host::Counterpart: HasPeer<Host::Counterpart>,
1114        Notif: JsonRpcNotification,
1115        F: AsyncFnMut(Notif, ConnectionTo<Host::Counterpart>) -> Result<T, crate::Error> + Send,
1116        T: IntoHandled<(Notif, ConnectionTo<Host::Counterpart>)>,
1117        ToFut: Fn(
1118                &mut F,
1119                Notif,
1120                ConnectionTo<Host::Counterpart>,
1121            ) -> crate::BoxFuture<'_, Result<T, crate::Error>>
1122            + Send
1123            + Sync,
1124    {
1125        let handler = NotificationHandler::new(
1126            self.host.counterpart(),
1127            self.host.counterpart(),
1128            op,
1129            to_future_hack,
1130        );
1131        self.with_handler(handler)
1132    }
1133
1134    /// Register a handler for messages from a specific peer.
1135    ///
1136    /// This is similar to [`on_receive_dispatch`](Self::on_receive_dispatch), but allows
1137    /// specifying the source peer explicitly. This is useful when receiving messages
1138    /// from a peer that requires message transformation (e.g., unwrapping `SuccessorMessage`
1139    /// envelopes when receiving from an agent via a proxy).
1140    ///
1141    /// For the common case of receiving from the default counterpart, use
1142    /// [`on_receive_dispatch`](Self::on_receive_dispatch) instead.
1143    ///
1144    /// # Ordering
1145    ///
1146    /// This callback runs inside the dispatch loop and blocks further message processing
1147    /// until it completes. See the [`ordering`](crate::concepts::ordering) module for details on
1148    /// ordering guarantees and how to avoid deadlocks.
1149    pub fn on_receive_dispatch_from<
1150        Req: JsonRpcRequest,
1151        Notif: JsonRpcNotification,
1152        Peer: Role,
1153        F,
1154        T,
1155        ToFut,
1156    >(
1157        self,
1158        peer: Peer,
1159        op: F,
1160        to_future_hack: ToFut,
1161    ) -> Builder<Host, impl HandleDispatchFrom<Host::Counterpart>, Runner>
1162    where
1163        Host::Counterpart: HasPeer<Peer>,
1164        F: AsyncFnMut(
1165                Dispatch<Req, Notif>,
1166                ConnectionTo<Host::Counterpart>,
1167            ) -> Result<T, crate::Error>
1168            + Send,
1169        T: IntoHandled<Dispatch<Req, Notif>>,
1170        ToFut: Fn(
1171                &mut F,
1172                Dispatch<Req, Notif>,
1173                ConnectionTo<Host::Counterpart>,
1174            ) -> crate::BoxFuture<'_, Result<T, crate::Error>>
1175            + Send
1176            + Sync,
1177    {
1178        let handler = MessageHandler::new(self.host.counterpart(), peer, op, to_future_hack);
1179        self.with_handler(handler)
1180    }
1181
1182    /// Register a handler for JSON-RPC requests from a specific peer.
1183    ///
1184    /// This is similar to [`on_receive_request`](Self::on_receive_request), but allows
1185    /// specifying the source peer explicitly. This is useful when receiving messages
1186    /// from a peer that requires message transformation (e.g., unwrapping `SuccessorRequest`
1187    /// envelopes when receiving from an agent via a proxy).
1188    ///
1189    /// For the common case of receiving from the default counterpart, use
1190    /// [`on_receive_request`](Self::on_receive_request) instead.
1191    ///
1192    /// # Example
1193    ///
1194    /// ```ignore
1195    /// use agent_client_protocol::Agent;
1196    /// use agent_client_protocol::schema::v1::InitializeRequest;
1197    ///
1198    /// // Conductor receiving from agent direction - messages will be unwrapped from SuccessorMessage
1199    /// connection.on_receive_request_from(Agent, async |req: InitializeRequest, responder, cx| {
1200    ///     // Handle the request
1201    ///     responder.respond(InitializeResponse::make())
1202    /// })
1203    /// ```
1204    ///
1205    /// # Ordering
1206    ///
1207    /// This callback runs inside the dispatch loop and blocks further message processing
1208    /// until it completes. See the [`ordering`](crate::concepts::ordering) module for details on
1209    /// ordering guarantees and how to avoid deadlocks.
1210    pub fn on_receive_request_from<Req: JsonRpcRequest, Peer: Role, F, T, ToFut>(
1211        self,
1212        peer: Peer,
1213        op: F,
1214        to_future_hack: ToFut,
1215    ) -> Builder<Host, impl HandleDispatchFrom<Host::Counterpart>, Runner>
1216    where
1217        Host::Counterpart: HasPeer<Peer>,
1218        F: AsyncFnMut(
1219                Req,
1220                Responder<Req::Response>,
1221                ConnectionTo<Host::Counterpart>,
1222            ) -> Result<T, crate::Error>
1223            + Send,
1224        T: IntoHandled<(Req, Responder<Req::Response>)>,
1225        ToFut: Fn(
1226                &mut F,
1227                Req,
1228                Responder<Req::Response>,
1229                ConnectionTo<Host::Counterpart>,
1230            ) -> crate::BoxFuture<'_, Result<T, crate::Error>>
1231            + Send
1232            + Sync,
1233    {
1234        let handler = RequestHandler::new(self.host.counterpart(), peer, op, to_future_hack);
1235        self.with_handler(handler)
1236    }
1237
1238    /// Register a handler for JSON-RPC notifications from a specific peer.
1239    ///
1240    /// This is similar to [`on_receive_notification`](Self::on_receive_notification), but allows
1241    /// specifying the source peer explicitly. This is useful when receiving messages
1242    /// from a peer that requires message transformation (e.g., unwrapping `SuccessorNotification`
1243    /// envelopes when receiving from an agent via a proxy).
1244    ///
1245    /// For the common case of receiving from the default counterpart, use
1246    /// [`on_receive_notification`](Self::on_receive_notification) instead.
1247    ///
1248    /// # Ordering
1249    ///
1250    /// This callback runs inside the dispatch loop and blocks further message processing
1251    /// until it completes. See the [`ordering`](crate::concepts::ordering) module for details on
1252    /// ordering guarantees and how to avoid deadlocks.
1253    pub fn on_receive_notification_from<Notif: JsonRpcNotification, Peer: Role, F, T, ToFut>(
1254        self,
1255        peer: Peer,
1256        op: F,
1257        to_future_hack: ToFut,
1258    ) -> Builder<Host, impl HandleDispatchFrom<Host::Counterpart>, Runner>
1259    where
1260        Host::Counterpart: HasPeer<Peer>,
1261        F: AsyncFnMut(Notif, ConnectionTo<Host::Counterpart>) -> Result<T, crate::Error> + Send,
1262        T: IntoHandled<(Notif, ConnectionTo<Host::Counterpart>)>,
1263        ToFut: Fn(
1264                &mut F,
1265                Notif,
1266                ConnectionTo<Host::Counterpart>,
1267            ) -> crate::BoxFuture<'_, Result<T, crate::Error>>
1268            + Send
1269            + Sync,
1270    {
1271        let handler = NotificationHandler::new(self.host.counterpart(), peer, op, to_future_hack);
1272        self.with_handler(handler)
1273    }
1274
1275    /// Add an MCP server that will be added to all new sessions that are proxied through this connection.
1276    ///
1277    /// Only applicable to proxies.
1278    pub fn with_mcp_server(
1279        self,
1280        mcp_server: McpServer<Host::Counterpart, impl RunWithConnectionTo<Host::Counterpart>>,
1281    ) -> Builder<
1282        Host,
1283        impl HandleDispatchFrom<Host::Counterpart>,
1284        impl RunWithConnectionTo<Host::Counterpart>,
1285    >
1286    where
1287        Host::Counterpart: HasPeer<Agent> + HasPeer<Client>,
1288    {
1289        let (handler, responder) = mcp_server.into_handler_and_responder();
1290        self.with_handler(handler).with_responder(responder)
1291    }
1292
1293    /// Run in server mode with the provided transport.
1294    ///
1295    /// This drives the connection by continuously processing messages from the transport
1296    /// and dispatching them to your registered handlers. The connection will run until:
1297    /// - The transport closes (e.g., EOF on byte streams)
1298    /// - An error occurs
1299    /// - One of your handlers returns an error
1300    ///
1301    /// The transport is responsible for serializing and deserializing [`RawJsonRpcMessage`]
1302    /// values to/from the underlying I/O mechanism (byte streams, channels, etc.).
1303    ///
1304    /// Use this mode when you only need to respond to incoming messages and don't need
1305    /// to initiate your own requests. If you need to send requests to the other side,
1306    /// use [`connect_with`](Self::connect_with) instead.
1307    ///
1308    /// # Example: Byte Stream Transport
1309    ///
1310    /// ```no_run
1311    /// # use agent_client_protocol::UntypedRole;
1312    /// # use agent_client_protocol::{Builder};
1313    /// # use agent_client_protocol::Stdio;
1314    /// # use agent_client_protocol_test::*;
1315    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
1316    /// let transport = Stdio::new();
1317    ///
1318    /// UntypedRole.builder()
1319    ///     .on_receive_request(async |req: MyRequest, responder, cx| {
1320    ///         responder.respond(MyResponse { status: "ok".into() })
1321    ///     }, agent_client_protocol::on_receive_request!())
1322    ///     .connect_to(transport)
1323    ///     .await?;
1324    /// # Ok(())
1325    /// # }
1326    /// ```
1327    pub async fn connect_to(
1328        self,
1329        transport: impl ConnectTo<Host> + 'static,
1330    ) -> Result<(), crate::Error> {
1331        self.connect_with(transport, async move |_cx| future::pending().await)
1332            .await
1333    }
1334
1335    /// Run the connection until the provided closure completes.
1336    ///
1337    /// This drives the connection by:
1338    /// 1. Running your registered handlers in the background to process incoming messages
1339    /// 2. Executing your `main_fn` closure with a [`ConnectionTo<R>`] for sending requests/notifications
1340    ///
1341    /// The connection stays active until your `main_fn` returns, then shuts down gracefully.
1342    /// If the connection closes unexpectedly before `main_fn` completes, this returns an error.
1343    ///
1344    /// Use this mode when you need to initiate communication (send requests/notifications)
1345    /// in addition to responding to incoming messages. For server-only mode where you just
1346    /// respond to messages, use [`connect_to`](Self::connect_to) instead.
1347    ///
1348    /// # Example
1349    ///
1350    /// ```no_run
1351    /// # use agent_client_protocol::UntypedRole;
1352    /// # use agent_client_protocol::{Builder};
1353    /// # use agent_client_protocol::ByteStreams;
1354    /// # use agent_client_protocol::schema::v1::InitializeRequest;
1355    /// # use agent_client_protocol::Stdio;
1356    /// # use agent_client_protocol_test::*;
1357    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
1358    /// let transport = Stdio::new();
1359    ///
1360    /// UntypedRole.builder()
1361    ///     .on_receive_request(async |req: MyRequest, responder, cx| {
1362    ///         // Handle incoming requests in the background
1363    ///         responder.respond(MyResponse { status: "ok".into() })
1364    ///     }, agent_client_protocol::on_receive_request!())
1365    ///     .connect_with(transport, async |cx| {
1366    ///         // Initialize the protocol
1367    ///         let init_response = cx.send_request(InitializeRequest::make())
1368    ///             .block_task()
1369    ///             .await?;
1370    ///
1371    ///         // Send more requests...
1372    ///         let result = cx.send_request(MyRequest {})
1373    ///             .block_task()
1374    ///             .await?;
1375    ///
1376    ///         // When this closure returns, the connection shuts down
1377    ///         Ok(())
1378    ///     })
1379    ///     .await?;
1380    /// # Ok(())
1381    /// # }
1382    /// ```
1383    ///
1384    /// # Parameters
1385    ///
1386    /// - `main_fn`: Your client logic. Receives a [`ConnectionTo<R>`] for sending messages.
1387    ///
1388    /// # Errors
1389    ///
1390    /// Returns an error if the connection closes before `main_fn` completes.
1391    pub async fn connect_with<R>(
1392        self,
1393        transport: impl ConnectTo<Host> + 'static,
1394        main_fn: impl AsyncFnOnce(ConnectionTo<Host::Counterpart>) -> Result<R, crate::Error>,
1395    ) -> Result<R, crate::Error> {
1396        let (_, future) = self.into_connection_and_future(transport, main_fn);
1397        future.await
1398    }
1399
1400    /// Helper that returns a [`ConnectionTo<R>`] and a future that runs this connection until `main_fn` returns.
1401    fn into_connection_and_future<R>(
1402        self,
1403        transport: impl ConnectTo<Host> + 'static,
1404        main_fn: impl AsyncFnOnce(ConnectionTo<Host::Counterpart>) -> Result<R, crate::Error>,
1405    ) -> (
1406        ConnectionTo<Host::Counterpart>,
1407        impl Future<Output = Result<R, crate::Error>>,
1408    ) {
1409        let Self {
1410            name,
1411            handler,
1412            responder,
1413            host: me,
1414            protocol_mode,
1415        } = self;
1416
1417        let (outgoing_tx, outgoing_rx) = mpsc::unbounded();
1418        let (new_task_tx, new_task_rx) = mpsc::unbounded();
1419        let (dynamic_handler_tx, dynamic_handler_rx) = mpsc::unbounded();
1420        let connection = ConnectionTo::new(
1421            me.counterpart(),
1422            outgoing_tx,
1423            new_task_tx,
1424            dynamic_handler_tx,
1425        );
1426
1427        // Convert transport into server - this returns a channel for us to use
1428        // and a future that runs the transport
1429        let transport_component = crate::DynConnectTo::new(transport);
1430        let (transport_channel, transport_future) = transport_component.into_channel_and_future();
1431        let spawn_result = connection.spawn(transport_future);
1432
1433        // Destructure the channel endpoints
1434        let Channel {
1435            rx: transport_incoming_rx,
1436            tx: transport_outgoing_tx,
1437        } = transport_channel;
1438
1439        let (reply_tx, reply_rx) = mpsc::unbounded();
1440        let protocol_compat = ProtocolCompat::new(protocol_mode);
1441
1442        let future = crate::util::instrument_with_connection_name(name, {
1443            let connection = connection.clone();
1444            async move {
1445                let () = spawn_result?;
1446
1447                let background = async {
1448                    futures::try_join!(
1449                        // Protocol layer: OutgoingMessage -> RawJsonRpcMessage
1450                        outgoing_actor::outgoing_protocol_actor(
1451                            outgoing_rx,
1452                            reply_tx.clone(),
1453                            transport_outgoing_tx,
1454                            protocol_compat.clone(),
1455                        ),
1456                        // Protocol layer: RawJsonRpcMessage -> handler/reply routing
1457                        incoming_actor::incoming_protocol_actor(
1458                            me.counterpart(),
1459                            &connection,
1460                            transport_incoming_rx,
1461                            dynamic_handler_rx,
1462                            reply_rx,
1463                            handler,
1464                            protocol_compat,
1465                        ),
1466                        task_actor::task_actor(new_task_rx, &connection),
1467                        responder.run_with_connection_to(connection.clone()),
1468                    )?;
1469                    Ok(())
1470                };
1471
1472                crate::util::run_until(Box::pin(background), Box::pin(main_fn(connection.clone())))
1473                    .await
1474            }
1475        });
1476
1477        (connection, future)
1478    }
1479}
1480
1481impl<R, H, Run> ConnectTo<R::Counterpart> for Builder<R, H, Run>
1482where
1483    R: Role,
1484    H: HandleDispatchFrom<R::Counterpart> + 'static,
1485    Run: RunWithConnectionTo<R::Counterpart> + 'static,
1486{
1487    async fn connect_to(self, client: impl ConnectTo<R>) -> Result<(), crate::Error> {
1488        Builder::connect_to(self, client).await
1489    }
1490}
1491
1492/// The payload sent through the response oneshot channel.
1493///
1494/// Includes the response value and an optional ack channel for dispatch loop
1495/// synchronization.
1496pub(crate) struct ResponsePayload {
1497    /// The response result - either the JSON value or an error.
1498    pub(crate) result: Result<serde_json::Value, crate::Error>,
1499
1500    /// Optional acknowledgment channel for dispatch loop synchronization.
1501    ///
1502    /// When present, the receiver must send on this channel to signal that
1503    /// response processing is complete, allowing the dispatch loop to continue
1504    /// to the next message.
1505    ///
1506    /// This is `None` for error paths where the response is sent directly
1507    /// (e.g., when the outgoing channel is broken) rather than through the
1508    /// normal dispatch loop flow.
1509    pub(crate) ack_tx: Option<oneshot::Sender<()>>,
1510}
1511
1512impl std::fmt::Debug for ResponsePayload {
1513    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1514        f.debug_struct("ResponsePayload")
1515            .field("result", &self.result)
1516            .field("ack_tx", &self.ack_tx.as_ref().map(|_| "..."))
1517            .finish()
1518    }
1519}
1520
1521/// Message sent to the incoming actor for reply subscription management.
1522enum ReplyMessage {
1523    /// Subscribe to receive a response for the given request id.
1524    /// When a response with this id arrives, it will be sent through the oneshot
1525    /// along with an ack channel that must be signaled when processing is complete.
1526    /// The method name is stored to allow routing responses through typed handlers.
1527    Subscribe {
1528        id: RequestId,
1529
1530        /// id of the peer this request was sent to
1531        role_id: RoleId,
1532
1533        /// (original) method of the request -- the actual request may have been transformed
1534        /// to a successor method, but this will reflect the method of the wrapped request
1535        method: String,
1536
1537        sender: oneshot::Sender<ResponsePayload>,
1538
1539        #[cfg(feature = "unstable_cancel_request")]
1540        cancellation_disarm: SentRequestCancellationDisarm,
1541    },
1542}
1543
1544impl std::fmt::Debug for ReplyMessage {
1545    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1546        match self {
1547            ReplyMessage::Subscribe { id, method, .. } => f
1548                .debug_struct("Subscribe")
1549                .field("id", id)
1550                .field("method", method)
1551                .finish(),
1552        }
1553    }
1554}
1555
1556/// A request-local marker that is set when the peer asks to cancel the request.
1557///
1558/// Request handlers can get this handle from [`Responder::cancellation`] and
1559/// use it from spawned work to stop long-running request processing
1560/// cooperatively.
1561#[cfg(feature = "unstable_cancel_request")]
1562#[derive(Clone)]
1563pub struct RequestCancellation {
1564    state: Arc<RequestCancellationState>,
1565}
1566
1567#[cfg(feature = "unstable_cancel_request")]
1568struct RequestCancellationState {
1569    cancelled: AtomicBool,
1570    signal_tx: Mutex<Option<oneshot::Sender<()>>>,
1571    signal_rx: future::Shared<BoxFuture<'static, ()>>,
1572}
1573
1574#[cfg(feature = "unstable_cancel_request")]
1575impl RequestCancellation {
1576    fn new() -> Self {
1577        let (signal_tx, signal_rx) = oneshot::channel();
1578        let signal_rx = signal_rx.map(|_| ()).boxed().shared();
1579        Self {
1580            state: Arc::new(RequestCancellationState {
1581                cancelled: AtomicBool::new(false),
1582                signal_tx: Mutex::new(Some(signal_tx)),
1583                signal_rx,
1584            }),
1585        }
1586    }
1587
1588    /// Wait until the peer sends `$/cancel_request` for this request.
1589    ///
1590    /// If cancellation was already requested, this returns immediately.
1591    pub async fn cancelled(&self) {
1592        self.state.signal_rx.clone().await;
1593    }
1594
1595    /// Run request work until it completes or the peer asks to cancel it.
1596    ///
1597    /// If cancellation is requested first, this returns
1598    /// [`Error::request_cancelled`]. This is a convenience for request handlers
1599    /// that want to respond with the normal result or the standard
1600    /// cancellation error.
1601    ///
1602    /// When cancellation wins, `future` is dropped: work stops at its next
1603    /// await point, partial results are lost, and any cleanup must happen in
1604    /// `Drop` implementations. Handlers that need to flush partial results or
1605    /// run async cleanup should instead watch [`cancelled`](Self::cancelled)
1606    /// or poll [`is_cancelled`](Self::is_cancelled) from inside the work.
1607    ///
1608    /// [`Error::request_cancelled`]: crate::Error::request_cancelled
1609    pub async fn run_until_cancelled<T>(
1610        &self,
1611        future: impl std::future::Future<Output = Result<T, crate::Error>>,
1612    ) -> Result<T, crate::Error> {
1613        if self.is_cancelled() {
1614            return Err(crate::Error::request_cancelled());
1615        }
1616
1617        match future::select(pin!(future), pin!(self.cancelled())).await {
1618            Either::Left((result, _)) => result,
1619            Either::Right(((), _)) => Err(crate::Error::request_cancelled()),
1620        }
1621    }
1622
1623    /// Returns whether the peer has already requested cancellation.
1624    #[must_use]
1625    pub fn is_cancelled(&self) -> bool {
1626        self.state.cancelled.load(Ordering::Acquire)
1627    }
1628
1629    fn cancel(&self) {
1630        if self.state.cancelled.swap(true, Ordering::AcqRel) {
1631            return;
1632        }
1633
1634        let signal_tx = self
1635            .state
1636            .signal_tx
1637            .lock()
1638            .expect("request cancellation signal mutex poisoned")
1639            .take();
1640
1641        // Complete the oneshot outside the lock: it wakes waiters, and
1642        // arbitrary waker code must not observe the lock held.
1643        if let Some(signal_tx) = signal_tx {
1644            let _ = signal_tx.send(());
1645        }
1646    }
1647}
1648
1649#[cfg(feature = "unstable_cancel_request")]
1650impl Debug for RequestCancellation {
1651    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1652        formatter
1653            .debug_struct("RequestCancellation")
1654            .field("is_cancelled", &self.is_cancelled())
1655            .finish_non_exhaustive()
1656    }
1657}
1658
1659/// Per-request cancellation state tracked by [`RequestCancellationRegistry`].
1660///
1661/// The full [`RequestCancellation`] marker (with its wakeup machinery) is only
1662/// allocated once a handler asks for it via [`Responder::cancellation`]; until
1663/// then an incoming `$/cancel_request` just flips the entry to `Cancelled`.
1664/// This keeps the per-request cost of the registry to a single map entry.
1665#[cfg(feature = "unstable_cancel_request")]
1666#[derive(Debug)]
1667enum RequestCancellationEntry {
1668    /// The request is in flight; no marker handed out, no cancellation yet.
1669    Armed,
1670    /// `$/cancel_request` arrived before a marker was handed out.
1671    Cancelled,
1672    /// A marker was handed out via [`Responder::cancellation`].
1673    Marker(RequestCancellation),
1674}
1675
1676/// A registered request's cancellation state, tagged with the generation of
1677/// its registration.
1678///
1679/// The generation distinguishes a registration from earlier ones that used
1680/// the same request ID, so that when a (protocol-violating) peer reuses the
1681/// ID of a request that is still in flight, the stale request's responder can
1682/// neither remove nor observe the cancellation state of the newer request.
1683#[cfg(feature = "unstable_cancel_request")]
1684#[derive(Debug)]
1685struct RequestCancellationSlot {
1686    generation: u64,
1687    entry: RequestCancellationEntry,
1688}
1689
1690#[cfg(feature = "unstable_cancel_request")]
1691#[derive(Debug, Default)]
1692struct RequestCancellationRegistryInner {
1693    slots: HashMap<RequestId, RequestCancellationSlot>,
1694    next_generation: u64,
1695}
1696
1697#[cfg(feature = "unstable_cancel_request")]
1698#[derive(Clone, Debug, Default)]
1699struct RequestCancellationRegistry {
1700    inner: Arc<Mutex<RequestCancellationRegistryInner>>,
1701}
1702
1703#[cfg(not(feature = "unstable_cancel_request"))]
1704#[derive(Clone, Debug, Default)]
1705struct RequestCancellationRegistry;
1706
1707#[cfg(feature = "unstable_cancel_request")]
1708#[derive(Debug)]
1709struct ResponderCancellation {
1710    id: RequestId,
1711    generation: u64,
1712    registry: RequestCancellationRegistry,
1713}
1714
1715#[cfg(not(feature = "unstable_cancel_request"))]
1716#[derive(Debug)]
1717struct ResponderCancellation;
1718
1719#[cfg(feature = "unstable_cancel_request")]
1720impl RequestCancellationRegistry {
1721    fn new() -> Self {
1722        Self::default()
1723    }
1724
1725    fn register(&self, id: &RequestId) -> ResponderCancellation {
1726        let generation = {
1727            let mut inner = self
1728                .inner
1729                .lock()
1730                .expect("request cancellation registry mutex poisoned");
1731            let generation = inner.next_generation;
1732            inner.next_generation += 1;
1733            if inner
1734                .slots
1735                .insert(
1736                    id.clone(),
1737                    RequestCancellationSlot {
1738                        generation,
1739                        entry: RequestCancellationEntry::Armed,
1740                    },
1741                )
1742                .is_some()
1743            {
1744                tracing::debug!(
1745                    ?id,
1746                    "peer reused the ID of a request that is still in flight"
1747                );
1748            }
1749            generation
1750        };
1751        ResponderCancellation {
1752            id: id.clone(),
1753            generation,
1754            registry: self.clone(),
1755        }
1756    }
1757
1758    /// Get the cancellation marker for a registered request, creating it on
1759    /// first use. Repeated calls return markers that share the same state.
1760    ///
1761    /// Exception: when the registration is stale (a protocol-violating peer
1762    /// reused this request ID and the slot now belongs to a newer request, or
1763    /// was already removed by it), every call returns a fresh *detached*
1764    /// marker. Detached markers can never fire, and detached markers from
1765    /// repeated calls do not share state with each other.
1766    fn marker(&self, id: &RequestId, generation: u64) -> RequestCancellation {
1767        let mut inner = self
1768            .inner
1769            .lock()
1770            .expect("request cancellation registry mutex poisoned");
1771        let Some(slot) = inner.slots.get_mut(id) else {
1772            // The slot lives as long as the responder that owns it, so this
1773            // is only reachable if the peer reused this request ID and the
1774            // newer request's responder already removed the replacement slot.
1775            // Hand out a detached marker rather than panicking.
1776            return RequestCancellation::new();
1777        };
1778        if slot.generation != generation {
1779            // The peer reused this request ID while the request was still in
1780            // flight, and the slot now belongs to the newer request. Hand the
1781            // stale responder a detached marker instead of cross-wiring the
1782            // two requests' cancellation states.
1783            return RequestCancellation::new();
1784        }
1785        let entry = &mut slot.entry;
1786        match entry {
1787            RequestCancellationEntry::Marker(marker) => marker.clone(),
1788            RequestCancellationEntry::Armed => {
1789                let marker = RequestCancellation::new();
1790                *entry = RequestCancellationEntry::Marker(marker.clone());
1791                marker
1792            }
1793            RequestCancellationEntry::Cancelled => {
1794                // No one can be waiting on a marker that did not exist yet,
1795                // so firing it while holding the registry lock is fine.
1796                let marker = RequestCancellation::new();
1797                marker.cancel();
1798                *entry = RequestCancellationEntry::Marker(marker.clone());
1799                marker
1800            }
1801        }
1802    }
1803
1804    fn cancel_if_requested(&self, dispatch: &Dispatch) -> Result<bool, crate::Error> {
1805        let Some(request_id) = cancellation_request_id(dispatch)? else {
1806            return Ok(false);
1807        };
1808        Ok(self.cancel(&request_id))
1809    }
1810
1811    /// Mark whichever request currently owns `request_id` as cancelled.
1812    fn cancel(&self, request_id: &RequestId) -> bool {
1813        let marker = {
1814            let mut inner = self
1815                .inner
1816                .lock()
1817                .expect("request cancellation registry mutex poisoned");
1818            let Some(slot) = inner.slots.get_mut(request_id) else {
1819                return false;
1820            };
1821            let entry = &mut slot.entry;
1822            match entry {
1823                RequestCancellationEntry::Marker(marker) => marker.clone(),
1824                RequestCancellationEntry::Cancelled => return true,
1825                RequestCancellationEntry::Armed => {
1826                    *entry = RequestCancellationEntry::Cancelled;
1827                    return true;
1828                }
1829            }
1830        };
1831
1832        // Fire the marker outside the registry lock: waking waiters runs
1833        // arbitrary waker code that must not observe the lock held.
1834        marker.cancel();
1835        true
1836    }
1837
1838    /// Remove the slot for `request_id`, but only if it still belongs to the
1839    /// registration identified by `generation`.
1840    fn remove(&self, request_id: &RequestId, generation: u64) {
1841        let mut inner = self
1842            .inner
1843            .lock()
1844            .expect("request cancellation registry mutex poisoned");
1845        if inner
1846            .slots
1847            .get(request_id)
1848            .is_some_and(|slot| slot.generation == generation)
1849        {
1850            inner.slots.remove(request_id);
1851        }
1852    }
1853}
1854
1855#[cfg(not(feature = "unstable_cancel_request"))]
1856impl RequestCancellationRegistry {
1857    fn new() -> Self {
1858        Self
1859    }
1860
1861    #[expect(
1862        clippy::unused_self,
1863        reason = "feature-disabled stub mirrors the real registry API"
1864    )]
1865    fn register(&self, _id: &RequestId) -> ResponderCancellation {
1866        ResponderCancellation
1867    }
1868
1869    #[expect(
1870        clippy::unused_self,
1871        clippy::unnecessary_wraps,
1872        reason = "feature-disabled stub mirrors the real registry API"
1873    )]
1874    fn cancel_if_requested(&self, _dispatch: &Dispatch) -> Result<bool, crate::Error> {
1875        Ok(false)
1876    }
1877}
1878
1879#[cfg(feature = "unstable_cancel_request")]
1880impl ResponderCancellation {
1881    fn cancellation(&self) -> RequestCancellation {
1882        self.registry.marker(&self.id, self.generation)
1883    }
1884}
1885
1886#[cfg(feature = "unstable_cancel_request")]
1887impl Drop for ResponderCancellation {
1888    fn drop(&mut self) {
1889        self.registry.remove(&self.id, self.generation);
1890    }
1891}
1892
1893#[cfg(feature = "unstable_cancel_request")]
1894fn cancellation_request_id(dispatch: &Dispatch) -> Result<Option<RequestId>, crate::Error> {
1895    let Dispatch::Notification(message) = dispatch else {
1896        return Ok(None);
1897    };
1898    cancellation_request_id_from_message(message)
1899}
1900
1901#[cfg(feature = "unstable_cancel_request")]
1902fn cancellation_request_id_from_message(
1903    message: &UntypedMessage,
1904) -> Result<Option<RequestId>, crate::Error> {
1905    let (method, params) = peel_successor_envelopes(&message.method, &message.params);
1906    if !crate::schema::v1::CancelRequestNotification::matches_method(method) {
1907        return Ok(None);
1908    }
1909
1910    let notification = crate::schema::v1::CancelRequestNotification::parse_message(method, params)?;
1911    Ok(Some(notification.request_id))
1912}
1913
1914/// Peel any [`SuccessorMessage`] envelopes off a notification by reference,
1915/// returning the innermost method and params.
1916///
1917/// This only peeks at the envelope's `method`/`params` fields instead of
1918/// deserializing the envelope, for two reasons:
1919///
1920/// - It avoids deep-cloning the params of every wrapped notification on the
1921///   hot dispatch path just to inspect the inner method name.
1922/// - It is deliberately lenient: a malformed envelope is left as-is here and
1923///   flows on to the handler chain, which is responsible for reporting it.
1924///
1925/// [`SuccessorMessage`]: crate::schema::SuccessorMessage
1926#[cfg(feature = "unstable_cancel_request")]
1927fn peel_successor_envelopes<'message>(
1928    mut method: &'message str,
1929    mut params: &'message serde_json::Value,
1930) -> (&'message str, &'message serde_json::Value) {
1931    while crate::schema::SuccessorMessage::<UntypedMessage>::matches_method(method) {
1932        let Some(inner_method) = params.get("method").and_then(serde_json::Value::as_str) else {
1933            break;
1934        };
1935        method = inner_method;
1936        params = params.get("params").unwrap_or(&serde_json::Value::Null);
1937    }
1938    (method, params)
1939}
1940
1941/// Whether a notification is a `$/cancel_request`, even when it is still
1942/// wrapped in `_proxy/successor` envelopes.
1943///
1944/// `$/cancel_request` is connection-scoped: its `requestId` was allocated on
1945/// the connection the notification arrived over and means nothing on any
1946/// other connection. Generic forwarding code (such as
1947/// [`ConnectionTo::send_proxied_message_to`]) uses this check to drop the raw
1948/// notification instead of tunneling it across a hop; the cancellation still
1949/// propagates because [`forward_response_to`](SentRequest::forward_response_to)
1950/// re-issues it with the forwarded request's own ID.
1951///
1952/// Checking a notification whose method is not the successor envelope is a
1953/// plain method-name comparison. Only successor-wrapped notifications pay for
1954/// a serialization to peel the envelope.
1955#[cfg(feature = "unstable_cancel_request")]
1956#[must_use]
1957pub fn is_cancel_request_notification<N: JsonRpcNotification>(notification: &N) -> bool {
1958    let method = notification.method();
1959    if crate::schema::v1::CancelRequestNotification::matches_method(method) {
1960        return true;
1961    }
1962    if !crate::schema::SuccessorMessage::<UntypedMessage>::matches_method(method) {
1963        return false;
1964    }
1965
1966    match notification.to_untyped_message() {
1967        Ok(untyped) => {
1968            let (method, _params) = peel_successor_envelopes(&untyped.method, &untyped.params);
1969            crate::schema::v1::CancelRequestNotification::matches_method(method)
1970        }
1971        Err(error) => {
1972            tracing::debug!(
1973                ?error,
1974                "failed to inspect successor-wrapped notification for cancellation"
1975            );
1976            false
1977        }
1978    }
1979}
1980
1981/// Messages send to be serialized over the transport.
1982#[derive(Debug)]
1983enum OutgoingMessage {
1984    /// Send a request to the server.
1985    Request {
1986        /// id assigned to this request (generated by sender)
1987        id: RequestId,
1988
1989        /// the original method
1990        method: String,
1991
1992        /// the peer we sent this to
1993        role_id: RoleId,
1994
1995        /// the message to send; this may have a distinct method
1996        /// depending on the peer
1997        untyped: UntypedMessage,
1998
1999        /// where to send the response when it arrives (includes ack channel)
2000        response_tx: oneshot::Sender<ResponsePayload>,
2001
2002        #[cfg(feature = "unstable_cancel_request")]
2003        cancellation_disarm: SentRequestCancellationDisarm,
2004    },
2005
2006    /// Send a notification to the server.
2007    Notification {
2008        /// the message to send; this may have a distinct method
2009        /// depending on the peer
2010        untyped: UntypedMessage,
2011    },
2012
2013    /// Send a response to a message from the server
2014    Response {
2015        id: RequestId,
2016
2017        /// Method of the incoming request this response completes.
2018        method: String,
2019
2020        response: Result<serde_json::Value, crate::Error>,
2021    },
2022
2023    /// Send a generalized error message
2024    Error { error: crate::Error },
2025}
2026
2027/// Return type from JrHandler; indicates whether the request was handled or not.
2028#[must_use]
2029#[derive(Debug)]
2030pub enum Handled<T> {
2031    /// The message was handled
2032    Yes,
2033
2034    /// The message was not handled; returns the original value.
2035    ///
2036    /// If `retry` is true,
2037    No {
2038        /// The message to be passed to subsequent handlers
2039        /// (typically the original message, but it may have been
2040        /// mutated.)
2041        message: T,
2042
2043        /// If true, request the message to be queued and retried with
2044        /// dynamic handlers as they are added.
2045        ///
2046        /// This is used for managing session updates since the dynamic
2047        /// handler for a session cannot be added until the response to the
2048        /// new session request has been processed and there may be updates
2049        /// that get processed at the same time.
2050        retry: bool,
2051    },
2052}
2053
2054/// Trait for converting handler return values into [`Handled`].
2055///
2056/// This trait allows handlers to return either `()` (which becomes `Handled::Yes`)
2057/// or an explicit `Handled<T>` value for more control over handler propagation.
2058pub trait IntoHandled<T> {
2059    /// Convert this value into a `Handled<T>`.
2060    fn into_handled(self) -> Handled<T>;
2061}
2062
2063impl<T> IntoHandled<T> for () {
2064    fn into_handled(self) -> Handled<T> {
2065        Handled::Yes
2066    }
2067}
2068
2069impl<T> IntoHandled<T> for Handled<T> {
2070    fn into_handled(self) -> Handled<T> {
2071        self
2072    }
2073}
2074
2075/// Connection context for sending messages and spawning tasks.
2076///
2077/// This is the primary handle for interacting with the JSON-RPC connection from
2078/// within handler callbacks. You can use it to:
2079///
2080/// * Send requests and notifications to the other side
2081/// * Spawn concurrent tasks that run alongside the connection
2082/// * Respond to requests (via [`Responder`] which wraps this)
2083///
2084/// # Cloning
2085///
2086/// `ConnectionTo` is cheaply cloneable - all clones refer to the same underlying connection.
2087/// This makes it easy to share across async tasks.
2088///
2089/// # Event Loop and Concurrency
2090///
2091/// Handler callbacks run on the event loop, which means the connection cannot process new
2092/// messages while your handler is running. Use [`spawn`](Self::spawn) to offload any
2093/// expensive or blocking work to concurrent tasks.
2094///
2095/// See the [Event Loop and Concurrency](Builder#event-loop-and-concurrency) section
2096/// for more details.
2097#[derive(Clone, Debug)]
2098pub struct ConnectionTo<Counterpart: Role> {
2099    counterpart: Counterpart,
2100    message_tx: OutgoingMessageTx,
2101    task_tx: TaskTx,
2102    dynamic_handler_tx: mpsc::UnboundedSender<DynamicHandlerMessage<Counterpart>>,
2103}
2104
2105impl<Counterpart: Role> ConnectionTo<Counterpart> {
2106    fn new(
2107        counterpart: Counterpart,
2108        message_tx: mpsc::UnboundedSender<OutgoingMessage>,
2109        task_tx: mpsc::UnboundedSender<Task>,
2110        dynamic_handler_tx: mpsc::UnboundedSender<DynamicHandlerMessage<Counterpart>>,
2111    ) -> Self {
2112        Self {
2113            counterpart,
2114            message_tx,
2115            task_tx,
2116            dynamic_handler_tx,
2117        }
2118    }
2119
2120    /// Return the counterpart role this connection is talking to.
2121    pub fn counterpart(&self) -> Counterpart {
2122        self.counterpart.clone()
2123    }
2124
2125    /// Spawns a task that will run so long as the JSON-RPC connection is being served.
2126    ///
2127    /// This is the primary mechanism for offloading expensive work from handler callbacks
2128    /// to avoid blocking the event loop. Spawned tasks run concurrently with the connection,
2129    /// allowing the server to continue processing messages.
2130    ///
2131    /// # Event Loop
2132    ///
2133    /// Handler callbacks run on the event loop, which cannot process new messages while
2134    /// your handler is running. Use `spawn` for any expensive operations:
2135    ///
2136    /// ```no_run
2137    /// # use agent_client_protocol_test::*;
2138    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
2139    /// # let connection = mock_connection();
2140    /// connection.on_receive_request(async |req: ProcessRequest, responder, cx| {
2141    ///     // Clone cx for the spawned task
2142    ///     cx.spawn({
2143    ///         let connection = cx.clone();
2144    ///         async move {
2145    ///             let result = expensive_operation(&req.data).await?;
2146    ///             connection.send_notification(ProcessComplete { result })?;
2147    ///             Ok(())
2148    ///         }
2149    ///     })?;
2150    ///
2151    ///     // Respond immediately
2152    ///     responder.respond(ProcessResponse { result: "started".into() })
2153    /// }, agent_client_protocol::on_receive_request!())
2154    /// # .connect_to(agent_client_protocol_test::MockTransport).await?;
2155    /// # Ok(())
2156    /// # }
2157    /// ```
2158    ///
2159    /// # Errors
2160    ///
2161    /// If the spawned task returns an error, the entire server will shut down.
2162    #[track_caller]
2163    pub fn spawn(
2164        &self,
2165        task: impl IntoFuture<Output = Result<(), crate::Error>, IntoFuture: Send + 'static>,
2166    ) -> Result<(), crate::Error> {
2167        let location = std::panic::Location::caller();
2168        let task = task.into_future();
2169        Task::new(location, task).spawn(&self.task_tx)
2170    }
2171
2172    /// Spawn a JSON-RPC connection in the background and return a [`ConnectionTo`] for sending messages to it.
2173    ///
2174    /// This is useful for creating multiple connections that communicate with each other,
2175    /// such as implementing proxy patterns or connecting to multiple backend services.
2176    ///
2177    /// # Arguments
2178    ///
2179    /// - `builder`: The connection builder with handlers configured
2180    /// - `transport`: The transport component to connect to
2181    ///
2182    /// # Returns
2183    ///
2184    /// A `ConnectionTo` that you can use to send requests and notifications to the spawned connection.
2185    ///
2186    /// # Example: Proxying to a backend connection
2187    ///
2188    /// ```
2189    /// # use agent_client_protocol::UntypedRole;
2190    /// # use agent_client_protocol::{Builder, ConnectionTo};
2191    /// # use agent_client_protocol_test::*;
2192    /// # async fn example(cx: ConnectionTo<UntypedRole>) -> Result<(), agent_client_protocol::Error> {
2193    /// // Set up a backend connection builder
2194    /// let backend = UntypedRole.builder()
2195    ///     .on_receive_request(async |req: MyRequest, responder, _cx| {
2196    ///         responder.respond(MyResponse { status: "ok".into() })
2197    ///     }, agent_client_protocol::on_receive_request!());
2198    ///
2199    /// // Spawn it and get a context to send requests to it
2200    /// let backend_connection = cx.spawn_connection(backend, MockTransport)?;
2201    ///
2202    /// // Now you can forward requests to the backend
2203    /// let response = backend_connection.send_request(MyRequest {}).block_task().await?;
2204    /// # Ok(())
2205    /// # }
2206    /// ```
2207    #[track_caller]
2208    pub fn spawn_connection<R: Role>(
2209        &self,
2210        builder: Builder<
2211            R,
2212            impl HandleDispatchFrom<R::Counterpart> + 'static,
2213            impl RunWithConnectionTo<R::Counterpart> + 'static,
2214        >,
2215        transport: impl ConnectTo<R> + 'static,
2216    ) -> Result<ConnectionTo<R::Counterpart>, crate::Error> {
2217        let (connection, future) =
2218            builder.into_connection_and_future(transport, |_| std::future::pending());
2219        Task::new(std::panic::Location::caller(), future).spawn(&self.task_tx)?;
2220        Ok(connection)
2221    }
2222
2223    /// Send a request/notification and forward the response appropriately.
2224    ///
2225    /// The request context's response type matches the request's response type,
2226    /// enabling type-safe message forwarding.
2227    pub fn send_proxied_message<Req: JsonRpcRequest<Response: Send>, Notif: JsonRpcNotification>(
2228        &self,
2229        message: Dispatch<Req, Notif>,
2230    ) -> Result<(), crate::Error>
2231    where
2232        Counterpart: HasPeer<Counterpart>,
2233    {
2234        self.send_proxied_message_to(self.counterpart(), message)
2235    }
2236
2237    /// Send a request/notification and forward the response appropriately.
2238    ///
2239    /// The request context's response type matches the request's response type,
2240    /// enabling type-safe message forwarding.
2241    ///
2242    /// When the `unstable_cancel_request` feature is enabled, `$/cancel_request`
2243    /// notifications are *not* forwarded: their `requestId` refers to a request
2244    /// on the connection they arrived over and would be meaningless to `peer`.
2245    /// Cancellation instead propagates hop by hop, because the responders
2246    /// passed to [`forward_response_to`](SentRequest::forward_response_to)
2247    /// observe it and re-issue the cancellation with the forwarded request's
2248    /// own ID.
2249    pub fn send_proxied_message_to<
2250        Peer: Role,
2251        Req: JsonRpcRequest<Response: Send>,
2252        Notif: JsonRpcNotification,
2253    >(
2254        &self,
2255        peer: Peer,
2256        message: Dispatch<Req, Notif>,
2257    ) -> Result<(), crate::Error>
2258    where
2259        Counterpart: HasPeer<Peer>,
2260    {
2261        match message {
2262            Dispatch::Request(request, responder) => self
2263                .send_request_to(peer, request)
2264                .forward_response_to(responder),
2265            Dispatch::Notification(notification) => {
2266                // `$/cancel_request` is connection-scoped: its `requestId` was
2267                // allocated on the connection the notification arrived over
2268                // and means nothing to `peer`. The cancellation has already
2269                // been recorded on this connection's responder markers, and
2270                // `forward_response_to` re-issues it for the forwarded request
2271                // with the correct per-hop ID, so drop the raw notification
2272                // instead of tunneling a meaningless ID across the hop.
2273                #[cfg(feature = "unstable_cancel_request")]
2274                if is_cancel_request_notification(&notification) {
2275                    tracing::debug!(
2276                        "not forwarding hop-scoped `$/cancel_request` notification across proxy hop"
2277                    );
2278                    return Ok(());
2279                }
2280                self.send_notification_to(peer, notification)
2281            }
2282            Dispatch::Response(result, router) => {
2283                // Responses are forwarded directly to their destination
2284                router.respond_with_result(result)
2285            }
2286        }
2287    }
2288
2289    /// Send an outgoing request and return a [`SentRequest`] for handling the reply.
2290    ///
2291    /// The returned [`SentRequest`] provides methods for receiving the response without
2292    /// blocking the event loop:
2293    ///
2294    /// * [`on_receiving_result`](SentRequest::on_receiving_result) - Schedule
2295    ///   a callback to run when the response arrives (doesn't block the event loop)
2296    /// * [`block_task`](SentRequest::block_task) - Block the current task until the response
2297    ///   arrives (only safe in spawned tasks, not in handlers)
2298    ///
2299    /// # Anti-Footgun Design
2300    ///
2301    /// The API intentionally makes it difficult to block on the result directly to prevent
2302    /// the common mistake of blocking the event loop while waiting for a response:
2303    ///
2304    /// ```compile_fail
2305    /// # use agent_client_protocol_test::*;
2306    /// # async fn example(cx: agent_client_protocol::ConnectionTo<agent_client_protocol::UntypedRole>) -> Result<(), agent_client_protocol::Error> {
2307    /// // ❌ This doesn't compile - prevents blocking the event loop
2308    /// let response = cx.send_request(MyRequest {}).await?;
2309    /// # Ok(())
2310    /// # }
2311    /// ```
2312    ///
2313    /// ```no_run
2314    /// # use agent_client_protocol_test::*;
2315    /// # async fn example(cx: agent_client_protocol::ConnectionTo<agent_client_protocol::UntypedRole>) -> Result<(), agent_client_protocol::Error> {
2316    /// // ✅ Option 1: Schedule callback (safe in handlers)
2317    /// cx.send_request(MyRequest {})
2318    ///     .on_receiving_result(async |result| {
2319    ///         // Handle the response
2320    ///         Ok(())
2321    ///     })?;
2322    ///
2323    /// // ✅ Option 2: Block in spawned task (safe because task is concurrent)
2324    /// cx.spawn({
2325    ///     let cx = cx.clone();
2326    ///     async move {
2327    ///         let response = cx.send_request(MyRequest {})
2328    ///             .block_task()
2329    ///             .await?;
2330    ///         // Process response...
2331    ///         Ok(())
2332    ///     }
2333    /// })?;
2334    /// # Ok(())
2335    /// # }
2336    /// ```
2337    /// Send an outgoing request to the default counterpart peer.
2338    ///
2339    /// This is a convenience method that sends to the counterpart role `R`.
2340    /// For explicit control over the target peer, use [`send_request_to`](Self::send_request_to).
2341    pub fn send_request<Req: JsonRpcRequest>(&self, request: Req) -> SentRequest<Req::Response>
2342    where
2343        Counterpart: HasPeer<Counterpart>,
2344    {
2345        self.send_request_to(self.counterpart.clone(), request)
2346    }
2347
2348    /// Send an outgoing request to a specific peer.
2349    ///
2350    /// The message will be transformed according to the [`HasPeer`](crate::role::HasPeer)
2351    /// implementation before being sent.
2352    pub fn send_request_to<Peer: Role, Req: JsonRpcRequest>(
2353        &self,
2354        peer: Peer,
2355        request: Req,
2356    ) -> SentRequest<Req::Response>
2357    where
2358        Counterpart: HasPeer<Peer>,
2359    {
2360        let method = request.method().to_string();
2361        let id = RequestId::Str(uuid::Uuid::new_v4().to_string());
2362        let (response_tx, response_rx) = oneshot::channel();
2363        let role_id = peer.role_id();
2364        let remote_style = self.counterpart.remote_style(peer);
2365        #[cfg(feature = "unstable_cancel_request")]
2366        let cancellation =
2367            SentRequestCancellation::new(self.message_tx.clone(), remote_style, id.clone());
2368        match remote_style.transform_outgoing_message(request) {
2369            Ok(untyped) => {
2370                // Transform the message for the target role
2371                let message = OutgoingMessage::Request {
2372                    id: id.clone(),
2373                    method: method.clone(),
2374                    role_id,
2375                    untyped,
2376                    response_tx,
2377                    #[cfg(feature = "unstable_cancel_request")]
2378                    cancellation_disarm: cancellation.disarm_handle(),
2379                };
2380
2381                match self.message_tx.unbounded_send(message) {
2382                    Ok(()) => (),
2383                    Err(error) => {
2384                        #[cfg(feature = "unstable_cancel_request")]
2385                        cancellation.disarm();
2386
2387                        let OutgoingMessage::Request {
2388                            method,
2389                            response_tx,
2390                            ..
2391                        } = error.into_inner()
2392                        else {
2393                            unreachable!();
2394                        };
2395
2396                        response_tx
2397                            .send(ResponsePayload {
2398                                result: Err(crate::util::internal_error(format!(
2399                                    "failed to send outgoing request `{method}"
2400                                ))),
2401                                ack_tx: None,
2402                            })
2403                            .unwrap();
2404                    }
2405                }
2406            }
2407
2408            Err(err) => {
2409                #[cfg(feature = "unstable_cancel_request")]
2410                cancellation.disarm();
2411
2412                response_tx
2413                    .send(ResponsePayload {
2414                        result: Err(crate::util::internal_error(format!(
2415                            "failed to create untyped request for `{method}`: {err}"
2416                        ))),
2417                        ack_tx: None,
2418                    })
2419                    .unwrap();
2420            }
2421        }
2422
2423        SentRequest::new(
2424            id,
2425            method.clone(),
2426            self.task_tx.clone(),
2427            response_rx,
2428            #[cfg(feature = "unstable_cancel_request")]
2429            cancellation,
2430        )
2431        .map(move |json| <Req::Response>::from_value(&method, json))
2432    }
2433
2434    /// Send an outgoing notification to the default counterpart peer (no reply expected).
2435    ///
2436    /// Notifications are fire-and-forget messages that don't have IDs and don't expect responses.
2437    /// This method sends the notification immediately and returns.
2438    ///
2439    /// This is a convenience method that sends to the counterpart role `R`.
2440    /// For explicit control over the target peer, use [`send_notification_to`](Self::send_notification_to).
2441    ///
2442    /// ```no_run
2443    /// # use agent_client_protocol_test::*;
2444    /// # async fn example(cx: agent_client_protocol::ConnectionTo<agent_client_protocol::Agent>) -> Result<(), agent_client_protocol::Error> {
2445    /// cx.send_notification(StatusUpdate {
2446    ///     message: "Processing...".into(),
2447    /// })?;
2448    /// # Ok(())
2449    /// # }
2450    /// ```
2451    pub fn send_notification<N: JsonRpcNotification>(
2452        &self,
2453        notification: N,
2454    ) -> Result<(), crate::Error>
2455    where
2456        Counterpart: HasPeer<Counterpart>,
2457    {
2458        self.send_notification_to(self.counterpart.clone(), notification)
2459    }
2460
2461    /// Send an outgoing notification to a specific peer (no reply expected).
2462    ///
2463    /// The message will be transformed according to the [`HasPeer`](crate::role::HasPeer)
2464    /// implementation before being sent.
2465    pub fn send_notification_to<Peer: Role, N: JsonRpcNotification>(
2466        &self,
2467        peer: Peer,
2468        notification: N,
2469    ) -> Result<(), crate::Error>
2470    where
2471        Counterpart: HasPeer<Peer>,
2472    {
2473        let remote_style = self.counterpart.remote_style(peer);
2474        tracing::debug!(
2475            role = std::any::type_name::<Counterpart>(),
2476            peer = std::any::type_name::<Peer>(),
2477            notification_type = std::any::type_name::<N>(),
2478            ?remote_style,
2479            original_method = notification.method(),
2480            "send_notification_to"
2481        );
2482        let transformed = remote_style.transform_outgoing_message(notification)?;
2483        tracing::debug!(
2484            transformed_method = %transformed.method,
2485            "send_notification_to transformed"
2486        );
2487        send_raw_message(
2488            &self.message_tx,
2489            OutgoingMessage::Notification {
2490                untyped: transformed,
2491            },
2492        )
2493    }
2494
2495    /// Send a `$/cancel_request` notification for an arbitrary request ID to
2496    /// the default counterpart peer.
2497    ///
2498    /// Prefer [`SentRequest::cancel`] when you have the request handle: it
2499    /// already knows the correct peer, request ID, and proxy wrapping. Use this
2500    /// low-level method only when implementing custom routing with a request ID
2501    /// that is valid on this connection.
2502    #[cfg(feature = "unstable_cancel_request")]
2503    pub fn send_cancel_request(
2504        &self,
2505        request_id: impl Into<crate::schema::v1::RequestId>,
2506    ) -> Result<(), crate::Error>
2507    where
2508        Counterpart: HasPeer<Counterpart>,
2509    {
2510        self.send_cancel_request_to(self.counterpart.clone(), request_id)
2511    }
2512
2513    /// Send a `$/cancel_request` notification for an arbitrary request ID to a
2514    /// specific peer.
2515    ///
2516    /// Prefer [`SentRequest::cancel`] when you have the request handle: it
2517    /// already knows the correct peer, request ID, and proxy wrapping. Use this
2518    /// low-level method only when implementing custom routing with a request ID
2519    /// that is valid on the target peer's connection.
2520    #[cfg(feature = "unstable_cancel_request")]
2521    pub fn send_cancel_request_to<Peer: Role>(
2522        &self,
2523        peer: Peer,
2524        request_id: impl Into<crate::schema::v1::RequestId>,
2525    ) -> Result<(), crate::Error>
2526    where
2527        Counterpart: HasPeer<Peer>,
2528    {
2529        self.send_notification_to(
2530            peer,
2531            crate::schema::v1::CancelRequestNotification::new(request_id),
2532        )
2533    }
2534
2535    /// Send an error notification (no reply expected).
2536    pub fn send_error_notification(&self, error: crate::Error) -> Result<(), crate::Error> {
2537        send_raw_message(&self.message_tx, OutgoingMessage::Error { error })
2538    }
2539
2540    /// Register a dynamic message handler, used to intercept messages specific to a particular session
2541    /// or some similar modal thing.
2542    ///
2543    /// Dynamic message handlers are called first for every incoming message.
2544    ///
2545    /// If they decline to handle the message, then the message is passed to the regular registered handlers.
2546    ///
2547    /// The handler will stay registered until the returned registration guard is dropped.
2548    pub fn add_dynamic_handler(
2549        &self,
2550        handler: impl HandleDispatchFrom<Counterpart> + 'static,
2551    ) -> Result<DynamicHandlerRegistration<Counterpart>, crate::Error> {
2552        let uuid = Uuid::new_v4();
2553        self.dynamic_handler_tx
2554            .unbounded_send(DynamicHandlerMessage::AddDynamicHandler(
2555                uuid,
2556                Box::new(handler),
2557            ))
2558            .map_err(crate::util::internal_error)?;
2559
2560        Ok(DynamicHandlerRegistration::new(uuid, self.clone()))
2561    }
2562
2563    fn remove_dynamic_handler(&self, uuid: Uuid) {
2564        // Ignore errors
2565        drop(
2566            self.dynamic_handler_tx
2567                .unbounded_send(DynamicHandlerMessage::RemoveDynamicHandler(uuid)),
2568        );
2569    }
2570}
2571
2572#[derive(Clone, Debug)]
2573pub struct DynamicHandlerRegistration<R: Role> {
2574    uuid: Uuid,
2575    cx: ConnectionTo<R>,
2576}
2577
2578impl<R: Role> DynamicHandlerRegistration<R> {
2579    fn new(uuid: Uuid, cx: ConnectionTo<R>) -> Self {
2580        Self { uuid, cx }
2581    }
2582
2583    /// Prevents the dynamic handler from being removed when dropped.
2584    pub fn run_indefinitely(self) {
2585        std::mem::forget(self);
2586    }
2587}
2588
2589impl<R: Role> Drop for DynamicHandlerRegistration<R> {
2590    fn drop(&mut self) {
2591        self.cx.remove_dynamic_handler(self.uuid);
2592    }
2593}
2594
2595/// The context to respond to an incoming request.
2596///
2597/// This context is provided to request handlers and serves a dual role:
2598///
2599/// 1. **Respond to the request** - Use [`respond`](Self::respond) or
2600///    [`respond_with_result`](Self::respond_with_result) to send the response
2601/// 2. **Send other messages** - Use the [`ConnectionTo`] parameter passed to your
2602///    handler, which provides [`send_request`](`ConnectionTo::send_request`),
2603///    [`send_notification`](`ConnectionTo::send_notification`), and
2604///    [`spawn`](`ConnectionTo::spawn`)
2605///
2606/// # Example
2607///
2608/// ```no_run
2609/// # use agent_client_protocol_test::*;
2610/// # async fn example() -> Result<(), agent_client_protocol::Error> {
2611/// # let connection = mock_connection();
2612/// connection.on_receive_request(async |req: ProcessRequest, responder, cx| {
2613///     // Send a notification while processing
2614///     cx.send_notification(StatusUpdate {
2615///         message: "processing".into(),
2616///     })?;
2617///
2618///     // Do some work...
2619///     let result = process(&req.data)?;
2620///
2621///     // Respond to the request
2622///     responder.respond(ProcessResponse { result })
2623/// }, agent_client_protocol::on_receive_request!())
2624/// # .connect_to(agent_client_protocol_test::MockTransport).await?;
2625/// # Ok(())
2626/// # }
2627/// ```
2628///
2629/// # Event Loop Considerations
2630///
2631/// Like all handlers, request handlers run on the event loop. Use
2632/// [`spawn`](ConnectionTo::spawn) for expensive operations to avoid blocking
2633/// the connection.
2634///
2635/// See the [Event Loop and Concurrency](Builder#event-loop-and-concurrency)
2636/// section for more details.
2637#[must_use]
2638pub struct Responder<T: JsonRpcResponse = serde_json::Value> {
2639    /// The method of the request.
2640    method: String,
2641
2642    /// The `id` of the message we are replying to.
2643    id: RequestId,
2644
2645    /// Request-local cancellation state.
2646    cancellation: ResponderCancellation,
2647
2648    /// Function to send the response to its destination.
2649    ///
2650    /// For incoming requests: serializes to JSON and sends over the wire.
2651    /// For incoming responses: sends to the waiting oneshot channel.
2652    send_fn: Box<dyn FnOnce(Result<T, crate::Error>) -> Result<(), crate::Error> + Send>,
2653}
2654
2655impl<T: JsonRpcResponse> std::fmt::Debug for Responder<T> {
2656    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2657        f.debug_struct("Responder")
2658            .field("method", &self.method)
2659            .field("id", &self.id)
2660            .field("response_type", &std::any::type_name::<T>())
2661            .finish_non_exhaustive()
2662    }
2663}
2664
2665impl Responder<serde_json::Value> {
2666    /// Create a new request context for an incoming request.
2667    ///
2668    /// The response will be serialized to JSON and sent over the wire.
2669    fn new(
2670        message_tx: OutgoingMessageTx,
2671        method: String,
2672        id: RequestId,
2673        cancellation_registry: &RequestCancellationRegistry,
2674    ) -> Self {
2675        let id_clone = id.clone();
2676        let method_clone = method.clone();
2677        let cancellation = cancellation_registry.register(&id);
2678        Self {
2679            method,
2680            id,
2681            cancellation,
2682            send_fn: Box::new(move |response: Result<serde_json::Value, crate::Error>| {
2683                send_raw_message(
2684                    &message_tx,
2685                    OutgoingMessage::Response {
2686                        id: id_clone,
2687                        method: method_clone,
2688                        response,
2689                    },
2690                )
2691            }),
2692        }
2693    }
2694
2695    /// Cast this request context to a different response type.
2696    ///
2697    /// The provided type `T` will be serialized to JSON before sending.
2698    pub fn cast<T: JsonRpcResponse>(self) -> Responder<T> {
2699        self.wrap_params(move |method, value| match value {
2700            Ok(value) => T::into_json(value, method),
2701            Err(e) => Err(e),
2702        })
2703    }
2704}
2705
2706impl<T: JsonRpcResponse> Responder<T> {
2707    /// Method of the incoming request
2708    #[must_use]
2709    pub fn method(&self) -> &str {
2710        &self.method
2711    }
2712
2713    /// ID of the incoming request/response as a JSON value
2714    #[must_use]
2715    pub fn id(&self) -> serde_json::Value {
2716        crate::util::id_to_json(&self.id)
2717    }
2718
2719    /// Returns the cancellation marker for this request.
2720    ///
2721    /// The marker is set when the peer sends `$/cancel_request` for this
2722    /// request's JSON-RPC ID. Cancellation is cooperative: handlers should use
2723    /// the marker to stop long-running work and then decide whether to respond
2724    /// with [`Error::request_cancelled`] or partial data.
2725    ///
2726    /// [`Error::request_cancelled`]: crate::Error::request_cancelled
2727    #[cfg(feature = "unstable_cancel_request")]
2728    #[must_use]
2729    pub fn cancellation(&self) -> RequestCancellation {
2730        self.cancellation.cancellation()
2731    }
2732
2733    /// Convert to a `Responder` that expects a JSON value
2734    /// and which checks (dynamically) that the JSON value it receives
2735    /// can be converted to `T`.
2736    pub fn erase_to_json(self) -> Responder<serde_json::Value> {
2737        self.wrap_params(|method, value| T::from_value(method, value?))
2738    }
2739
2740    /// Return a new Responder with a different method name.
2741    pub fn wrap_method(self, method: String) -> Responder<T> {
2742        Responder {
2743            method,
2744            id: self.id,
2745            cancellation: self.cancellation,
2746            send_fn: self.send_fn,
2747        }
2748    }
2749
2750    /// Return a new Responder that expects a response of type U.
2751    ///
2752    /// `wrap_fn` will be invoked with the method name and the result to transform
2753    /// type `U` into type `T` before sending.
2754    pub fn wrap_params<U: JsonRpcResponse>(
2755        self,
2756        wrap_fn: impl FnOnce(&str, Result<U, crate::Error>) -> Result<T, crate::Error> + Send + 'static,
2757    ) -> Responder<U> {
2758        let method = self.method.clone();
2759        Responder {
2760            method: self.method,
2761            id: self.id,
2762            cancellation: self.cancellation,
2763            send_fn: Box::new(move |input: Result<U, crate::Error>| {
2764                let t_value = wrap_fn(&method, input);
2765                (self.send_fn)(t_value)
2766            }),
2767        }
2768    }
2769
2770    /// Respond to the JSON-RPC request with either a value (`Ok`) or an error (`Err`).
2771    pub fn respond_with_result(
2772        self,
2773        response: Result<T, crate::Error>,
2774    ) -> Result<(), crate::Error> {
2775        tracing::debug!(id = ?self.id, "respond called");
2776        (self.send_fn)(response)
2777    }
2778
2779    /// Respond to the JSON-RPC request with a value.
2780    pub fn respond(self, response: T) -> Result<(), crate::Error> {
2781        self.respond_with_result(Ok(response))
2782    }
2783
2784    /// Respond to the JSON-RPC request with an internal error containing a message.
2785    pub fn respond_with_internal_error(self, message: impl ToString) -> Result<(), crate::Error> {
2786        self.respond_with_error(crate::util::internal_error(message))
2787    }
2788
2789    /// Respond to the JSON-RPC request with an error.
2790    pub fn respond_with_error(self, error: crate::Error) -> Result<(), crate::Error> {
2791        tracing::debug!(id = ?self.id, ?error, "respond_with_error called");
2792        self.respond_with_result(Err(error))
2793    }
2794}
2795
2796/// Context for handling an incoming JSON-RPC response.
2797///
2798/// This is the response-side counterpart to [`Responder`]. While `Responder` handles
2799/// incoming requests (where you send a response over the wire), `ResponseRouter` handles
2800/// incoming responses (where you route the response to a local task waiting for it).
2801///
2802/// Both are fundamentally "sinks" that push the message through a `send_fn`, but they
2803/// represent different points in the message lifecycle and carry different metadata.
2804///
2805/// # Drop Behavior
2806///
2807/// Dropping a `ResponseRouter` without responding (for example, from a
2808/// dispatch handler that claims a [`Dispatch::Response`]) discards the
2809/// response: the local awaiter observes the response as never received. The
2810/// request still counts as settled — when the `unstable_cancel_request`
2811/// feature is enabled, routing a response this far disarms the originating
2812/// [`SentRequest`]'s drop-time auto-cancellation even if the router is never
2813/// invoked, since the peer has already answered.
2814#[must_use]
2815pub struct ResponseRouter<T: JsonRpcResponse = serde_json::Value> {
2816    /// The method of the original request.
2817    method: String,
2818
2819    /// The `id` of the original request.
2820    id: RequestId,
2821
2822    /// The RoleId to which the original request was sent
2823    /// (and hence from which the reply is expected).
2824    role_id: RoleId,
2825
2826    /// Function to send the response to the waiting task.
2827    send_fn: Box<dyn FnOnce(Result<T, crate::Error>) -> Result<(), crate::Error> + Send>,
2828}
2829
2830impl<T: JsonRpcResponse> std::fmt::Debug for ResponseRouter<T> {
2831    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2832        f.debug_struct("ResponseRouter")
2833            .field("method", &self.method)
2834            .field("id", &self.id)
2835            .field("response_type", &std::any::type_name::<T>())
2836            .finish_non_exhaustive()
2837    }
2838}
2839
2840impl ResponseRouter<serde_json::Value> {
2841    /// Create a new response context for routing a response to a local awaiter.
2842    ///
2843    /// When `respond_with_result` is called, the response is sent through the oneshot
2844    /// channel to the code that originally sent the request. If that receiver was
2845    /// dropped, the response is discarded because there is no local awaiter left.
2846    pub(crate) fn new(
2847        method: String,
2848        id: RequestId,
2849        role_id: RoleId,
2850        sender: oneshot::Sender<ResponsePayload>,
2851        #[cfg(feature = "unstable_cancel_request")]
2852        cancellation_disarm: SentRequestCancellationDisarm,
2853    ) -> Self {
2854        let response_method = method.clone();
2855        let response_id = id.clone();
2856        // A response for the request reached this router, so the request is
2857        // settled from the peer's perspective and a `$/cancel_request` could
2858        // only ever be redundant. Disarm immediately so handlers may retain
2859        // the router without leaving auto-cancellation armed.
2860        #[cfg(feature = "unstable_cancel_request")]
2861        cancellation_disarm.disarm();
2862        Self {
2863            method,
2864            id,
2865            role_id,
2866            send_fn: Box::new(move |response: Result<serde_json::Value, crate::Error>| {
2867                if sender
2868                    .send(ResponsePayload {
2869                        result: response,
2870                        ack_tx: None,
2871                    })
2872                    .is_err()
2873                {
2874                    tracing::debug!(
2875                        method = %response_method,
2876                        id = ?response_id,
2877                        "dropped response because local receiver was gone"
2878                    );
2879                }
2880                Ok(())
2881            }),
2882        }
2883    }
2884
2885    /// Cast this response context to a different response type.
2886    ///
2887    /// The provided type `T` will be serialized to JSON before sending.
2888    pub fn cast<T: JsonRpcResponse>(self) -> ResponseRouter<T> {
2889        self.wrap_params(move |method, value| match value {
2890            Ok(value) => T::into_json(value, method),
2891            Err(e) => Err(e),
2892        })
2893    }
2894}
2895
2896impl<T: JsonRpcResponse> ResponseRouter<T> {
2897    /// Method of the original request
2898    #[must_use]
2899    pub fn method(&self) -> &str {
2900        &self.method
2901    }
2902
2903    /// ID of the original request as a JSON value
2904    #[must_use]
2905    pub fn id(&self) -> serde_json::Value {
2906        crate::util::id_to_json(&self.id)
2907    }
2908
2909    /// The peer to which the original request was sent.
2910    ///
2911    /// This is the peer from which we expect to receive the response.
2912    #[must_use]
2913    pub fn role_id(&self) -> RoleId {
2914        self.role_id.clone()
2915    }
2916
2917    /// Convert to a `ResponseRouter` that expects a JSON value
2918    /// and which checks (dynamically) that the JSON value it receives
2919    /// can be converted to `T`.
2920    pub fn erase_to_json(self) -> ResponseRouter<serde_json::Value> {
2921        self.wrap_params(|method, value| T::from_value(method, value?))
2922    }
2923
2924    /// Return a new ResponseRouter that expects a response of type U.
2925    ///
2926    /// `wrap_fn` will be invoked with the method name and the result to transform
2927    /// type `U` into type `T` before sending.
2928    fn wrap_params<U: JsonRpcResponse>(
2929        self,
2930        wrap_fn: impl FnOnce(&str, Result<U, crate::Error>) -> Result<T, crate::Error> + Send + 'static,
2931    ) -> ResponseRouter<U> {
2932        let method = self.method.clone();
2933        ResponseRouter {
2934            method: self.method,
2935            id: self.id,
2936            role_id: self.role_id,
2937            send_fn: Box::new(move |input: Result<U, crate::Error>| {
2938                let t_value = wrap_fn(&method, input);
2939                (self.send_fn)(t_value)
2940            }),
2941        }
2942    }
2943
2944    /// Complete the response by sending the result to the waiting task.
2945    pub fn respond_with_result(
2946        self,
2947        response: Result<T, crate::Error>,
2948    ) -> Result<(), crate::Error> {
2949        tracing::debug!(id = ?self.id, "response routed to awaiter");
2950        (self.send_fn)(response)
2951    }
2952
2953    /// Complete the response by sending a value to the waiting task.
2954    pub fn respond(self, response: T) -> Result<(), crate::Error> {
2955        self.respond_with_result(Ok(response))
2956    }
2957
2958    /// Complete the response by sending an internal error to the waiting task.
2959    pub fn respond_with_internal_error(self, message: impl ToString) -> Result<(), crate::Error> {
2960        self.respond_with_error(crate::util::internal_error(message))
2961    }
2962
2963    /// Complete the response by sending an error to the waiting task.
2964    pub fn respond_with_error(self, error: crate::Error) -> Result<(), crate::Error> {
2965        tracing::debug!(id = ?self.id, ?error, "error routed to awaiter");
2966        self.respond_with_result(Err(error))
2967    }
2968}
2969
2970/// Common bounds for any JSON-RPC message.
2971///
2972/// # Derive Macro
2973///
2974/// For simple message types, you can use the `JsonRpcRequest` or `JsonRpcNotification` derive macros
2975/// which will implement both `JsonRpcMessage` and the respective trait. See [`JsonRpcRequest`] and
2976/// [`JsonRpcNotification`] for examples.
2977pub trait JsonRpcMessage: 'static + Debug + Sized + Send + Clone {
2978    /// Check if this message type matches the given method name.
2979    fn matches_method(method: &str) -> bool;
2980
2981    /// The method name for the message.
2982    fn method(&self) -> &str;
2983
2984    /// Convert this message into an untyped message.
2985    fn to_untyped_message(&self) -> Result<UntypedMessage, crate::Error>;
2986
2987    /// Parse this type from a method name and parameters.
2988    ///
2989    /// Returns an error if the method doesn't match or deserialization fails.
2990    /// Callers should use `matches_method` first to check if this type handles the method.
2991    fn parse_message(method: &str, params: &impl Serialize) -> Result<Self, crate::Error>;
2992}
2993
2994/// Defines the "payload" of a successful response to a JSON-RPC request.
2995///
2996/// # Derive Macro
2997///
2998/// Use `#[derive(JsonRpcResponse)]` to automatically implement this trait:
2999///
3000/// ```ignore
3001/// use agent_client_protocol::JsonRpcResponse;
3002/// use serde::{Serialize, Deserialize};
3003///
3004/// #[derive(Debug, Serialize, Deserialize, JsonRpcResponse)]
3005/// #[response(method = "_hello")]
3006/// struct HelloResponse {
3007///     greeting: String,
3008/// }
3009/// ```
3010pub trait JsonRpcResponse: 'static + Debug + Sized + Send + Clone {
3011    /// Convert this message into a JSON value.
3012    fn into_json(self, method: &str) -> Result<serde_json::Value, crate::Error>;
3013
3014    /// Parse a JSON value into the response type.
3015    fn from_value(method: &str, value: serde_json::Value) -> Result<Self, crate::Error>;
3016}
3017
3018impl JsonRpcResponse for serde_json::Value {
3019    fn from_value(_method: &str, value: serde_json::Value) -> Result<Self, crate::Error> {
3020        Ok(value)
3021    }
3022
3023    fn into_json(self, _method: &str) -> Result<serde_json::Value, crate::Error> {
3024        Ok(self)
3025    }
3026}
3027
3028/// A struct that represents a notification (JSON-RPC message that does not expect a response).
3029///
3030/// # Derive Macro
3031///
3032/// Use `#[derive(JsonRpcNotification)]` to automatically implement both `JsonRpcMessage` and `JsonRpcNotification`:
3033///
3034/// ```ignore
3035/// use agent_client_protocol::JsonRpcNotification;
3036/// use serde::{Serialize, Deserialize};
3037///
3038/// #[derive(Debug, Clone, Serialize, Deserialize, JsonRpcNotification)]
3039/// #[notification(method = "_ping")]
3040/// struct PingNotification {
3041///     timestamp: u64,
3042/// }
3043/// ```
3044pub trait JsonRpcNotification: JsonRpcMessage {}
3045
3046/// A struct that represents a request (JSON-RPC message expecting a response).
3047///
3048/// # Derive Macro
3049///
3050/// Use `#[derive(JsonRpcRequest)]` to automatically implement both `JsonRpcMessage` and `JsonRpcRequest`:
3051///
3052/// ```ignore
3053/// use agent_client_protocol::{JsonRpcRequest, JsonRpcResponse};
3054/// use serde::{Serialize, Deserialize};
3055///
3056/// #[derive(Debug, Clone, Serialize, Deserialize, JsonRpcRequest)]
3057/// #[request(method = "_hello", response = HelloResponse)]
3058/// struct HelloRequest {
3059///     name: String,
3060/// }
3061///
3062/// #[derive(Debug, Serialize, Deserialize, JsonRpcResponse)]
3063/// struct HelloResponse {
3064///     greeting: String,
3065/// }
3066/// ```
3067pub trait JsonRpcRequest: JsonRpcMessage {
3068    /// The type of data expected in response.
3069    type Response: JsonRpcResponse;
3070}
3071
3072/// An enum capturing an in-flight request or notification.
3073/// In the case of a request, also includes the context used to respond to the request.
3074///
3075/// Type parameters allow specifying the concrete request and notification types.
3076/// By default, both are `UntypedMessage` for dynamic dispatch.
3077/// The request context's response type matches the request's response type.
3078#[derive(Debug)]
3079pub enum Dispatch<Req: JsonRpcRequest = UntypedMessage, Notif: JsonRpcMessage = UntypedMessage> {
3080    /// Incoming request and the context where the response should be sent.
3081    Request(Req, Responder<Req::Response>),
3082
3083    /// Incoming notification.
3084    Notification(Notif),
3085
3086    /// Incoming response to a request we sent.
3087    ///
3088    /// The first field is the response result (success or error from the remote).
3089    /// The second field is the context for forwarding the response to its destination
3090    /// (typically a waiting oneshot channel).
3091    Response(
3092        Result<Req::Response, crate::Error>,
3093        ResponseRouter<Req::Response>,
3094    ),
3095}
3096
3097impl<Req: JsonRpcRequest, Notif: JsonRpcMessage> Dispatch<Req, Notif> {
3098    /// Map the request and notification types to new types.
3099    ///
3100    /// Note: Response variants are passed through unchanged since they don't
3101    /// contain a parseable message payload.
3102    pub fn map<Req1, Notif1>(
3103        self,
3104        map_request: impl FnOnce(Req, Responder<Req::Response>) -> (Req1, Responder<Req1::Response>),
3105        map_notification: impl FnOnce(Notif) -> Notif1,
3106    ) -> Dispatch<Req1, Notif1>
3107    where
3108        Req1: JsonRpcRequest<Response = Req::Response>,
3109        Notif1: JsonRpcMessage,
3110    {
3111        match self {
3112            Dispatch::Request(request, responder) => {
3113                let (new_request, new_responder) = map_request(request, responder);
3114                Dispatch::Request(new_request, new_responder)
3115            }
3116            Dispatch::Notification(notification) => {
3117                let new_notification = map_notification(notification);
3118                Dispatch::Notification(new_notification)
3119            }
3120            Dispatch::Response(result, router) => Dispatch::Response(result, router),
3121        }
3122    }
3123
3124    /// Respond to the message with an error.
3125    ///
3126    /// If this message is a request, this error becomes the reply to the request.
3127    ///
3128    /// If this message is a notification, the error is sent as a notification.
3129    ///
3130    /// If this message is a response, the error is forwarded to the waiting handler.
3131    pub fn respond_with_error<R: Role>(
3132        self,
3133        error: crate::Error,
3134        cx: ConnectionTo<R>,
3135    ) -> Result<(), crate::Error> {
3136        match self {
3137            Dispatch::Request(_, responder) => responder.respond_with_error(error),
3138            Dispatch::Notification(_) => cx.send_error_notification(error),
3139            Dispatch::Response(_, responder) => responder.respond_with_error(error),
3140        }
3141    }
3142
3143    /// Convert to a `Responder` that expects a JSON value
3144    /// and which checks (dynamically) that the JSON value it receives
3145    /// can be converted to `T`.
3146    ///
3147    /// Note: Response variants cannot be erased since their payload is already
3148    /// parsed. This returns an error for Response variants.
3149    pub fn erase_to_json(self) -> Result<Dispatch, crate::Error> {
3150        match self {
3151            Dispatch::Request(response, responder) => Ok(Dispatch::Request(
3152                response.to_untyped_message()?,
3153                responder.erase_to_json(),
3154            )),
3155            Dispatch::Notification(notification) => {
3156                Ok(Dispatch::Notification(notification.to_untyped_message()?))
3157            }
3158            Dispatch::Response(_, _) => Err(crate::util::internal_error(
3159                "cannot erase Response variant to JSON",
3160            )),
3161        }
3162    }
3163
3164    /// Convert the message in self to an untyped message.
3165    ///
3166    /// Note: Response variants don't have an untyped message representation.
3167    /// This returns an error for Response variants.
3168    pub fn to_untyped_message(&self) -> Result<UntypedMessage, crate::Error> {
3169        match self {
3170            Dispatch::Request(request, _) => request.to_untyped_message(),
3171            Dispatch::Notification(notification) => notification.to_untyped_message(),
3172            Dispatch::Response(_, _) => Err(crate::util::internal_error(
3173                "Response variant has no untyped message representation",
3174            )),
3175        }
3176    }
3177
3178    /// Convert self to an untyped message context.
3179    ///
3180    /// Note: Response variants cannot be converted. This returns an error for Response variants.
3181    pub fn into_untyped_dispatch(self) -> Result<Dispatch, crate::Error> {
3182        match self {
3183            Dispatch::Request(request, responder) => Ok(Dispatch::Request(
3184                request.to_untyped_message()?,
3185                responder.erase_to_json(),
3186            )),
3187            Dispatch::Notification(notification) => {
3188                Ok(Dispatch::Notification(notification.to_untyped_message()?))
3189            }
3190            Dispatch::Response(_, _) => Err(crate::util::internal_error(
3191                "cannot convert Response variant to untyped message context",
3192            )),
3193        }
3194    }
3195
3196    /// Returns the request ID if this is a request or response, None if notification.
3197    pub fn id(&self) -> Option<serde_json::Value> {
3198        match self {
3199            Dispatch::Request(_, cx) => Some(cx.id()),
3200            Dispatch::Notification(_) => None,
3201            Dispatch::Response(_, cx) => Some(cx.id()),
3202        }
3203    }
3204
3205    /// Returns the method of the message.
3206    ///
3207    /// For requests and notifications, this is the method from the message payload.
3208    /// For responses, this is the method of the original request.
3209    pub fn method(&self) -> &str {
3210        match self {
3211            Dispatch::Request(msg, _) => msg.method(),
3212            Dispatch::Notification(msg) => msg.method(),
3213            Dispatch::Response(_, cx) => cx.method(),
3214        }
3215    }
3216}
3217
3218impl Dispatch {
3219    /// Attempts to parse `self` into a typed message context.
3220    ///
3221    /// # Returns
3222    ///
3223    /// * `Ok(Ok(typed))` if this is a request/notification of the given types
3224    /// * `Ok(Err(self))` if not
3225    /// * `Err` if has the correct method for the given types but parsing fails
3226    #[tracing::instrument(skip(self), fields(Request = ?std::any::type_name::<Req>(), Notif = ?std::any::type_name::<Notif>()), level = "trace", ret)]
3227    pub(crate) fn into_typed_dispatch<Req: JsonRpcRequest, Notif: JsonRpcNotification>(
3228        self,
3229    ) -> Result<Result<Dispatch<Req, Notif>, Dispatch>, crate::Error> {
3230        tracing::debug!(
3231            message = ?self,
3232            "into_typed_dispatch"
3233        );
3234        match self {
3235            Dispatch::Request(message, responder) => {
3236                if Req::matches_method(&message.method) {
3237                    match Req::parse_message(&message.method, &message.params) {
3238                        Ok(req) => {
3239                            tracing::trace!(?req, "parsed ok");
3240                            Ok(Ok(Dispatch::Request(req, responder.cast())))
3241                        }
3242                        Err(err) => {
3243                            tracing::trace!(?err, "parse error");
3244                            Err(err)
3245                        }
3246                    }
3247                } else {
3248                    tracing::trace!("method doesn't match");
3249                    Ok(Err(Dispatch::Request(message, responder)))
3250                }
3251            }
3252
3253            Dispatch::Notification(message) => {
3254                if Notif::matches_method(&message.method) {
3255                    match Notif::parse_message(&message.method, &message.params) {
3256                        Ok(notif) => {
3257                            tracing::trace!(?notif, "parse ok");
3258                            Ok(Ok(Dispatch::Notification(notif)))
3259                        }
3260                        Err(err) => {
3261                            tracing::trace!(?err, "parse error");
3262                            Err(err)
3263                        }
3264                    }
3265                } else {
3266                    tracing::trace!("method doesn't match");
3267                    Ok(Err(Dispatch::Notification(message)))
3268                }
3269            }
3270
3271            Dispatch::Response(result, cx) => {
3272                let method = cx.method();
3273                if Req::matches_method(method) {
3274                    // Parse the response result
3275                    let typed_result = match result {
3276                        Ok(value) => {
3277                            match <Req::Response as JsonRpcResponse>::from_value(method, value) {
3278                                Ok(parsed) => {
3279                                    tracing::trace!(?parsed, "parse ok");
3280                                    Ok(parsed)
3281                                }
3282                                Err(err) => {
3283                                    tracing::trace!(?err, "parse error");
3284                                    return Err(err);
3285                                }
3286                            }
3287                        }
3288                        Err(err) => {
3289                            tracing::trace!("error, passthrough");
3290                            Err(err)
3291                        }
3292                    };
3293                    Ok(Ok(Dispatch::Response(typed_result, cx.cast())))
3294                } else {
3295                    tracing::trace!("method doesn't match");
3296                    Ok(Err(Dispatch::Response(result, cx)))
3297                }
3298            }
3299        }
3300    }
3301
3302    /// True if this message has a field with the given name.
3303    ///
3304    /// Returns `false` for Response variants.
3305    #[must_use]
3306    pub fn has_field(&self, field_name: &str) -> bool {
3307        self.message()
3308            .and_then(|m| m.params().get(field_name))
3309            .is_some()
3310    }
3311
3312    /// Returns true if this message has a session-id field.
3313    ///
3314    /// Returns `false` for Response variants.
3315    pub(crate) fn has_session_id(&self) -> bool {
3316        self.has_field("sessionId")
3317    }
3318
3319    /// Extract the ACP session-id from this message (if any).
3320    ///
3321    /// Returns `Ok(None)` for Response variants.
3322    pub(crate) fn get_session_id(&self) -> Result<Option<SessionId>, crate::Error> {
3323        let Some(message) = self.message() else {
3324            return Ok(None);
3325        };
3326        let Some(value) = message.params().get("sessionId") else {
3327            return Ok(None);
3328        };
3329        let session_id = serde_json::from_value(value.clone())?;
3330        Ok(Some(session_id))
3331    }
3332
3333    /// Try to parse this as a notification of the given type.
3334    ///
3335    /// # Returns
3336    ///
3337    /// * `Ok(Ok(typed))` if this is a request/notification of the given types
3338    /// * `Ok(Err(self))` if not
3339    /// * `Err` if has the correct method for the given types but parsing fails
3340    pub fn into_notification<N: JsonRpcNotification>(
3341        self,
3342    ) -> Result<Result<N, Dispatch>, crate::Error> {
3343        match self {
3344            Dispatch::Notification(msg) => {
3345                if !N::matches_method(&msg.method) {
3346                    return Ok(Err(Dispatch::Notification(msg)));
3347                }
3348                match N::parse_message(&msg.method, &msg.params) {
3349                    Ok(n) => Ok(Ok(n)),
3350                    Err(err) => Err(err),
3351                }
3352            }
3353            Dispatch::Request(..) | Dispatch::Response(..) => Ok(Err(self)),
3354        }
3355    }
3356
3357    /// Try to parse this as a request of the given type.
3358    ///
3359    /// # Returns
3360    ///
3361    /// * `Ok(Ok(typed))` if this is a request/notification of the given types
3362    /// * `Ok(Err(self))` if not
3363    /// * `Err` if has the correct method for the given types but parsing fails
3364    pub fn into_request<Req: JsonRpcRequest>(
3365        self,
3366    ) -> Result<Result<(Req, Responder<Req::Response>), Dispatch>, crate::Error> {
3367        match self {
3368            Dispatch::Request(msg, responder) => {
3369                if !Req::matches_method(&msg.method) {
3370                    return Ok(Err(Dispatch::Request(msg, responder)));
3371                }
3372                match Req::parse_message(&msg.method, &msg.params) {
3373                    Ok(req) => Ok(Ok((req, responder.cast()))),
3374                    Err(err) => Err(err),
3375                }
3376            }
3377            Dispatch::Notification(..) | Dispatch::Response(..) => Ok(Err(self)),
3378        }
3379    }
3380}
3381
3382impl<M: JsonRpcRequest + JsonRpcNotification> Dispatch<M, M> {
3383    /// Returns the message payload for requests and notifications.
3384    ///
3385    /// Returns `None` for Response variants since they don't contain a message payload.
3386    pub fn message(&self) -> Option<&M> {
3387        match self {
3388            Dispatch::Request(msg, _) | Dispatch::Notification(msg) => Some(msg),
3389            Dispatch::Response(_, _) => None,
3390        }
3391    }
3392
3393    /// Map the request/notification message.
3394    ///
3395    /// Response variants pass through unchanged.
3396    pub(crate) fn try_map_message(
3397        self,
3398        map_message: impl FnOnce(M) -> Result<M, crate::Error>,
3399    ) -> Result<Dispatch<M, M>, crate::Error> {
3400        match self {
3401            Dispatch::Request(request, cx) => Ok(Dispatch::Request(map_message(request)?, cx)),
3402            Dispatch::Notification(notification) => {
3403                Ok(Dispatch::<M, M>::Notification(map_message(notification)?))
3404            }
3405            Dispatch::Response(result, cx) => Ok(Dispatch::Response(result, cx)),
3406        }
3407    }
3408}
3409
3410/// An incoming JSON message without any typing. Can be a request or a notification.
3411#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
3412pub struct UntypedMessage {
3413    /// The JSON-RPC method name
3414    pub method: String,
3415    /// The JSON-RPC parameters as a raw JSON value
3416    pub params: serde_json::Value,
3417}
3418
3419impl UntypedMessage {
3420    /// Returns an untyped message with the given method and parameters.
3421    pub fn new(method: &str, params: impl Serialize) -> Result<Self, crate::Error> {
3422        let params = serde_json::to_value(params)?;
3423        Ok(Self {
3424            method: method.to_string(),
3425            params,
3426        })
3427    }
3428
3429    /// Returns the method name
3430    #[must_use]
3431    pub fn method(&self) -> &str {
3432        &self.method
3433    }
3434
3435    /// Returns the parameters as a JSON value
3436    #[must_use]
3437    pub fn params(&self) -> &serde_json::Value {
3438        &self.params
3439    }
3440
3441    /// Consumes this message and returns the method and params
3442    #[must_use]
3443    pub fn into_parts(self) -> (String, serde_json::Value) {
3444        (self.method, self.params)
3445    }
3446
3447    /// Convert `self` to a raw JSON-RPC message.
3448    pub(crate) fn into_raw_jsonrpc_message(
3449        self,
3450        id: Option<RequestId>,
3451    ) -> Result<RawJsonRpcMessage, crate::Error> {
3452        let Self { method, params } = self;
3453        match id {
3454            Some(id) => RawJsonRpcMessage::request(method, params, id),
3455            None => RawJsonRpcMessage::notification(method, params),
3456        }
3457    }
3458}
3459
3460impl JsonRpcMessage for UntypedMessage {
3461    fn matches_method(_method: &str) -> bool {
3462        // UntypedMessage matches any method - it's the untyped fallback
3463        true
3464    }
3465
3466    fn method(&self) -> &str {
3467        &self.method
3468    }
3469
3470    fn to_untyped_message(&self) -> Result<UntypedMessage, crate::Error> {
3471        Ok(self.clone())
3472    }
3473
3474    fn parse_message(method: &str, params: &impl Serialize) -> Result<Self, crate::Error> {
3475        UntypedMessage::new(method, params)
3476    }
3477}
3478
3479impl JsonRpcRequest for UntypedMessage {
3480    type Response = serde_json::Value;
3481}
3482
3483impl JsonRpcNotification for UntypedMessage {}
3484
3485/// Represents a pending response of type `R` from an outgoing request.
3486///
3487/// Returned by [`ConnectionTo::send_request`], this type provides methods for handling
3488/// the response without blocking the event loop. The API is intentionally designed to make
3489/// it difficult to accidentally block.
3490///
3491/// # Anti-Footgun Design
3492///
3493/// You cannot directly `.await` a `SentRequest`. Instead, you must choose how to handle
3494/// the response:
3495///
3496/// ## Option 1: Schedule a Callback (Safe in Handlers)
3497///
3498/// Use [`on_receiving_result`](Self::on_receiving_result) to schedule a task
3499/// that runs when the response arrives. This doesn't block the event loop:
3500///
3501/// ```no_run
3502/// # use agent_client_protocol_test::*;
3503/// # async fn example(cx: agent_client_protocol::ConnectionTo<agent_client_protocol::UntypedRole>) -> Result<(), agent_client_protocol::Error> {
3504/// cx.send_request(MyRequest {})
3505///     .on_receiving_result(async |result| {
3506///         match result {
3507///             Ok(response) => {
3508///                 // Handle successful response
3509///                 Ok(())
3510///             }
3511///             Err(error) => {
3512///                 // Handle error
3513///                 Err(error)
3514///             }
3515///         }
3516///     })?;
3517/// # Ok(())
3518/// # }
3519/// ```
3520///
3521/// ## Option 2: Block in a Spawned Task (Safe Only in `spawn`)
3522///
3523/// Use [`block_task`](Self::block_task) to block until the response arrives, but **only**
3524/// in a spawned task (never in a handler):
3525///
3526/// ```no_run
3527/// # use agent_client_protocol_test::*;
3528/// # async fn example(cx: agent_client_protocol::ConnectionTo<agent_client_protocol::UntypedRole>) -> Result<(), agent_client_protocol::Error> {
3529/// // ✅ Safe: Spawned task runs concurrently
3530/// cx.spawn({
3531///     let cx = cx.clone();
3532///     async move {
3533///         let response = cx.send_request(MyRequest {})
3534///             .block_task()
3535///             .await?;
3536///         // Process response...
3537///         Ok(())
3538///     }
3539/// })?;
3540/// # Ok(())
3541/// # }
3542/// ```
3543///
3544/// ```no_run
3545/// # use agent_client_protocol_test::*;
3546/// # async fn example() -> Result<(), agent_client_protocol::Error> {
3547/// # let connection = mock_connection();
3548/// // ❌ NEVER do this in a handler - blocks the event loop!
3549/// connection.on_receive_request(async |req: MyRequest, responder, cx| {
3550///     let response = cx.send_request(MyRequest {})
3551///         .block_task()  // This will deadlock!
3552///         .await?;
3553///     responder.respond(response)
3554/// }, agent_client_protocol::on_receive_request!())
3555/// # .connect_to(agent_client_protocol_test::MockTransport).await?;
3556/// # Ok(())
3557/// # }
3558/// ```
3559///
3560/// # Why This Design?
3561///
3562/// If you block the event loop while waiting for a response, the connection cannot process
3563/// the incoming response message, creating a deadlock. This API design prevents that footgun
3564/// by making blocking explicit and encouraging non-blocking patterns.
3565///
3566/// # Drop Behavior
3567///
3568/// By default, dropping a `SentRequest` without consuming it discards the
3569/// response when it arrives. When the `unstable_cancel_request` feature is
3570/// enabled, dropping a `SentRequest` before the SDK has received the response
3571/// additionally sends a `$/cancel_request` notification asking the peer to
3572/// cancel the request; requests whose eventual response should be ignored, but
3573/// which should keep running on the peer, should use [`detach`](Self::detach)
3574/// instead.
3575#[must_use = "dropping a SentRequest discards the response (and, with the \
3576              `unstable_cancel_request` feature, asks the peer to cancel the \
3577              request); consume it with `block_task`, `on_receiving_result`, \
3578              `forward_response_to`, or `detach`"]
3579pub struct SentRequest<T> {
3580    id: RequestId,
3581    method: String,
3582    task_tx: TaskTx,
3583    response_rx: oneshot::Receiver<ResponsePayload>,
3584    to_result: Box<dyn Fn(serde_json::Value) -> Result<T, crate::Error> + Send>,
3585    #[cfg(feature = "unstable_cancel_request")]
3586    cancellation: SentRequestCancellation,
3587    /// Cancellation markers of other (incoming) requests whose cancellation
3588    /// should be forwarded to this request. See
3589    /// [`forward_cancellation_from`](Self::forward_cancellation_from).
3590    #[cfg(feature = "unstable_cancel_request")]
3591    cancellation_sources: Vec<RequestCancellation>,
3592}
3593
3594#[cfg(feature = "unstable_cancel_request")]
3595#[derive(Clone, Debug)]
3596pub(crate) struct SentRequestCancellationDisarm {
3597    armed: Arc<AtomicBool>,
3598}
3599
3600#[cfg(feature = "unstable_cancel_request")]
3601impl SentRequestCancellationDisarm {
3602    fn new() -> Self {
3603        Self {
3604            armed: Arc::new(AtomicBool::new(true)),
3605        }
3606    }
3607
3608    fn disarm(&self) {
3609        self.armed.store(false, Ordering::Release);
3610    }
3611}
3612
3613#[cfg(feature = "unstable_cancel_request")]
3614struct SentRequestCancellation {
3615    message_tx: OutgoingMessageTx,
3616    remote_style: crate::role::RemoteStyle,
3617    request_id: RequestId,
3618    disarm: SentRequestCancellationDisarm,
3619}
3620
3621#[cfg(feature = "unstable_cancel_request")]
3622impl SentRequestCancellation {
3623    fn new(
3624        message_tx: OutgoingMessageTx,
3625        remote_style: crate::role::RemoteStyle,
3626        request_id: RequestId,
3627    ) -> Self {
3628        Self {
3629            message_tx,
3630            remote_style,
3631            request_id,
3632            disarm: SentRequestCancellationDisarm::new(),
3633        }
3634    }
3635
3636    fn disarm(&self) {
3637        self.disarm.disarm();
3638    }
3639
3640    fn disarm_handle(&self) -> SentRequestCancellationDisarm {
3641        self.disarm.clone()
3642    }
3643
3644    fn send(&self) -> Result<(), crate::Error> {
3645        if !self.disarm.armed.swap(false, Ordering::AcqRel) {
3646            return Ok(());
3647        }
3648
3649        // Build the notification lazily: most requests are never cancelled,
3650        // so this avoids serializing a notification per outgoing request.
3651        let untyped = self.remote_style.transform_outgoing_message(
3652            crate::schema::v1::CancelRequestNotification::new(self.request_id.clone()),
3653        )?;
3654
3655        send_raw_message(&self.message_tx, OutgoingMessage::Notification { untyped })
3656    }
3657}
3658
3659#[cfg(feature = "unstable_cancel_request")]
3660impl Drop for SentRequestCancellation {
3661    fn drop(&mut self) {
3662        if let Err(error) = self.send() {
3663            tracing::debug!(?error, "failed to auto-cancel dropped request");
3664        }
3665    }
3666}
3667
3668#[cfg(feature = "unstable_cancel_request")]
3669impl Debug for SentRequestCancellation {
3670    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3671        f.debug_struct("SentRequestCancellation")
3672            .field("request_id", &self.request_id)
3673            .field("remote_style", &self.remote_style)
3674            .field("armed", &self.disarm.armed.load(Ordering::Acquire))
3675            .finish_non_exhaustive()
3676    }
3677}
3678
3679/// Await the response payload for an outgoing request, watching `sources` for
3680/// cancellation of the upstream requests it was registered with.
3681///
3682/// When any source reports cancellation, a `$/cancel_request` is forwarded to
3683/// the outgoing request (at most once, shared with [`SentRequest::cancel`] and
3684/// drop-time auto-cancellation), and the response is *still* awaited: the peer
3685/// always answers, with normal data or a cancellation error.
3686///
3687/// Watching is deliberately bounded by response arrival so that completed
3688/// requests do not leak waiters on markers that will never fire.
3689#[cfg(feature = "unstable_cancel_request")]
3690async fn await_response_forwarding_cancellation(
3691    response_rx: oneshot::Receiver<ResponsePayload>,
3692    cancellation: &SentRequestCancellation,
3693    sources: &[RequestCancellation],
3694) -> Result<ResponsePayload, oneshot::Canceled> {
3695    // Failing to forward the cancellation must not abort the wait: the
3696    // response (normal data or a cancellation error) may still arrive and
3697    // must still be processed.
3698    let forward_cancellation = || {
3699        if let Err(error) = cancellation.send() {
3700            tracing::debug!(
3701                ?error,
3702                "failed to forward cancellation to downstream request"
3703            );
3704        }
3705    };
3706
3707    let response = if sources.is_empty() {
3708        response_rx.await
3709    } else if sources.iter().any(RequestCancellation::is_cancelled) {
3710        forward_cancellation();
3711        response_rx.await
3712    } else {
3713        let cancelled = sources.iter().map(|source| source.state.signal_rx.clone());
3714        match future::select(future::select_all(cancelled), response_rx).await {
3715            Either::Left((_, response_rx)) => {
3716                forward_cancellation();
3717                response_rx.await
3718            }
3719            Either::Right((response, _)) => response,
3720        }
3721    };
3722
3723    cancellation.disarm();
3724    response
3725}
3726
3727impl<T: Debug> Debug for SentRequest<T> {
3728    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3729        let mut debug = f.debug_struct("SentRequest");
3730        debug
3731            .field("id", &self.id)
3732            .field("method", &self.method)
3733            .field("task_tx", &self.task_tx)
3734            .field("response_rx", &self.response_rx);
3735        #[cfg(feature = "unstable_cancel_request")]
3736        debug
3737            .field("cancellation", &self.cancellation)
3738            .field("cancellation_sources", &self.cancellation_sources);
3739        debug.finish_non_exhaustive()
3740    }
3741}
3742
3743impl SentRequest<serde_json::Value> {
3744    fn new(
3745        id: RequestId,
3746        method: String,
3747        task_tx: mpsc::UnboundedSender<Task>,
3748        response_rx: oneshot::Receiver<ResponsePayload>,
3749        #[cfg(feature = "unstable_cancel_request")] cancellation: SentRequestCancellation,
3750    ) -> Self {
3751        Self {
3752            id,
3753            method,
3754            response_rx,
3755            task_tx,
3756            to_result: Box::new(Ok),
3757            #[cfg(feature = "unstable_cancel_request")]
3758            cancellation,
3759            #[cfg(feature = "unstable_cancel_request")]
3760            cancellation_sources: Vec::new(),
3761        }
3762    }
3763}
3764
3765impl<T> SentRequest<T> {
3766    /// Detach this request handle without waiting for its response.
3767    ///
3768    /// The response will be discarded when it arrives. When the
3769    /// `unstable_cancel_request` feature is enabled, this also disarms the
3770    /// drop-time automatic cancellation described in
3771    /// [Drop Behavior](Self#drop-behavior), so use it for requests whose
3772    /// eventual response should be ignored, but which should keep running on the
3773    /// peer. The peer is still expected to answer the JSON-RPC request
3774    /// eventually; use a notification instead when no response is expected at
3775    /// all.
3776    ///
3777    /// To ask the peer to stop the request, enable `unstable_cancel_request`
3778    /// and call `cancel` instead, or drop the handle while automatic
3779    /// cancellation is enabled.
3780    pub fn detach(self) {
3781        #[cfg(feature = "unstable_cancel_request")]
3782        self.cancellation.disarm();
3783    }
3784
3785    /// Send a `$/cancel_request` notification for this outgoing request.
3786    ///
3787    /// This uses the same peer and message wrapping that were used to send the
3788    /// original request, so it is the preferred way to cancel a [`SentRequest`]
3789    /// when the request handle is still available.
3790    ///
3791    /// At most one `$/cancel_request` is ever sent per request: the first
3792    /// `cancel` call sends it (and also prevents the drop-time automatic
3793    /// cancellation described in [Drop Behavior](Self#drop-behavior)), while
3794    /// later calls return `Ok(())` without sending anything. Likewise, once
3795    /// the SDK has routed the response to this handle, `cancel` becomes a
3796    /// no-op: there is nothing left to cancel.
3797    ///
3798    /// Errors are only reported by the call that attempts to send the
3799    /// notification.
3800    #[cfg(feature = "unstable_cancel_request")]
3801    pub fn cancel(&self) -> Result<(), crate::Error> {
3802        self.cancellation.send()
3803    }
3804
3805    /// Forward cancellation of another request to this one.
3806    ///
3807    /// When the request that `source` belongs to is cancelled by its peer,
3808    /// a `$/cancel_request` for *this* request is sent to its peer, using the
3809    /// same wrapping as the original request. The response is still awaited
3810    /// and delivered as usual (normal data or a cancellation error), so this
3811    /// composes with [`block_task`](Self::block_task) and
3812    /// [`on_receiving_result`](Self::on_receiving_result).
3813    ///
3814    /// This is the building block for proxies that forward a request with
3815    /// custom logic instead of [`forward_response_to`](Self::forward_response_to)
3816    /// (which wires this up automatically from its responder). Without it,
3817    /// custom forwarding *absorbs* cancellation: the upstream marker is still
3818    /// set, but nothing is sent downstream.
3819    ///
3820    /// ```
3821    /// # use agent_client_protocol::{ConnectionTo, Error, Responder, UntypedRole};
3822    /// # use agent_client_protocol_test::{MyRequest, MyResponse};
3823    /// # async fn example(request: MyRequest, responder: Responder<MyResponse>, backend: ConnectionTo<UntypedRole>) -> Result<(), Error> {
3824    /// backend
3825    ///     .send_request(request)
3826    ///     .forward_cancellation_from(responder.cancellation())
3827    ///     .on_receiving_result(async move |result| {
3828    ///         // Custom result handling, e.g. bookkeeping or rewriting.
3829    ///         responder.respond_with_result(result)
3830    ///     })?;
3831    /// # Ok(())
3832    /// # }
3833    /// ```
3834    ///
3835    /// May be called multiple times; cancellation of any registered source
3836    /// triggers the forwarding (at most one `$/cancel_request` is ever sent
3837    /// per request). Sources are observed while the response is being
3838    /// awaited — that is, once the handle is consumed with
3839    /// [`block_task`](Self::block_task),
3840    /// [`on_receiving_result`](Self::on_receiving_result), or
3841    /// [`forward_response_to`](Self::forward_response_to); a source that was
3842    /// already cancelled by then is honored immediately.
3843    #[cfg(feature = "unstable_cancel_request")]
3844    pub fn forward_cancellation_from(mut self, source: RequestCancellation) -> Self {
3845        self.cancellation_sources.push(source);
3846        self
3847    }
3848}
3849
3850impl<T: JsonRpcResponse> SentRequest<T> {
3851    /// The id of the outgoing request.
3852    #[must_use]
3853    pub fn id(&self) -> serde_json::Value {
3854        crate::util::id_to_json(&self.id)
3855    }
3856
3857    /// The method of the request this is in response to.
3858    #[must_use]
3859    pub fn method(&self) -> &str {
3860        &self.method
3861    }
3862
3863    /// Create a new response that maps the result of the response to a new type.
3864    pub fn map<U>(
3865        self,
3866        map_fn: impl Fn(T) -> Result<U, crate::Error> + 'static + Send,
3867    ) -> SentRequest<U> {
3868        SentRequest {
3869            id: self.id,
3870            method: self.method,
3871            response_rx: self.response_rx,
3872            task_tx: self.task_tx,
3873            to_result: Box::new(move |value| map_fn((self.to_result)(value)?)),
3874            #[cfg(feature = "unstable_cancel_request")]
3875            cancellation: self.cancellation,
3876            #[cfg(feature = "unstable_cancel_request")]
3877            cancellation_sources: self.cancellation_sources,
3878        }
3879    }
3880
3881    /// Forward the response (success or error) to a request context when it arrives.
3882    ///
3883    /// This is a convenience method for proxying messages between connections. When the
3884    /// response arrives, it will be automatically sent to the provided request context,
3885    /// whether it's a successful response or an error.
3886    ///
3887    /// # Example: Proxying requests
3888    ///
3889    /// ```
3890    /// # use agent_client_protocol::UntypedRole;
3891    /// # use agent_client_protocol::{Builder, ConnectionTo};
3892    /// # use agent_client_protocol_test::*;
3893    /// # async fn example(cx: ConnectionTo<UntypedRole>) -> Result<(), agent_client_protocol::Error> {
3894    /// // Set up backend connection builder
3895    /// let backend = UntypedRole.builder()
3896    ///     .on_receive_request(async |req: MyRequest, responder, cx| {
3897    ///         responder.respond(MyResponse { status: "ok".into() })
3898    ///     }, agent_client_protocol::on_receive_request!());
3899    ///
3900    /// // Spawn backend and get a context to send to it
3901    /// let backend_connection = cx.spawn_connection(backend, MockTransport)?;
3902    ///
3903    /// // Set up proxy that forwards requests to backend
3904    /// UntypedRole.builder()
3905    ///     .on_receive_request({
3906    ///         let backend_connection = backend_connection.clone();
3907    ///         async move |req: MyRequest, responder, cx| {
3908    ///             // Forward the request to backend and proxy the response back
3909    ///             backend_connection.send_request(req)
3910    ///                 .forward_response_to(responder)?;
3911    ///             Ok(())
3912    ///         }
3913    ///     }, agent_client_protocol::on_receive_request!());
3914    /// # Ok(())
3915    /// # }
3916    /// ```
3917    ///
3918    /// # Type Safety
3919    ///
3920    /// The request context's response type must match the request's response type,
3921    /// ensuring type-safe message forwarding.
3922    ///
3923    /// # When to Use
3924    ///
3925    /// Use this when:
3926    /// - You're implementing a proxy or gateway pattern
3927    /// - You want to forward responses without processing them
3928    /// - The response types match between the outgoing request and incoming request
3929    ///
3930    /// This is equivalent to calling `on_receiving_result` and manually forwarding
3931    /// the result, with two proxy-specific additions:
3932    ///
3933    /// - If the pending response is dropped without ever being delivered (for
3934    ///   example, the downstream connection closed), the incoming request is
3935    ///   answered with an internal error instead of being left unanswered.
3936    /// - When the `unstable_cancel_request` feature is enabled and the peer
3937    ///   cancels the incoming request, the cancellation is forwarded to the
3938    ///   outgoing request, and the downstream response (normal data or a
3939    ///   cancellation error) is still forwarded back. This is equivalent to
3940    ///   registering the responder's marker with `forward_cancellation_from`.
3941    #[track_caller]
3942    pub fn forward_response_to(self, responder: Responder<T>) -> Result<(), crate::Error>
3943    where
3944        T: Send,
3945    {
3946        #[cfg(feature = "unstable_cancel_request")]
3947        let this = self.forward_cancellation_from(responder.cancellation());
3948        #[cfg(not(feature = "unstable_cancel_request"))]
3949        let this = self;
3950
3951        this.consume_with(async move |response| {
3952            // A response that was never delivered (outer `Err`, e.g. the
3953            // downstream connection closed) is forwarded as an error: the
3954            // incoming request must not be left unanswered.
3955            responder.respond_with_result(response.unwrap_or_else(Err))
3956        })
3957    }
3958
3959    /// Spawn the response-consumption task shared by
3960    /// [`on_receiving_result`](Self::on_receiving_result) and
3961    /// [`forward_response_to`](Self::forward_response_to).
3962    ///
3963    /// The task awaits the response (forwarding cancellation from registered
3964    /// sources while waiting, when the `unstable_cancel_request` feature is
3965    /// enabled), converts the payload, and invokes `handle` with the typed
3966    /// result (`Ok(Result<T, _>)`). The dispatch loop's ack, if any, is sent
3967    /// after `handle` completes.
3968    ///
3969    /// If the pending response is dropped without ever being delivered (for
3970    /// example, the connection closed), `handle` receives the outer `Err`
3971    /// describing the loss; there is no ack in that case.
3972    #[track_caller]
3973    fn consume_with<F>(
3974        self,
3975        handle: impl FnOnce(Result<Result<T, crate::Error>, crate::Error>) -> F + 'static + Send,
3976    ) -> Result<(), crate::Error>
3977    where
3978        F: Future<Output = Result<(), crate::Error>> + 'static + Send,
3979        T: Send,
3980    {
3981        let task_tx = self.task_tx.clone();
3982        let method = self.method;
3983        let response_rx = self.response_rx;
3984        let to_result = self.to_result;
3985        #[cfg(feature = "unstable_cancel_request")]
3986        let cancellation = self.cancellation;
3987        #[cfg(feature = "unstable_cancel_request")]
3988        let cancellation_sources = self.cancellation_sources;
3989        let location = Location::caller();
3990
3991        Task::new(location, async move {
3992            #[cfg(feature = "unstable_cancel_request")]
3993            let response = await_response_forwarding_cancellation(
3994                response_rx,
3995                &cancellation,
3996                &cancellation_sources,
3997            )
3998            .await;
3999            #[cfg(not(feature = "unstable_cancel_request"))]
4000            let response = response_rx.await;
4001
4002            match response {
4003                Ok(ResponsePayload { result, ack_tx }) => {
4004                    // Convert the result using to_result for Ok values
4005                    let typed_result = match result {
4006                        Ok(json_value) => to_result(json_value),
4007                        Err(err) => Err(err),
4008                    };
4009
4010                    let outcome = handle(Ok(typed_result)).await;
4011
4012                    // Ack AFTER the handler completes - this is the key
4013                    // difference from block_task. The dispatch loop waits for
4014                    // this ack.
4015                    if let Some(tx) = ack_tx {
4016                        let _ = tx.send(());
4017                    }
4018
4019                    outcome
4020                }
4021                Err(err) => {
4022                    handle(Err(crate::util::internal_error(format!(
4023                        "response to `{method}` never received: {err}"
4024                    ))))
4025                    .await
4026                }
4027            }
4028        })
4029        .spawn(&task_tx)
4030    }
4031
4032    /// Block the current task until the response is received.
4033    ///
4034    /// **Warning:** This method blocks the current async task. It is **only safe** to use
4035    /// in spawned tasks created with [`ConnectionTo::spawn`]. Using it directly in a
4036    /// handler callback will deadlock the connection.
4037    ///
4038    /// # Safe Usage (in spawned tasks)
4039    ///
4040    /// ```no_run
4041    /// # use agent_client_protocol_test::*;
4042    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
4043    /// # let connection = mock_connection();
4044    /// connection.on_receive_request(async |req: MyRequest, responder, cx| {
4045    ///     // Spawn a task to handle the request
4046    ///     cx.spawn({
4047    ///         let connection = cx.clone();
4048    ///         async move {
4049    ///             // Safe: We're in a spawned task, not blocking the event loop
4050    ///             let response = connection.send_request(OtherRequest {})
4051    ///                 .block_task()
4052    ///                 .await?;
4053    ///
4054    ///             // Process the response...
4055    ///             Ok(())
4056    ///         }
4057    ///     })?;
4058    ///
4059    ///     // Respond immediately
4060    ///     responder.respond(MyResponse { status: "ok".into() })
4061    /// }, agent_client_protocol::on_receive_request!())
4062    /// # .connect_to(agent_client_protocol_test::MockTransport).await?;
4063    /// # Ok(())
4064    /// # }
4065    /// ```
4066    ///
4067    /// # Unsafe Usage (in handlers - will deadlock!)
4068    ///
4069    /// ```no_run
4070    /// # use agent_client_protocol_test::*;
4071    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
4072    /// # let connection = mock_connection();
4073    /// connection.on_receive_request(async |req: MyRequest, responder, cx| {
4074    ///     // ❌ DEADLOCK: Handler blocks event loop, which can't process the response
4075    ///     let response = cx.send_request(OtherRequest {})
4076    ///         .block_task()
4077    ///         .await?;
4078    ///
4079    ///     responder.respond(MyResponse { status: response.value })
4080    /// }, agent_client_protocol::on_receive_request!())
4081    /// # .connect_to(agent_client_protocol_test::MockTransport).await?;
4082    /// # Ok(())
4083    /// # }
4084    /// ```
4085    ///
4086    /// # When to Use
4087    ///
4088    /// Use this method when:
4089    /// - You're in a spawned task (via [`ConnectionTo::spawn`])
4090    /// - You need the response value to proceed with your logic
4091    /// - Linear control flow is more natural than callbacks
4092    ///
4093    /// For handler callbacks, use [`on_receiving_result`](Self::on_receiving_result) instead.
4094    pub async fn block_task(self) -> Result<T, crate::Error>
4095    where
4096        T: Send,
4097    {
4098        #[cfg(feature = "unstable_cancel_request")]
4099        let response = await_response_forwarding_cancellation(
4100            self.response_rx,
4101            &self.cancellation,
4102            &self.cancellation_sources,
4103        )
4104        .await;
4105        #[cfg(not(feature = "unstable_cancel_request"))]
4106        let response = self.response_rx.await;
4107
4108        match response {
4109            Ok(ResponsePayload {
4110                result: Ok(json_value),
4111                ack_tx,
4112            }) => {
4113                // Ack immediately - we're in a spawned task, so the dispatch loop
4114                // can continue while we process the value.
4115                if let Some(tx) = ack_tx {
4116                    let _ = tx.send(());
4117                }
4118                match (self.to_result)(json_value) {
4119                    Ok(value) => Ok(value),
4120                    Err(err) => Err(err),
4121                }
4122            }
4123            Ok(ResponsePayload {
4124                result: Err(err),
4125                ack_tx,
4126            }) => {
4127                if let Some(tx) = ack_tx {
4128                    let _ = tx.send(());
4129                }
4130                Err(err)
4131            }
4132            Err(err) => Err(crate::util::internal_error(format!(
4133                "response to `{}` never received: {}",
4134                self.method, err
4135            ))),
4136        }
4137    }
4138
4139    /// Schedule an async task to run when a successful response is received.
4140    ///
4141    /// This is a convenience wrapper around [`on_receiving_result`](Self::on_receiving_result)
4142    /// for the common pattern of forwarding errors to a request context while only processing
4143    /// successful responses.
4144    ///
4145    /// # Behavior
4146    ///
4147    /// - If the response is `Ok(value)`, your task receives the value and the request context
4148    /// - If the response is `Err(error)`, the error is automatically sent to `responder`
4149    ///   and your task is not called
4150    ///
4151    /// # Example: Chaining requests
4152    ///
4153    /// ```no_run
4154    /// # use agent_client_protocol_test::*;
4155    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
4156    /// # let connection = mock_connection();
4157    /// connection.on_receive_request(async |req: ValidateRequest, responder, cx| {
4158    ///     // Send initial request
4159    ///     cx.send_request(ValidateRequest { data: req.data.clone() })
4160    ///         .on_receiving_ok_result(responder, async |validation, responder| {
4161    ///             // Only runs if validation succeeded
4162    ///             if validation.is_valid {
4163    ///                 // Respond to original request
4164    ///                 responder.respond(ValidateResponse { is_valid: true, error: None })
4165    ///             } else {
4166    ///                 responder.respond_with_error(agent_client_protocol::util::internal_error("validation failed"))
4167    ///             }
4168    ///         })?;
4169    ///
4170    ///     Ok(())
4171    /// }, agent_client_protocol::on_receive_request!())
4172    /// # .connect_to(agent_client_protocol_test::MockTransport).await?;
4173    /// # Ok(())
4174    /// # }
4175    /// ```
4176    ///
4177    /// # Ordering
4178    ///
4179    /// Like [`on_receiving_result`](Self::on_receiving_result), the callback blocks the
4180    /// dispatch loop until it completes. See the [`ordering`](crate::concepts::ordering) module
4181    /// for details.
4182    ///
4183    /// # When to Use
4184    ///
4185    /// Use this when:
4186    /// - You need to respond to a request based on another request's result
4187    /// - You want errors to automatically propagate to the request context
4188    /// - You only care about the success case
4189    ///
4190    /// For more control over error handling, use [`on_receiving_result`](Self::on_receiving_result).
4191    #[track_caller]
4192    pub fn on_receiving_ok_result<F>(
4193        self,
4194        responder: Responder<T>,
4195        task: impl FnOnce(T, Responder<T>) -> F + 'static + Send,
4196    ) -> Result<(), crate::Error>
4197    where
4198        F: Future<Output = Result<(), crate::Error>> + 'static + Send,
4199        T: Send,
4200    {
4201        self.on_receiving_result(async move |result| match result {
4202            Ok(value) => task(value, responder).await,
4203            Err(err) => responder.respond_with_error(err),
4204        })
4205    }
4206
4207    /// Schedule an async task to run when the response is received.
4208    ///
4209    /// This is the recommended way to handle responses in handler callbacks, as it doesn't
4210    /// block the event loop. The task will be spawned automatically when the response arrives.
4211    ///
4212    /// # Example: Handle response in callback
4213    ///
4214    /// ```no_run
4215    /// # use agent_client_protocol_test::*;
4216    /// # async fn example() -> Result<(), agent_client_protocol::Error> {
4217    /// # let connection = mock_connection();
4218    /// connection.on_receive_request(async |req: MyRequest, responder, cx| {
4219    ///     // Send a request and schedule a callback for the response
4220    ///     cx.send_request(QueryRequest { id: 22 })
4221    ///         .on_receiving_result({
4222    ///             let connection = cx.clone();
4223    ///             async move |result| {
4224    ///                 match result {
4225    ///                     Ok(response) => {
4226    ///                         println!("Got response: {:?}", response);
4227    ///                         // Can send more messages here
4228    ///                         connection.send_notification(QueryComplete {})?;
4229    ///                         Ok(())
4230    ///                 }
4231    ///                     Err(error) => {
4232    ///                         eprintln!("Request failed: {}", error);
4233    ///                         Err(error)
4234    ///                     }
4235    ///                 }
4236    ///             }
4237    ///         })?;
4238    ///
4239    ///     // Handler continues immediately without waiting
4240    ///     responder.respond(MyResponse { status: "processing".into() })
4241    /// }, agent_client_protocol::on_receive_request!())
4242    /// # .connect_to(agent_client_protocol_test::MockTransport).await?;
4243    /// # Ok(())
4244    /// # }
4245    /// ```
4246    ///
4247    /// # Ordering
4248    ///
4249    /// The callback runs as a spawned task, but the dispatch loop waits for it to complete
4250    /// before processing the next message. This gives you ordering guarantees: no other
4251    /// messages will be processed while your callback runs.
4252    ///
4253    /// This differs from [`block_task`](Self::block_task), which signals completion immediately
4254    /// upon receiving the response (before your code processes it).
4255    ///
4256    /// See the [`ordering`](crate::concepts::ordering) module for details on ordering guarantees
4257    /// and how to avoid deadlocks.
4258    ///
4259    /// # Error Handling
4260    ///
4261    /// If the scheduled task returns `Err`, the entire server will shut down. Make sure to handle
4262    /// errors appropriately within your task.
4263    ///
4264    /// # When to Use
4265    ///
4266    /// Use this method when:
4267    /// - You're in a handler callback (not a spawned task)
4268    /// - You want ordering guarantees (no other messages processed during your callback)
4269    /// - You need to do async work before "releasing" control back to the dispatch loop
4270    ///
4271    /// For spawned tasks where you don't need ordering guarantees, consider [`block_task`](Self::block_task).
4272    #[track_caller]
4273    pub fn on_receiving_result<F>(
4274        self,
4275        task: impl FnOnce(Result<T, crate::Error>) -> F + 'static + Send,
4276    ) -> Result<(), crate::Error>
4277    where
4278        F: Future<Output = Result<(), crate::Error>> + 'static + Send,
4279        T: Send,
4280    {
4281        self.consume_with(async move |response| {
4282            match response {
4283                // Run the user's callback on the peer's result.
4284                Ok(result) => task(result).await,
4285                // A response that was never delivered fails the consuming
4286                // task instead of invoking the callback.
4287                Err(err) => Err(err),
4288            }
4289        })
4290    }
4291}
4292
4293// ============================================================================
4294// IntoJrConnectionTransport Implementations
4295// ============================================================================
4296
4297/// A component that communicates over line streams.
4298///
4299/// `Lines` implements the [`ConnectTo`] trait for any pair of line-based streams
4300/// (a `Stream<Item = io::Result<String>>` for incoming and a `Sink<String>` for outgoing),
4301/// handling serialization of JSON-RPC messages to/from newline-delimited JSON.
4302///
4303/// This is a lower-level primitive than [`ByteStreams`] that enables interception and
4304/// transformation of individual lines before they are parsed or after they are serialized.
4305/// This is particularly useful for debugging, logging, or implementing custom line-based
4306/// protocols.
4307///
4308/// # Use Cases
4309///
4310/// - **Line-by-line logging**: Intercept and log each line before parsing
4311/// - **Custom protocols**: Transform lines before/after JSON-RPC processing
4312/// - **Debugging**: Inspect raw message strings
4313/// - **Line filtering**: Skip or modify specific messages
4314///
4315/// Most users should use [`ByteStreams`] instead, which provides a simpler interface
4316/// for byte-based I/O.
4317///
4318/// [`ConnectTo`]: crate::ConnectTo
4319#[derive(Debug)]
4320pub struct Lines<OutgoingSink, IncomingStream> {
4321    /// Outgoing line sink (where we write serialized JSON-RPC messages)
4322    pub outgoing: OutgoingSink,
4323    /// Incoming line stream (where we read and parse JSON-RPC messages)
4324    pub incoming: IncomingStream,
4325}
4326
4327impl<OutgoingSink, IncomingStream> Lines<OutgoingSink, IncomingStream>
4328where
4329    OutgoingSink: futures::Sink<String, Error = std::io::Error> + Send + 'static,
4330    IncomingStream: futures::Stream<Item = std::io::Result<String>> + Send + 'static,
4331{
4332    /// Create a new line stream transport.
4333    pub fn new(outgoing: OutgoingSink, incoming: IncomingStream) -> Self {
4334        Self { outgoing, incoming }
4335    }
4336}
4337
4338impl<OutgoingSink, IncomingStream, R: Role> ConnectTo<R> for Lines<OutgoingSink, IncomingStream>
4339where
4340    OutgoingSink: futures::Sink<String, Error = std::io::Error> + Send + 'static,
4341    IncomingStream: futures::Stream<Item = std::io::Result<String>> + Send + 'static,
4342{
4343    async fn connect_to(self, client: impl ConnectTo<R::Counterpart>) -> Result<(), crate::Error> {
4344        let (channel, serve_self) = ConnectTo::<R>::into_channel_and_future(self);
4345        match futures::future::select(Box::pin(client.connect_to(channel)), serve_self).await {
4346            Either::Left((result, _)) | Either::Right((result, _)) => result,
4347        }
4348    }
4349
4350    fn into_channel_and_future(self) -> (Channel, BoxFuture<'static, Result<(), crate::Error>>) {
4351        let Self { outgoing, incoming } = self;
4352
4353        // Create a channel pair for the client to use
4354        let (channel_for_caller, channel_for_lines) = Channel::duplex();
4355
4356        // Create the server future that runs the line stream actors
4357        let server_future = Box::pin(async move {
4358            let Channel { rx, tx } = channel_for_lines;
4359
4360            // Run both actors concurrently
4361            let outgoing_future = transport_actor::transport_outgoing_lines_actor(rx, outgoing);
4362            let incoming_future = transport_actor::transport_incoming_lines_actor(incoming, tx);
4363
4364            // Wait for both to complete
4365            futures::try_join!(outgoing_future, incoming_future)?;
4366
4367            Ok(())
4368        });
4369
4370        (channel_for_caller, server_future)
4371    }
4372}
4373
4374/// A component that communicates over byte streams (stdin/stdout, sockets, pipes, etc.).
4375///
4376/// `ByteStreams` implements the [`ConnectTo`] trait for any pair of `AsyncRead` and `AsyncWrite`
4377/// streams, handling serialization of JSON-RPC messages to/from newline-delimited JSON.
4378/// This is the standard way to communicate with external processes or network connections.
4379///
4380/// # Use Cases
4381///
4382/// - **Stdio communication**: Connect to agents or proxies via stdin/stdout
4383/// - **Network sockets**: TCP, Unix domain sockets, or other stream-based protocols
4384/// - **Named pipes**: Cross-process communication on the same machine
4385/// - **File I/O**: Reading from and writing to file descriptors
4386///
4387/// # Example
4388///
4389/// Connecting to an agent via stdio:
4390///
4391/// ```no_run
4392/// use agent_client_protocol::UntypedRole;
4393/// # use agent_client_protocol::{ByteStreams};
4394/// use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
4395///
4396/// # async fn example() -> Result<(), agent_client_protocol::Error> {
4397/// let component = ByteStreams::new(
4398///     tokio::io::stdout().compat_write(),
4399///     tokio::io::stdin().compat(),
4400/// );
4401///
4402/// // Use as a component in a connection
4403/// agent_client_protocol::UntypedRole.builder()
4404///     .name("my-client")
4405///     .connect_to(component)
4406///     .await?;
4407/// # Ok(())
4408/// # }
4409/// ```
4410///
4411/// [`ConnectTo`]: crate::ConnectTo
4412#[derive(Debug)]
4413pub struct ByteStreams<OB, IB> {
4414    /// Outgoing byte stream (where we write serialized messages)
4415    pub outgoing: OB,
4416    /// Incoming byte stream (where we read and parse messages)
4417    pub incoming: IB,
4418}
4419
4420impl<OB, IB> ByteStreams<OB, IB>
4421where
4422    OB: AsyncWrite + Send + 'static,
4423    IB: AsyncRead + Send + 'static,
4424{
4425    /// Create a new byte stream transport.
4426    pub fn new(outgoing: OB, incoming: IB) -> Self {
4427        Self { outgoing, incoming }
4428    }
4429}
4430
4431impl<OB, IB, R: Role> ConnectTo<R> for ByteStreams<OB, IB>
4432where
4433    OB: AsyncWrite + Send + 'static,
4434    IB: AsyncRead + Send + 'static,
4435{
4436    async fn connect_to(self, client: impl ConnectTo<R::Counterpart>) -> Result<(), crate::Error> {
4437        let (channel, serve_self) = ConnectTo::<R>::into_channel_and_future(self);
4438        match futures::future::select(pin!(client.connect_to(channel)), serve_self).await {
4439            Either::Left((result, _)) | Either::Right((result, _)) => result,
4440        }
4441    }
4442
4443    fn into_channel_and_future(self) -> (Channel, BoxFuture<'static, Result<(), crate::Error>>) {
4444        use futures::AsyncBufReadExt;
4445        use futures::AsyncWriteExt;
4446        use futures::io::BufReader;
4447        let Self { outgoing, incoming } = self;
4448
4449        // Convert byte streams to line streams
4450        // Box both streams to satisfy Unpin requirements
4451        let incoming_lines = Box::pin(BufReader::new(incoming).lines());
4452
4453        // Create a sink that writes lines (with newlines) to the outgoing byte stream
4454        // We need to Box the writer since it may not be Unpin
4455        let outgoing_sink =
4456            futures::sink::unfold(Box::pin(outgoing), async move |mut writer, line: String| {
4457                let mut bytes = line.into_bytes();
4458                bytes.push(b'\n');
4459                writer.write_all(&bytes).await?;
4460                Ok::<_, std::io::Error>(writer)
4461            });
4462
4463        // Delegate to Lines component
4464        ConnectTo::<R>::into_channel_and_future(Lines::new(outgoing_sink, incoming_lines))
4465    }
4466}
4467
4468/// A channel endpoint representing one side of a bidirectional message channel.
4469///
4470/// `Channel` represents a single endpoint's view of a bidirectional communication channel.
4471/// Each endpoint has:
4472/// - `rx`: A receiver for incoming messages (or errors) from the counterpart
4473/// - `tx`: A sender for outgoing messages (or errors) to the counterpart
4474///
4475/// # Example
4476///
4477/// ```no_run
4478/// # use agent_client_protocol::UntypedRole;
4479/// # use agent_client_protocol::{Channel, Builder};
4480/// # async fn example() -> Result<(), agent_client_protocol::Error> {
4481/// // Create a pair of connected channels
4482/// let (channel_a, channel_b) = Channel::duplex();
4483///
4484/// // Each channel can be used by a different component
4485/// UntypedRole.builder()
4486///     .name("connection-a")
4487///     .connect_to(channel_a)
4488///     .await?;
4489/// # Ok(())
4490/// # }
4491/// ```
4492#[derive(Debug)]
4493pub struct Channel {
4494    /// Receives messages (or errors) from the counterpart.
4495    pub rx: mpsc::UnboundedReceiver<Result<RawJsonRpcMessage, crate::Error>>,
4496    /// Sends messages (or errors) to the counterpart.
4497    pub tx: mpsc::UnboundedSender<Result<RawJsonRpcMessage, crate::Error>>,
4498}
4499
4500impl Channel {
4501    /// Create a pair of connected channel endpoints.
4502    ///
4503    /// Returns two `Channel` instances that are connected to each other:
4504    /// - Messages sent via `channel_a.tx` are received on `channel_b.rx`
4505    /// - Messages sent via `channel_b.tx` are received on `channel_a.rx`
4506    ///
4507    /// # Returns
4508    ///
4509    /// A tuple `(channel_a, channel_b)` of connected channel endpoints.
4510    #[must_use]
4511    pub fn duplex() -> (Self, Self) {
4512        // Create channels: A sends Result<Message> which B receives as Message
4513        let (a_tx, b_rx) = mpsc::unbounded();
4514        let (b_tx, a_rx) = mpsc::unbounded();
4515
4516        let channel_a = Self { rx: a_rx, tx: a_tx };
4517        let channel_b = Self { rx: b_rx, tx: b_tx };
4518
4519        (channel_a, channel_b)
4520    }
4521
4522    /// Copy messages from `rx` to `tx`.
4523    ///
4524    /// # Returns
4525    ///
4526    /// A `Result` indicating success or failure.
4527    pub async fn copy(mut self) -> Result<(), crate::Error> {
4528        while let Some(msg) = self.rx.next().await {
4529            self.tx
4530                .unbounded_send(msg)
4531                .map_err(crate::util::internal_error)?;
4532        }
4533        Ok(())
4534    }
4535}
4536
4537impl<R: Role> ConnectTo<R> for Channel {
4538    async fn connect_to(self, client: impl ConnectTo<R::Counterpart>) -> Result<(), crate::Error> {
4539        let (client_channel, client_serve) = client.into_channel_and_future();
4540
4541        match futures::try_join!(
4542            Channel {
4543                rx: client_channel.rx,
4544                tx: self.tx
4545            }
4546            .copy(),
4547            Channel {
4548                rx: self.rx,
4549                tx: client_channel.tx
4550            }
4551            .copy(),
4552            client_serve
4553        ) {
4554            Ok(((), (), ())) => Ok(()),
4555            Err(err) => Err(err),
4556        }
4557    }
4558
4559    fn into_channel_and_future(self) -> (Channel, BoxFuture<'static, Result<(), crate::Error>>) {
4560        (self, Box::pin(future::ready(Ok(()))))
4561    }
4562}
4563
4564#[cfg(test)]
4565mod tests {
4566    use super::*;
4567
4568    #[cfg(feature = "unstable_cancel_request")]
4569    #[test]
4570    fn peel_successor_envelopes_returns_plain_messages_unchanged() {
4571        let params = serde_json::json!({ "key": "value" });
4572        let (method, peeled) = peel_successor_envelopes("session/update", &params);
4573        assert_eq!(method, "session/update");
4574        assert_eq!(peeled, &params);
4575    }
4576
4577    #[cfg(feature = "unstable_cancel_request")]
4578    #[test]
4579    fn peel_successor_envelopes_unwraps_nested_envelopes() {
4580        let params = serde_json::json!({
4581            "method": "_proxy/successor",
4582            "params": {
4583                "method": "$/cancel_request",
4584                "params": { "requestId": "req-1" }
4585            }
4586        });
4587        let (method, peeled) = peel_successor_envelopes("_proxy/successor", &params);
4588        assert_eq!(method, "$/cancel_request");
4589        assert_eq!(peeled, &serde_json::json!({ "requestId": "req-1" }));
4590    }
4591
4592    #[cfg(feature = "unstable_cancel_request")]
4593    #[test]
4594    fn peel_successor_envelopes_leaves_malformed_envelopes_intact() {
4595        // No string `method` field: the envelope cannot be peeled, so the
4596        // message is returned as-is for the handler chain to deal with.
4597        let params = serde_json::json!({ "unexpected": true });
4598        let (method, peeled) = peel_successor_envelopes("_proxy/successor", &params);
4599        assert_eq!(method, "_proxy/successor");
4600        assert_eq!(peeled, &params);
4601    }
4602
4603    #[cfg(feature = "unstable_cancel_request")]
4604    mod cancel_request {
4605        use super::super::*;
4606
4607        fn notification(method: &str, params: serde_json::Value) -> UntypedMessage {
4608            UntypedMessage::new(method, params).expect("well-formed JSON")
4609        }
4610
4611        #[test]
4612        fn cancellation_request_id_is_extracted_from_wrapped_notifications() {
4613            let message = notification(
4614                "_proxy/successor",
4615                serde_json::json!({
4616                    "method": "$/cancel_request",
4617                    "params": { "requestId": "req-1" }
4618                }),
4619            );
4620            let request_id = cancellation_request_id_from_message(&message)
4621                .expect("wrapped cancel should parse");
4622            assert_eq!(request_id, Some(RequestId::Str("req-1".into())));
4623        }
4624
4625        #[test]
4626        fn malformed_successor_envelope_is_not_treated_as_cancellation() {
4627            // The envelope cannot be peeled; the message must flow on to the
4628            // handler chain instead of erroring the dispatch.
4629            let message = notification("_proxy/successor", serde_json::json!({ "bogus": true }));
4630            let request_id = cancellation_request_id_from_message(&message)
4631                .expect("malformed envelope should be left to the handler chain");
4632            assert_eq!(request_id, None);
4633        }
4634
4635        #[test]
4636        fn cancel_request_notifications_are_detected_even_when_wrapped() {
4637            let plain = notification("$/cancel_request", serde_json::json!({ "requestId": 1 }));
4638            assert!(is_cancel_request_notification(&plain));
4639
4640            let wrapped = notification(
4641                "_proxy/successor",
4642                serde_json::json!({
4643                    "method": "$/cancel_request",
4644                    "params": { "requestId": 1 }
4645                }),
4646            );
4647            assert!(is_cancel_request_notification(&wrapped));
4648
4649            let other_wrapped = notification(
4650                "_proxy/successor",
4651                serde_json::json!({
4652                    "method": "session/update",
4653                    "params": {}
4654                }),
4655            );
4656            assert!(!is_cancel_request_notification(&other_wrapped));
4657
4658            let malformed_envelope =
4659                notification("_proxy/successor", serde_json::json!({ "bogus": true }));
4660            assert!(!is_cancel_request_notification(&malformed_envelope));
4661        }
4662
4663        #[test]
4664        fn malformed_cancel_request_params_error() {
4665            let message = notification(
4666                "$/cancel_request",
4667                serde_json::json!({ "requestId": { "not": "an id" } }),
4668            );
4669            cancellation_request_id_from_message(&message)
4670                .expect_err("malformed cancel params should error");
4671        }
4672
4673        #[test]
4674        fn registry_marks_and_removes_requests() {
4675            let registry = RequestCancellationRegistry::new();
4676            let id = RequestId::Str("req-1".into());
4677
4678            let responder_cancellation = registry.register(&id);
4679            let marker = responder_cancellation.cancellation();
4680            assert!(!marker.is_cancelled());
4681
4682            assert!(registry.cancel(&id));
4683            assert!(marker.is_cancelled());
4684            assert!(responder_cancellation.cancellation().is_cancelled());
4685
4686            drop(responder_cancellation);
4687            assert!(!registry.cancel(&id), "slot should be removed on drop");
4688        }
4689
4690        #[test]
4691        fn reused_request_id_does_not_cross_wire_cancellation_state() {
4692            let registry = RequestCancellationRegistry::new();
4693            let id = RequestId::Str("dup".into());
4694
4695            // A protocol-violating peer reuses an in-flight request ID.
4696            let first = registry.register(&id);
4697            let first_marker = first.cancellation();
4698            let second = registry.register(&id);
4699            let second_marker = second.cancellation();
4700
4701            // A cancellation targets whichever request currently owns the ID.
4702            assert!(registry.cancel(&id));
4703            assert!(second_marker.is_cancelled());
4704            assert!(
4705                !first_marker.is_cancelled(),
4706                "the stale request must not observe the newer request's cancellation"
4707            );
4708
4709            // The stale responder must hand out detached markers, not the
4710            // newer request's marker.
4711            assert!(!first.cancellation().is_cancelled());
4712
4713            // Dropping the stale responder must not remove the newer
4714            // request's slot.
4715            drop(first);
4716            assert!(registry.cancel(&id), "newer slot should still be present");
4717
4718            drop(second);
4719            assert!(!registry.cancel(&id), "slot should be removed on drop");
4720        }
4721    }
4722}