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