1pub(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#[cfg(feature = "unstable_protocol_v2")]
62#[derive(Debug, Clone, PartialEq, Eq)]
63#[non_exhaustive]
64pub enum InitializeOutcome {
65 Initialized(crate::schema::v2::InitializeResponse),
67 LowerVersion(InitializationVersionMismatch),
73}
74
75#[cfg(feature = "unstable_protocol_v2")]
82#[derive(Debug, Clone, PartialEq, Eq)]
83#[non_exhaustive]
84pub struct InitializationVersionMismatch {
85 pub requested: crate::schema::ProtocolVersion,
87 pub selected: crate::schema::ProtocolVersion,
89 pub response: SelectedInitializeResponse,
91}
92
93#[cfg(feature = "unstable_protocol_v2")]
94impl InitializationVersionMismatch {
95 #[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#[cfg(feature = "unstable_protocol_v2")]
128#[derive(Debug, Clone, PartialEq, Eq)]
129#[non_exhaustive]
130pub enum SelectedInitializeResponse {
131 V1(crate::schema::v1::InitializeResponse),
133}
134
135#[cfg(feature = "unstable_protocol_v2")]
136impl SelectedInitializeResponse {
137 #[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 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#[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 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 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 enum InitializeResponseCheck {
407 Negotiated(ProtocolVersionKind),
408 #[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 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 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 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 #[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 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 #[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 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 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};