1#[cfg(feature = "unstable_protocol_v2")]
7pub(crate) const UNKNOWN_IMPLEMENTATION: &str = "unknown";
8
9#[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
54fn 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
71fn 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
83pub(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#[cfg(feature = "unstable_protocol_v2")]
162#[derive(Debug, Clone, PartialEq, Eq)]
163#[non_exhaustive]
164pub enum InitializeOutcome {
165 Initialized(crate::schema::v2::InitializeResponse),
167 LowerVersion(InitializationVersionMismatch),
173}
174
175#[cfg(feature = "unstable_protocol_v2")]
182#[derive(Debug, Clone, PartialEq, Eq)]
183#[non_exhaustive]
184pub struct InitializationVersionMismatch {
185 pub requested: crate::schema::ProtocolVersion,
187 pub selected: crate::schema::ProtocolVersion,
189 pub response: SelectedInitializeResponse,
191}
192
193#[cfg(feature = "unstable_protocol_v2")]
194impl InitializationVersionMismatch {
195 #[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#[cfg(feature = "unstable_protocol_v2")]
228#[derive(Debug, Clone, PartialEq, Eq)]
229#[non_exhaustive]
230pub enum SelectedInitializeResponse {
231 V1(crate::schema::v1::InitializeResponse),
233}
234
235#[cfg(feature = "unstable_protocol_v2")]
236impl SelectedInitializeResponse {
237 #[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 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#[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 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 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 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 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 initialized: bool,
528 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 enum InitializeResponseCheck {
548 Negotiated(ProtocolVersionKind),
549 #[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 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 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 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 #[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 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 #[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 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 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 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 if state.initialized {
1057 state.initialization = InitializationState::Ready;
1058 state.repeatable = true;
1059 } else {
1060 state.initialization = InitializationState::Uninitialized;
1061 }
1062 }
1063 }
1064
1065 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 #[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 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 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 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 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 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 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 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 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 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 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 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};