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