Skip to main content

agent_client_protocol/jsonrpc/
protocol_compat.rs

1/// Normalize a newer peer's initialization into the stable v1 surface.
2///
3/// Only shared capabilities are carried across. In particular, unknown newer
4/// fields must never enable the v1 file-system or terminal-execution surfaces.
5pub(crate) fn normalize_v1_initialize_request(
6    params: &serde_json::Value,
7) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
8    use crate::schema::{ProtocolVersion, v1};
9
10    let info: v1::Implementation =
11        serde_json::from_value(params.get("info").cloned().unwrap_or_default())?;
12    let mut capabilities = serde_json::Map::new();
13    if let Some(source) = params
14        .get("capabilities")
15        .and_then(serde_json::Value::as_object)
16    {
17        for key in ["auth", "elicitation", "nes", "positionEncodings", "_meta"] {
18            if let Some(value) = source.get(key) {
19                capabilities.insert(key.into(), value.clone());
20            }
21        }
22    }
23    if let Some(auth) = capabilities
24        .get_mut("auth")
25        .and_then(serde_json::Value::as_object_mut)
26    {
27        let terminal = auth
28            .remove("terminal")
29            .is_some_and(|value| value.is_object());
30        auth.insert("terminal".into(), terminal.into());
31    }
32    capabilities.insert(
33        "session".into(),
34        serde_json::json!({ "configOptions": { "boolean": {} } }),
35    );
36    let mut target = serde_json::json!({
37        "protocolVersion": ProtocolVersion::V1,
38        "clientInfo": info,
39        "clientCapabilities": capabilities,
40    });
41    if let Some(meta) = params.get("_meta") {
42        target["_meta"] = meta.clone();
43    }
44    let initialize: v1::InitializeRequest = serde_json::from_value(target)?;
45    let serde_json::Value::Object(normalized) =
46        serde_json::to_value(initialize).map_err(crate::Error::into_internal_error)?
47    else {
48        return Err(crate::util::internal_error(
49            "initialize params did not serialize to an object",
50        ));
51    };
52    Ok(normalized)
53}
54
55/// The outcome of a v2 client's [`send_initialize`](crate::V2ConnectionTo::send_initialize).
56///
57/// A v1-only agent may legitimately answer a v2 initialization attempt with a
58/// v1 response. That is a successful reply, not an error, so it is reported as
59/// [`InitializeOutcome::LowerVersion`]. Errors from the agent, malformed
60/// responses, and transport failures still resolve the request with an error.
61#[cfg(feature = "unstable_protocol_v2")]
62#[derive(Debug, Clone, PartialEq, Eq)]
63#[non_exhaustive]
64pub enum InitializeOutcome {
65    /// The agent selected v2 and the connection is ready for v2 traffic.
66    Initialized(crate::schema::v2::InitializeResponse),
67    /// The agent selected a lower protocol version.
68    ///
69    /// The connection stays uninitialized: a v2 connection cannot carry the
70    /// selected version's traffic, and the SDK never converts it. Fall back to
71    /// an implementation of the selected version if that is acceptable.
72    LowerVersion(InitializationVersionMismatch),
73}
74
75/// A successful `initialize` response that selected a lower protocol version
76/// than the local API requires.
77///
78/// Reported by [`InitializeOutcome::LowerVersion`]. This is not the `-32022`
79/// (`Unsupported protocol version`) error an agent returns when it supports no
80/// version at or below the requested one.
81#[cfg(feature = "unstable_protocol_v2")]
82#[derive(Debug, Clone, PartialEq, Eq)]
83#[non_exhaustive]
84pub struct InitializationVersionMismatch {
85    /// The protocol version the local connection requested.
86    pub requested: crate::schema::ProtocolVersion,
87    /// The lower protocol version the peer selected.
88    pub selected: crate::schema::ProtocolVersion,
89    /// The peer's validated initialization response for the selected version.
90    pub response: SelectedInitializeResponse,
91}
92
93#[cfg(feature = "unstable_protocol_v2")]
94impl InitializationVersionMismatch {
95    /// Describe a peer that answered a request for `requested` with
96    /// `response`, selecting the response's protocol version.
97    ///
98    /// Useful for exercising fallback paths in tests.
99    #[must_use]
100    pub fn new(
101        requested: crate::schema::ProtocolVersion,
102        response: SelectedInitializeResponse,
103    ) -> Self {
104        Self {
105            requested,
106            selected: response.protocol_version(),
107            response,
108        }
109    }
110}
111
112#[cfg(feature = "unstable_protocol_v2")]
113impl std::fmt::Display for InitializationVersionMismatch {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        write!(
116            f,
117            "requested ACP protocol version {} but peer selected {}",
118            self.requested, self.selected,
119        )
120    }
121}
122
123/// A peer's initialization response, typed by the protocol version it selected.
124///
125/// New variants are added as later protocol versions make more versions
126/// selectable below the one a client requests.
127#[cfg(feature = "unstable_protocol_v2")]
128#[derive(Debug, Clone, PartialEq, Eq)]
129#[non_exhaustive]
130pub enum SelectedInitializeResponse {
131    /// The peer selected ACP protocol v1.
132    V1(crate::schema::v1::InitializeResponse),
133}
134
135#[cfg(feature = "unstable_protocol_v2")]
136impl SelectedInitializeResponse {
137    /// The protocol version the response selects.
138    #[must_use]
139    pub fn protocol_version(&self) -> crate::schema::ProtocolVersion {
140        match self {
141            Self::V1(response) => response.protocol_version,
142        }
143    }
144
145    /// Parse `value` as the initialization response for `version`, or return
146    /// `None` if the SDK has no response type for that version.
147    fn from_value(
148        version: crate::schema::ProtocolVersion,
149        value: serde_json::Value,
150    ) -> Option<Result<Self, crate::Error>> {
151        (version == crate::schema::ProtocolVersion::V1).then(|| {
152            <crate::schema::v1::InitializeResponse as crate::JsonRpcResponse>::from_value(
153                "initialize",
154                value,
155            )
156            .map(Self::V1)
157        })
158    }
159}
160
161#[cfg(feature = "unstable_protocol_v2")]
162impl crate::JsonRpcResponse for InitializeOutcome {
163    fn into_json(self, method: &str) -> Result<serde_json::Value, crate::Error> {
164        match self {
165            Self::Initialized(response) => response.into_json(method),
166            Self::LowerVersion(mismatch) => match mismatch.response {
167                SelectedInitializeResponse::V1(response) => response.into_json(method),
168            },
169        }
170    }
171
172    fn from_value(method: &str, value: serde_json::Value) -> Result<Self, crate::Error> {
173        let selected: crate::schema::ProtocolVersion = value
174            .get("protocolVersion")
175            .cloned()
176            .map(serde_json::from_value)
177            .transpose()
178            .ok()
179            .flatten()
180            .ok_or_else(|| {
181                crate::Error::invalid_params()
182                    .data("initialize.protocolVersion must be a valid ACP protocol version")
183            })?;
184        let requested = crate::schema::ProtocolVersion::V2;
185        if selected == requested {
186            return crate::schema::v2::InitializeResponse::from_value(method, value)
187                .map(Self::Initialized);
188        }
189        match SelectedInitializeResponse::from_value(selected, value) {
190            Some(response) if selected < requested => Ok(Self::LowerVersion(
191                InitializationVersionMismatch::new(requested, response?),
192            )),
193            _ => Err(crate::Error::invalid_request().data(format!(
194                "unsupported ACP protocol version {selected} in initialize response",
195            ))),
196        }
197    }
198}
199
200/// The request sent by [`send_initialize`](crate::V2ConnectionTo::send_initialize).
201///
202/// Its pending reply opts into receiving a lower-version response as a
203/// successful [`InitializeOutcome`] instead of the connection's local error.
204#[cfg(feature = "unstable_protocol_v2")]
205#[derive(Debug, Clone)]
206pub(crate) struct InitializeNegotiation(pub(crate) crate::schema::v2::InitializeRequest);
207
208#[cfg(feature = "unstable_protocol_v2")]
209impl crate::JsonRpcMessage for InitializeNegotiation {
210    fn matches_method(method: &str) -> bool {
211        crate::schema::v2::InitializeRequest::matches_method(method)
212    }
213
214    fn method(&self) -> &str {
215        self.0.method()
216    }
217
218    fn to_untyped_message(&self) -> Result<crate::UntypedMessage, crate::Error> {
219        self.0.to_untyped_message()
220    }
221
222    fn parse_message(method: &str, params: &impl serde::Serialize) -> Result<Self, crate::Error> {
223        crate::schema::v2::InitializeRequest::parse_message(method, params).map(Self)
224    }
225}
226
227#[cfg(feature = "unstable_protocol_v2")]
228impl crate::JsonRpcRequest for InitializeNegotiation {
229    type Response = InitializeOutcome;
230}
231
232mod imp {
233    use std::sync::{Arc, Mutex};
234
235    use crate::schema::{ProtocolVersion, v1::RequestId};
236    use crate::{UntypedMessage, role::RemoteStyle};
237
238    #[derive(Clone, Copy, Debug)]
239    pub(crate) enum ProtocolMode {
240        Disabled,
241        Acp(AcpProtocolMode),
242    }
243
244    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
245    pub(crate) struct AcpProtocolMode {
246        api: ProtocolVersionKind,
247        initialize_surface: InitializeSurface,
248        initialization_role: InitializationRole,
249    }
250
251    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
252    enum InitializeSurface {
253        Peer,
254        Proxy,
255    }
256
257    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
258    #[cfg_attr(not(feature = "unstable_protocol_v2"), allow(dead_code))]
259    enum InitializationRole {
260        /// Preserve the v1 and proxy initialization behavior. A v2 proxy has
261        /// both an incoming predecessor initialization and an outgoing
262        /// successor initialization, so the peer lifecycle does not apply to
263        /// it.
264        Unchecked,
265        Initiator,
266        Responder,
267    }
268
269    impl AcpProtocolMode {
270        fn is_incoming_initialize_request(self, method: &str) -> bool {
271            match self.initialize_surface {
272                InitializeSurface::Peer => method == "initialize",
273                InitializeSurface::Proxy => method == "_proxy/initialize",
274            }
275        }
276
277        fn is_incoming_initialize_response(method: &str) -> bool {
278            // Pending replies retain the logical method from before successor
279            // wrapping, so a proxy's downstream initialize response is also
280            // keyed by `initialize`.
281            method == "initialize"
282        }
283
284        fn is_outgoing_initialize_response(self, method: &str) -> bool {
285            match self.initialize_surface {
286                InitializeSurface::Peer => method == "initialize",
287                InitializeSurface::Proxy => method == "_proxy/initialize",
288            }
289        }
290    }
291
292    impl ProtocolMode {
293        pub(crate) fn disabled() -> Self {
294            Self::Disabled
295        }
296
297        pub(crate) fn v1_agent() -> Self {
298            Self::Acp(AcpProtocolMode {
299                api: ProtocolVersionKind::V1,
300                initialize_surface: InitializeSurface::Peer,
301                initialization_role: InitializationRole::Unchecked,
302            })
303        }
304
305        pub(crate) fn v1_client() -> Self {
306            Self::Acp(AcpProtocolMode {
307                api: ProtocolVersionKind::V1,
308                initialize_surface: InitializeSurface::Peer,
309                initialization_role: InitializationRole::Unchecked,
310            })
311        }
312
313        pub(crate) fn v1_proxy() -> Self {
314            Self::Acp(AcpProtocolMode {
315                api: ProtocolVersionKind::V1,
316                initialize_surface: InitializeSurface::Proxy,
317                initialization_role: InitializationRole::Unchecked,
318            })
319        }
320
321        #[cfg(feature = "unstable_protocol_v2")]
322        pub(crate) fn v2_agent() -> Self {
323            Self::Acp(AcpProtocolMode {
324                api: ProtocolVersionKind::V2,
325                initialize_surface: InitializeSurface::Peer,
326                initialization_role: InitializationRole::Responder,
327            })
328        }
329
330        #[cfg(feature = "unstable_protocol_v2")]
331        pub(crate) fn v2_client() -> Self {
332            Self::Acp(AcpProtocolMode {
333                api: ProtocolVersionKind::V2,
334                initialize_surface: InitializeSurface::Peer,
335                initialization_role: InitializationRole::Initiator,
336            })
337        }
338
339        #[cfg(feature = "unstable_protocol_v2")]
340        pub(crate) fn v2_proxy() -> Self {
341            Self::Acp(AcpProtocolMode {
342                api: ProtocolVersionKind::V2,
343                initialize_surface: InitializeSurface::Proxy,
344                initialization_role: InitializationRole::Unchecked,
345            })
346        }
347
348        pub(crate) fn merge(self, other: Self) -> Self {
349            match (self, other) {
350                (Self::Disabled, other) => other,
351                (this, Self::Disabled) => this,
352                (Self::Acp(this), Self::Acp(other)) => {
353                    assert_eq!(
354                        this.api, other.api,
355                        "cannot merge ACP builders with different API protocol versions; \
356                         handler chains share a single API surface",
357                    );
358                    assert_eq!(
359                        this.initialize_surface, other.initialize_surface,
360                        "cannot merge standard ACP and proxy ACP builders; \
361                         handler chains share one initialization surface",
362                    );
363                    assert_eq!(
364                        this.initialization_role, other.initialization_role,
365                        "cannot merge ACP builders with different initialization roles; \
366                         handler chains share one connection lifecycle",
367                    );
368                    Self::Acp(this)
369                }
370            }
371        }
372
373        #[cfg(feature = "unstable_protocol_v2")]
374        pub(crate) fn api_protocol_version(self) -> Option<ProtocolVersion> {
375            match self {
376                Self::Disabled => None,
377                Self::Acp(mode) => Some(mode.api.as_protocol_version()),
378            }
379        }
380    }
381
382    #[derive(Clone, Debug)]
383    pub(crate) struct ProtocolCompat {
384        mode: Option<AcpProtocolMode>,
385        state: Arc<Mutex<ProtocolState>>,
386    }
387
388    #[derive(Debug)]
389    struct ProtocolState {
390        negotiated: ProtocolVersionKind,
391        pending_initialize: Option<ProtocolVersionKind>,
392        incoming_initialize_id: Option<RequestId>,
393        initialization: InitializationState,
394    }
395
396    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
397    enum InitializationState {
398        Unchecked,
399        Uninitialized,
400        Initializing,
401        Ready,
402    }
403
404    /// How an initialize response that passed validation completes the
405    /// handshake.
406    enum InitializeResponseCheck {
407        Negotiated(ProtocolVersionKind),
408        /// A validated lower selection for a `send_initialize` request.
409        #[cfg_attr(not(feature = "unstable_protocol_v2"), allow(dead_code))]
410        LowerVersion,
411    }
412
413    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
414    enum ProtocolVersionKind {
415        V1,
416        V2,
417    }
418
419    impl ProtocolVersionKind {
420        fn as_protocol_version(self) -> ProtocolVersion {
421            match self {
422                Self::V1 => ProtocolVersion::V1,
423                Self::V2 => ProtocolVersion::from(2_u16),
424            }
425        }
426
427        fn from_protocol_version(version: ProtocolVersion) -> Option<Self> {
428            if version == ProtocolVersion::V1 {
429                Some(Self::V1)
430            } else if version == ProtocolVersion::from(2_u16) {
431                Some(Self::V2)
432            } else {
433                None
434            }
435        }
436    }
437
438    impl ProtocolCompat {
439        pub(crate) fn new(mode: ProtocolMode) -> Self {
440            let mode = match mode {
441                ProtocolMode::Disabled => None,
442                ProtocolMode::Acp(mode) => Some(mode),
443            };
444            let negotiated = mode.map_or(ProtocolVersionKind::V1, |mode| mode.api);
445            let initialization = match mode.map(|mode| mode.initialization_role) {
446                Some(InitializationRole::Initiator | InitializationRole::Responder) => {
447                    InitializationState::Uninitialized
448                }
449                Some(InitializationRole::Unchecked) | None => InitializationState::Unchecked,
450            };
451
452            Self {
453                mode,
454                state: Arc::new(Mutex::new(ProtocolState {
455                    negotiated,
456                    pending_initialize: None,
457                    incoming_initialize_id: None,
458                    initialization,
459                })),
460            }
461        }
462
463        #[cfg(all(test, feature = "unstable_protocol_v2"))]
464        pub(crate) fn incoming_message(
465            &self,
466            message: UntypedMessage,
467        ) -> Result<UntypedMessage, crate::Error> {
468            self.incoming_request(&RequestId::Null, message)
469        }
470
471        pub(crate) fn incoming_request(
472            &self,
473            id: &RequestId,
474            message: UntypedMessage,
475        ) -> Result<UntypedMessage, crate::Error> {
476            let Some(mode) = self.mode else {
477                return Ok(message);
478            };
479
480            if mode.is_incoming_initialize_request(message.method()) {
481                return self.incoming_initialize_request(mode, id, message);
482            }
483            if mode.initialize_surface == InitializeSurface::Proxy
484                && (message.method() == "initialize" || successor_encloses_initialize(&message))
485            {
486                return Err(invalid_proxy_initialize_direction());
487            }
488
489            self.ensure_initialized(message.method())?;
490            ensure_matching_protocol_version(
491                message.method(),
492                self.active_wire_version(),
493                mode.api,
494            )?;
495            Ok(message)
496        }
497
498        pub(crate) fn outgoing_message(
499            &self,
500            mut message: UntypedMessage,
501            remote_style: RemoteStyle,
502        ) -> Result<UntypedMessage, crate::Error> {
503            let Some(mode) = self.mode else {
504                return Ok(message);
505            };
506
507            let wire_version = if let Some(params) =
508                outgoing_initialize_params(mode, remote_style, &mut message)?
509            {
510                set_protocol_version(params, mode.api)?;
511                validate_native_v2_initialize_request(mode, params)?;
512                self.begin_outgoing_initialize(mode)?;
513                mode.api
514            } else {
515                self.ensure_initialized(message.method())?;
516                self.active_wire_version()
517            };
518
519            ensure_matching_protocol_version(message.method(), mode.api, wire_version)?;
520            Ok(message)
521        }
522
523        pub(crate) fn incoming_notification(
524            &self,
525            message: UntypedMessage,
526        ) -> Result<Vec<UntypedMessage>, crate::Error> {
527            let Some(mode) = self.mode else {
528                return Ok(vec![message]);
529            };
530
531            self.ensure_notification_allowed(message.method())?;
532            ensure_matching_protocol_version(
533                message.method(),
534                self.active_wire_version(),
535                mode.api,
536            )?;
537            Ok(vec![message])
538        }
539
540        pub(crate) fn outgoing_notification(
541            &self,
542            message: UntypedMessage,
543        ) -> Result<Vec<UntypedMessage>, crate::Error> {
544            let Some(mode) = self.mode else {
545                return Ok(vec![message]);
546            };
547
548            self.ensure_notification_allowed(message.method())?;
549            ensure_matching_protocol_version(
550                message.method(),
551                mode.api,
552                self.active_wire_version(),
553            )?;
554            Ok(vec![message])
555        }
556
557        /// Check an incoming response against the protocol state.
558        ///
559        /// `accepts_lower_version` marks a pending
560        /// [`super::InitializeNegotiation`], which receives a validated
561        /// lower-version initialize response as a success.
562        pub(crate) fn incoming_response(
563            &self,
564            method: &str,
565            accepts_lower_version: bool,
566            result: Result<serde_json::Value, crate::Error>,
567        ) -> Result<serde_json::Value, crate::Error> {
568            let Some(mode) = self.mode else {
569                return result;
570            };
571
572            if AcpProtocolMode::is_incoming_initialize_response(method) {
573                return self.incoming_initialize_response(mode, accepts_lower_version, result);
574            }
575
576            let value = result?;
577            self.ensure_initialized(method)?;
578            ensure_matching_protocol_version(method, self.active_wire_version(), mode.api)?;
579            Ok(value)
580        }
581
582        #[cfg(all(test, feature = "unstable_protocol_v2"))]
583        pub(crate) fn outgoing_response(
584            &self,
585            method: &str,
586            result: Result<serde_json::Value, crate::Error>,
587        ) -> Result<serde_json::Value, crate::Error> {
588            self.outgoing_response_to(&RequestId::Null, method, result)
589        }
590
591        pub(crate) fn outgoing_response_to(
592            &self,
593            id: &RequestId,
594            method: &str,
595            result: Result<serde_json::Value, crate::Error>,
596        ) -> Result<serde_json::Value, crate::Error> {
597            let Some(mode) = self.mode else {
598                return result;
599            };
600
601            if mode.is_outgoing_initialize_response(method) {
602                match mode.initialization_role {
603                    InitializationRole::Initiator => {
604                        return result.and_then(|_| {
605                            Err(unexpected_initialize_response(mode.initialization_role))
606                        });
607                    }
608                    InitializationRole::Responder if !self.is_pending_incoming_initialize(id) => {
609                        return result.and_then(|_| {
610                            Err(unexpected_initialize_response(mode.initialization_role))
611                        });
612                    }
613                    InitializationRole::Unchecked | InitializationRole::Responder => {}
614                }
615                let mut value = match result {
616                    Ok(value) => value,
617                    Err(error) => {
618                        self.fail_initialize();
619                        return Err(error);
620                    }
621                };
622                let negotiated = self.pending_initialize().or_else(|| {
623                    (mode.initialization_role == InitializationRole::Unchecked).then_some(mode.api)
624                });
625                let negotiated = negotiated
626                    .ok_or_else(|| unexpected_initialize_response(mode.initialization_role))?;
627                if let Err(error) = ensure_matching_protocol_version(method, mode.api, negotiated)
628                    .and_then(|()| set_protocol_version(&mut value, negotiated))
629                    .and_then(|()| validate_native_v2_initialize_response(mode, &value))
630                {
631                    self.fail_initialize();
632                    return Err(error);
633                }
634                self.complete_initialize(negotiated)?;
635                return Ok(value);
636            }
637
638            let value = result?;
639            self.ensure_initialized(method)?;
640            let wire_version = self.active_wire_version();
641
642            ensure_matching_protocol_version(method, mode.api, wire_version)?;
643            Ok(value)
644        }
645
646        fn incoming_initialize_request(
647            &self,
648            mode: AcpProtocolMode,
649            id: &RequestId,
650            mut message: UntypedMessage,
651        ) -> Result<UntypedMessage, crate::Error> {
652            let requested = required_protocol_version_from_value(message.params())?;
653            if mode.initialize_surface == InitializeSurface::Peer {
654                // V1 retains its original fallback-to-latest bootstrap. Only
655                // v2 endpoints reject a version below their supported range.
656                if mode.api == ProtocolVersionKind::V2 && requested < mode.api.as_protocol_version()
657                {
658                    return Err(crate::util::unsupported_protocol_version(
659                        requested,
660                        &[mode.api.as_protocol_version()],
661                    ));
662                }
663                if mode.api == ProtocolVersionKind::V1 && requested > ProtocolVersion::V1 {
664                    message.params = serde_json::Value::Object(
665                        super::normalize_v1_initialize_request(message.params())?,
666                    );
667                }
668            } else if ProtocolVersionKind::from_protocol_version(requested) != Some(mode.api) {
669                // The conductor has already selected a proxy's wire version.
670                return Err(unsupported_protocol_version(requested, mode.api));
671            }
672
673            set_protocol_version(&mut message.params, mode.api)?;
674            validate_native_v2_initialize_request(mode, message.params())?;
675            self.begin_incoming_initialize(mode, id)?;
676            Ok(message)
677        }
678
679        fn incoming_initialize_response(
680            &self,
681            mode: AcpProtocolMode,
682            accepts_lower_version: bool,
683            result: Result<serde_json::Value, crate::Error>,
684        ) -> Result<serde_json::Value, crate::Error> {
685            let mut value = match result {
686                Ok(value) => value,
687                Err(error) => {
688                    self.fail_initialize();
689                    return Err(error);
690                }
691            };
692            let response = (|| {
693                let pending = self.pending_initialize().or_else(|| {
694                    (mode.initialization_role == InitializationRole::Unchecked).then_some(mode.api)
695                });
696                let pending = pending
697                    .ok_or_else(|| unexpected_initialize_response(mode.initialization_role))?;
698                let response_version = required_protocol_version_from_value(&value)?;
699                let wire_version = ProtocolVersionKind::from_protocol_version(response_version)
700                    .ok_or_else(|| unsupported_protocol_version(response_version, mode.api))?;
701                // Only a client's own handshake, sent by `send_initialize`,
702                // has a caller that can fall back. A proxy relays its
703                // successor's outcome, and the conductor has already fixed
704                // that wire version.
705                #[cfg(feature = "unstable_protocol_v2")]
706                if accepts_lower_version
707                    && mode.initialization_role == InitializationRole::Initiator
708                    && wire_version < mode.api
709                    && let Some(response) = super::SelectedInitializeResponse::from_value(
710                        wire_version.as_protocol_version(),
711                        value.clone(),
712                    )
713                {
714                    response?;
715                    return Ok(InitializeResponseCheck::LowerVersion);
716                }
717                #[cfg(not(feature = "unstable_protocol_v2"))]
718                let _ = accepts_lower_version;
719                if wire_version != mode.api {
720                    return Err(required_protocol_version(mode.api, wire_version));
721                }
722                ensure_matching_protocol_version("initialize", pending, wire_version)?;
723                set_protocol_version(&mut value, wire_version)?;
724                validate_native_v2_initialize_response(mode, &value)?;
725                Ok(InitializeResponseCheck::Negotiated(wire_version))
726            })();
727
728            match response {
729                Ok(InitializeResponseCheck::Negotiated(wire_version)) => {
730                    self.complete_initialize(wire_version)?;
731                    Ok(value)
732                }
733                // The caller sees the response, but this connection cannot
734                // use it.
735                Ok(InitializeResponseCheck::LowerVersion) => {
736                    self.fail_initialize();
737                    Ok(value)
738                }
739                Err(error) => {
740                    self.fail_initialize();
741                    Err(error)
742                }
743            }
744        }
745
746        fn begin_incoming_initialize(
747            &self,
748            mode: AcpProtocolMode,
749            id: &RequestId,
750        ) -> Result<(), crate::Error> {
751            match mode.initialization_role {
752                InitializationRole::Initiator => return Err(invalid_initialize_direction()),
753                InitializationRole::Unchecked | InitializationRole::Responder => {}
754            }
755            self.begin_initialize(mode.api, Some(id))
756        }
757
758        fn begin_outgoing_initialize(&self, mode: AcpProtocolMode) -> Result<(), crate::Error> {
759            match mode.initialization_role {
760                InitializationRole::Responder => return Err(invalid_initialize_direction()),
761                InitializationRole::Unchecked | InitializationRole::Initiator => {}
762            }
763            self.begin_initialize(mode.api, None)
764        }
765
766        fn begin_initialize(
767            &self,
768            requested: ProtocolVersionKind,
769            incoming_id: Option<&RequestId>,
770        ) -> Result<(), crate::Error> {
771            let mut state = self
772                .state
773                .lock()
774                .expect("protocol compatibility state mutex poisoned");
775            match state.initialization {
776                InitializationState::Unchecked => {
777                    state.pending_initialize = Some(requested);
778                    Ok(())
779                }
780                InitializationState::Uninitialized => {
781                    state.initialization = InitializationState::Initializing;
782                    state.pending_initialize = Some(requested);
783                    state.incoming_initialize_id = incoming_id.cloned();
784                    Ok(())
785                }
786                InitializationState::Initializing => Err(crate::Error::invalid_request()
787                    .data("ACP initialization is already in progress on this connection")),
788                InitializationState::Ready => Err(crate::Error::invalid_request().data(
789                    "ACP connections may only be initialized once; reconnect to initialize again",
790                )),
791            }
792        }
793
794        fn ensure_initialized(&self, method: &str) -> Result<(), crate::Error> {
795            let state = self
796                .state
797                .lock()
798                .expect("protocol compatibility state mutex poisoned")
799                .initialization;
800            match state {
801                InitializationState::Unchecked | InitializationState::Ready => Ok(()),
802                InitializationState::Uninitialized | InitializationState::Initializing => {
803                    Err(crate::Error::invalid_request().data(format!(
804                        "ACP initialization must complete before `{method}` can be used",
805                    )))
806                }
807            }
808        }
809
810        fn ensure_notification_allowed(&self, method: &str) -> Result<(), crate::Error> {
811            let initialization = self
812                .state
813                .lock()
814                .expect("protocol compatibility state mutex poisoned")
815                .initialization;
816            if initialization == InitializationState::Initializing && method == "$/cancel_request" {
817                return Ok(());
818            }
819            self.ensure_initialized(method)
820        }
821
822        fn complete_initialize(&self, negotiated: ProtocolVersionKind) -> Result<(), crate::Error> {
823            let mut state = self
824                .state
825                .lock()
826                .expect("protocol compatibility state mutex poisoned");
827            if state.pending_initialize.is_none()
828                && state.initialization != InitializationState::Unchecked
829            {
830                return Err(unexpected_initialize_response(
831                    self.mode
832                        .expect("protocol initialization requires an ACP mode")
833                        .initialization_role,
834                ));
835            }
836            if !matches!(
837                state.initialization,
838                InitializationState::Unchecked | InitializationState::Initializing
839            ) {
840                return Err(unexpected_initialize_response(
841                    self.mode
842                        .expect("protocol initialization requires an ACP mode")
843                        .initialization_role,
844                ));
845            }
846            state.pending_initialize = None;
847            state.incoming_initialize_id = None;
848            state.negotiated = negotiated;
849            if state.initialization == InitializationState::Initializing {
850                state.initialization = InitializationState::Ready;
851            }
852            Ok(())
853        }
854
855        fn fail_initialize(&self) {
856            let mut state = self
857                .state
858                .lock()
859                .expect("protocol compatibility state mutex poisoned");
860            state.pending_initialize = None;
861            state.incoming_initialize_id = None;
862            if state.initialization == InitializationState::Initializing {
863                state.initialization = InitializationState::Uninitialized;
864            }
865        }
866
867        fn is_pending_incoming_initialize(&self, id: &RequestId) -> bool {
868            self.state
869                .lock()
870                .expect("protocol compatibility state mutex poisoned")
871                .incoming_initialize_id
872                .as_ref()
873                == Some(id)
874        }
875
876        fn active_wire_version(&self) -> ProtocolVersionKind {
877            let state = self
878                .state
879                .lock()
880                .expect("protocol compatibility state mutex poisoned");
881            state.pending_initialize.unwrap_or(state.negotiated)
882        }
883
884        fn pending_initialize(&self) -> Option<ProtocolVersionKind> {
885            self.state
886                .lock()
887                .expect("protocol compatibility state mutex poisoned")
888                .pending_initialize
889        }
890    }
891
892    fn required_protocol_version_from_value(
893        value: &serde_json::Value,
894    ) -> Result<ProtocolVersion, crate::Error> {
895        let Some(version) = value.get("protocolVersion") else {
896            return Err(invalid_initialize_protocol_version());
897        };
898
899        serde_json::from_value(version.clone()).map_err(|_| invalid_initialize_protocol_version())
900    }
901
902    fn outgoing_initialize_params(
903        mode: AcpProtocolMode,
904        remote_style: RemoteStyle,
905        message: &mut UntypedMessage,
906    ) -> Result<Option<&mut serde_json::Value>, crate::Error> {
907        if successor_encloses_initialize(message) {
908            return Err(invalid_prewrapped_initialize());
909        }
910
911        match mode.initialize_surface {
912            InitializeSurface::Peer => {
913                Ok((message.method() == "initialize").then_some(&mut message.params))
914            }
915            InitializeSurface::Proxy => {
916                if message.method() == "initialize" {
917                    if remote_style != RemoteStyle::Successor {
918                        return Err(invalid_proxy_initialize_direction());
919                    }
920                    return Ok(Some(&mut message.params));
921                }
922                if message.method() == "_proxy/initialize" {
923                    return Err(invalid_proxy_initialize_direction());
924                }
925                Ok(None)
926            }
927        }
928    }
929
930    fn successor_encloses_initialize(message: &UntypedMessage) -> bool {
931        let mut method = message.method();
932        let mut params = message.params();
933        let mut wrapped = false;
934
935        while method == "_proxy/successor" {
936            wrapped = true;
937            let Some(inner_method) = params.get("method").and_then(serde_json::Value::as_str)
938            else {
939                return false;
940            };
941            method = inner_method;
942            if method == "_proxy/successor" {
943                let Some(inner_params) = params.get("params") else {
944                    return false;
945                };
946                params = inner_params;
947            }
948        }
949
950        wrapped && matches!(method, "initialize" | "_proxy/initialize")
951    }
952
953    fn invalid_initialize_protocol_version() -> crate::Error {
954        crate::Error::invalid_params()
955            .data("initialize.protocolVersion must be a valid ACP protocol version")
956    }
957
958    fn invalid_initialize_direction() -> crate::Error {
959        crate::Error::invalid_request()
960            .data("ACP clients send `initialize` requests and ACP agents respond to them")
961    }
962
963    fn unexpected_initialize_response(role: InitializationRole) -> crate::Error {
964        let detail = match role {
965            InitializationRole::Initiator => "before an initialize request is pending",
966            InitializationRole::Responder => "before an initialize request was received",
967            InitializationRole::Unchecked => "without a pending initialize request",
968        };
969        crate::Error::invalid_request().data(format!("received an initialize response {detail}"))
970    }
971
972    fn invalid_proxy_initialize_direction() -> crate::Error {
973        crate::Error::invalid_request().data(
974            "proxy initialization must arrive as `_proxy/initialize`; outgoing `initialize` must target the successor so the connection can apply `_proxy/successor`",
975        )
976    }
977
978    fn invalid_prewrapped_initialize() -> crate::Error {
979        crate::Error::invalid_request().data(
980            "initialize requests must be sent as a logical `initialize` message; `_proxy/successor` wrapping is applied by connection routing",
981        )
982    }
983
984    fn set_protocol_version(
985        value: &mut serde_json::Value,
986        version: ProtocolVersionKind,
987    ) -> Result<(), crate::Error> {
988        let serde_json::Value::Object(object) = value else {
989            return Err(invalid_initialize_protocol_version());
990        };
991        object.insert(
992            "protocolVersion".into(),
993            serde_json::to_value(version.as_protocol_version())
994                .map_err(crate::Error::into_internal_error)?,
995        );
996        Ok(())
997    }
998
999    // The shared guard stays fallible when v2 validation is compiled out.
1000    #[cfg_attr(
1001        not(feature = "unstable_protocol_v2"),
1002        allow(clippy::unnecessary_wraps)
1003    )]
1004    fn validate_native_v2_initialize_request(
1005        mode: AcpProtocolMode,
1006        value: &serde_json::Value,
1007    ) -> Result<(), crate::Error> {
1008        #[cfg(feature = "unstable_protocol_v2")]
1009        if mode.initialization_role != InitializationRole::Unchecked {
1010            <crate::schema::v2::InitializeRequest as crate::JsonRpcMessage>::parse_message(
1011                "initialize",
1012                value,
1013            )?;
1014        }
1015        #[cfg(not(feature = "unstable_protocol_v2"))]
1016        let _ = (mode, value);
1017        Ok(())
1018    }
1019
1020    #[cfg_attr(
1021        not(feature = "unstable_protocol_v2"),
1022        allow(clippy::unnecessary_wraps)
1023    )]
1024    fn validate_native_v2_initialize_response(
1025        mode: AcpProtocolMode,
1026        value: &serde_json::Value,
1027    ) -> Result<(), crate::Error> {
1028        #[cfg(feature = "unstable_protocol_v2")]
1029        if mode.initialization_role != InitializationRole::Unchecked {
1030            <crate::schema::v2::InitializeResponse as crate::JsonRpcResponse>::from_value(
1031                "initialize",
1032                value.clone(),
1033            )?;
1034        }
1035        #[cfg(not(feature = "unstable_protocol_v2"))]
1036        let _ = (mode, value);
1037        Ok(())
1038    }
1039
1040    fn ensure_matching_protocol_version(
1041        method: &str,
1042        from: ProtocolVersionKind,
1043        to: ProtocolVersionKind,
1044    ) -> Result<(), crate::Error> {
1045        if from == to {
1046            return Ok(());
1047        }
1048
1049        Err(crate::Error::invalid_request().data(format!(
1050            "ACP protocol translation from {} to {} is not supported for `{method}`; register a handler for the negotiated protocol version",
1051            from.as_protocol_version(),
1052            to.as_protocol_version(),
1053        )))
1054    }
1055
1056    fn unsupported_protocol_version(
1057        version: ProtocolVersion,
1058        supported: ProtocolVersionKind,
1059    ) -> crate::Error {
1060        crate::Error::invalid_request().data(format!(
1061            "unsupported ACP protocol version {version}; this endpoint only supports ACP protocol version {}",
1062            supported.as_protocol_version(),
1063        ))
1064    }
1065
1066    fn required_protocol_version(
1067        required: ProtocolVersionKind,
1068        negotiated: ProtocolVersionKind,
1069    ) -> crate::Error {
1070        crate::Error::invalid_request().data(format!(
1071            "required ACP protocol version {} but peer negotiated {}; use a matching implementation for the negotiated protocol version",
1072            required.as_protocol_version(),
1073            negotiated.as_protocol_version(),
1074        ))
1075    }
1076
1077    #[cfg(all(test, feature = "unstable_protocol_v2"))]
1078    mod tests {
1079        use super::*;
1080        use agent_client_protocol_schema::v2;
1081
1082        fn negotiated(compat: &ProtocolCompat) -> ProtocolVersionKind {
1083            compat
1084                .state
1085                .lock()
1086                .expect("protocol compatibility state mutex poisoned")
1087                .negotiated
1088        }
1089
1090        fn pending_initialize(compat: &ProtocolCompat) -> Option<ProtocolVersionKind> {
1091            compat
1092                .state
1093                .lock()
1094                .expect("protocol compatibility state mutex poisoned")
1095                .pending_initialize
1096        }
1097
1098        fn initialization_state(compat: &ProtocolCompat) -> InitializationState {
1099            compat
1100                .state
1101                .lock()
1102                .expect("protocol compatibility state mutex poisoned")
1103                .initialization
1104        }
1105
1106        fn v2_implementation() -> v2::Implementation {
1107            v2::Implementation::new("protocol-compat-test", env!("CARGO_PKG_VERSION"))
1108        }
1109
1110        fn v2_initialize_request(protocol_version: ProtocolVersion) -> v2::InitializeRequest {
1111            v2::InitializeRequest::new(protocol_version, v2_implementation())
1112        }
1113
1114        fn v2_initialize_response(protocol_version: ProtocolVersion) -> v2::InitializeResponse {
1115            v2::InitializeResponse::new(protocol_version, v2_implementation())
1116        }
1117
1118        #[test]
1119        fn initialize_request_sets_active_wire_version_before_response() -> Result<(), crate::Error>
1120        {
1121            let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1122            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1123            assert_eq!(
1124                initialization_state(&compat),
1125                InitializationState::Uninitialized
1126            );
1127
1128            compat.incoming_message(UntypedMessage::new(
1129                "initialize",
1130                v2_initialize_request(ProtocolVersion::V2),
1131            )?)?;
1132
1133            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1134            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1135            assert_eq!(
1136                initialization_state(&compat),
1137                InitializationState::Initializing
1138            );
1139
1140            compat.outgoing_response(
1141                "initialize",
1142                Ok(serde_json::to_value(v2_initialize_response(
1143                    ProtocolVersion::V2,
1144                ))?),
1145            )?;
1146
1147            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1148            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1149            assert_eq!(initialization_state(&compat), InitializationState::Ready);
1150            Ok(())
1151        }
1152
1153        #[test]
1154        fn outgoing_initialize_sets_active_wire_version_before_response() -> Result<(), crate::Error>
1155        {
1156            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1157            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1158            assert_eq!(
1159                initialization_state(&compat),
1160                InitializationState::Uninitialized
1161            );
1162
1163            compat.outgoing_message(
1164                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1165                RemoteStyle::Counterpart,
1166            )?;
1167
1168            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1169            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1170            assert_eq!(
1171                initialization_state(&compat),
1172                InitializationState::Initializing
1173            );
1174
1175            compat.incoming_response(
1176                "initialize",
1177                false,
1178                Ok(serde_json::to_value(v2_initialize_response(
1179                    ProtocolVersion::V2,
1180                ))?),
1181            )?;
1182
1183            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1184            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1185            assert_eq!(initialization_state(&compat), InitializationState::Ready);
1186            Ok(())
1187        }
1188
1189        #[test]
1190        fn failed_incoming_initialize_response_clears_pending_wire_version()
1191        -> Result<(), crate::Error> {
1192            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1193            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1194
1195            compat.outgoing_message(
1196                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1197                RemoteStyle::Counterpart,
1198            )?;
1199
1200            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1201            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1202
1203            let result = compat.incoming_response(
1204                "initialize",
1205                false,
1206                Err(crate::Error::invalid_request().data("initialize failed")),
1207            );
1208
1209            assert!(result.is_err());
1210            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1211            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1212            assert_eq!(
1213                initialization_state(&compat),
1214                InitializationState::Uninitialized
1215            );
1216            compat.outgoing_message(
1217                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1218                RemoteStyle::Counterpart,
1219            )?;
1220            assert_eq!(
1221                initialization_state(&compat),
1222                InitializationState::Initializing
1223            );
1224            Ok(())
1225        }
1226
1227        #[test]
1228        fn incoming_initialize_response_requires_protocol_version() -> Result<(), crate::Error> {
1229            for value in [
1230                serde_json::json!({}),
1231                serde_json::json!({ "protocolVersion": 100_000 }),
1232            ] {
1233                let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1234                compat.outgoing_message(
1235                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1236                    RemoteStyle::Counterpart,
1237                )?;
1238
1239                let error = compat
1240                    .incoming_response("initialize", false, Ok(value))
1241                    .expect_err("initialize responses must declare an ACP protocol version");
1242                let data = error
1243                    .data
1244                    .as_ref()
1245                    .and_then(|data| data.as_str())
1246                    .unwrap_or_default();
1247                assert!(data.contains("protocolVersion"), "{error:?}");
1248                assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1249                assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1250                assert_eq!(
1251                    initialization_state(&compat),
1252                    InitializationState::Uninitialized
1253                );
1254            }
1255
1256            Ok(())
1257        }
1258
1259        /// Send a v2 client's initialize, then deliver `response` to it.
1260        /// `negotiation` marks the request as sent by `send_initialize`.
1261        fn v2_client_receiving(
1262            negotiation: bool,
1263            response: Result<serde_json::Value, crate::Error>,
1264        ) -> Result<(ProtocolCompat, Result<serde_json::Value, crate::Error>), crate::Error>
1265        {
1266            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1267            compat.outgoing_message(
1268                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1269                RemoteStyle::Counterpart,
1270            )?;
1271            let result = compat.incoming_response("initialize", negotiation, response);
1272            Ok((compat, result))
1273        }
1274
1275        fn v1_initialize_response() -> crate::schema::v1::InitializeResponse {
1276            crate::schema::v1::InitializeResponse::new(ProtocolVersion::V1)
1277                .agent_info(crate::schema::v1::Implementation::new("v1-agent", "1.0.0"))
1278        }
1279
1280        #[test]
1281        fn plain_initialize_request_rejects_lower_version() -> Result<(), crate::Error> {
1282            let (compat, result) =
1283                v2_client_receiving(false, Ok(serde_json::to_value(v1_initialize_response())?))?;
1284            let error = result.expect_err("send_request keeps the local version guard");
1285            let data = error
1286                .data
1287                .as_ref()
1288                .and_then(|data| data.as_str())
1289                .unwrap_or_default();
1290            assert!(data.contains("peer negotiated 1"), "{error:?}");
1291            assert_eq!(
1292                initialization_state(&compat),
1293                InitializationState::Uninitialized
1294            );
1295            Ok(())
1296        }
1297
1298        #[test]
1299        fn initialize_negotiation_receives_lower_version_as_success() -> Result<(), crate::Error> {
1300            let v1_response = serde_json::to_value(v1_initialize_response())?;
1301            let (compat, result) = v2_client_receiving(true, Ok(v1_response.clone()))?;
1302            assert_eq!(result?, v1_response);
1303
1304            // The connection stays uninitialized rather than switching to v1.
1305            assert_eq!(
1306                initialization_state(&compat),
1307                InitializationState::Uninitialized
1308            );
1309            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1310            compat
1311                .outgoing_message(
1312                    UntypedMessage::new("session/new", serde_json::json!({}))?,
1313                    RemoteStyle::Counterpart,
1314                )
1315                .expect_err("v2 traffic still requires a v2 handshake");
1316            compat.outgoing_message(
1317                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1318                RemoteStyle::Counterpart,
1319            )?;
1320            Ok(())
1321        }
1322
1323        #[test]
1324        fn initialize_negotiation_keeps_failures_as_errors() -> Result<(), crate::Error> {
1325            let remote = crate::util::unsupported_protocol_version(
1326                ProtocolVersion::V2,
1327                &[ProtocolVersion::V1],
1328            );
1329            let (compat, result) = v2_client_receiving(true, Err(remote.clone()))?;
1330            assert_eq!(result, Err(remote));
1331            assert_eq!(
1332                initialization_state(&compat),
1333                InitializationState::Uninitialized
1334            );
1335
1336            for malformed in [
1337                serde_json::json!({}),
1338                serde_json::json!({ "protocolVersion": "one" }),
1339                serde_json::json!({ "protocolVersion": 0 }),
1340            ] {
1341                let (compat, result) = v2_client_receiving(true, Ok(malformed))?;
1342                result.expect_err("malformed initialize responses are errors");
1343                assert_eq!(
1344                    initialization_state(&compat),
1345                    InitializationState::Uninitialized
1346                );
1347            }
1348            Ok(())
1349        }
1350
1351        #[test]
1352        fn initialize_negotiation_does_not_change_proxies() -> Result<(), crate::Error> {
1353            let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
1354            compat.outgoing_message(
1355                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1356                RemoteStyle::Successor,
1357            )?;
1358            compat
1359                .incoming_response(
1360                    "initialize",
1361                    true,
1362                    Ok(serde_json::to_value(v1_initialize_response())?),
1363                )
1364                .expect_err("only a client's own handshake can report a lower version");
1365            Ok(())
1366        }
1367
1368        #[test]
1369        fn initialize_outcome_parses_by_selected_version() -> Result<(), crate::Error> {
1370            use crate::JsonRpcResponse as _;
1371
1372            let v2_response = v2_initialize_response(ProtocolVersion::V2);
1373            assert_eq!(
1374                super::super::InitializeOutcome::from_value(
1375                    "initialize",
1376                    serde_json::to_value(&v2_response)?
1377                )?,
1378                super::super::InitializeOutcome::Initialized(v2_response)
1379            );
1380
1381            let v1_response = v1_initialize_response();
1382            let outcome = super::super::InitializeOutcome::from_value(
1383                "initialize",
1384                serde_json::to_value(&v1_response)?,
1385            )?;
1386            assert_eq!(
1387                outcome,
1388                super::super::InitializeOutcome::LowerVersion(
1389                    super::super::InitializationVersionMismatch::new(
1390                        ProtocolVersion::V2,
1391                        super::super::SelectedInitializeResponse::V1(v1_response),
1392                    )
1393                )
1394            );
1395            let super::super::InitializeOutcome::LowerVersion(mismatch) = &outcome else {
1396                unreachable!();
1397            };
1398            assert_eq!(mismatch.requested, ProtocolVersion::V2);
1399            assert_eq!(mismatch.selected, ProtocolVersion::V1);
1400
1401            for value in [
1402                serde_json::json!({}),
1403                serde_json::json!({ "protocolVersion": 3 }),
1404            ] {
1405                super::super::InitializeOutcome::from_value("initialize", value)
1406                    .expect_err("only v2 and lower known versions are outcomes");
1407            }
1408            Ok(())
1409        }
1410
1411        #[test]
1412        fn malformed_v2_initialize_requests_do_not_begin_a_handshake() -> Result<(), crate::Error> {
1413            let malformed = serde_json::json!({ "protocolVersion": ProtocolVersion::V2 });
1414
1415            let client = ProtocolCompat::new(ProtocolMode::v2_client());
1416            client
1417                .outgoing_message(
1418                    UntypedMessage::new("initialize", malformed.clone())?,
1419                    RemoteStyle::Counterpart,
1420                )
1421                .expect_err("native v2 clients must send the complete initialize request shape");
1422            assert_eq!(
1423                initialization_state(&client),
1424                InitializationState::Uninitialized
1425            );
1426            client.outgoing_message(
1427                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1428                RemoteStyle::Counterpart,
1429            )?;
1430            assert_eq!(
1431                initialization_state(&client),
1432                InitializationState::Initializing
1433            );
1434
1435            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1436            agent
1437                .incoming_message(UntypedMessage::new("initialize", malformed)?)
1438                .expect_err("native v2 agents must receive the complete initialize request shape");
1439            assert_eq!(
1440                initialization_state(&agent),
1441                InitializationState::Uninitialized
1442            );
1443            agent.incoming_message(UntypedMessage::new(
1444                "initialize",
1445                v2_initialize_request(ProtocolVersion::V2),
1446            )?)?;
1447            assert_eq!(
1448                initialization_state(&agent),
1449                InitializationState::Initializing
1450            );
1451            Ok(())
1452        }
1453
1454        #[test]
1455        fn malformed_v2_initialize_success_leaves_client_uninitialized_for_retry()
1456        -> Result<(), crate::Error> {
1457            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1458            compat.outgoing_message(
1459                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1460                RemoteStyle::Counterpart,
1461            )?;
1462
1463            compat
1464                .incoming_response(
1465                    "initialize",
1466                    false,
1467                    Ok(serde_json::json!({ "protocolVersion": ProtocolVersion::V2 })),
1468                )
1469                .expect_err("initialize success must contain the complete v2 response shape");
1470            assert_eq!(
1471                initialization_state(&compat),
1472                InitializationState::Uninitialized
1473            );
1474            assert_eq!(pending_initialize(&compat), None);
1475
1476            compat.outgoing_message(
1477                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1478                RemoteStyle::Counterpart,
1479            )?;
1480            assert_eq!(
1481                initialization_state(&compat),
1482                InitializationState::Initializing
1483            );
1484            Ok(())
1485        }
1486
1487        #[test]
1488        fn malformed_v2_initialize_success_leaves_agent_uninitialized_for_retry()
1489        -> Result<(), crate::Error> {
1490            let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1491            compat.incoming_message(UntypedMessage::new(
1492                "initialize",
1493                v2_initialize_request(ProtocolVersion::V2),
1494            )?)?;
1495
1496            compat
1497                .outgoing_response(
1498                    "initialize",
1499                    Ok(serde_json::json!({ "protocolVersion": ProtocolVersion::V2 })),
1500                )
1501                .expect_err("initialize success must contain the complete v2 response shape");
1502            assert_eq!(
1503                initialization_state(&compat),
1504                InitializationState::Uninitialized
1505            );
1506            assert_eq!(pending_initialize(&compat), None);
1507
1508            compat.incoming_message(UntypedMessage::new(
1509                "initialize",
1510                v2_initialize_request(ProtocolVersion::V2),
1511            )?)?;
1512            assert_eq!(
1513                initialization_state(&compat),
1514                InitializationState::Initializing
1515            );
1516            Ok(())
1517        }
1518
1519        #[test]
1520        fn v2_peer_traffic_requires_completed_initialization() -> Result<(), crate::Error> {
1521            for compat in [
1522                ProtocolCompat::new(ProtocolMode::v2_agent()),
1523                ProtocolCompat::new(ProtocolMode::v2_client()),
1524            ] {
1525                for error in [
1526                    compat
1527                        .incoming_message(UntypedMessage::new(
1528                            "session/new",
1529                            serde_json::json!({}),
1530                        )?)
1531                        .expect_err("incoming requests must wait for initialization"),
1532                    compat
1533                        .outgoing_message(
1534                            UntypedMessage::new("session/new", serde_json::json!({}))?,
1535                            RemoteStyle::Counterpart,
1536                        )
1537                        .expect_err("outgoing requests must wait for initialization"),
1538                    compat
1539                        .incoming_notification(UntypedMessage::new(
1540                            "session/update",
1541                            serde_json::json!({}),
1542                        )?)
1543                        .expect_err("incoming notifications must wait for initialization"),
1544                    compat
1545                        .outgoing_notification(UntypedMessage::new(
1546                            "session/update",
1547                            serde_json::json!({}),
1548                        )?)
1549                        .expect_err("outgoing notifications must wait for initialization"),
1550                    compat
1551                        .incoming_response("session/new", false, Ok(serde_json::json!({})))
1552                        .expect_err("incoming responses must wait for initialization"),
1553                    compat
1554                        .outgoing_response("session/new", Ok(serde_json::json!({})))
1555                        .expect_err("outgoing responses must wait for initialization"),
1556                ] {
1557                    let data = error
1558                        .data
1559                        .as_ref()
1560                        .and_then(|data| data.as_str())
1561                        .unwrap_or_default();
1562                    assert!(data.contains("initialization must complete"), "{error:?}");
1563                }
1564            }
1565            Ok(())
1566        }
1567
1568        #[test]
1569        fn v2_initialization_allows_only_protocol_cancellation_notifications()
1570        -> Result<(), crate::Error> {
1571            let client = ProtocolCompat::new(ProtocolMode::v2_client());
1572            client.outgoing_message(
1573                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1574                RemoteStyle::Counterpart,
1575            )?;
1576            client.outgoing_notification(UntypedMessage::new(
1577                "$/cancel_request",
1578                serde_json::json!({ "requestId": 1 }),
1579            )?)?;
1580            client
1581                .outgoing_notification(UntypedMessage::new(
1582                    "session/update",
1583                    serde_json::json!({}),
1584                )?)
1585                .expect_err("ordinary notifications must wait for initialization");
1586
1587            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1588            agent.incoming_message(UntypedMessage::new(
1589                "initialize",
1590                v2_initialize_request(ProtocolVersion::V2),
1591            )?)?;
1592            agent.incoming_notification(UntypedMessage::new(
1593                "$/cancel_request",
1594                serde_json::json!({ "requestId": 1 }),
1595            )?)?;
1596            agent
1597                .incoming_notification(UntypedMessage::new(
1598                    "session/update",
1599                    serde_json::json!({}),
1600                )?)
1601                .expect_err("ordinary notifications must wait for initialization");
1602            Ok(())
1603        }
1604
1605        #[test]
1606        fn v2_peer_initialization_has_one_direction_and_one_successful_round_trip()
1607        -> Result<(), crate::Error> {
1608            let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1609            let error = agent
1610                .outgoing_message(
1611                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1612                    RemoteStyle::Counterpart,
1613                )
1614                .expect_err("agents must not initiate initialization");
1615            assert!(
1616                error
1617                    .data
1618                    .as_ref()
1619                    .and_then(|data| data.as_str())
1620                    .is_some_and(|data| data.contains("clients send `initialize`")),
1621                "{error:?}"
1622            );
1623            assert_eq!(
1624                initialization_state(&agent),
1625                InitializationState::Uninitialized
1626            );
1627
1628            agent.incoming_message(UntypedMessage::new(
1629                "initialize",
1630                v2_initialize_request(ProtocolVersion::V2),
1631            )?)?;
1632            agent.outgoing_response(
1633                "initialize",
1634                Ok(serde_json::to_value(v2_initialize_response(
1635                    ProtocolVersion::V2,
1636                ))?),
1637            )?;
1638            let error = agent
1639                .incoming_message(UntypedMessage::new(
1640                    "initialize",
1641                    v2_initialize_request(ProtocolVersion::V2),
1642                )?)
1643                .expect_err("agents must reject reinitialization");
1644            assert!(
1645                error
1646                    .data
1647                    .as_ref()
1648                    .and_then(|data| data.as_str())
1649                    .is_some_and(|data| data.contains("only be initialized once")),
1650                "{error:?}"
1651            );
1652            assert_eq!(initialization_state(&agent), InitializationState::Ready);
1653
1654            let client = ProtocolCompat::new(ProtocolMode::v2_client());
1655            let error = client
1656                .incoming_message(UntypedMessage::new(
1657                    "initialize",
1658                    v2_initialize_request(ProtocolVersion::V2),
1659                )?)
1660                .expect_err("clients must not receive initialization requests");
1661            assert!(
1662                error
1663                    .data
1664                    .as_ref()
1665                    .and_then(|data| data.as_str())
1666                    .is_some_and(|data| data.contains("clients send `initialize`")),
1667                "{error:?}"
1668            );
1669            assert_eq!(
1670                initialization_state(&client),
1671                InitializationState::Uninitialized
1672            );
1673
1674            client.outgoing_message(
1675                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1676                RemoteStyle::Counterpart,
1677            )?;
1678            client.incoming_response(
1679                "initialize",
1680                false,
1681                Ok(serde_json::to_value(v2_initialize_response(
1682                    ProtocolVersion::V2,
1683                ))?),
1684            )?;
1685            let error = client
1686                .outgoing_message(
1687                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1688                    RemoteStyle::Counterpart,
1689                )
1690                .expect_err("clients must reject reinitialization");
1691            assert!(
1692                error
1693                    .data
1694                    .as_ref()
1695                    .and_then(|data| data.as_str())
1696                    .is_some_and(|data| data.contains("only be initialized once")),
1697                "{error:?}"
1698            );
1699            assert_eq!(initialization_state(&client), InitializationState::Ready);
1700            Ok(())
1701        }
1702
1703        #[test]
1704        fn rejected_concurrent_initialize_does_not_clear_the_active_handshake()
1705        -> Result<(), crate::Error> {
1706            let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1707            let accepted_id = RequestId::Number(1);
1708            let rejected_id = RequestId::Number(2);
1709
1710            compat.incoming_request(
1711                &accepted_id,
1712                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1713            )?;
1714            let duplicate_error = compat
1715                .incoming_request(
1716                    &rejected_id,
1717                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1718                )
1719                .expect_err("a second initialize request must be rejected while one is active");
1720            compat
1721                .outgoing_response_to(&rejected_id, "initialize", Err(duplicate_error))
1722                .expect_err("the rejected initialize receives its own error response");
1723
1724            assert_eq!(
1725                initialization_state(&compat),
1726                InitializationState::Initializing
1727            );
1728            assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
1729
1730            compat.outgoing_response_to(
1731                &accepted_id,
1732                "initialize",
1733                Ok(serde_json::to_value(v2_initialize_response(
1734                    ProtocolVersion::V2,
1735                ))?),
1736            )?;
1737            assert_eq!(initialization_state(&compat), InitializationState::Ready);
1738            Ok(())
1739        }
1740
1741        #[test]
1742        fn rejected_wrong_direction_initialize_does_not_clear_client_handshake()
1743        -> Result<(), crate::Error> {
1744            let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1745            compat.outgoing_message(
1746                UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1747                RemoteStyle::Counterpart,
1748            )?;
1749
1750            let wrong_direction_id = RequestId::Number(2);
1751            let wrong_direction_error = compat
1752                .incoming_request(
1753                    &wrong_direction_id,
1754                    UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1755                )
1756                .expect_err("agents must not send initialize requests to clients");
1757            compat
1758                .outgoing_response_to(
1759                    &wrong_direction_id,
1760                    "initialize",
1761                    Err(wrong_direction_error),
1762                )
1763                .expect_err("the wrong-direction initialize receives its own error response");
1764
1765            assert_eq!(
1766                initialization_state(&compat),
1767                InitializationState::Initializing
1768            );
1769            assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
1770
1771            compat.incoming_response(
1772                "initialize",
1773                false,
1774                Ok(serde_json::to_value(v2_initialize_response(
1775                    ProtocolVersion::V2,
1776                ))?),
1777            )?;
1778            assert_eq!(initialization_state(&compat), InitializationState::Ready);
1779            Ok(())
1780        }
1781
1782        #[test]
1783        fn incoming_initialize_request_negotiates_newer_protocol_versions()
1784        -> Result<(), crate::Error> {
1785            for requested in [2_u16, 3, u16::MAX] {
1786                let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1787                let request = compat.incoming_message(UntypedMessage::new(
1788                    "initialize",
1789                    v2_initialize_request(ProtocolVersion::from(requested)),
1790                )?)?;
1791                assert_eq!(request.params()["protocolVersion"], serde_json::json!(2));
1792                assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
1793
1794                let response = compat.outgoing_response(
1795                    "initialize",
1796                    Ok(serde_json::to_value(v2_initialize_response(
1797                        ProtocolVersion::V2,
1798                    ))?),
1799                )?;
1800                assert_eq!(response["protocolVersion"], serde_json::json!(2));
1801                assert_eq!(initialization_state(&compat), InitializationState::Ready);
1802                assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1803            }
1804            Ok(())
1805        }
1806
1807        #[test]
1808        fn incoming_initialize_request_rejects_unsupported_protocol_version()
1809        -> Result<(), crate::Error> {
1810            let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1811            let error = compat
1812                .incoming_message(UntypedMessage::new(
1813                    "initialize",
1814                    serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
1815                )?)
1816                .expect_err("v2 agents should reject v1 initialization without a v1 handler");
1817            assert_eq!(i32::from(error.code), -32022);
1818            assert_eq!(
1819                error.data,
1820                Some(serde_json::json!({ "requested": 1, "supported": [2] }))
1821            );
1822            assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1823            assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1824            assert_eq!(
1825                initialization_state(&compat),
1826                InitializationState::Uninitialized
1827            );
1828
1829            Ok(())
1830        }
1831
1832        #[test]
1833        fn proxy_initialize_request_requires_the_selected_protocol_version()
1834        -> Result<(), crate::Error> {
1835            for (mode, selected, unsupported, selected_kind) in [
1836                (
1837                    ProtocolMode::v1_proxy(),
1838                    ProtocolVersion::V1,
1839                    ProtocolVersion::V2,
1840                    ProtocolVersionKind::V1,
1841                ),
1842                (
1843                    ProtocolMode::v2_proxy(),
1844                    ProtocolVersion::V2,
1845                    ProtocolVersion::V1,
1846                    ProtocolVersionKind::V2,
1847                ),
1848            ] {
1849                let compat = ProtocolCompat::new(mode);
1850                let error = compat
1851                    .incoming_message(UntypedMessage::new(
1852                        "_proxy/initialize",
1853                        serde_json::json!({ "protocolVersion": unsupported }),
1854                    )?)
1855                    .expect_err(
1856                        "proxy initialization must reject a protocol version outside its API",
1857                    );
1858                let data = error
1859                    .data
1860                    .as_ref()
1861                    .and_then(|data| data.as_str())
1862                    .unwrap_or_default();
1863                assert!(
1864                    data.contains(&format!("only supports ACP protocol version {selected}")),
1865                    "{error:?}"
1866                );
1867                assert_eq!(negotiated(&compat), selected_kind);
1868                assert_eq!(pending_initialize(&compat), None);
1869                assert_eq!(compat.active_wire_version(), selected_kind);
1870            }
1871
1872            Ok(())
1873        }
1874
1875        #[test]
1876        fn proxy_initialize_requests_reject_wrong_directions_and_wrapping()
1877        -> Result<(), crate::Error> {
1878            for mode in [ProtocolMode::v1_proxy(), ProtocolMode::v2_proxy()] {
1879                let compat = ProtocolCompat::new(mode);
1880                for error in [
1881                    compat
1882                        .incoming_message(UntypedMessage::new(
1883                            "initialize",
1884                            serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
1885                        )?)
1886                        .expect_err("proxy initialization must arrive through `_proxy/initialize`"),
1887                    compat
1888                        .incoming_message(UntypedMessage::new(
1889                            "_proxy/successor",
1890                            serde_json::json!({
1891                                "method": "initialize",
1892                                "params": { "protocolVersion": ProtocolVersion::V2 }
1893                            }),
1894                        )?)
1895                        .expect_err("successor traffic must not carry initialization"),
1896                    compat
1897                        .incoming_message(UntypedMessage::new(
1898                            "_proxy/successor",
1899                            serde_json::json!({
1900                                "method": "_proxy/successor",
1901                                "params": {
1902                                    "method": "_proxy/initialize",
1903                                    "params": { "protocolVersion": ProtocolVersion::V2 }
1904                                }
1905                            }),
1906                        )?)
1907                        .expect_err("nested successor traffic must not carry initialization"),
1908                    compat
1909                        .outgoing_message(
1910                            UntypedMessage::new(
1911                                "initialize",
1912                                serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
1913                            )?,
1914                            RemoteStyle::Predecessor,
1915                        )
1916                        .expect_err("downstream proxy initialization must use `_proxy/successor`"),
1917                    compat
1918                        .outgoing_message(
1919                            UntypedMessage::new(
1920                                "_proxy/initialize",
1921                                serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
1922                            )?,
1923                            RemoteStyle::Successor,
1924                        )
1925                        .expect_err("a proxy must not send `_proxy/initialize` downstream"),
1926                ] {
1927                    let data = error
1928                        .data
1929                        .as_ref()
1930                        .and_then(|data| data.as_str())
1931                        .unwrap_or_default();
1932                    assert!(data.contains("_proxy/initialize"), "{error:?}");
1933                    assert!(data.contains("_proxy/successor"), "{error:?}");
1934                }
1935                assert_eq!(pending_initialize(&compat), None);
1936            }
1937
1938            Ok(())
1939        }
1940
1941        #[test]
1942        fn outgoing_initialize_rejects_non_object_params() -> Result<(), crate::Error> {
1943            for (compat, message, remote_style) in [
1944                (
1945                    ProtocolCompat::new(ProtocolMode::v2_client()),
1946                    UntypedMessage::new("initialize", serde_json::Value::Null)?,
1947                    RemoteStyle::Counterpart,
1948                ),
1949                (
1950                    ProtocolCompat::new(ProtocolMode::v2_proxy()),
1951                    UntypedMessage::new("initialize", serde_json::Value::Null)?,
1952                    RemoteStyle::Successor,
1953                ),
1954            ] {
1955                let error = compat
1956                    .outgoing_message(message, remote_style)
1957                    .expect_err("initialize params must be an object");
1958                let data = error
1959                    .data
1960                    .as_ref()
1961                    .and_then(|data| data.as_str())
1962                    .unwrap_or_default();
1963                assert!(data.contains("protocolVersion"), "{error:?}");
1964                assert_eq!(pending_initialize(&compat), None);
1965            }
1966
1967            Ok(())
1968        }
1969
1970        #[test]
1971        fn outgoing_initialize_rejects_explicit_successor_wrapping() -> Result<(), crate::Error> {
1972            for message in [
1973                UntypedMessage::new(
1974                    "_proxy/successor",
1975                    serde_json::json!({
1976                        "method": "initialize",
1977                        "params": { "protocolVersion": ProtocolVersion::V1 }
1978                    }),
1979                )?,
1980                UntypedMessage::new(
1981                    "_proxy/successor",
1982                    serde_json::json!({
1983                        "method": "_proxy/successor",
1984                        "params": {
1985                            "method": "initialize",
1986                            "params": { "protocolVersion": ProtocolVersion::V1 }
1987                        }
1988                    }),
1989                )?,
1990            ] {
1991                let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
1992                let error = compat
1993                    .outgoing_message(message, RemoteStyle::Successor)
1994                    .expect_err("connection routing must own successor wrapping");
1995                let data = error
1996                    .data
1997                    .as_ref()
1998                    .and_then(|data| data.as_str())
1999                    .unwrap_or_default();
2000                assert!(data.contains("logical `initialize`"), "{error:?}");
2001                assert!(data.contains("_proxy/successor"), "{error:?}");
2002                assert_eq!(pending_initialize(&compat), None);
2003            }
2004
2005            Ok(())
2006        }
2007
2008        #[test]
2009        fn proxy_initialize_round_trip_uses_the_selected_protocol_version()
2010        -> Result<(), crate::Error> {
2011            for (mode, selected, selected_kind) in [
2012                (
2013                    ProtocolMode::v1_proxy(),
2014                    ProtocolVersion::V1,
2015                    ProtocolVersionKind::V1,
2016                ),
2017                (
2018                    ProtocolMode::v2_proxy(),
2019                    ProtocolVersion::V2,
2020                    ProtocolVersionKind::V2,
2021                ),
2022            ] {
2023                let compat = ProtocolCompat::new(mode);
2024                let request = compat.incoming_message(UntypedMessage::new(
2025                    "_proxy/initialize",
2026                    serde_json::json!({ "protocolVersion": selected }),
2027                )?)?;
2028                assert_eq!(
2029                    required_protocol_version_from_value(request.params())?,
2030                    selected
2031                );
2032                assert_eq!(pending_initialize(&compat), Some(selected_kind));
2033
2034                let response = compat.outgoing_response(
2035                    "_proxy/initialize",
2036                    Ok(serde_json::json!({ "protocolVersion": selected })),
2037                )?;
2038                assert_eq!(required_protocol_version_from_value(&response)?, selected);
2039                assert_eq!(negotiated(&compat), selected_kind);
2040                assert_eq!(pending_initialize(&compat), None);
2041                assert_eq!(compat.active_wire_version(), selected_kind);
2042            }
2043
2044            Ok(())
2045        }
2046
2047        #[test]
2048        fn proxy_initialize_response_rejects_the_wrong_version_and_clears_pending_state()
2049        -> Result<(), crate::Error> {
2050            for (mode, selected, unsupported, selected_kind) in [
2051                (
2052                    ProtocolMode::v1_proxy(),
2053                    ProtocolVersion::V1,
2054                    ProtocolVersion::V2,
2055                    ProtocolVersionKind::V1,
2056                ),
2057                (
2058                    ProtocolMode::v2_proxy(),
2059                    ProtocolVersion::V2,
2060                    ProtocolVersion::V1,
2061                    ProtocolVersionKind::V2,
2062                ),
2063            ] {
2064                let compat = ProtocolCompat::new(mode);
2065                let request = compat.outgoing_message(
2066                    UntypedMessage::new(
2067                        "initialize",
2068                        serde_json::json!({ "protocolVersion": unsupported }),
2069                    )?,
2070                    RemoteStyle::Successor,
2071                )?;
2072                assert_eq!(
2073                    required_protocol_version_from_value(request.params())?,
2074                    selected
2075                );
2076                assert_eq!(pending_initialize(&compat), Some(selected_kind));
2077
2078                let error = compat
2079                    .incoming_response(
2080                        "initialize",
2081                        false,
2082                        Ok(serde_json::json!({ "protocolVersion": unsupported })),
2083                    )
2084                    .expect_err("proxy initialization must reject a mismatched response version");
2085                let data = error
2086                    .data
2087                    .as_ref()
2088                    .and_then(|data| data.as_str())
2089                    .unwrap_or_default();
2090                assert!(
2091                    data.contains(&format!(
2092                        "required ACP protocol version {selected} but peer negotiated {unsupported}"
2093                    )),
2094                    "{error:?}"
2095                );
2096                assert_eq!(negotiated(&compat), selected_kind);
2097                assert_eq!(pending_initialize(&compat), None);
2098                assert_eq!(compat.active_wire_version(), selected_kind);
2099            }
2100
2101            Ok(())
2102        }
2103
2104        #[test]
2105        #[should_panic(expected = "cannot merge ACP builders with different API protocol versions")]
2106        fn merging_different_api_protocol_modes_panics() {
2107            let _ = ProtocolMode::v1_agent().merge(ProtocolMode::v2_agent());
2108        }
2109
2110        #[test]
2111        #[should_panic(expected = "cannot merge standard ACP and proxy ACP builders")]
2112        fn merging_standard_and_proxy_protocol_modes_panics() {
2113            let _ = ProtocolMode::v1_agent().merge(ProtocolMode::v1_proxy());
2114        }
2115    }
2116}
2117
2118pub(crate) use imp::{ProtocolCompat, ProtocolMode};