Skip to main content

agent_client_protocol/jsonrpc/
protocol_compat.rs

1/// The name and version substituted for a missing or malformed `info` when
2/// an agent selects protocol v2 for a client that requested a newer version.
3///
4/// `info` is required in v2, but a newer client's identity may not parse
5/// under v2. The request is accepted with this placeholder instead.
6#[cfg(feature = "unstable_protocol_v2")]
7pub(crate) const UNKNOWN_IMPLEMENTATION: &str = "unknown";
8
9/// Canonicalize a newer client's `initialize` parameters as a v2 request.
10///
11/// The agent selected v2 for a request written for a newer version, so it
12/// must ignore what it can't parse instead of rejecting the request. The
13/// result is the v2 request that remains: each capability that does not parse
14/// is dropped, `_meta` is dropped unless it is an object, fields v2 does not
15/// define are dropped, and a missing or malformed `info` is replaced by
16/// [`UNKNOWN_IMPLEMENTATION`] for both name and version. `protocolVersion` is
17/// set to v2.
18#[cfg(feature = "unstable_protocol_v2")]
19pub(crate) fn normalize_newer_v2_initialize_request(
20    params: &serde_json::Map<String, serde_json::Value>,
21) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
22    use crate::{schema::ProtocolVersion, v2::schema as v2};
23    use serde::Deserialize as _;
24
25    let mut params = params.clone();
26    let info_parses = params
27        .get("info")
28        .is_some_and(|info| v2::Implementation::deserialize(info).is_ok());
29    if !info_parses {
30        let unknown = v2::Implementation::new(UNKNOWN_IMPLEMENTATION, UNKNOWN_IMPLEMENTATION);
31        params.insert(
32            "info".into(),
33            serde_json::to_value(unknown).map_err(crate::Error::into_internal_error)?,
34        );
35    }
36    retain_parsable_capabilities::<v2::ClientCapabilities>(&mut params, "capabilities");
37    retain_object(&mut params, "_meta");
38    params.insert(
39        "protocolVersion".into(),
40        serde_json::to_value(ProtocolVersion::V2).map_err(crate::Error::into_internal_error)?,
41    );
42    let initialize: v2::InitializeRequest =
43        serde_json::from_value(serde_json::Value::Object(params))?;
44    let serde_json::Value::Object(normalized) =
45        serde_json::to_value(initialize).map_err(crate::Error::into_internal_error)?
46    else {
47        return Err(crate::util::internal_error(
48            "initialize params did not serialize to an object",
49        ));
50    };
51    Ok(normalized)
52}
53
54/// Drop each capability under `key` that does not parse as part of `T`, or
55/// the whole entry if it is not an object.
56fn retain_parsable_capabilities<T: serde::de::DeserializeOwned>(
57    params: &mut serde_json::Map<String, serde_json::Value>,
58    key: &str,
59) {
60    if !retain_object(params, key) {
61        return;
62    }
63    if let Some(serde_json::Value::Object(capabilities)) = params.get_mut(key) {
64        capabilities.retain(|name, value| {
65            let single = serde_json::Map::from_iter([(name.clone(), value.clone())]);
66            serde_json::from_value::<T>(serde_json::Value::Object(single)).is_ok()
67        });
68    }
69}
70
71/// Remove `key` unless it holds an object. Returns whether an object remains.
72fn retain_object(params: &mut serde_json::Map<String, serde_json::Value>, key: &str) -> bool {
73    match params.get(key) {
74        Some(serde_json::Value::Object(_)) => true,
75        Some(_) => {
76            params.remove(key);
77            false
78        }
79        None => false,
80    }
81}
82
83/// Normalize a newer peer's initialization into the stable v1 surface.
84///
85/// Only shared capabilities are carried across. In particular, unknown newer
86/// fields must never enable the v1 file-system or terminal-execution surfaces.
87///
88/// The request was written for a newer version, so parameters that don't
89/// parse under v1 are dropped instead of rejecting it: a missing or malformed
90/// `info` leaves v1's optional `clientInfo` unset, capabilities that don't
91/// parse are dropped, and `_meta` is dropped unless it is an object.
92pub(crate) fn normalize_v1_initialize_request(
93    params: &serde_json::Value,
94) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
95    use crate::{schema::ProtocolVersion, v1::schema as v1};
96    use serde::Deserialize as _;
97
98    let info = params
99        .get("info")
100        .and_then(|info| v1::Implementation::deserialize(info).ok());
101    let mut capabilities = serde_json::Map::new();
102    if let Some(source) = params
103        .get("capabilities")
104        .and_then(serde_json::Value::as_object)
105    {
106        for key in ["auth", "elicitation", "_meta"] {
107            if let Some(value) = source.get(key) {
108                capabilities.insert(key.into(), value.clone());
109            }
110        }
111    }
112    if let Some(auth) = capabilities
113        .get_mut("auth")
114        .and_then(serde_json::Value::as_object_mut)
115    {
116        let terminal = auth
117            .remove("terminal")
118            .is_some_and(|value| value.is_object());
119        auth.insert("terminal".into(), terminal.into());
120    }
121    capabilities.insert(
122        "session".into(),
123        serde_json::json!({ "configOptions": { "boolean": {} } }),
124    );
125    let mut target = serde_json::Map::from_iter([
126        (
127            "protocolVersion".to_owned(),
128            serde_json::to_value(ProtocolVersion::V1).map_err(crate::Error::into_internal_error)?,
129        ),
130        ("clientCapabilities".to_owned(), capabilities.into()),
131    ]);
132    if let Some(info) = info {
133        target.insert(
134            "clientInfo".into(),
135            serde_json::to_value(info).map_err(crate::Error::into_internal_error)?,
136        );
137    }
138    if let Some(meta) = params.get("_meta") {
139        target.insert("_meta".into(), meta.clone());
140    }
141    retain_parsable_capabilities::<v1::ClientCapabilities>(&mut target, "clientCapabilities");
142    retain_object(&mut target, "_meta");
143    let initialize: v1::InitializeRequest =
144        serde_json::from_value(serde_json::Value::Object(target))?;
145    let serde_json::Value::Object(normalized) =
146        serde_json::to_value(initialize).map_err(crate::Error::into_internal_error)?
147    else {
148        return Err(crate::util::internal_error(
149            "initialize params did not serialize to an object",
150        ));
151    };
152    Ok(normalized)
153}
154
155/// The outcome of a v2 client's [`send_initialize`](crate::v2::ConnectionTo::send_initialize).
156///
157/// A lower selection is reported with the crate-root
158/// [`InitializationVersionMismatch`](crate::InitializationVersionMismatch).
159///
160/// A v1-only agent may legitimately answer a v2 initialization attempt with a
161/// v1 response. That is a successful reply, not an error, so it is reported as
162/// [`InitializeOutcome::LowerVersion`]. Errors from the agent, malformed
163/// responses, and transport failures still resolve the request with an error.
164#[cfg(feature = "unstable_protocol_v2")]
165#[derive(Debug, Clone, PartialEq, Eq)]
166#[non_exhaustive]
167pub enum InitializeOutcome {
168    /// The agent selected v2 and the connection is ready for v2 traffic.
169    Initialized(crate::v2::schema::InitializeResponse),
170    /// The agent selected a lower protocol version.
171    ///
172    /// The connection stays uninitialized: a v2 connection cannot carry the
173    /// selected version's traffic, and the SDK never converts it. Fall back to
174    /// an implementation of the selected version if that is acceptable.
175    LowerVersion(InitializationVersionMismatch),
176}
177
178/// A successful `initialize` response that selected a lower protocol version
179/// than the local API requires.
180///
181/// Reported by [`v2::InitializeOutcome::LowerVersion`](crate::v2::InitializeOutcome::LowerVersion).
182/// This is not the `-32022` (`Unsupported protocol version`) error an agent
183/// returns when it supports no version at or below the requested one.
184///
185/// It lives at the crate root because it is not tied to one protocol
186/// version. `v2::ConnectionTo::send_initialize` reports a lower selection
187/// with it.
188#[cfg(feature = "unstable_protocol_v2")]
189#[derive(Debug, Clone, PartialEq, Eq)]
190#[non_exhaustive]
191pub struct InitializationVersionMismatch {
192    /// The protocol version the local connection requested.
193    pub requested: crate::schema::ProtocolVersion,
194    /// The peer's validated initialization response for the version it
195    /// selected. [`selected`](Self::selected) returns that version.
196    pub response: SelectedInitializeResponse,
197}
198
199#[cfg(feature = "unstable_protocol_v2")]
200impl InitializationVersionMismatch {
201    /// Describe a peer that answered a request for `requested` with
202    /// `response`, selecting the response's protocol version.
203    ///
204    /// Useful for exercising fallback paths in tests.
205    #[must_use]
206    pub fn new(
207        requested: crate::schema::ProtocolVersion,
208        response: SelectedInitializeResponse,
209    ) -> Self {
210        Self {
211            requested,
212            response,
213        }
214    }
215
216    /// The lower protocol version the peer selected: the version of
217    /// [`response`](Self::response).
218    #[must_use]
219    pub fn selected(&self) -> crate::schema::ProtocolVersion {
220        self.response.protocol_version()
221    }
222}
223
224#[cfg(feature = "unstable_protocol_v2")]
225impl std::fmt::Display for InitializationVersionMismatch {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        write!(
228            f,
229            "requested ACP protocol version {} but peer selected {}",
230            self.requested,
231            self.selected(),
232        )
233    }
234}
235
236/// A peer's initialization response, typed by the protocol version it selected.
237///
238/// It lives at the crate root because it is not tied to one protocol
239/// version. A version is only ever selected below a higher requested one, so
240/// each variant is available with the feature of the next higher version:
241/// `V1` with `unstable_protocol_v2`. New variants are added as later protocol
242/// versions make more versions selectable below the one a client requests.
243#[cfg(feature = "unstable_protocol_v2")]
244#[derive(Debug, Clone, PartialEq, Eq)]
245#[non_exhaustive]
246pub enum SelectedInitializeResponse {
247    /// The peer selected ACP protocol v1.
248    V1(crate::v1::schema::InitializeResponse),
249}
250
251#[cfg(feature = "unstable_protocol_v2")]
252impl SelectedInitializeResponse {
253    /// The protocol version the response selects.
254    #[must_use]
255    pub fn protocol_version(&self) -> crate::schema::ProtocolVersion {
256        match self {
257            Self::V1(response) => response.protocol_version,
258        }
259    }
260
261    /// Parse `value` as the initialization response for `version`, or return
262    /// `None` if the SDK has no response type for that version.
263    fn from_json(
264        version: crate::schema::ProtocolVersion,
265        value: serde_json::Value,
266    ) -> Option<Result<Self, crate::Error>> {
267        (version == crate::schema::ProtocolVersion::V1).then(|| {
268            <crate::v1::schema::InitializeResponse as crate::JsonRpcResponse>::from_json(
269                "initialize",
270                value,
271            )
272            .map(Self::V1)
273        })
274    }
275}
276
277#[cfg(feature = "unstable_protocol_v2")]
278impl crate::JsonRpcResponse for InitializeOutcome {
279    fn into_json(self, method: &str) -> Result<serde_json::Value, crate::Error> {
280        match self {
281            Self::Initialized(response) => response.into_json(method),
282            Self::LowerVersion(mismatch) => match mismatch.response {
283                SelectedInitializeResponse::V1(response) => response.into_json(method),
284            },
285        }
286    }
287
288    fn from_json(method: &str, value: serde_json::Value) -> Result<Self, crate::Error> {
289        let selected: crate::schema::ProtocolVersion = value
290            .get("protocolVersion")
291            .cloned()
292            .map(serde_json::from_value)
293            .transpose()
294            .ok()
295            .flatten()
296            .ok_or_else(|| {
297                crate::Error::invalid_params()
298                    .data("initialize.protocolVersion must be a valid ACP protocol version")
299            })?;
300        let requested = crate::schema::ProtocolVersion::V2;
301        if selected == requested {
302            return crate::v2::schema::InitializeResponse::from_json(method, value)
303                .map(Self::Initialized);
304        }
305        match SelectedInitializeResponse::from_json(selected, value) {
306            Some(response) if selected < requested => Ok(Self::LowerVersion(
307                InitializationVersionMismatch::new(requested, response?),
308            )),
309            _ => Err(crate::Error::invalid_request().data(format!(
310                "unsupported ACP protocol version {selected} in initialize response",
311            ))),
312        }
313    }
314}
315
316/// The request sent by [`send_initialize`](crate::v2::ConnectionTo::send_initialize).
317///
318/// Its pending reply opts into receiving a lower-version response as a
319/// successful [`InitializeOutcome`] instead of the connection's local error.
320#[cfg(feature = "unstable_protocol_v2")]
321#[derive(Debug, Clone)]
322pub(crate) struct InitializeNegotiation(pub(crate) crate::v2::schema::InitializeRequest);
323
324#[cfg(feature = "unstable_protocol_v2")]
325impl crate::JsonRpcMessage for InitializeNegotiation {
326    fn matches_method(method: &str) -> bool {
327        crate::v2::schema::InitializeRequest::matches_method(method)
328    }
329
330    fn method(&self) -> &str {
331        self.0.method()
332    }
333
334    fn to_untyped_message(&self) -> Result<crate::UntypedMessage, crate::Error> {
335        self.0.to_untyped_message()
336    }
337
338    fn parse_message(method: &str, params: &serde_json::Value) -> Result<Self, crate::Error> {
339        crate::v2::schema::InitializeRequest::parse_message(method, params).map(Self)
340    }
341}
342
343#[cfg(feature = "unstable_protocol_v2")]
344impl crate::JsonRpcRequest for InitializeNegotiation {
345    type Response = InitializeOutcome;
346}
347
348mod imp {
349    use std::sync::{Arc, Mutex};
350
351    use crate::RawJsonRpcId;
352    use crate::schema::ProtocolVersion;
353    use crate::{UntypedMessage, role::RemoteStyle};
354
355    #[derive(Clone, Copy, Debug)]
356    pub(crate) enum ProtocolMode {
357        Disabled,
358        Acp(AcpProtocolMode),
359    }
360
361    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
362    pub(crate) struct AcpProtocolMode {
363        api: ProtocolVersionKind,
364        initialize_surface: InitializeSurface,
365        initialization_role: InitializationRole,
366        /// Reject incoming requests other than `initialize` until an
367        /// initialization has completed, without the rest of the peer
368        /// lifecycle. V1 agents with an initialize handler set this; v2
369        /// responders get it from [`InitializationRole::Responder`].
370        requires_initialize: bool,
371    }
372
373    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
374    enum InitializeSurface {
375        Peer,
376        Proxy,
377    }
378
379    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
380    #[cfg_attr(not(feature = "unstable_protocol_v2"), allow(dead_code))]
381    enum InitializationRole {
382        /// Preserve the v1 and proxy initialization behavior. A v2 proxy has
383        /// both an incoming predecessor initialization and an outgoing
384        /// successor initialization, so the peer lifecycle does not apply to
385        /// it.
386        Unchecked,
387        Initiator,
388        Responder,
389    }
390
391    impl AcpProtocolMode {
392        fn is_incoming_initialize_request(self, method: &str) -> bool {
393            match self.initialize_surface {
394                InitializeSurface::Peer => method == "initialize",
395                InitializeSurface::Proxy => method == "proxy/initialize",
396            }
397        }
398
399        fn is_incoming_initialize_response(method: &str) -> bool {
400            // Pending replies retain the logical method from before successor
401            // wrapping, so a proxy's downstream initialize response is also
402            // keyed by `initialize`.
403            method == "initialize"
404        }
405
406        fn is_outgoing_initialize_response(self, method: &str) -> bool {
407            match self.initialize_surface {
408                InitializeSurface::Peer => method == "initialize",
409                InitializeSurface::Proxy => method == "proxy/initialize",
410            }
411        }
412    }
413
414    impl ProtocolMode {
415        pub(crate) fn disabled() -> Self {
416            Self::Disabled
417        }
418
419        pub(crate) fn v1_agent() -> Self {
420            Self::Acp(AcpProtocolMode {
421                api: ProtocolVersionKind::V1,
422                initialize_surface: InitializeSurface::Peer,
423                initialization_role: InitializationRole::Unchecked,
424                requires_initialize: false,
425            })
426        }
427
428        /// Reject incoming requests other than `initialize` until an
429        /// initialization completes. Only applies to v1 peer modes; v2
430        /// responders already enforce the full lifecycle.
431        pub(crate) fn require_initialize(self) -> Self {
432            match self {
433                Self::Acp(mode)
434                    if mode.api == ProtocolVersionKind::V1
435                        && mode.initialize_surface == InitializeSurface::Peer =>
436                {
437                    Self::Acp(AcpProtocolMode {
438                        requires_initialize: true,
439                        ..mode
440                    })
441                }
442                mode => mode,
443            }
444        }
445
446        pub(crate) fn v1_client() -> Self {
447            Self::Acp(AcpProtocolMode {
448                api: ProtocolVersionKind::V1,
449                initialize_surface: InitializeSurface::Peer,
450                initialization_role: InitializationRole::Unchecked,
451                requires_initialize: false,
452            })
453        }
454
455        pub(crate) fn v1_proxy() -> Self {
456            Self::Acp(AcpProtocolMode {
457                api: ProtocolVersionKind::V1,
458                initialize_surface: InitializeSurface::Proxy,
459                initialization_role: InitializationRole::Unchecked,
460                requires_initialize: false,
461            })
462        }
463
464        #[cfg(feature = "unstable_protocol_v2")]
465        pub(crate) fn v2_agent() -> Self {
466            Self::Acp(AcpProtocolMode {
467                api: ProtocolVersionKind::V2,
468                initialize_surface: InitializeSurface::Peer,
469                initialization_role: InitializationRole::Responder,
470                requires_initialize: false,
471            })
472        }
473
474        #[cfg(feature = "unstable_protocol_v2")]
475        pub(crate) fn v2_client() -> Self {
476            Self::Acp(AcpProtocolMode {
477                api: ProtocolVersionKind::V2,
478                initialize_surface: InitializeSurface::Peer,
479                initialization_role: InitializationRole::Initiator,
480                requires_initialize: false,
481            })
482        }
483
484        #[cfg(feature = "unstable_protocol_v2")]
485        pub(crate) fn v2_proxy() -> Self {
486            Self::Acp(AcpProtocolMode {
487                api: ProtocolVersionKind::V2,
488                initialize_surface: InitializeSurface::Proxy,
489                initialization_role: InitializationRole::Unchecked,
490                requires_initialize: false,
491            })
492        }
493
494        pub(crate) fn merge(self, other: Self) -> Self {
495            match (self, other) {
496                (Self::Disabled, other) => other,
497                (this, Self::Disabled) => this,
498                (Self::Acp(this), Self::Acp(other)) => {
499                    assert_eq!(
500                        this.api, other.api,
501                        "cannot merge ACP builders with different API protocol versions; \
502                         handler chains share a single API surface",
503                    );
504                    assert_eq!(
505                        this.initialize_surface, other.initialize_surface,
506                        "cannot merge standard ACP and proxy ACP builders; \
507                         handler chains share one initialization surface",
508                    );
509                    assert_eq!(
510                        this.initialization_role, other.initialization_role,
511                        "cannot merge ACP builders with different initialization roles; \
512                         handler chains share one connection lifecycle",
513                    );
514                    Self::Acp(AcpProtocolMode {
515                        requires_initialize: this.requires_initialize || other.requires_initialize,
516                        ..this
517                    })
518                }
519            }
520        }
521
522        pub(crate) fn api_protocol_version(self) -> Option<ProtocolVersion> {
523            match self {
524                Self::Disabled => None,
525                Self::Acp(mode) => Some(mode.api.as_protocol_version()),
526            }
527        }
528    }
529
530    #[derive(Clone, Debug)]
531    pub(crate) struct ProtocolCompat {
532        mode: Option<AcpProtocolMode>,
533        state: Arc<Mutex<ProtocolState>>,
534    }
535
536    #[derive(Debug)]
537    struct ProtocolState {
538        negotiated: ProtocolVersionKind,
539        pending_initialize: Option<ProtocolVersionKind>,
540        incoming_initialize_id: Option<RawJsonRpcId>,
541        initialization: InitializationState,
542        /// Whether any initialization has completed on this connection.
543        initialized: bool,
544        /// Whether a completed handshake may still be repeated.
545        ///
546        /// A client may repeat `initialize` after the previous one completed
547        /// and before it sends any other request; the latest one is
548        /// authoritative. The first request the client sends closes this
549        /// window for the rest of the connection.
550        repeatable: bool,
551    }
552
553    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
554    enum InitializationState {
555        Unchecked,
556        Uninitialized,
557        Initializing,
558        Ready,
559    }
560
561    /// How an initialize response that passed validation completes the
562    /// handshake.
563    enum InitializeResponseCheck {
564        Negotiated(ProtocolVersionKind),
565        /// A validated lower selection for a `send_initialize` request.
566        #[cfg_attr(not(feature = "unstable_protocol_v2"), allow(dead_code))]
567        LowerVersion,
568    }
569
570    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
571    enum ProtocolVersionKind {
572        V1,
573        V2,
574    }
575
576    impl ProtocolVersionKind {
577        fn as_protocol_version(self) -> ProtocolVersion {
578            match self {
579                Self::V1 => ProtocolVersion::V1,
580                Self::V2 => ProtocolVersion::from(2_u16),
581            }
582        }
583
584        fn from_protocol_version(version: ProtocolVersion) -> Option<Self> {
585            if version == ProtocolVersion::V1 {
586                Some(Self::V1)
587            } else if version == ProtocolVersion::from(2_u16) {
588                Some(Self::V2)
589            } else {
590                None
591            }
592        }
593    }
594
595    impl ProtocolCompat {
596        pub(crate) fn new(mode: ProtocolMode) -> Self {
597            let mode = match mode {
598                ProtocolMode::Disabled => None,
599                ProtocolMode::Acp(mode) => Some(mode),
600            };
601            let negotiated = mode.map_or(ProtocolVersionKind::V1, |mode| mode.api);
602            let initialization = match mode.map(|mode| mode.initialization_role) {
603                Some(InitializationRole::Initiator | InitializationRole::Responder) => {
604                    InitializationState::Uninitialized
605                }
606                Some(InitializationRole::Unchecked) | None => InitializationState::Unchecked,
607            };
608
609            Self {
610                mode,
611                state: Arc::new(Mutex::new(ProtocolState {
612                    negotiated,
613                    pending_initialize: None,
614                    incoming_initialize_id: None,
615                    initialization,
616                    initialized: false,
617                    repeatable: false,
618                })),
619            }
620        }
621
622        #[cfg(all(test, feature = "unstable_protocol_v2"))]
623        pub(crate) fn incoming_message(
624            &self,
625            message: UntypedMessage,
626        ) -> Result<UntypedMessage, crate::Error> {
627            self.incoming_request(&RawJsonRpcId::Number(0), message)
628        }
629
630        pub(crate) fn incoming_request(
631            &self,
632            id: &RawJsonRpcId,
633            message: UntypedMessage,
634        ) -> Result<UntypedMessage, crate::Error> {
635            let Some(mode) = self.mode else {
636                return Ok(message);
637            };
638
639            // V2 request ids must be strings or integers. The rejection is
640            // still answered with `id: null`, as for any request whose id
641            // cannot be determined.
642            if mode.api == ProtocolVersionKind::V2 && *id == RawJsonRpcId::Null {
643                return Err(crate::jsonrpc::raw_id::null_v2_request_id());
644            }
645
646            if mode.is_incoming_initialize_request(message.method()) {
647                return self.incoming_initialize_request(mode, id, message);
648            }
649            if mode.initialize_surface == InitializeSurface::Proxy
650                && (message.method() == "initialize" || successor_encloses_initialize(&message))
651            {
652                return Err(invalid_proxy_initialize_direction());
653            }
654
655            if mode.requires_initialize && !self.has_initialized() {
656                return Err(initialization_required(message.method()));
657            }
658            self.ensure_initialized(message.method())?;
659            ensure_matching_protocol_version(
660                message.method(),
661                self.active_wire_version(),
662                mode.api,
663            )?;
664            if mode.initialization_role == InitializationRole::Responder {
665                self.close_repeat_window();
666            }
667            Ok(message)
668        }
669
670        pub(crate) fn outgoing_message(
671            &self,
672            mut message: UntypedMessage,
673            remote_style: RemoteStyle,
674        ) -> Result<UntypedMessage, crate::Error> {
675            let Some(mode) = self.mode else {
676                return Ok(message);
677            };
678
679            let wire_version = if let Some(params) =
680                outgoing_initialize_params(mode, remote_style, &mut message)?
681            {
682                set_protocol_version(params, mode.api)?;
683                validate_native_v2_initialize_request(mode, params)?;
684                self.begin_outgoing_initialize(mode)?;
685                mode.api
686            } else {
687                self.ensure_initialized(message.method())?;
688                self.active_wire_version()
689            };
690
691            ensure_matching_protocol_version(message.method(), mode.api, wire_version)?;
692            if mode.initialization_role == InitializationRole::Initiator {
693                self.close_repeat_window();
694            }
695            Ok(message)
696        }
697
698        pub(crate) fn incoming_notification(
699            &self,
700            message: UntypedMessage,
701        ) -> Result<Vec<UntypedMessage>, crate::Error> {
702            let Some(mode) = self.mode else {
703                return Ok(vec![message]);
704            };
705
706            self.ensure_notification_allowed(message.method())?;
707            ensure_matching_protocol_version(
708                message.method(),
709                self.active_wire_version(),
710                mode.api,
711            )?;
712            Ok(vec![message])
713        }
714
715        pub(crate) fn outgoing_notification(
716            &self,
717            message: UntypedMessage,
718        ) -> Result<Vec<UntypedMessage>, crate::Error> {
719            let Some(mode) = self.mode else {
720                return Ok(vec![message]);
721            };
722
723            self.ensure_notification_allowed(message.method())?;
724            ensure_matching_protocol_version(
725                message.method(),
726                mode.api,
727                self.active_wire_version(),
728            )?;
729            Ok(vec![message])
730        }
731
732        /// Check an incoming response against the protocol state.
733        ///
734        /// `accepts_lower_version` marks a pending
735        /// [`super::InitializeNegotiation`], which receives a validated
736        /// lower-version initialize response as a success.
737        pub(crate) fn incoming_response(
738            &self,
739            method: &str,
740            accepts_lower_version: bool,
741            result: Result<serde_json::Value, crate::Error>,
742        ) -> Result<serde_json::Value, crate::Error> {
743            let Some(mode) = self.mode else {
744                return result;
745            };
746
747            if AcpProtocolMode::is_incoming_initialize_response(method) {
748                return self.incoming_initialize_response(mode, accepts_lower_version, result);
749            }
750
751            let value = result?;
752            self.ensure_initialized(method)?;
753            ensure_matching_protocol_version(method, self.active_wire_version(), mode.api)?;
754            Ok(value)
755        }
756
757        #[cfg(all(test, feature = "unstable_protocol_v2"))]
758        pub(crate) fn outgoing_response(
759            &self,
760            method: &str,
761            result: Result<serde_json::Value, crate::Error>,
762        ) -> Result<serde_json::Value, crate::Error> {
763            self.outgoing_response_to(&RawJsonRpcId::Number(0), method, result)
764        }
765
766        pub(crate) fn outgoing_response_to(
767            &self,
768            id: &RawJsonRpcId,
769            method: &str,
770            result: Result<serde_json::Value, crate::Error>,
771        ) -> Result<serde_json::Value, crate::Error> {
772            let Some(mode) = self.mode else {
773                return result;
774            };
775
776            if mode.is_outgoing_initialize_response(method) {
777                match mode.initialization_role {
778                    InitializationRole::Initiator => {
779                        return result.and_then(|_| {
780                            Err(unexpected_initialize_response(mode.initialization_role))
781                        });
782                    }
783                    InitializationRole::Responder if !self.is_pending_incoming_initialize(id) => {
784                        return result.and_then(|_| {
785                            Err(unexpected_initialize_response(mode.initialization_role))
786                        });
787                    }
788                    InitializationRole::Unchecked | InitializationRole::Responder => {}
789                }
790                let mut value = match result {
791                    Ok(value) => value,
792                    Err(error) => {
793                        self.fail_initialize();
794                        return Err(error);
795                    }
796                };
797                let negotiated = self.pending_initialize().or_else(|| {
798                    (mode.initialization_role == InitializationRole::Unchecked).then_some(mode.api)
799                });
800                let negotiated = negotiated
801                    .ok_or_else(|| unexpected_initialize_response(mode.initialization_role))?;
802                if let Err(error) = ensure_matching_protocol_version(method, mode.api, negotiated)
803                    .and_then(|()| set_protocol_version(&mut value, negotiated))
804                    .and_then(|()| validate_native_v2_initialize_response(mode, &value))
805                {
806                    self.fail_initialize();
807                    return Err(error);
808                }
809                self.complete_initialize(negotiated)?;
810                return Ok(value);
811            }
812
813            let value = result?;
814            self.ensure_initialized(method)?;
815            let wire_version = self.active_wire_version();
816
817            ensure_matching_protocol_version(method, mode.api, wire_version)?;
818            Ok(value)
819        }
820
821        fn incoming_initialize_request(
822            &self,
823            mode: AcpProtocolMode,
824            id: &RawJsonRpcId,
825            mut message: UntypedMessage,
826        ) -> Result<UntypedMessage, crate::Error> {
827            let requested = required_protocol_version_from_value(message.params())?;
828            // A repeated initialize is negotiated like the first one, and an
829            // error returned here leaves the previous initialization in place.
830            //
831            // A proxy negotiates `proxy/initialize` exactly as an agent
832            // negotiates `initialize`. V1 retains its original
833            // fallback-to-latest bootstrap. Only v2 endpoints reject a version
834            // below their supported range, with `-32022`. That includes a
835            // repeat asking a v2 connection for v1: the connection cannot
836            // switch to another version's API, so v2 is the only version it
837            // supports.
838            #[cfg(feature = "unstable_protocol_v2")]
839            if mode.api == ProtocolVersionKind::V2 && requested < mode.api.as_protocol_version() {
840                return Err(crate::v2::schema::Error::unsupported_protocol_version(
841                    requested,
842                    &[mode.api.as_protocol_version()],
843                )
844                .into());
845            }
846            if mode.api == ProtocolVersionKind::V1 && requested > ProtocolVersion::V1 {
847                message.params = serde_json::Value::Object(super::normalize_v1_initialize_request(
848                    message.params(),
849                )?);
850            }
851            // A request for a newer version keeps what parses under v2. An
852            // exact v2 request is validated strictly below.
853            #[cfg(feature = "unstable_protocol_v2")]
854            if mode.api == ProtocolVersionKind::V2 && requested > mode.api.as_protocol_version() {
855                let serde_json::Value::Object(params) = message.params() else {
856                    return Err(invalid_initialize_protocol_version());
857                };
858                message.params = serde_json::Value::Object(
859                    super::normalize_newer_v2_initialize_request(params)?,
860                );
861            }
862
863            set_protocol_version(&mut message.params, mode.api)?;
864            validate_native_v2_initialize_request(mode, message.params())?;
865            self.begin_incoming_initialize(mode, id)?;
866            Ok(message)
867        }
868
869        fn incoming_initialize_response(
870            &self,
871            mode: AcpProtocolMode,
872            accepts_lower_version: bool,
873            result: Result<serde_json::Value, crate::Error>,
874        ) -> Result<serde_json::Value, crate::Error> {
875            let mut value = match result {
876                Ok(value) => value,
877                Err(error) => {
878                    self.fail_initialize();
879                    return Err(error);
880                }
881            };
882            let response = (|| {
883                let pending = self.pending_initialize().or_else(|| {
884                    (mode.initialization_role == InitializationRole::Unchecked).then_some(mode.api)
885                });
886                let pending = pending
887                    .ok_or_else(|| unexpected_initialize_response(mode.initialization_role))?;
888                let response_version = required_protocol_version_from_value(&value)?;
889                let wire_version = ProtocolVersionKind::from_protocol_version(response_version)
890                    .ok_or_else(|| unsupported_protocol_version(response_version, mode.api))?;
891                // Only a client's own handshake, sent by `send_initialize`,
892                // has a caller that can fall back. A proxy relays its
893                // successor's outcome: it initialized its successor with its
894                // own selected version, and cannot relay a lower one.
895                #[cfg(feature = "unstable_protocol_v2")]
896                if accepts_lower_version
897                    && mode.initialization_role == InitializationRole::Initiator
898                    && wire_version < mode.api
899                    && let Some(response) = super::SelectedInitializeResponse::from_json(
900                        wire_version.as_protocol_version(),
901                        value.clone(),
902                    )
903                {
904                    response?;
905                    return Ok(InitializeResponseCheck::LowerVersion);
906                }
907                #[cfg(not(feature = "unstable_protocol_v2"))]
908                let _ = accepts_lower_version;
909                if wire_version != mode.api {
910                    return Err(required_protocol_version(mode.api, wire_version));
911                }
912                ensure_matching_protocol_version("initialize", pending, wire_version)?;
913                set_protocol_version(&mut value, wire_version)?;
914                validate_native_v2_initialize_response(mode, &value)?;
915                Ok(InitializeResponseCheck::Negotiated(wire_version))
916            })();
917
918            match response {
919                Ok(InitializeResponseCheck::Negotiated(wire_version)) => {
920                    self.complete_initialize(wire_version)?;
921                    Ok(value)
922                }
923                // The caller sees the response, but this connection cannot
924                // use it.
925                Ok(InitializeResponseCheck::LowerVersion) => {
926                    self.fail_initialize();
927                    Ok(value)
928                }
929                Err(error) => {
930                    self.fail_initialize();
931                    Err(error)
932                }
933            }
934        }
935
936        fn begin_incoming_initialize(
937            &self,
938            mode: AcpProtocolMode,
939            id: &RawJsonRpcId,
940        ) -> Result<(), crate::Error> {
941            match mode.initialization_role {
942                InitializationRole::Initiator => return Err(invalid_initialize_direction()),
943                InitializationRole::Unchecked | InitializationRole::Responder => {}
944            }
945            self.begin_initialize(mode.api, Some(id))
946        }
947
948        fn begin_outgoing_initialize(&self, mode: AcpProtocolMode) -> Result<(), crate::Error> {
949            match mode.initialization_role {
950                InitializationRole::Responder => return Err(invalid_initialize_direction()),
951                InitializationRole::Unchecked | InitializationRole::Initiator => {}
952            }
953            self.begin_initialize(mode.api, None)
954        }
955
956        fn begin_initialize(
957            &self,
958            requested: ProtocolVersionKind,
959            incoming_id: Option<&RawJsonRpcId>,
960        ) -> Result<(), crate::Error> {
961            let mut state = self
962                .state
963                .lock()
964                .expect("protocol compatibility state mutex poisoned");
965            match state.initialization {
966                InitializationState::Unchecked => {
967                    state.pending_initialize = Some(requested);
968                    Ok(())
969                }
970                InitializationState::Uninitialized => {
971                    state.initialization = InitializationState::Initializing;
972                    state.pending_initialize = Some(requested);
973                    state.incoming_initialize_id = incoming_id.cloned();
974                    Ok(())
975                }
976                InitializationState::Initializing => Err(crate::Error::invalid_request()
977                    .data("ACP initialization is already in progress on this connection")),
978                // The client may repeat a completed handshake before its first
979                // other request. The repeat replaces the previous one.
980                InitializationState::Ready if state.repeatable => {
981                    state.initialization = InitializationState::Initializing;
982                    state.repeatable = false;
983                    state.pending_initialize = Some(requested);
984                    state.incoming_initialize_id = incoming_id.cloned();
985                    Ok(())
986                }
987                InitializationState::Ready => Err(crate::Error::invalid_request().data(
988                    "ACP initialization cannot be repeated after any other request; reconnect to initialize again",
989                )),
990            }
991        }
992
993        /// Record that the client sent a request other than `initialize`.
994        fn close_repeat_window(&self) {
995            self.state
996                .lock()
997                .expect("protocol compatibility state mutex poisoned")
998                .repeatable = false;
999        }
1000
1001        fn ensure_initialized(&self, method: &str) -> Result<(), crate::Error> {
1002            let state = self
1003                .state
1004                .lock()
1005                .expect("protocol compatibility state mutex poisoned")
1006                .initialization;
1007            match state {
1008                InitializationState::Unchecked | InitializationState::Ready => Ok(()),
1009                InitializationState::Uninitialized | InitializationState::Initializing => {
1010                    Err(initialization_required(method))
1011                }
1012            }
1013        }
1014
1015        fn ensure_notification_allowed(&self, method: &str) -> Result<(), crate::Error> {
1016            let initialization = self
1017                .state
1018                .lock()
1019                .expect("protocol compatibility state mutex poisoned")
1020                .initialization;
1021            if initialization == InitializationState::Initializing && method == "$/cancel_request" {
1022                return Ok(());
1023            }
1024            self.ensure_initialized(method)
1025        }
1026
1027        fn complete_initialize(&self, negotiated: ProtocolVersionKind) -> Result<(), crate::Error> {
1028            let mut state = self
1029                .state
1030                .lock()
1031                .expect("protocol compatibility state mutex poisoned");
1032            if state.pending_initialize.is_none()
1033                && state.initialization != InitializationState::Unchecked
1034            {
1035                return Err(unexpected_initialize_response(
1036                    self.mode
1037                        .expect("protocol initialization requires an ACP mode")
1038                        .initialization_role,
1039                ));
1040            }
1041            if !matches!(
1042                state.initialization,
1043                InitializationState::Unchecked | InitializationState::Initializing
1044            ) {
1045                return Err(unexpected_initialize_response(
1046                    self.mode
1047                        .expect("protocol initialization requires an ACP mode")
1048                        .initialization_role,
1049                ));
1050            }
1051            state.pending_initialize = None;
1052            state.incoming_initialize_id = None;
1053            state.negotiated = negotiated;
1054            state.initialized = true;
1055            if state.initialization == InitializationState::Initializing {
1056                state.initialization = InitializationState::Ready;
1057                state.repeatable = true;
1058            }
1059            Ok(())
1060        }
1061
1062        fn fail_initialize(&self) {
1063            let mut state = self
1064                .state
1065                .lock()
1066                .expect("protocol compatibility state mutex poisoned");
1067            state.pending_initialize = None;
1068            state.incoming_initialize_id = None;
1069            if state.initialization == InitializationState::Initializing {
1070                // A failed repeat keeps the previous initialization in place,
1071                // and the client may repeat again.
1072                if state.initialized {
1073                    state.initialization = InitializationState::Ready;
1074                    state.repeatable = true;
1075                } else {
1076                    state.initialization = InitializationState::Uninitialized;
1077                }
1078            }
1079        }
1080
1081        fn has_initialized(&self) -> bool {
1082            self.state
1083                .lock()
1084                .expect("protocol compatibility state mutex poisoned")
1085                .initialized
1086        }
1087
1088        fn is_pending_incoming_initialize(&self, id: &RawJsonRpcId) -> bool {
1089            self.state
1090                .lock()
1091                .expect("protocol compatibility state mutex poisoned")
1092                .incoming_initialize_id
1093                .as_ref()
1094                == Some(id)
1095        }
1096
1097        fn active_wire_version(&self) -> ProtocolVersionKind {
1098            let state = self
1099                .state
1100                .lock()
1101                .expect("protocol compatibility state mutex poisoned");
1102            state.pending_initialize.unwrap_or(state.negotiated)
1103        }
1104
1105        fn pending_initialize(&self) -> Option<ProtocolVersionKind> {
1106            self.state
1107                .lock()
1108                .expect("protocol compatibility state mutex poisoned")
1109                .pending_initialize
1110        }
1111    }
1112
1113    fn required_protocol_version_from_value(
1114        value: &serde_json::Value,
1115    ) -> Result<ProtocolVersion, crate::Error> {
1116        let Some(version) = value.get("protocolVersion") else {
1117            return Err(invalid_initialize_protocol_version());
1118        };
1119
1120        serde_json::from_value(version.clone()).map_err(|_| invalid_initialize_protocol_version())
1121    }
1122
1123    fn outgoing_initialize_params(
1124        mode: AcpProtocolMode,
1125        remote_style: RemoteStyle,
1126        message: &mut UntypedMessage,
1127    ) -> Result<Option<&mut serde_json::Value>, crate::Error> {
1128        if successor_encloses_initialize(message) {
1129            return Err(invalid_prewrapped_initialize());
1130        }
1131
1132        match mode.initialize_surface {
1133            InitializeSurface::Peer => {
1134                Ok((message.method() == "initialize").then_some(&mut message.params))
1135            }
1136            InitializeSurface::Proxy => {
1137                if message.method() == "initialize" {
1138                    if remote_style != RemoteStyle::Successor {
1139                        return Err(invalid_proxy_initialize_direction());
1140                    }
1141                    return Ok(Some(&mut message.params));
1142                }
1143                if message.method() == "proxy/initialize" {
1144                    return Err(invalid_proxy_initialize_direction());
1145                }
1146                Ok(None)
1147            }
1148        }
1149    }
1150
1151    fn successor_encloses_initialize(message: &UntypedMessage) -> bool {
1152        let mut method = message.method();
1153        let mut params = message.params();
1154        let mut wrapped = false;
1155
1156        while method == "proxy/successor" {
1157            wrapped = true;
1158            let Some(inner_method) = params.get("method").and_then(serde_json::Value::as_str)
1159            else {
1160                return false;
1161            };
1162            method = inner_method;
1163            if method == "proxy/successor" {
1164                let Some(inner_params) = params.get("params") else {
1165                    return false;
1166                };
1167                params = inner_params;
1168            }
1169        }
1170
1171        wrapped && matches!(method, "initialize" | "proxy/initialize")
1172    }
1173
1174    fn invalid_initialize_protocol_version() -> crate::Error {
1175        crate::Error::invalid_params()
1176            .data("initialize.protocolVersion must be a valid ACP protocol version")
1177    }
1178
1179    fn initialization_required(method: &str) -> crate::Error {
1180        crate::Error::invalid_request().data(format!(
1181            "ACP initialization must complete before `{method}` can be used",
1182        ))
1183    }
1184
1185    fn invalid_initialize_direction() -> crate::Error {
1186        crate::Error::invalid_request()
1187            .data("ACP clients send `initialize` requests and ACP agents respond to them")
1188    }
1189
1190    fn unexpected_initialize_response(role: InitializationRole) -> crate::Error {
1191        let detail = match role {
1192            InitializationRole::Initiator => "before an initialize request is pending",
1193            InitializationRole::Responder => "before an initialize request was received",
1194            InitializationRole::Unchecked => "without a pending initialize request",
1195        };
1196        crate::Error::invalid_request().data(format!("received an initialize response {detail}"))
1197    }
1198
1199    fn invalid_proxy_initialize_direction() -> crate::Error {
1200        crate::Error::invalid_request().data(
1201            "proxy initialization must arrive as `proxy/initialize`; outgoing `initialize` must target the successor so the connection can apply `proxy/successor`",
1202        )
1203    }
1204
1205    fn invalid_prewrapped_initialize() -> crate::Error {
1206        crate::Error::invalid_request().data(
1207            "initialize requests must be sent as a logical `initialize` message; `proxy/successor` wrapping is applied by connection routing",
1208        )
1209    }
1210
1211    fn set_protocol_version(
1212        value: &mut serde_json::Value,
1213        version: ProtocolVersionKind,
1214    ) -> Result<(), crate::Error> {
1215        let serde_json::Value::Object(object) = value else {
1216            return Err(invalid_initialize_protocol_version());
1217        };
1218        object.insert(
1219            "protocolVersion".into(),
1220            serde_json::to_value(version.as_protocol_version())
1221                .map_err(crate::Error::into_internal_error)?,
1222        );
1223        Ok(())
1224    }
1225
1226    // The shared guard stays fallible when v2 validation is compiled out.
1227    #[cfg_attr(
1228        not(feature = "unstable_protocol_v2"),
1229        allow(clippy::unnecessary_wraps)
1230    )]
1231    fn validate_native_v2_initialize_request(
1232        mode: AcpProtocolMode,
1233        value: &serde_json::Value,
1234    ) -> Result<(), crate::Error> {
1235        #[cfg(feature = "unstable_protocol_v2")]
1236        if mode.initialization_role != InitializationRole::Unchecked {
1237            <crate::v2::schema::InitializeRequest as crate::JsonRpcMessage>::parse_message(
1238                "initialize",
1239                value,
1240            )?;
1241        }
1242        #[cfg(not(feature = "unstable_protocol_v2"))]
1243        let _ = (mode, value);
1244        Ok(())
1245    }
1246
1247    #[cfg_attr(
1248        not(feature = "unstable_protocol_v2"),
1249        allow(clippy::unnecessary_wraps)
1250    )]
1251    fn validate_native_v2_initialize_response(
1252        mode: AcpProtocolMode,
1253        value: &serde_json::Value,
1254    ) -> Result<(), crate::Error> {
1255        #[cfg(feature = "unstable_protocol_v2")]
1256        if mode.initialization_role != InitializationRole::Unchecked {
1257            <crate::v2::schema::InitializeResponse as crate::JsonRpcResponse>::from_json(
1258                "initialize",
1259                value.clone(),
1260            )?;
1261        }
1262        #[cfg(not(feature = "unstable_protocol_v2"))]
1263        let _ = (mode, value);
1264        Ok(())
1265    }
1266
1267    fn ensure_matching_protocol_version(
1268        method: &str,
1269        from: ProtocolVersionKind,
1270        to: ProtocolVersionKind,
1271    ) -> Result<(), crate::Error> {
1272        if from == to {
1273            return Ok(());
1274        }
1275
1276        Err(crate::Error::invalid_request().data(format!(
1277            "ACP protocol translation from {} to {} is not supported for `{method}`; register a handler for the negotiated protocol version",
1278            from.as_protocol_version(),
1279            to.as_protocol_version(),
1280        )))
1281    }
1282
1283    fn unsupported_protocol_version(
1284        version: ProtocolVersion,
1285        supported: ProtocolVersionKind,
1286    ) -> crate::Error {
1287        crate::Error::invalid_request().data(format!(
1288            "unsupported ACP protocol version {version}; this endpoint only supports ACP protocol version {}",
1289            supported.as_protocol_version(),
1290        ))
1291    }
1292
1293    fn required_protocol_version(
1294        required: ProtocolVersionKind,
1295        negotiated: ProtocolVersionKind,
1296    ) -> crate::Error {
1297        crate::Error::invalid_request().data(format!(
1298            "required ACP protocol version {} but peer negotiated {}; use a matching implementation for the negotiated protocol version",
1299            required.as_protocol_version(),
1300            negotiated.as_protocol_version(),
1301        ))
1302    }
1303
1304    #[cfg(all(test, feature = "unstable_protocol_v2"))]
1305    mod tests {
1306        use super::*;
1307        use agent_client_protocol_schema::v2;
1308
1309        fn negotiated(compat: &ProtocolCompat) -> ProtocolVersionKind {
1310            compat
1311                .state
1312                .lock()
1313                .expect("protocol compatibility state mutex poisoned")
1314                .negotiated
1315        }
1316
1317        fn pending_initialize(compat: &ProtocolCompat) -> Option<ProtocolVersionKind> {
1318            compat
1319                .state
1320                .lock()
1321                .expect("protocol compatibility state mutex poisoned")
1322                .pending_initialize
1323        }
1324
1325        fn initialization_state(compat: &ProtocolCompat) -> InitializationState {
1326            compat
1327                .state
1328                .lock()
1329                .expect("protocol compatibility state mutex poisoned")
1330                .initialization
1331        }
1332
1333        fn v2_implementation() -> v2::Implementation {
1334            v2::Implementation::new("protocol-compat-test", env!("CARGO_PKG_VERSION"))
1335        }
1336
1337        fn v2_initialize_request(protocol_version: ProtocolVersion) -> v2::InitializeRequest {
1338            v2::InitializeRequest::new(protocol_version, v2_implementation())
1339        }
1340
1341        fn v2_initialize_response(protocol_version: ProtocolVersion) -> v2::InitializeResponse {
1342            v2::InitializeResponse::new(protocol_version, v2_implementation())
1343        }
1344
1345        #[test]
1346        fn initialize_request_sets_active_wire_version_before_response() -> Result<(), crate::Error>
1347        {
1348            let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1349            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1350            assert_eq!(
1351                initialization_state(&compat),
1352                InitializationState::Uninitialized
1353            );
1354
1355            compat.incoming_message(UntypedMessage::new(
1356                "initialize",
1357                v2_initialize_request(ProtocolVersion::V2),
1358            )?)?;
1359
1360            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1361            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1362            assert_eq!(
1363                initialization_state(&compat),
1364                InitializationState::Initializing
1365            );
1366
1367            compat.outgoing_response(
1368                "initialize",
1369                Ok(serde_json::to_value(v2_initialize_response(
1370                    ProtocolVersion::V2,
1371                ))?),
1372            )?;
1373
1374            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1375            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1376            assert_eq!(initialization_state(&compat), InitializationState::Ready);
1377            Ok(())
1378        }
1379
1380        #[test]
1381        fn outgoing_initialize_sets_active_wire_version_before_response() -> Result<(), crate::Error>
1382        {
1383            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1384            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1385            assert_eq!(
1386                initialization_state(&compat),
1387                InitializationState::Uninitialized
1388            );
1389
1390            compat.outgoing_message(
1391                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1392                RemoteStyle::Counterpart,
1393            )?;
1394
1395            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1396            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1397            assert_eq!(
1398                initialization_state(&compat),
1399                InitializationState::Initializing
1400            );
1401
1402            compat.incoming_response(
1403                "initialize",
1404                false,
1405                Ok(serde_json::to_value(v2_initialize_response(
1406                    ProtocolVersion::V2,
1407                ))?),
1408            )?;
1409
1410            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1411            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1412            assert_eq!(initialization_state(&compat), InitializationState::Ready);
1413            Ok(())
1414        }
1415
1416        #[test]
1417        fn failed_incoming_initialize_response_clears_pending_wire_version()
1418        -> Result<(), crate::Error> {
1419            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1420            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1421
1422            compat.outgoing_message(
1423                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1424                RemoteStyle::Counterpart,
1425            )?;
1426
1427            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1428            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1429
1430            let result = compat.incoming_response(
1431                "initialize",
1432                false,
1433                Err(crate::Error::invalid_request().data("initialize failed")),
1434            );
1435
1436            assert!(result.is_err());
1437            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1438            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1439            assert_eq!(
1440                initialization_state(&compat),
1441                InitializationState::Uninitialized
1442            );
1443            compat.outgoing_message(
1444                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1445                RemoteStyle::Counterpart,
1446            )?;
1447            assert_eq!(
1448                initialization_state(&compat),
1449                InitializationState::Initializing
1450            );
1451            Ok(())
1452        }
1453
1454        #[test]
1455        fn incoming_initialize_response_requires_protocol_version() -> Result<(), crate::Error> {
1456            for value in [
1457                serde_json::json!({}),
1458                serde_json::json!({ "protocolVersion": 100_000 }),
1459            ] {
1460                let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1461                compat.outgoing_message(
1462                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1463                    RemoteStyle::Counterpart,
1464                )?;
1465
1466                let error = compat
1467                    .incoming_response("initialize", false, Ok(value))
1468                    .expect_err("initialize responses must declare an ACP protocol version");
1469                let data = error
1470                    .data
1471                    .as_ref()
1472                    .and_then(|data| data.as_str())
1473                    .unwrap_or_default();
1474                assert!(data.contains("protocolVersion"), "{error:?}");
1475                assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1476                assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1477                assert_eq!(
1478                    initialization_state(&compat),
1479                    InitializationState::Uninitialized
1480                );
1481            }
1482
1483            Ok(())
1484        }
1485
1486        /// Send a v2 client's initialize, then deliver `response` to it.
1487        /// `negotiation` marks the request as sent by `send_initialize`.
1488        fn v2_client_receiving(
1489            negotiation: bool,
1490            response: Result<serde_json::Value, crate::Error>,
1491        ) -> Result<(ProtocolCompat, Result<serde_json::Value, crate::Error>), crate::Error>
1492        {
1493            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1494            compat.outgoing_message(
1495                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1496                RemoteStyle::Counterpart,
1497            )?;
1498            let result = compat.incoming_response("initialize", negotiation, response);
1499            Ok((compat, result))
1500        }
1501
1502        fn v1_initialize_response() -> crate::v1::schema::InitializeResponse {
1503            crate::v1::schema::InitializeResponse::new(ProtocolVersion::V1)
1504                .agent_info(crate::v1::schema::Implementation::new("v1-agent", "1.0.0"))
1505        }
1506
1507        #[test]
1508        fn plain_initialize_request_rejects_lower_version() -> Result<(), crate::Error> {
1509            let (compat, result) =
1510                v2_client_receiving(false, Ok(serde_json::to_value(v1_initialize_response())?))?;
1511            let error = result.expect_err("send_request keeps the local version guard");
1512            let data = error
1513                .data
1514                .as_ref()
1515                .and_then(|data| data.as_str())
1516                .unwrap_or_default();
1517            assert!(data.contains("peer negotiated 1"), "{error:?}");
1518            assert_eq!(
1519                initialization_state(&compat),
1520                InitializationState::Uninitialized
1521            );
1522            Ok(())
1523        }
1524
1525        #[test]
1526        fn initialize_negotiation_receives_lower_version_as_success() -> Result<(), crate::Error> {
1527            let v1_response = serde_json::to_value(v1_initialize_response())?;
1528            let (compat, result) = v2_client_receiving(true, Ok(v1_response.clone()))?;
1529            assert_eq!(result?, v1_response);
1530
1531            // The connection stays uninitialized rather than switching to v1.
1532            assert_eq!(
1533                initialization_state(&compat),
1534                InitializationState::Uninitialized
1535            );
1536            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1537            compat
1538                .outgoing_message(
1539                    UntypedMessage::new("session/new", serde_json::json!({}))?,
1540                    RemoteStyle::Counterpart,
1541                )
1542                .expect_err("v2 traffic still requires a v2 handshake");
1543            compat.outgoing_message(
1544                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1545                RemoteStyle::Counterpart,
1546            )?;
1547            Ok(())
1548        }
1549
1550        #[test]
1551        fn initialize_negotiation_keeps_failures_as_errors() -> Result<(), crate::Error> {
1552            let remote: crate::Error = crate::v2::schema::Error::unsupported_protocol_version(
1553                ProtocolVersion::V2,
1554                &[ProtocolVersion::V1],
1555            )
1556            .into();
1557            let (compat, result) = v2_client_receiving(true, Err(remote.clone()))?;
1558            assert_eq!(result, Err(remote));
1559            assert_eq!(
1560                initialization_state(&compat),
1561                InitializationState::Uninitialized
1562            );
1563
1564            for malformed in [
1565                serde_json::json!({}),
1566                serde_json::json!({ "protocolVersion": "one" }),
1567                serde_json::json!({ "protocolVersion": 0 }),
1568            ] {
1569                let (compat, result) = v2_client_receiving(true, Ok(malformed))?;
1570                result.expect_err("malformed initialize responses are errors");
1571                assert_eq!(
1572                    initialization_state(&compat),
1573                    InitializationState::Uninitialized
1574                );
1575            }
1576            Ok(())
1577        }
1578
1579        #[test]
1580        fn initialize_negotiation_does_not_change_proxies() -> Result<(), crate::Error> {
1581            let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
1582            compat.outgoing_message(
1583                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1584                RemoteStyle::Successor,
1585            )?;
1586            compat
1587                .incoming_response(
1588                    "initialize",
1589                    true,
1590                    Ok(serde_json::to_value(v1_initialize_response())?),
1591                )
1592                .expect_err("only a client's own handshake can report a lower version");
1593            Ok(())
1594        }
1595
1596        #[test]
1597        fn initialize_outcome_parses_by_selected_version() -> Result<(), crate::Error> {
1598            use crate::JsonRpcResponse as _;
1599
1600            let v2_response = v2_initialize_response(ProtocolVersion::V2);
1601            assert_eq!(
1602                super::super::InitializeOutcome::from_json(
1603                    "initialize",
1604                    serde_json::to_value(&v2_response)?
1605                )?,
1606                super::super::InitializeOutcome::Initialized(v2_response)
1607            );
1608
1609            let v1_response = v1_initialize_response();
1610            let outcome = super::super::InitializeOutcome::from_json(
1611                "initialize",
1612                serde_json::to_value(&v1_response)?,
1613            )?;
1614            assert_eq!(
1615                outcome,
1616                super::super::InitializeOutcome::LowerVersion(
1617                    super::super::InitializationVersionMismatch::new(
1618                        ProtocolVersion::V2,
1619                        super::super::SelectedInitializeResponse::V1(v1_response),
1620                    )
1621                )
1622            );
1623            let super::super::InitializeOutcome::LowerVersion(mismatch) = &outcome else {
1624                unreachable!();
1625            };
1626            assert_eq!(mismatch.requested, ProtocolVersion::V2);
1627            assert_eq!(mismatch.selected(), ProtocolVersion::V1);
1628
1629            for value in [
1630                serde_json::json!({}),
1631                serde_json::json!({ "protocolVersion": 3 }),
1632            ] {
1633                super::super::InitializeOutcome::from_json("initialize", value)
1634                    .expect_err("only v2 and lower known versions are outcomes");
1635            }
1636            Ok(())
1637        }
1638
1639        #[test]
1640        fn malformed_v2_initialize_requests_do_not_begin_a_handshake() -> Result<(), crate::Error> {
1641            let malformed = serde_json::json!({ "protocolVersion": ProtocolVersion::V2 });
1642
1643            let client = ProtocolCompat::new(ProtocolMode::v2_client());
1644            client
1645                .outgoing_message(
1646                    UntypedMessage::new("initialize", malformed.clone())?,
1647                    RemoteStyle::Counterpart,
1648                )
1649                .expect_err("native v2 clients must send the complete initialize request shape");
1650            assert_eq!(
1651                initialization_state(&client),
1652                InitializationState::Uninitialized
1653            );
1654            client.outgoing_message(
1655                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1656                RemoteStyle::Counterpart,
1657            )?;
1658            assert_eq!(
1659                initialization_state(&client),
1660                InitializationState::Initializing
1661            );
1662
1663            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1664            agent
1665                .incoming_message(UntypedMessage::new("initialize", malformed)?)
1666                .expect_err("native v2 agents must receive the complete initialize request shape");
1667            assert_eq!(
1668                initialization_state(&agent),
1669                InitializationState::Uninitialized
1670            );
1671            agent.incoming_message(UntypedMessage::new(
1672                "initialize",
1673                v2_initialize_request(ProtocolVersion::V2),
1674            )?)?;
1675            assert_eq!(
1676                initialization_state(&agent),
1677                InitializationState::Initializing
1678            );
1679            Ok(())
1680        }
1681
1682        #[test]
1683        fn malformed_v2_initialize_success_leaves_client_uninitialized_for_retry()
1684        -> Result<(), crate::Error> {
1685            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1686            compat.outgoing_message(
1687                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1688                RemoteStyle::Counterpart,
1689            )?;
1690
1691            compat
1692                .incoming_response(
1693                    "initialize",
1694                    false,
1695                    Ok(serde_json::json!({ "protocolVersion": ProtocolVersion::V2 })),
1696                )
1697                .expect_err("initialize success must contain the complete v2 response shape");
1698            assert_eq!(
1699                initialization_state(&compat),
1700                InitializationState::Uninitialized
1701            );
1702            assert_eq!(pending_initialize(&compat), None);
1703
1704            compat.outgoing_message(
1705                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1706                RemoteStyle::Counterpart,
1707            )?;
1708            assert_eq!(
1709                initialization_state(&compat),
1710                InitializationState::Initializing
1711            );
1712            Ok(())
1713        }
1714
1715        #[test]
1716        fn malformed_v2_initialize_success_leaves_agent_uninitialized_for_retry()
1717        -> Result<(), crate::Error> {
1718            let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1719            compat.incoming_message(UntypedMessage::new(
1720                "initialize",
1721                v2_initialize_request(ProtocolVersion::V2),
1722            )?)?;
1723
1724            compat
1725                .outgoing_response(
1726                    "initialize",
1727                    Ok(serde_json::json!({ "protocolVersion": ProtocolVersion::V2 })),
1728                )
1729                .expect_err("initialize success must contain the complete v2 response shape");
1730            assert_eq!(
1731                initialization_state(&compat),
1732                InitializationState::Uninitialized
1733            );
1734            assert_eq!(pending_initialize(&compat), None);
1735
1736            compat.incoming_message(UntypedMessage::new(
1737                "initialize",
1738                v2_initialize_request(ProtocolVersion::V2),
1739            )?)?;
1740            assert_eq!(
1741                initialization_state(&compat),
1742                InitializationState::Initializing
1743            );
1744            Ok(())
1745        }
1746
1747        #[test]
1748        fn v2_peer_traffic_requires_completed_initialization() -> Result<(), crate::Error> {
1749            for compat in [
1750                ProtocolCompat::new(ProtocolMode::v2_agent()),
1751                ProtocolCompat::new(ProtocolMode::v2_client()),
1752            ] {
1753                for error in [
1754                    compat
1755                        .incoming_message(UntypedMessage::new(
1756                            "session/new",
1757                            serde_json::json!({}),
1758                        )?)
1759                        .expect_err("incoming requests must wait for initialization"),
1760                    compat
1761                        .outgoing_message(
1762                            UntypedMessage::new("session/new", serde_json::json!({}))?,
1763                            RemoteStyle::Counterpart,
1764                        )
1765                        .expect_err("outgoing requests must wait for initialization"),
1766                    compat
1767                        .incoming_notification(UntypedMessage::new(
1768                            "session/update",
1769                            serde_json::json!({}),
1770                        )?)
1771                        .expect_err("incoming notifications must wait for initialization"),
1772                    compat
1773                        .outgoing_notification(UntypedMessage::new(
1774                            "session/update",
1775                            serde_json::json!({}),
1776                        )?)
1777                        .expect_err("outgoing notifications must wait for initialization"),
1778                    compat
1779                        .incoming_response("session/new", false, Ok(serde_json::json!({})))
1780                        .expect_err("incoming responses must wait for initialization"),
1781                    compat
1782                        .outgoing_response("session/new", Ok(serde_json::json!({})))
1783                        .expect_err("outgoing responses must wait for initialization"),
1784                ] {
1785                    let data = error
1786                        .data
1787                        .as_ref()
1788                        .and_then(|data| data.as_str())
1789                        .unwrap_or_default();
1790                    assert!(data.contains("initialization must complete"), "{error:?}");
1791                }
1792            }
1793            Ok(())
1794        }
1795
1796        #[test]
1797        fn v2_initialization_allows_only_protocol_cancellation_notifications()
1798        -> Result<(), crate::Error> {
1799            let client = ProtocolCompat::new(ProtocolMode::v2_client());
1800            client.outgoing_message(
1801                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1802                RemoteStyle::Counterpart,
1803            )?;
1804            client.outgoing_notification(UntypedMessage::new(
1805                "$/cancel_request",
1806                serde_json::json!({ "requestId": 1 }),
1807            )?)?;
1808            client
1809                .outgoing_notification(UntypedMessage::new(
1810                    "session/update",
1811                    serde_json::json!({}),
1812                )?)
1813                .expect_err("ordinary notifications must wait for initialization");
1814
1815            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1816            agent.incoming_message(UntypedMessage::new(
1817                "initialize",
1818                v2_initialize_request(ProtocolVersion::V2),
1819            )?)?;
1820            agent.incoming_notification(UntypedMessage::new(
1821                "$/cancel_request",
1822                serde_json::json!({ "requestId": 1 }),
1823            )?)?;
1824            agent
1825                .incoming_notification(UntypedMessage::new(
1826                    "session/update",
1827                    serde_json::json!({}),
1828                )?)
1829                .expect_err("ordinary notifications must wait for initialization");
1830            Ok(())
1831        }
1832
1833        fn error_data(error: &crate::Error) -> &str {
1834            error
1835                .data
1836                .as_ref()
1837                .and_then(|data| data.as_str())
1838                .unwrap_or_default()
1839        }
1840
1841        #[test]
1842        fn v2_peer_initialization_has_one_direction() -> Result<(), crate::Error> {
1843            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1844            let error = agent
1845                .outgoing_message(
1846                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1847                    RemoteStyle::Counterpart,
1848                )
1849                .expect_err("agents must not initiate initialization");
1850            assert!(
1851                error_data(&error).contains("clients send `initialize`"),
1852                "{error:?}"
1853            );
1854            assert_eq!(
1855                initialization_state(&agent),
1856                InitializationState::Uninitialized
1857            );
1858
1859            let client = ProtocolCompat::new(ProtocolMode::v2_client());
1860            let error = client
1861                .incoming_message(UntypedMessage::new(
1862                    "initialize",
1863                    v2_initialize_request(ProtocolVersion::V2),
1864                )?)
1865                .expect_err("clients must not receive initialization requests");
1866            assert!(
1867                error_data(&error).contains("clients send `initialize`"),
1868                "{error:?}"
1869            );
1870            assert_eq!(
1871                initialization_state(&client),
1872                InitializationState::Uninitialized
1873            );
1874            Ok(())
1875        }
1876
1877        /// Drive an agent through one successful handshake.
1878        fn initialized_v2_agent() -> Result<ProtocolCompat, crate::Error> {
1879            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1880            agent.incoming_message(UntypedMessage::new(
1881                "initialize",
1882                v2_initialize_request(ProtocolVersion::V2),
1883            )?)?;
1884            agent.outgoing_response(
1885                "initialize",
1886                Ok(serde_json::to_value(v2_initialize_response(
1887                    ProtocolVersion::V2,
1888                ))?),
1889            )?;
1890            Ok(agent)
1891        }
1892
1893        /// Drive a client through one successful handshake.
1894        fn initialized_v2_client() -> Result<ProtocolCompat, crate::Error> {
1895            let client = ProtocolCompat::new(ProtocolMode::v2_client());
1896            client.outgoing_message(
1897                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1898                RemoteStyle::Counterpart,
1899            )?;
1900            client.incoming_response(
1901                "initialize",
1902                false,
1903                Ok(serde_json::to_value(v2_initialize_response(
1904                    ProtocolVersion::V2,
1905                ))?),
1906            )?;
1907            Ok(client)
1908        }
1909
1910        #[test]
1911        fn v2_agent_accepts_repeated_initialize_before_other_requests() -> Result<(), crate::Error>
1912        {
1913            let agent = initialized_v2_agent()?;
1914            assert_eq!(initialization_state(&agent), InitializationState::Ready);
1915
1916            // Notifications do not close the window; only requests do.
1917            agent.incoming_notification(UntypedMessage::new(
1918                "$/cancel_request",
1919                serde_json::json!({ "requestId": 1 }),
1920            )?)?;
1921
1922            for _ in 0..2 {
1923                agent.incoming_message(UntypedMessage::new(
1924                    "initialize",
1925                    v2_initialize_request(ProtocolVersion::V2),
1926                )?)?;
1927                assert_eq!(
1928                    initialization_state(&agent),
1929                    InitializationState::Initializing
1930                );
1931                agent
1932                    .incoming_message(UntypedMessage::new("session/new", serde_json::json!({}))?)
1933                    .expect_err("requests must wait for the repeated initialize");
1934                agent.outgoing_response(
1935                    "initialize",
1936                    Ok(serde_json::to_value(v2_initialize_response(
1937                        ProtocolVersion::V2,
1938                    ))?),
1939                )?;
1940                assert_eq!(initialization_state(&agent), InitializationState::Ready);
1941                assert_eq!(negotiated(&agent), ProtocolVersionKind::V2);
1942            }
1943
1944            agent.incoming_message(UntypedMessage::new("session/new", serde_json::json!({}))?)?;
1945            let error = agent
1946                .incoming_message(UntypedMessage::new(
1947                    "initialize",
1948                    v2_initialize_request(ProtocolVersion::V2),
1949                )?)
1950                .expect_err("initialize must not be repeated after another request");
1951            assert_eq!(i32::from(error.code), -32600);
1952            assert!(
1953                error_data(&error).contains("cannot be repeated after any other request"),
1954                "{error:?}"
1955            );
1956            assert_eq!(initialization_state(&agent), InitializationState::Ready);
1957            Ok(())
1958        }
1959
1960        #[test]
1961        fn v2_agent_keeps_window_open_when_a_request_is_rejected() -> Result<(), crate::Error> {
1962            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1963            agent.incoming_message(UntypedMessage::new(
1964                "initialize",
1965                v2_initialize_request(ProtocolVersion::V2),
1966            )?)?;
1967            // Rejected while the handshake is in flight, so it does not count.
1968            agent
1969                .incoming_message(UntypedMessage::new("session/new", serde_json::json!({}))?)
1970                .expect_err("requests must wait for initialization");
1971            agent.outgoing_response(
1972                "initialize",
1973                Ok(serde_json::to_value(v2_initialize_response(
1974                    ProtocolVersion::V2,
1975                ))?),
1976            )?;
1977            agent.incoming_message(UntypedMessage::new(
1978                "initialize",
1979                v2_initialize_request(ProtocolVersion::V2),
1980            )?)?;
1981            assert_eq!(
1982                initialization_state(&agent),
1983                InitializationState::Initializing
1984            );
1985            Ok(())
1986        }
1987
1988        #[test]
1989        fn failed_repeated_initialize_keeps_previous_initialization() -> Result<(), crate::Error> {
1990            let agent = initialized_v2_agent()?;
1991            agent.incoming_message(UntypedMessage::new(
1992                "initialize",
1993                v2_initialize_request(ProtocolVersion::V2),
1994            )?)?;
1995            agent
1996                .outgoing_response(
1997                    "initialize",
1998                    Err(crate::Error::invalid_params().data("rejected")),
1999                )
2000                .expect_err("the handler's error is forwarded");
2001            assert_eq!(initialization_state(&agent), InitializationState::Ready);
2002
2003            // A malformed repeat is rejected before it begins.
2004            agent
2005                .incoming_message(UntypedMessage::new(
2006                    "initialize",
2007                    serde_json::json!({ "protocolVersion": 2 }),
2008                )?)
2009                .expect_err("an exact v2 repeat must be a valid v2 request");
2010            assert_eq!(initialization_state(&agent), InitializationState::Ready);
2011
2012            // The client may still repeat.
2013            agent.incoming_message(UntypedMessage::new(
2014                "initialize",
2015                v2_initialize_request(ProtocolVersion::V2),
2016            )?)?;
2017            agent.outgoing_response(
2018                "initialize",
2019                Ok(serde_json::to_value(v2_initialize_response(
2020                    ProtocolVersion::V2,
2021                ))?),
2022            )?;
2023            agent.incoming_message(UntypedMessage::new("session/new", serde_json::json!({}))?)?;
2024            Ok(())
2025        }
2026
2027        #[test]
2028        fn v2_agent_answers_lower_version_repeat_like_a_first_initialize()
2029        -> Result<(), crate::Error> {
2030            let agent = initialized_v2_agent()?;
2031            let error = agent
2032                .incoming_message(UntypedMessage::new(
2033                    "initialize",
2034                    serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2035                )?)
2036                .expect_err("a v2 agent supports no version at or below v1");
2037            // Handled like a first initialize: no supported version is at or
2038            // below v1, so the repeat gets -32022 with the supported versions.
2039            assert_eq!(i32::from(error.code), -32022);
2040            assert_eq!(
2041                error.data,
2042                Some(serde_json::json!({ "requested": 1, "supported": [2] }))
2043            );
2044            // The failed repeat leaves the previous initialization in effect.
2045            assert_eq!(initialization_state(&agent), InitializationState::Ready);
2046            assert_eq!(agent.active_wire_version(), ProtocolVersionKind::V2);
2047            agent.incoming_message(UntypedMessage::new(
2048                "initialize",
2049                v2_initialize_request(ProtocolVersion::V2),
2050            )?)?;
2051            Ok(())
2052        }
2053
2054        #[test]
2055        fn v2_client_may_repeat_initialize_before_other_requests() -> Result<(), crate::Error> {
2056            let client = initialized_v2_client()?;
2057            client.outgoing_message(
2058                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2059                RemoteStyle::Counterpart,
2060            )?;
2061            assert_eq!(
2062                initialization_state(&client),
2063                InitializationState::Initializing
2064            );
2065            client.incoming_response(
2066                "initialize",
2067                false,
2068                Ok(serde_json::to_value(v2_initialize_response(
2069                    ProtocolVersion::V2,
2070                ))?),
2071            )?;
2072            assert_eq!(initialization_state(&client), InitializationState::Ready);
2073
2074            // Requests from the agent do not close the client's window.
2075            client.incoming_message(UntypedMessage::new(
2076                "session/request_permission",
2077                serde_json::json!({}),
2078            )?)?;
2079            client.outgoing_message(
2080                UntypedMessage::new("session/new", serde_json::json!({}))?,
2081                RemoteStyle::Counterpart,
2082            )?;
2083            let error = client
2084                .outgoing_message(
2085                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2086                    RemoteStyle::Counterpart,
2087                )
2088                .expect_err("initialize must not be repeated after another request");
2089            assert!(
2090                error_data(&error).contains("cannot be repeated after any other request"),
2091                "{error:?}"
2092            );
2093            assert_eq!(initialization_state(&client), InitializationState::Ready);
2094            Ok(())
2095        }
2096
2097        #[test]
2098        fn concurrent_initialize_is_rejected_for_first_and_repeated_handshakes()
2099        -> Result<(), crate::Error> {
2100            for agent in [
2101                ProtocolCompat::new(ProtocolMode::v2_agent()),
2102                initialized_v2_agent()?,
2103            ] {
2104                agent.incoming_request(
2105                    &RawJsonRpcId::Number(1),
2106                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2107                )?;
2108                let error = agent
2109                    .incoming_request(
2110                        &RawJsonRpcId::Number(2),
2111                        UntypedMessage::new(
2112                            "initialize",
2113                            v2_initialize_request(ProtocolVersion::V2),
2114                        )?,
2115                    )
2116                    .expect_err("only one initialize may be in flight");
2117                assert_eq!(i32::from(error.code), -32600);
2118                assert!(error_data(&error).contains("in progress"), "{error:?}");
2119            }
2120            Ok(())
2121        }
2122
2123        #[test]
2124        fn v2_connections_reject_null_request_ids() -> Result<(), crate::Error> {
2125            for compat in [
2126                ProtocolCompat::new(ProtocolMode::v2_agent()),
2127                ProtocolCompat::new(ProtocolMode::v2_proxy()),
2128                initialized_v2_agent()?,
2129                initialized_v2_client()?,
2130            ] {
2131                for method in ["initialize", "proxy/initialize", "session/new"] {
2132                    let state = initialization_state(&compat);
2133                    let error = compat
2134                        .incoming_request(
2135                            &RawJsonRpcId::Null,
2136                            UntypedMessage::new(
2137                                method,
2138                                v2_initialize_request(ProtocolVersion::V2),
2139                            )?,
2140                        )
2141                        .expect_err("v2 request ids must not be null");
2142                    assert_eq!(i32::from(error.code), -32600);
2143                    assert!(error_data(&error).contains("not null"), "{error:?}");
2144                    assert_eq!(initialization_state(&compat), state);
2145                    assert_eq!(pending_initialize(&compat), None);
2146                }
2147            }
2148
2149            // V1 keeps accepting null ids.
2150            let v1_agent = ProtocolCompat::new(ProtocolMode::v1_agent());
2151            v1_agent.incoming_request(
2152                &RawJsonRpcId::Null,
2153                UntypedMessage::new(
2154                    "initialize",
2155                    serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2156                )?,
2157            )?;
2158            Ok(())
2159        }
2160
2161        #[test]
2162        fn newer_version_initialize_keeps_what_parses_under_v2() -> Result<(), crate::Error> {
2163            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
2164            let request = agent.incoming_message(UntypedMessage::new(
2165                "initialize",
2166                serde_json::json!({
2167                    "protocolVersion": 3,
2168                    "info": { "name": "future-client" },
2169                    "capabilities": {
2170                        "auth": "malformed",
2171                        "elicitation": {},
2172                        "_futureCapability": { "dropped": true }
2173                    },
2174                    "_meta": "malformed",
2175                    "_futureField": true
2176                }),
2177            )?)?;
2178            let initialize: v2::InitializeRequest =
2179                serde_json::from_value(request.params().clone())?;
2180            assert_eq!(initialize.protocol_version, ProtocolVersion::V2);
2181            assert_eq!(
2182                initialize.info,
2183                v2::Implementation::new("unknown", "unknown")
2184            );
2185            assert_eq!(initialize.capabilities.auth, None);
2186            assert!(initialize.capabilities.elicitation.is_some());
2187            assert_eq!(initialize.meta, None);
2188            assert_eq!(request.params(), &serde_json::to_value(&initialize)?);
2189
2190            // A parsable `info` and `_meta` are kept, and capabilities that
2191            // are not an object are dropped as a whole.
2192            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
2193            let request = agent.incoming_message(UntypedMessage::new(
2194                "initialize",
2195                serde_json::json!({
2196                    "protocolVersion": 3,
2197                    "info": { "name": "future-client", "version": "3.0.0" },
2198                    "capabilities": [],
2199                    "_meta": { "kept": true }
2200                }),
2201            )?)?;
2202            let initialize: v2::InitializeRequest =
2203                serde_json::from_value(request.params().clone())?;
2204            assert_eq!(
2205                initialize.info,
2206                v2::Implementation::new("future-client", "3.0.0")
2207            );
2208            assert_eq!(initialize.capabilities, v2::ClientCapabilities::new());
2209            assert_eq!(
2210                request.params()["_meta"],
2211                serde_json::json!({ "kept": true })
2212            );
2213            Ok(())
2214        }
2215
2216        #[test]
2217        fn exact_version_initialize_stays_strict() -> Result<(), crate::Error> {
2218            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
2219            let error = agent
2220                .incoming_message(UntypedMessage::new(
2221                    "initialize",
2222                    serde_json::json!({ "protocolVersion": 2, "info": { "name": "client" } }),
2223                )?)
2224                .expect_err("an exact v2 request must carry a valid `info`");
2225            assert_eq!(i32::from(error.code), -32602);
2226            assert_eq!(
2227                initialization_state(&agent),
2228                InitializationState::Uninitialized
2229            );
2230            Ok(())
2231        }
2232
2233        #[test]
2234        fn v1_downgrade_keeps_what_parses_under_v1() -> Result<(), crate::Error> {
2235            let agent = ProtocolCompat::new(ProtocolMode::v1_agent());
2236            let request = agent.incoming_message(UntypedMessage::new(
2237                "initialize",
2238                serde_json::json!({
2239                    "protocolVersion": 2,
2240                    "info": "malformed",
2241                    "capabilities": { "elicitation": {} },
2242                    "_meta": 7
2243                }),
2244            )?)?;
2245            let initialize: crate::v1::schema::InitializeRequest =
2246                serde_json::from_value(request.params().clone())?;
2247            assert_eq!(initialize.protocol_version, ProtocolVersion::V1);
2248            assert_eq!(initialize.client_info, None);
2249            assert!(initialize.client_capabilities.elicitation.is_some());
2250            assert_eq!(initialize.meta, None);
2251            assert!(request.params().get("clientInfo").is_none());
2252            assert!(request.params().get("_meta").is_none());
2253
2254            let agent = ProtocolCompat::new(ProtocolMode::v1_agent());
2255            let request = agent.incoming_message(UntypedMessage::new(
2256                "initialize",
2257                serde_json::json!({ "protocolVersion": 2 }),
2258            )?)?;
2259            let initialize: crate::v1::schema::InitializeRequest =
2260                serde_json::from_value(request.params().clone())?;
2261            assert_eq!(initialize.client_info, None);
2262            Ok(())
2263        }
2264
2265        #[test]
2266        fn rejected_concurrent_initialize_does_not_clear_the_active_handshake()
2267        -> Result<(), crate::Error> {
2268            let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
2269            let accepted_id = RawJsonRpcId::Number(1);
2270            let rejected_id = RawJsonRpcId::Number(2);
2271
2272            compat.incoming_request(
2273                &accepted_id,
2274                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2275            )?;
2276            let duplicate_error = compat
2277                .incoming_request(
2278                    &rejected_id,
2279                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2280                )
2281                .expect_err("a second initialize request must be rejected while one is active");
2282            compat
2283                .outgoing_response_to(&rejected_id, "initialize", Err(duplicate_error))
2284                .expect_err("the rejected initialize receives its own error response");
2285
2286            assert_eq!(
2287                initialization_state(&compat),
2288                InitializationState::Initializing
2289            );
2290            assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
2291
2292            compat.outgoing_response_to(
2293                &accepted_id,
2294                "initialize",
2295                Ok(serde_json::to_value(v2_initialize_response(
2296                    ProtocolVersion::V2,
2297                ))?),
2298            )?;
2299            assert_eq!(initialization_state(&compat), InitializationState::Ready);
2300            Ok(())
2301        }
2302
2303        #[test]
2304        fn rejected_wrong_direction_initialize_does_not_clear_client_handshake()
2305        -> Result<(), crate::Error> {
2306            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
2307            compat.outgoing_message(
2308                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2309                RemoteStyle::Counterpart,
2310            )?;
2311
2312            let wrong_direction_id = RawJsonRpcId::Number(2);
2313            let wrong_direction_error = compat
2314                .incoming_request(
2315                    &wrong_direction_id,
2316                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2317                )
2318                .expect_err("agents must not send initialize requests to clients");
2319            compat
2320                .outgoing_response_to(
2321                    &wrong_direction_id,
2322                    "initialize",
2323                    Err(wrong_direction_error),
2324                )
2325                .expect_err("the wrong-direction initialize receives its own error response");
2326
2327            assert_eq!(
2328                initialization_state(&compat),
2329                InitializationState::Initializing
2330            );
2331            assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
2332
2333            compat.incoming_response(
2334                "initialize",
2335                false,
2336                Ok(serde_json::to_value(v2_initialize_response(
2337                    ProtocolVersion::V2,
2338                ))?),
2339            )?;
2340            assert_eq!(initialization_state(&compat), InitializationState::Ready);
2341            Ok(())
2342        }
2343
2344        #[test]
2345        fn incoming_initialize_request_negotiates_newer_protocol_versions()
2346        -> Result<(), crate::Error> {
2347            for requested in [2_u16, 3, u16::MAX] {
2348                let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
2349                let request = compat.incoming_message(UntypedMessage::new(
2350                    "initialize",
2351                    v2_initialize_request(ProtocolVersion::from(requested)),
2352                )?)?;
2353                assert_eq!(request.params()["protocolVersion"], serde_json::json!(2));
2354                assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
2355
2356                let response = compat.outgoing_response(
2357                    "initialize",
2358                    Ok(serde_json::to_value(v2_initialize_response(
2359                        ProtocolVersion::V2,
2360                    ))?),
2361                )?;
2362                assert_eq!(response["protocolVersion"], serde_json::json!(2));
2363                assert_eq!(initialization_state(&compat), InitializationState::Ready);
2364                assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
2365            }
2366            Ok(())
2367        }
2368
2369        #[test]
2370        fn incoming_initialize_request_rejects_unsupported_protocol_version()
2371        -> Result<(), crate::Error> {
2372            let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
2373            let error = compat
2374                .incoming_message(UntypedMessage::new(
2375                    "initialize",
2376                    serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2377                )?)
2378                .expect_err("v2 agents should reject v1 initialization without a v1 handler");
2379            assert_eq!(i32::from(error.code), -32022);
2380            assert_eq!(
2381                error.data,
2382                Some(serde_json::json!({ "requested": 1, "supported": [2] }))
2383            );
2384            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
2385            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
2386            assert_eq!(
2387                initialization_state(&compat),
2388                InitializationState::Uninitialized
2389            );
2390
2391            Ok(())
2392        }
2393
2394        #[test]
2395        fn v1_proxy_negotiates_newer_proxy_initialize_down_to_v1() -> Result<(), crate::Error> {
2396            for requested in [2_u16, 3] {
2397                let compat = ProtocolCompat::new(ProtocolMode::v1_proxy());
2398                let request = compat.incoming_message(UntypedMessage::new(
2399                    "proxy/initialize",
2400                    serde_json::json!({
2401                        "protocolVersion": requested,
2402                        "info": { "name": "future-client", "version": "1.0.0" },
2403                        "capabilities": { "elicitation": {}, "_future": true },
2404                        "_futureField": true
2405                    }),
2406                )?)?;
2407                // The same normalization a v1 agent applies to `initialize`.
2408                let initialize: crate::v1::schema::InitializeRequest =
2409                    serde_json::from_value(request.params().clone())?;
2410                assert_eq!(initialize.protocol_version, ProtocolVersion::V1);
2411                assert_eq!(
2412                    initialize.client_info,
2413                    Some(crate::v1::schema::Implementation::new(
2414                        "future-client",
2415                        "1.0.0"
2416                    ))
2417                );
2418                assert!(initialize.client_capabilities.elicitation.is_some());
2419                assert!(request.params().get("_futureField").is_none());
2420                assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V1));
2421
2422                let response = compat.outgoing_response(
2423                    "proxy/initialize",
2424                    Ok(serde_json::to_value(v1_initialize_response())?),
2425                )?;
2426                assert_eq!(response["protocolVersion"], serde_json::json!(1));
2427            }
2428
2429            // Below v1, a v1 proxy keeps v1's fallback-to-latest rule.
2430            let compat = ProtocolCompat::new(ProtocolMode::v1_proxy());
2431            let request = compat.incoming_message(UntypedMessage::new(
2432                "proxy/initialize",
2433                serde_json::json!({ "protocolVersion": 0 }),
2434            )?)?;
2435            assert_eq!(request.params()["protocolVersion"], serde_json::json!(1));
2436            Ok(())
2437        }
2438
2439        #[test]
2440        fn v2_proxy_negotiates_proxy_initialize_like_a_v2_agent() -> Result<(), crate::Error> {
2441            let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
2442            let request = compat.incoming_message(UntypedMessage::new(
2443                "proxy/initialize",
2444                serde_json::json!({
2445                    "protocolVersion": 3,
2446                    "info": { "name": "future-client" },
2447                    "capabilities": { "elicitation": {}, "_future": { "dropped": true } },
2448                    "_futureField": true
2449                }),
2450            )?)?;
2451            let initialize: v2::InitializeRequest =
2452                serde_json::from_value(request.params().clone())?;
2453            assert_eq!(initialize.protocol_version, ProtocolVersion::V2);
2454            assert_eq!(
2455                initialize.info,
2456                v2::Implementation::new("unknown", "unknown")
2457            );
2458            assert!(initialize.capabilities.elicitation.is_some());
2459            assert_eq!(request.params(), &serde_json::to_value(&initialize)?);
2460            assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
2461
2462            // A v2 proxy rejects a version below v2 as a v2 agent does.
2463            let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
2464            let error = compat
2465                .incoming_message(UntypedMessage::new(
2466                    "proxy/initialize",
2467                    serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2468                )?)
2469                .expect_err("a v2 proxy supports no version at or below v1");
2470            assert_eq!(i32::from(error.code), -32022);
2471            assert_eq!(
2472                error.data,
2473                Some(serde_json::json!({ "requested": 1, "supported": [2] }))
2474            );
2475            assert_eq!(pending_initialize(&compat), None);
2476            Ok(())
2477        }
2478
2479        #[test]
2480        fn exact_version_proxy_initialize_is_unchanged() -> Result<(), crate::Error> {
2481            let compat = ProtocolCompat::new(ProtocolMode::v1_proxy());
2482            let params = serde_json::json!({
2483                "protocolVersion": 1,
2484                "clientCapabilities": {},
2485                "_futureField": true
2486            });
2487            let request = compat
2488                .incoming_message(UntypedMessage::new("proxy/initialize", params.clone())?)?;
2489            assert_eq!(request.params(), &params);
2490
2491            let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
2492            let params = serde_json::to_value(v2_initialize_request(ProtocolVersion::V2))?;
2493            let request = compat
2494                .incoming_message(UntypedMessage::new("proxy/initialize", params.clone())?)?;
2495            assert_eq!(request.params(), &params);
2496            Ok(())
2497        }
2498
2499        #[test]
2500        fn proxy_initialize_requests_reject_wrong_directions_and_wrapping()
2501        -> Result<(), crate::Error> {
2502            for mode in [ProtocolMode::v1_proxy(), ProtocolMode::v2_proxy()] {
2503                let compat = ProtocolCompat::new(mode);
2504                for error in [
2505                    compat
2506                        .incoming_message(UntypedMessage::new(
2507                            "initialize",
2508                            serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
2509                        )?)
2510                        .expect_err("proxy initialization must arrive through `proxy/initialize`"),
2511                    compat
2512                        .incoming_message(UntypedMessage::new(
2513                            "proxy/successor",
2514                            serde_json::json!({
2515                                "method": "initialize",
2516                                "params": { "protocolVersion": ProtocolVersion::V2 }
2517                            }),
2518                        )?)
2519                        .expect_err("successor traffic must not carry initialization"),
2520                    compat
2521                        .incoming_message(UntypedMessage::new(
2522                            "proxy/successor",
2523                            serde_json::json!({
2524                                "method": "proxy/successor",
2525                                "params": {
2526                                    "method": "proxy/initialize",
2527                                    "params": { "protocolVersion": ProtocolVersion::V2 }
2528                                }
2529                            }),
2530                        )?)
2531                        .expect_err("nested successor traffic must not carry initialization"),
2532                    compat
2533                        .outgoing_message(
2534                            UntypedMessage::new(
2535                                "initialize",
2536                                serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
2537                            )?,
2538                            RemoteStyle::Predecessor,
2539                        )
2540                        .expect_err("downstream proxy initialization must use `proxy/successor`"),
2541                    compat
2542                        .outgoing_message(
2543                            UntypedMessage::new(
2544                                "proxy/initialize",
2545                                serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
2546                            )?,
2547                            RemoteStyle::Successor,
2548                        )
2549                        .expect_err("a proxy must not send `proxy/initialize` downstream"),
2550                ] {
2551                    let data = error
2552                        .data
2553                        .as_ref()
2554                        .and_then(|data| data.as_str())
2555                        .unwrap_or_default();
2556                    assert!(data.contains("proxy/initialize"), "{error:?}");
2557                    assert!(data.contains("proxy/successor"), "{error:?}");
2558                }
2559                assert_eq!(pending_initialize(&compat), None);
2560            }
2561
2562            Ok(())
2563        }
2564
2565        #[test]
2566        fn outgoing_initialize_rejects_non_object_params() -> Result<(), crate::Error> {
2567            for (compat, message, remote_style) in [
2568                (
2569                    ProtocolCompat::new(ProtocolMode::v2_client()),
2570                    UntypedMessage::new("initialize", serde_json::Value::Null)?,
2571                    RemoteStyle::Counterpart,
2572                ),
2573                (
2574                    ProtocolCompat::new(ProtocolMode::v2_proxy()),
2575                    UntypedMessage::new("initialize", serde_json::Value::Null)?,
2576                    RemoteStyle::Successor,
2577                ),
2578            ] {
2579                let error = compat
2580                    .outgoing_message(message, remote_style)
2581                    .expect_err("initialize params must be an object");
2582                let data = error
2583                    .data
2584                    .as_ref()
2585                    .and_then(|data| data.as_str())
2586                    .unwrap_or_default();
2587                assert!(data.contains("protocolVersion"), "{error:?}");
2588                assert_eq!(pending_initialize(&compat), None);
2589            }
2590
2591            Ok(())
2592        }
2593
2594        #[test]
2595        fn outgoing_initialize_rejects_explicit_successor_wrapping() -> Result<(), crate::Error> {
2596            for message in [
2597                UntypedMessage::new(
2598                    "proxy/successor",
2599                    serde_json::json!({
2600                        "method": "initialize",
2601                        "params": { "protocolVersion": ProtocolVersion::V1 }
2602                    }),
2603                )?,
2604                UntypedMessage::new(
2605                    "proxy/successor",
2606                    serde_json::json!({
2607                        "method": "proxy/successor",
2608                        "params": {
2609                            "method": "initialize",
2610                            "params": { "protocolVersion": ProtocolVersion::V1 }
2611                        }
2612                    }),
2613                )?,
2614            ] {
2615                let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
2616                let error = compat
2617                    .outgoing_message(message, RemoteStyle::Successor)
2618                    .expect_err("connection routing must own successor wrapping");
2619                let data = error
2620                    .data
2621                    .as_ref()
2622                    .and_then(|data| data.as_str())
2623                    .unwrap_or_default();
2624                assert!(data.contains("logical `initialize`"), "{error:?}");
2625                assert!(data.contains("proxy/successor"), "{error:?}");
2626                assert_eq!(pending_initialize(&compat), None);
2627            }
2628
2629            Ok(())
2630        }
2631
2632        #[test]
2633        fn proxy_initialize_round_trip_uses_the_selected_protocol_version()
2634        -> Result<(), crate::Error> {
2635            for (mode, selected, selected_kind) in [
2636                (
2637                    ProtocolMode::v1_proxy(),
2638                    ProtocolVersion::V1,
2639                    ProtocolVersionKind::V1,
2640                ),
2641                (
2642                    ProtocolMode::v2_proxy(),
2643                    ProtocolVersion::V2,
2644                    ProtocolVersionKind::V2,
2645                ),
2646            ] {
2647                let compat = ProtocolCompat::new(mode);
2648                let request = compat.incoming_message(UntypedMessage::new(
2649                    "proxy/initialize",
2650                    serde_json::json!({ "protocolVersion": selected }),
2651                )?)?;
2652                assert_eq!(
2653                    required_protocol_version_from_value(request.params())?,
2654                    selected
2655                );
2656                assert_eq!(pending_initialize(&compat), Some(selected_kind));
2657
2658                let response = compat.outgoing_response(
2659                    "proxy/initialize",
2660                    Ok(serde_json::json!({ "protocolVersion": selected })),
2661                )?;
2662                assert_eq!(required_protocol_version_from_value(&response)?, selected);
2663                assert_eq!(negotiated(&compat), selected_kind);
2664                assert_eq!(pending_initialize(&compat), None);
2665                assert_eq!(compat.active_wire_version(), selected_kind);
2666            }
2667
2668            Ok(())
2669        }
2670
2671        #[test]
2672        fn proxy_initialize_response_rejects_the_wrong_version_and_clears_pending_state()
2673        -> Result<(), crate::Error> {
2674            for (mode, selected, unsupported, selected_kind) in [
2675                (
2676                    ProtocolMode::v1_proxy(),
2677                    ProtocolVersion::V1,
2678                    ProtocolVersion::V2,
2679                    ProtocolVersionKind::V1,
2680                ),
2681                (
2682                    ProtocolMode::v2_proxy(),
2683                    ProtocolVersion::V2,
2684                    ProtocolVersion::V1,
2685                    ProtocolVersionKind::V2,
2686                ),
2687            ] {
2688                let compat = ProtocolCompat::new(mode);
2689                let request = compat.outgoing_message(
2690                    UntypedMessage::new(
2691                        "initialize",
2692                        serde_json::json!({ "protocolVersion": unsupported }),
2693                    )?,
2694                    RemoteStyle::Successor,
2695                )?;
2696                assert_eq!(
2697                    required_protocol_version_from_value(request.params())?,
2698                    selected
2699                );
2700                assert_eq!(pending_initialize(&compat), Some(selected_kind));
2701
2702                let error = compat
2703                    .incoming_response(
2704                        "initialize",
2705                        false,
2706                        Ok(serde_json::json!({ "protocolVersion": unsupported })),
2707                    )
2708                    .expect_err("proxy initialization must reject a mismatched response version");
2709                let data = error
2710                    .data
2711                    .as_ref()
2712                    .and_then(|data| data.as_str())
2713                    .unwrap_or_default();
2714                assert!(
2715                    data.contains(&format!(
2716                        "required ACP protocol version {selected} but peer negotiated {unsupported}"
2717                    )),
2718                    "{error:?}"
2719                );
2720                assert_eq!(negotiated(&compat), selected_kind);
2721                assert_eq!(pending_initialize(&compat), None);
2722                assert_eq!(compat.active_wire_version(), selected_kind);
2723            }
2724
2725            Ok(())
2726        }
2727
2728        #[test]
2729        #[should_panic(expected = "cannot merge ACP builders with different API protocol versions")]
2730        fn merging_different_api_protocol_modes_panics() {
2731            let _ = ProtocolMode::v1_agent().merge(ProtocolMode::v2_agent());
2732        }
2733
2734        #[test]
2735        #[should_panic(expected = "cannot merge standard ACP and proxy ACP builders")]
2736        fn merging_standard_and_proxy_protocol_modes_panics() {
2737            let _ = ProtocolMode::v1_agent().merge(ProtocolMode::v1_proxy());
2738        }
2739    }
2740}
2741
2742pub(crate) use imp::{ProtocolCompat, ProtocolMode};