1use std::{fmt::Debug, future::Future, hash::Hash};
2
3#[cfg(feature = "unstable_protocol_v2")]
4use futures::{StreamExt as _, future};
5#[cfg(feature = "unstable_protocol_v2")]
6use serde::{Serialize, de::DeserializeOwned};
7
8#[cfg(feature = "unstable_protocol_v2")]
9use crate::DynConnectTo;
10use crate::jsonrpc::{Builder, handlers::NullHandler, run::NullRun};
11#[cfg(feature = "unstable_protocol_v2")]
12use crate::jsonrpc::{
13 TransportBatch, TransportBatchEntry, TransportFrame, V2Builder, is_response_only_shape,
14 raw_is_response_only_shape,
15};
16use crate::role::{HasPeer, RemoteStyle};
17#[cfg(not(feature = "unstable_protocol_v2"))]
18use crate::schema::InitializeProxyRequest;
19use crate::schema::METHOD_INITIALIZE_PROXY;
20use crate::schema::v1::{InitializeRequest, SessionId};
21#[cfg(not(feature = "unstable_protocol_v2"))]
22use crate::schema::v1::{NewSessionRequest, NewSessionResponse};
23#[cfg(feature = "unstable_protocol_v2")]
24use crate::schema::v1::{RequestId, Response as RpcResponse};
25#[cfg(feature = "unstable_protocol_v2")]
26use crate::schema::{ProtocolVersion, v2};
27use crate::util::MatchDispatchFrom;
28#[cfg(feature = "unstable_protocol_v2")]
29use crate::{Channel, RawJsonRpcMessage, RawJsonRpcParams};
30use crate::{ConnectTo, ConnectionTo, Dispatch, HandleDispatchFrom, Handled, Role, RoleId};
31
32#[cfg(feature = "unstable_protocol_v2")]
33#[derive(serde::Deserialize)]
34struct NewSessionResponseEnvelope {
35 #[serde(rename = "sessionId")]
36 session_id: SessionId,
37}
38
39#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct Client;
44
45impl Role for Client {
46 type Counterpart = Agent;
47
48 fn builder(self) -> Builder<Self> {
49 Builder::new(self).v1_client()
50 }
51
52 fn default_handle_dispatch_from(
53 &self,
54 message: Dispatch,
55 _connection: ConnectionTo<Client>,
56 ) -> impl Future<Output = Result<Handled<Dispatch>, crate::Error>> + Send {
57 std::future::ready(Ok(Handled::No {
58 message,
59 retry: false,
60 }))
61 }
62
63 fn role_id(&self) -> RoleId {
64 RoleId::from_singleton(self)
65 }
66
67 fn counterpart(&self) -> Self::Counterpart {
68 Agent
69 }
70}
71
72impl Client {
73 pub fn builder(self) -> Builder<Client, NullHandler, NullRun> {
75 <Self as Role>::builder(self)
76 }
77
78 #[cfg(feature = "unstable_protocol_v2")]
86 pub fn v2(self) -> V2Builder<Client, NullHandler, NullRun> {
87 self.builder().v2_client()
88 }
89
90 #[cfg(feature = "unstable_protocol_v2")]
103 #[must_use]
104 pub fn protocol_connector(self) -> ClientProtocolConnector {
105 ClientProtocolConnector::new()
106 }
107
108 pub async fn connect_with<R>(
113 self,
114 agent: impl ConnectTo<Client>,
115 main_fn: impl AsyncFnOnce(ConnectionTo<Agent>) -> Result<R, crate::Error>,
116 ) -> Result<R, crate::Error> {
117 self.builder().connect_with(agent, main_fn).await
118 }
119}
120
121#[cfg(feature = "unstable_protocol_v2")]
128#[derive(Debug, Default)]
129pub struct ClientProtocolConnector {
130 v1: Option<DynConnectToFactory<Agent>>,
131 v2: Option<DynConnectToFactory<Agent>>,
132}
133
134#[cfg(feature = "unstable_protocol_v2")]
135impl ClientProtocolConnector {
136 #[must_use]
138 pub fn new() -> Self {
139 Self::default()
140 }
141
142 #[must_use]
144 pub fn with_v1<C>(mut self, client: impl FnMut() -> C + Send + 'static) -> Self
145 where
146 C: ConnectTo<Agent>,
147 {
148 self.v1 = Some(DynConnectToFactory::new(client));
149 self
150 }
151
152 #[must_use]
154 pub fn with_v2<C>(mut self, client: impl FnMut() -> C + Send + 'static) -> Self
155 where
156 C: ConnectTo<Agent>,
157 {
158 self.v2 = Some(DynConnectToFactory::new(client));
159 self
160 }
161
162 pub async fn connect_to<C>(
165 mut self,
166 mut agent: impl FnMut() -> C + Send + 'static,
167 ) -> Result<(), crate::Error>
168 where
169 C: ConnectTo<Client>,
170 {
171 let supported = SupportedClientProtocols {
172 v1: self.v1.is_some(),
173 v2: self.v2.is_some(),
174 };
175 let Some(selected) = supported.highest_configured() else {
176 return Err(crate::Error::invalid_request()
177 .data("client protocol connector has no configured ACP protocol implementations"));
178 };
179
180 match selected {
181 ClientProtocol::V1 => {
182 let client = self
183 .v1
184 .as_mut()
185 .expect("selected protocol is configured")
186 .create();
187 connect_client_protocol(ClientProtocol::V1, client, agent()).await
188 }
189 ClientProtocol::V2 => {
190 let client = self
191 .v2
192 .as_mut()
193 .expect("selected protocol is configured")
194 .create();
195 let agent_connection = RunningProtocolPeer::new(agent());
196 let (client, initialize) =
197 start_client_protocol(ClientProtocol::V2, client).await?;
198 let v2_initialize_as_v1 = normalize_v2_initialize_params_for_reuse(&initialize);
202 let (client, agent_connection, initialize_response) =
203 send_initialize_and_receive(client, agent_connection, initialize).await?;
204
205 if initialize_response_negotiated_v1(&initialize_response)
206 && let Some(v1) = self.v1.as_mut()
207 {
208 let fallback_client = v1.create();
209 let (fallback_client, fallback_initialize) =
210 start_client_protocol(ClientProtocol::V1, fallback_client).await?;
211 let v1_initialize =
212 validated_initialize_params::<InitializeRequest>(&fallback_initialize)?;
213
214 if v2_initialize_as_v1
215 .as_ref()
216 .is_ok_and(|v2_initialize| v2_initialize == &v1_initialize)
217 {
218 let fallback_response = initialize_response.with_id(
219 initialize_request_id(&fallback_initialize)
220 .expect("validated initialize request has an id"),
221 );
222 drop(client);
227 fallback_client.send(fallback_response)?;
228 return pipe_protocol_peers_until_done(fallback_client, agent_connection)
229 .await;
230 }
231
232 drop((
236 client,
237 fallback_client,
238 agent_connection,
239 initialize_response,
240 ));
241 return connect_client_protocol(ClientProtocol::V1, v1.create(), agent()).await;
242 }
243
244 client.send(initialize_response.into_message())?;
245 pipe_protocol_peers_until_done(client, agent_connection).await
246 }
247 }
248 }
249}
250
251#[cfg(feature = "unstable_protocol_v2")]
252struct DynConnectToFactory<R: Role> {
253 inner: Box<dyn FnMut() -> DynConnectTo<R> + Send>,
254}
255
256#[cfg(feature = "unstable_protocol_v2")]
257impl<R: Role> DynConnectToFactory<R> {
258 fn new<C>(mut factory: impl FnMut() -> C + Send + 'static) -> Self
259 where
260 C: ConnectTo<R>,
261 {
262 Self {
263 inner: Box::new(move || DynConnectTo::new(factory())),
264 }
265 }
266
267 fn create(&mut self) -> DynConnectTo<R> {
268 (self.inner)()
269 }
270}
271
272#[cfg(feature = "unstable_protocol_v2")]
273impl<R: Role> Debug for DynConnectToFactory<R> {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 f.debug_struct("DynConnectToFactory")
276 .finish_non_exhaustive()
277 }
278}
279
280impl HasPeer<Client> for Client {
281 fn remote_style(&self, _peer: Client) -> RemoteStyle {
282 RemoteStyle::Counterpart
283 }
284}
285
286#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
291pub struct Agent;
292
293impl Role for Agent {
294 type Counterpart = Client;
295
296 fn builder(self) -> Builder<Self> {
297 Builder::new(self).v1_agent()
298 }
299
300 fn role_id(&self) -> RoleId {
301 RoleId::from_singleton(self)
302 }
303
304 fn counterpart(&self) -> Self::Counterpart {
305 Client
306 }
307
308 async fn default_handle_dispatch_from(
309 &self,
310 message: Dispatch,
311 connection: ConnectionTo<Agent>,
312 ) -> Result<Handled<Dispatch>, crate::Error> {
313 MatchDispatchFrom::new(message, &connection)
314 .if_dispatch_from(Agent, async |message: Dispatch| {
315 #[cfg(feature = "unstable_protocol_v2")]
323 let retry = message.has_session_id()
324 && connection.acp_protocol_version()
325 != Some(crate::schema::ProtocolVersion::V2);
326 #[cfg(not(feature = "unstable_protocol_v2"))]
327 let retry = message.has_session_id();
328 Ok(Handled::No { message, retry })
329 })
330 .await
331 .done()
332 }
333}
334
335impl Agent {
336 pub fn builder(self) -> Builder<Agent, NullHandler, NullRun> {
338 <Self as Role>::builder(self)
339 }
340
341 #[cfg(feature = "unstable_protocol_v2")]
348 pub fn v2(self) -> V2Builder<Agent, NullHandler, NullRun> {
349 self.builder().v2_agent()
350 }
351
352 #[cfg(feature = "unstable_protocol_v2")]
365 #[must_use]
366 pub fn protocol_router(self) -> AgentProtocolRouter {
367 AgentProtocolRouter::new()
368 }
369}
370
371#[cfg(feature = "unstable_protocol_v2")]
377#[derive(Debug, Default)]
378pub struct AgentProtocolRouter {
379 v1: Option<DynConnectTo<Client>>,
380 v2: Option<DynConnectTo<Client>>,
381}
382
383#[cfg(feature = "unstable_protocol_v2")]
384impl AgentProtocolRouter {
385 #[must_use]
387 pub fn new() -> Self {
388 Self::default()
389 }
390
391 #[must_use]
393 pub fn with_v1(mut self, agent: impl ConnectTo<Client>) -> Self {
394 self.v1 = Some(DynConnectTo::new(agent));
395 self
396 }
397
398 #[must_use]
400 pub fn with_v2(mut self, agent: impl ConnectTo<Client>) -> Self {
401 self.v2 = Some(DynConnectTo::new(agent));
402 self
403 }
404}
405
406#[cfg(feature = "unstable_protocol_v2")]
407impl ConnectTo<Client> for AgentProtocolRouter {
408 async fn connect_to(self, client: impl ConnectTo<Agent>) -> Result<(), crate::Error> {
409 let supported = SupportedProtocols {
410 v1: self.v1.is_some(),
411 v2: self.v2.is_some(),
412 };
413 let mut client = RunningProtocolPeer::new(client);
414 let (first_frame, client, selected) = loop {
415 let Some((mut frame, next_client)) = client.next_frame().await? else {
416 return Ok(());
417 };
418 let message = match initialize_message_mut(&mut frame) {
419 Ok(Some(message)) => message,
420 Ok(None) => {
421 client = next_client;
422 continue;
423 }
424 Err(error) => return reject_initialize(next_client, &frame, error).await,
425 };
426 let selected = match select_agent_protocol(message, supported) {
427 Ok(selected) => selected,
428 Err(error) => return reject_initialize(next_client, &frame, error).await,
429 };
430 break (frame, next_client, selected);
431 };
432 let Some(agent) = selected.take_agent(self) else {
433 let error = selected.unsupported_error(supported);
434 return reject_initialize(client, &first_frame, error).await;
435 };
436
437 let agent = RunningProtocolPeer::new(agent);
438 agent.send_frame(first_frame)?;
439 pipe_protocol_peers_until_closed(client, agent).await
440 }
441}
442
443#[cfg(feature = "unstable_protocol_v2")]
444#[derive(Debug, Clone, Copy, PartialEq, Eq)]
445enum SelectedProtocol {
446 V1,
447 V2,
448}
449
450#[cfg(feature = "unstable_protocol_v2")]
451impl SelectedProtocol {
452 fn take_agent(self, agent: AgentProtocolRouter) -> Option<DynConnectTo<Client>> {
453 match self {
454 Self::V1 => agent.v1,
455 Self::V2 => agent.v2,
456 }
457 }
458
459 fn version(self) -> ProtocolVersion {
460 match self {
461 Self::V1 => ProtocolVersion::V1,
462 Self::V2 => ProtocolVersion::V2,
463 }
464 }
465
466 fn name(self) -> &'static str {
467 match self {
468 Self::V1 => "1",
469 Self::V2 => "2",
470 }
471 }
472
473 fn unsupported_error(self, supported: SupportedProtocols) -> crate::Error {
474 crate::Error::invalid_request().data(format!(
475 "ACP protocol version {} is not configured; this endpoint supports {}",
476 self.name(),
477 supported.description()
478 ))
479 }
480}
481
482#[cfg(feature = "unstable_protocol_v2")]
483#[derive(Debug, Clone, Copy, PartialEq, Eq)]
484struct SupportedProtocols {
485 v1: bool,
486 v2: bool,
487}
488
489#[cfg(feature = "unstable_protocol_v2")]
490impl SupportedProtocols {
491 fn highest_compatible(self, requested: ProtocolVersion) -> Option<SelectedProtocol> {
492 if self.v2 && requested >= ProtocolVersion::V2 {
493 return Some(SelectedProtocol::V2);
494 }
495
496 if self.v1 && requested >= ProtocolVersion::V1 {
497 return Some(SelectedProtocol::V1);
498 }
499
500 None
501 }
502
503 fn exact(self, requested: ProtocolVersion) -> Option<SelectedProtocol> {
504 if self.v1 && requested == ProtocolVersion::V1 {
505 Some(SelectedProtocol::V1)
506 } else if self.v2 && requested == ProtocolVersion::V2 {
507 Some(SelectedProtocol::V2)
508 } else {
509 None
510 }
511 }
512
513 fn description(self) -> String {
514 match (self.v1, self.v2) {
515 (true, true) => "ACP protocol versions 1 and 2".into(),
516 (true, false) => "ACP protocol version 1".into(),
517 (false, true) => "ACP protocol version 2".into(),
518 (false, false) => "no ACP protocol versions".into(),
519 }
520 }
521}
522
523#[cfg(feature = "unstable_protocol_v2")]
524fn select_agent_protocol(
525 message: &mut RawJsonRpcMessage,
526 supported: SupportedProtocols,
527) -> Result<SelectedProtocol, crate::Error> {
528 let RawJsonRpcMessage::Request(request) = message else {
529 return Err(
530 crate::Error::invalid_request().data("first ACP message must be an initialize request")
531 );
532 };
533
534 if request.method.as_ref() != "initialize" {
535 return Err(crate::Error::invalid_request().data("first ACP request must be initialize"));
536 }
537
538 let Some(RawJsonRpcParams::Object(params)) = &mut request.params else {
539 return Err(invalid_initialize_protocol_version());
540 };
541 let Some(protocol_version) = params.get("protocolVersion") else {
542 return Err(invalid_initialize_protocol_version());
543 };
544
545 let requested = serde_json::from_value::<ProtocolVersion>(protocol_version.clone())
546 .map_err(|_| invalid_initialize_protocol_version())?;
547 let selected = highest_compatible_agent_protocol(requested, supported)?;
548 rewrite_initialize_params(params, requested, selected)?;
549
550 Ok(selected)
551}
552
553#[cfg(feature = "unstable_protocol_v2")]
554fn initialize_request_params(
555 message: &RawJsonRpcMessage,
556) -> Result<&serde_json::Map<String, serde_json::Value>, crate::Error> {
557 let RawJsonRpcMessage::Request(request) = message else {
558 return Err(
559 crate::Error::invalid_request().data("first ACP message must be an initialize request")
560 );
561 };
562
563 if request.method.as_ref() != "initialize" {
564 return Err(crate::Error::invalid_request().data("first ACP request must be initialize"));
565 }
566
567 let Some(RawJsonRpcParams::Object(params)) = &request.params else {
568 return Err(invalid_initialize_protocol_version());
569 };
570 if !params.contains_key("protocolVersion") {
571 return Err(invalid_initialize_protocol_version());
572 }
573 Ok(params)
574}
575
576#[cfg(feature = "unstable_protocol_v2")]
577fn validated_initialize_params<T: DeserializeOwned>(
578 message: &RawJsonRpcMessage,
579) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
580 let params = initialize_request_params(message)?;
581 parse_initialize_params::<T>(params)?;
582 Ok(params.clone())
583}
584
585#[cfg(feature = "unstable_protocol_v2")]
586fn normalize_v2_initialize_params_for_reuse(
587 message: &RawJsonRpcMessage,
588) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
589 let params = initialize_request_params(message)?;
590 let requested = params
591 .get("protocolVersion")
592 .cloned()
593 .ok_or_else(invalid_initialize_protocol_version)
594 .and_then(|version| {
595 serde_json::from_value::<ProtocolVersion>(version)
596 .map_err(|_| invalid_initialize_protocol_version())
597 })?;
598 if requested == ProtocolVersion::V1 {
599 parse_initialize_params::<InitializeRequest>(params)?;
600 return Ok(params.clone());
601 }
602 normalize_v2_initialize_params_for_v1(params, true)
603}
604
605#[cfg(feature = "unstable_protocol_v2")]
606fn rewrite_initialize_params(
607 params: &mut serde_json::Map<String, serde_json::Value>,
608 requested: ProtocolVersion,
609 selected: SelectedProtocol,
610) -> Result<(), crate::Error> {
611 if requested == selected.version() {
615 match selected {
616 SelectedProtocol::V1 => {
617 parse_initialize_params::<InitializeRequest>(params)?;
618 }
619 SelectedProtocol::V2 => {
620 parse_initialize_params::<v2::InitializeRequest>(params)?;
621 }
622 }
623 return Ok(());
624 }
625
626 match selected {
627 SelectedProtocol::V1 => {
628 debug_assert!(requested >= ProtocolVersion::V2);
629 *params = normalize_v2_initialize_params_for_v1(params, false)?;
630 Ok(())
631 }
632 SelectedProtocol::V2 => {
633 let mut initialize = parse_initialize_params::<v2::InitializeRequest>(params)?;
634 initialize.protocol_version = ProtocolVersion::V2;
635 *params = serialize_initialize_params(initialize)?;
636 Ok(())
637 }
638 }
639}
640
641#[cfg(feature = "unstable_protocol_v2")]
642fn normalize_v2_initialize_params_for_v1(
643 params: &serde_json::Map<String, serde_json::Value>,
644 require_lossless: bool,
645) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
646 let initialize = parse_initialize_params::<v2::InitializeRequest>(params)?;
651 let mut target = serialize_initialize_params(initialize)?;
652 if require_lossless && target != *params {
653 return Err(invalid_initialize_params(
654 "v2 initialize parameters are not losslessly representable in v1",
655 ));
656 }
657
658 target.insert(
659 "protocolVersion".into(),
660 serde_json::to_value(ProtocolVersion::V1).map_err(crate::Error::into_internal_error)?,
661 );
662 let info = target
663 .remove("info")
664 .ok_or_else(|| invalid_initialize_params("v2 InitializeRequest.info is required"))?;
665 target.insert("clientInfo".into(), info);
666 let capabilities = target
667 .remove("capabilities")
668 .and_then(|capabilities| capabilities.as_object().cloned())
669 .ok_or_else(|| {
670 crate::util::internal_error("v2 initialize capabilities did not serialize as an object")
671 })?;
672 let mut capabilities = capabilities;
673 if let Some(auth) = capabilities
674 .get_mut("auth")
675 .and_then(serde_json::Value::as_object_mut)
676 {
677 let terminal = auth.remove("terminal");
678 if require_lossless
679 && terminal
680 .as_ref()
681 .and_then(serde_json::Value::as_object)
682 .is_some_and(|terminal| terminal.contains_key("_meta"))
683 {
684 return Err(invalid_initialize_params(
685 "v2 terminal authentication metadata is not representable in v1",
686 ));
687 }
688 auth.insert("terminal".into(), terminal.is_some().into());
689 }
690 capabilities.insert(
691 "session".into(),
692 serde_json::json!({ "configOptions": { "boolean": {} } }),
693 );
694 target.insert("clientCapabilities".into(), capabilities.into());
695
696 let initialize = parse_initialize_params::<InitializeRequest>(&target)?;
697 let normalized = serialize_initialize_params(initialize)?;
698 if require_lossless && !json_object_contains(&normalized, &target) {
699 return Err(invalid_initialize_params(
700 "v2 initialize parameters are not losslessly representable in v1",
701 ));
702 }
703 Ok(normalized)
704}
705
706#[cfg(all(test, feature = "unstable_protocol_v2"))]
707mod initialize_normalization_tests {
708 use super::*;
709
710 fn v2_initialize_params() -> serde_json::Map<String, serde_json::Value> {
711 let value = serde_json::to_value(v2::InitializeRequest::new(
712 ProtocolVersion::V2,
713 v2::Implementation::new("test-client", "1.0.0"),
714 ))
715 .expect("serialize v2 initialize request");
716 value
717 .as_object()
718 .expect("initialize params serialize as an object")
719 .clone()
720 }
721
722 #[test]
723 fn v2_tolerant_fields_are_canonicalized_before_v1_normalization() {
724 let mut params = v2_initialize_params();
725 params.insert(
726 "capabilities".into(),
727 serde_json::Value::String("malformed".into()),
728 );
729 params.insert(
730 "_meta".into(),
731 serde_json::Value::String("malformed".into()),
732 );
733
734 let normalized = normalize_v2_initialize_params_for_v1(¶ms, false)
735 .expect("tolerant v2 fields should normalize through their defaults");
736 let normalized = serde_json::Value::Object(normalized);
737
738 assert!(normalized.get("_meta").is_none());
739 assert_eq!(
740 normalized.pointer("/clientCapabilities/session/configOptions/boolean"),
741 Some(&serde_json::json!({}))
742 );
743 }
744
745 #[test]
746 fn noncanonical_v2_fields_disable_reuse_but_not_v1_routing() {
747 let mut params = v2_initialize_params();
748 params
749 .get_mut("info")
750 .and_then(serde_json::Value::as_object_mut)
751 .expect("v2 initialize info is an object")
752 .insert("buildCommit".into(), serde_json::json!("abc123"));
753
754 normalize_v2_initialize_params_for_v1(¶ms, false)
755 .expect("v1 routing may ignore parameters unavailable in v1");
756 normalize_v2_initialize_params_for_v1(¶ms, true)
757 .expect_err("connection reuse requires lossless normalization");
758 }
759
760 #[test]
761 fn v1_reuse_probe_preserves_raw_initialize_params() {
762 let mut params = v2_initialize_params();
763 params.insert(
764 "protocolVersion".into(),
765 serde_json::json!(ProtocolVersion::V1),
766 );
767 let message = RawJsonRpcMessage::request(
768 "initialize".into(),
769 serde_json::Value::Object(params.clone()),
770 RequestId::Number(1),
771 )
772 .expect("build initialize request");
773
774 let normalized = normalize_v2_initialize_params_for_reuse(&message)
775 .expect("v1-shaped initialize request should be valid");
776
777 assert_eq!(normalized, params);
778 }
779
780 #[test]
781 fn null_v2_terminal_marker_meta_is_omitted_before_v1_normalization() {
782 let mut params = v2_initialize_params();
783 params.insert(
784 "capabilities".into(),
785 serde_json::json!({
786 "auth": {
787 "terminal": { "_meta": null }
788 }
789 }),
790 );
791
792 let normalized = normalize_v2_initialize_params_for_v1(¶ms, false)
793 .expect("null marker metadata is equivalent to omission");
794 let normalized = serde_json::Value::Object(normalized);
795
796 assert_eq!(
797 normalized.pointer("/clientCapabilities/auth/terminal"),
798 Some(&serde_json::Value::Bool(true))
799 );
800 }
801
802 #[test]
803 fn terminal_marker_metadata_disables_reuse_but_not_v1_routing() {
804 let mut params = v2_initialize_params();
805 params.insert(
806 "capabilities".into(),
807 serde_json::json!({
808 "auth": {
809 "terminal": {
810 "_meta": { "source": "test" }
811 }
812 }
813 }),
814 );
815
816 normalize_v2_initialize_params_for_v1(¶ms, false)
817 .expect("v1 routing may discard terminal marker metadata");
818 normalize_v2_initialize_params_for_v1(¶ms, true)
819 .expect_err("connection reuse must preserve terminal marker metadata");
820 }
821}
822
823#[cfg(feature = "unstable_protocol_v2")]
824fn parse_initialize_params<T: DeserializeOwned>(
825 params: &serde_json::Map<String, serde_json::Value>,
826) -> Result<T, crate::Error> {
827 serde_json::from_value(serde_json::Value::Object(params.clone()))
828 .map_err(invalid_initialize_params)
829}
830
831#[cfg(feature = "unstable_protocol_v2")]
832fn serialize_initialize_params(
833 initialize: impl Serialize,
834) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
835 let value = serde_json::to_value(initialize).map_err(crate::Error::into_internal_error)?;
836 let serde_json::Value::Object(object) = value else {
837 return Err(crate::util::internal_error(
838 "initialize params did not serialize to an object",
839 ));
840 };
841 Ok(object)
842}
843
844#[cfg(feature = "unstable_protocol_v2")]
845fn json_object_contains(
846 actual: &serde_json::Map<String, serde_json::Value>,
847 expected: &serde_json::Map<String, serde_json::Value>,
848) -> bool {
849 fn contains(actual: &serde_json::Value, expected: &serde_json::Value) -> bool {
850 match (actual, expected) {
851 (serde_json::Value::Object(actual), serde_json::Value::Object(expected)) => expected
852 .iter()
853 .all(|(key, value)| actual.get(key).is_some_and(|item| contains(item, value))),
854 _ => actual == expected,
855 }
856 }
857
858 expected
859 .iter()
860 .all(|(key, value)| actual.get(key).is_some_and(|item| contains(item, value)))
861}
862
863#[cfg(feature = "unstable_protocol_v2")]
864fn highest_compatible_agent_protocol(
865 requested: ProtocolVersion,
866 supported: SupportedProtocols,
867) -> Result<SelectedProtocol, crate::Error> {
868 supported.highest_compatible(requested).ok_or_else(|| {
869 crate::Error::invalid_request().data(format!(
870 "unsupported ACP protocol version {requested}; this endpoint supports {}",
871 supported.description()
872 ))
873 })
874}
875
876#[cfg(feature = "unstable_protocol_v2")]
877fn invalid_initialize_protocol_version() -> crate::Error {
878 crate::Error::invalid_params()
879 .data("initialize.protocolVersion must be a valid ACP protocol version")
880}
881
882#[cfg(feature = "unstable_protocol_v2")]
883fn invalid_initialize_params(error: impl ToString) -> crate::Error {
884 crate::Error::invalid_params().data(format!("invalid initialize params: {}", error.to_string()))
885}
886
887#[cfg(feature = "unstable_protocol_v2")]
888fn send_initialize_error(
889 tx: &futures::channel::mpsc::UnboundedSender<TransportFrame>,
890 frame: &TransportFrame,
891 error: crate::Error,
892) -> Result<(), crate::Error> {
893 fn response_for_message(
894 entry: &RawJsonRpcMessage,
895 initialize_error: &crate::Error,
896 ) -> Option<RawJsonRpcMessage> {
897 match entry {
898 RawJsonRpcMessage::Request(request) => Some(RawJsonRpcMessage::response(
899 request.id.clone(),
900 Err(initialize_error.clone()),
901 )),
902 RawJsonRpcMessage::Notification(_) | RawJsonRpcMessage::Response(_) => None,
903 }
904 }
905
906 fn response_for_entry(
907 entry: &TransportBatchEntry,
908 initialize_error: &crate::Error,
909 ) -> Option<RawJsonRpcMessage> {
910 match entry {
911 TransportBatchEntry::Message(message) => {
912 response_for_message(message, initialize_error)
913 }
914 TransportBatchEntry::Malformed { raw, error } if !is_response_only_shape(raw) => Some(
915 RawJsonRpcMessage::response(RequestId::Null, Err(error.clone())),
916 ),
917 TransportBatchEntry::Malformed { .. } => None,
918 }
919 }
920
921 let response = match frame {
922 TransportFrame::Single(entry) => {
923 let Some(response) = response_for_message(entry, &error) else {
924 return Ok(());
925 };
926 TransportFrame::Single(response)
927 }
928 TransportFrame::Malformed { raw, error } if !raw_is_response_only_shape(raw) => {
929 TransportFrame::Single(RawJsonRpcMessage::response(
930 RequestId::Null,
931 Err(error.clone()),
932 ))
933 }
934 TransportFrame::Malformed { .. } => return Ok(()),
935 TransportFrame::Batch(batch) => {
936 let responses = batch
937 .entries()
938 .filter_map(|entry| response_for_entry(entry, &error))
939 .collect::<Vec<_>>();
940 let Some(responses) = TransportBatch::from_messages(responses) else {
941 return Ok(());
942 };
943 TransportFrame::Batch(responses)
944 }
945 };
946
947 tx.unbounded_send(response)
948 .map_err(crate::util::internal_error)
949}
950
951#[cfg(feature = "unstable_protocol_v2")]
952async fn reject_initialize(
953 client: RunningProtocolPeer,
954 frame: &TransportFrame,
955 error: crate::Error,
956) -> Result<(), crate::Error> {
957 let RunningProtocolPeer { mut rx, tx, future } = client;
958 send_initialize_error(&tx, frame, error)?;
959 drop(tx);
960
961 let drain_incoming = async move {
962 while rx.next().await.is_some() {}
966 Ok::<_, crate::Error>(())
967 };
968
969 let ((), ()) = futures::try_join!(future, drain_incoming)?;
970 Ok(())
971}
972
973#[cfg(feature = "unstable_protocol_v2")]
974struct RunningProtocolPeer {
975 rx: futures::channel::mpsc::UnboundedReceiver<TransportFrame>,
976 tx: futures::channel::mpsc::UnboundedSender<TransportFrame>,
977 future: crate::BoxFuture<'static, Result<(), crate::Error>>,
978}
979
980#[cfg(feature = "unstable_protocol_v2")]
981impl RunningProtocolPeer {
982 fn new<R: Role>(component: impl ConnectTo<R>) -> Self {
983 let (Channel { rx, tx }, future) = component.into_channel_and_future();
984 Self { rx, tx, future }
985 }
986
987 async fn next_frame(self) -> Result<Option<(TransportFrame, Self)>, crate::Error> {
988 let Self { mut rx, tx, future } = self;
989 match future::select(Box::pin(rx.next()), future).await {
990 future::Either::Left((Some(frame), future)) => {
991 Ok(Some((frame, Self { rx, tx, future })))
992 }
993 future::Either::Left((None, future)) => {
994 future.await?;
995 Ok(None)
996 }
997 future::Either::Right((result, next_message)) => {
998 result?;
999 drop(next_message);
1000 let Some(frame) = rx.next().await else {
1001 return Ok(None);
1002 };
1003 Ok(Some((
1004 frame,
1005 Self {
1006 rx,
1007 tx,
1008 future: Box::pin(future::ready(Ok(()))),
1009 },
1010 )))
1011 }
1012 }
1013 }
1014
1015 async fn next_message(self) -> Result<Option<(RawJsonRpcMessage, Self)>, crate::Error> {
1016 let Some((frame, peer)) = self.next_frame().await? else {
1017 return Ok(None);
1018 };
1019 Ok(Some((initialize_message(frame)?, peer)))
1020 }
1021
1022 fn send(&self, message: RawJsonRpcMessage) -> Result<(), crate::Error> {
1023 self.send_frame(TransportFrame::Single(message))
1024 }
1025
1026 fn send_frame(&self, frame: TransportFrame) -> Result<(), crate::Error> {
1027 self.tx
1028 .unbounded_send(frame)
1029 .map_err(crate::util::internal_error)
1030 }
1031}
1032
1033#[cfg(feature = "unstable_protocol_v2")]
1034fn initialize_message(frame: TransportFrame) -> Result<RawJsonRpcMessage, crate::Error> {
1035 match frame {
1036 TransportFrame::Single(message) => Ok(message),
1037 TransportFrame::Malformed { error, .. } => Err(error),
1038 TransportFrame::Batch(_) => Err(crate::Error::invalid_request()
1039 .data("ACP initialize request and response messages must be sent individually")),
1040 }
1041}
1042
1043#[cfg(feature = "unstable_protocol_v2")]
1044fn initialize_message_mut(
1045 frame: &mut TransportFrame,
1046) -> Result<Option<&mut RawJsonRpcMessage>, crate::Error> {
1047 match frame {
1048 TransportFrame::Single(RawJsonRpcMessage::Response(_)) => Ok(None),
1049 TransportFrame::Single(entry) => Ok(Some(entry)),
1050 TransportFrame::Malformed { raw, .. } if raw_is_response_only_shape(raw) => Ok(None),
1051 TransportFrame::Malformed { error, .. } => Err(error.clone()),
1052 TransportFrame::Batch(batch) => {
1053 for entry in batch.entries_mut() {
1054 match entry {
1055 TransportBatchEntry::Message(RawJsonRpcMessage::Response(_)) => {}
1056 TransportBatchEntry::Message(message) => return Ok(Some(message)),
1057 TransportBatchEntry::Malformed { raw, .. } if is_response_only_shape(raw) => {}
1058 TransportBatchEntry::Malformed { error, .. } => return Err(error.clone()),
1059 }
1060 }
1061 Ok(None)
1062 }
1063 }
1064}
1065
1066#[cfg(feature = "unstable_protocol_v2")]
1067async fn pipe_protocol_peers_until_closed(
1068 left: RunningProtocolPeer,
1069 right: RunningProtocolPeer,
1070) -> Result<(), crate::Error> {
1071 let ((), (), (), ()) = futures::try_join!(
1072 left.future,
1073 right.future,
1074 Channel {
1075 rx: left.rx,
1076 tx: right.tx,
1077 }
1078 .copy(),
1079 Channel {
1080 rx: right.rx,
1081 tx: left.tx,
1082 }
1083 .copy(),
1084 )?;
1085
1086 Ok(())
1087}
1088
1089#[cfg(feature = "unstable_protocol_v2")]
1090async fn pipe_protocol_peers_until_done(
1091 left: RunningProtocolPeer,
1092 right: RunningProtocolPeer,
1093) -> Result<(), crate::Error> {
1094 let bridge = Box::pin(async move {
1095 let ((), ()) = futures::try_join!(
1096 Channel {
1097 rx: left.rx,
1098 tx: right.tx,
1099 }
1100 .copy(),
1101 Channel {
1102 rx: right.rx,
1103 tx: left.tx,
1104 }
1105 .copy(),
1106 )?;
1107 Ok(())
1108 });
1109
1110 match future::select(left.future, future::select(right.future, bridge)).await {
1111 future::Either::Left((result, _))
1112 | future::Either::Right((
1113 future::Either::Left((result, _)) | future::Either::Right((result, _)),
1114 _,
1115 )) => result,
1116 }
1117}
1118
1119#[cfg(feature = "unstable_protocol_v2")]
1120#[derive(Debug)]
1121struct InitializeResponse {
1122 id: RequestId,
1123 result: Result<serde_json::Value, crate::Error>,
1124}
1125
1126#[cfg(feature = "unstable_protocol_v2")]
1127impl InitializeResponse {
1128 fn from_message(message: RawJsonRpcMessage) -> Result<Self, crate::Error> {
1129 match message {
1130 RawJsonRpcMessage::Response(RpcResponse::Result { id, result }) => Ok(Self {
1131 id,
1132 result: Ok(result),
1133 }),
1134 RawJsonRpcMessage::Response(RpcResponse::Error { id, error }) => Ok(Self {
1135 id,
1136 result: Err(error),
1137 }),
1138 message => Err(crate::Error::invalid_request().data(format!(
1139 "first ACP response must be an initialize response, got {message:?}",
1140 ))),
1141 }
1142 }
1143
1144 fn into_message(self) -> RawJsonRpcMessage {
1145 RawJsonRpcMessage::response(self.id, self.result)
1146 }
1147
1148 fn with_id(self, id: RequestId) -> RawJsonRpcMessage {
1149 RawJsonRpcMessage::response(id, self.result)
1150 }
1151
1152 fn protocol_version(&self) -> Option<ProtocolVersion> {
1153 serde_json::from_value(self.result.as_ref().ok()?.get("protocolVersion")?.clone()).ok()
1154 }
1155}
1156
1157#[cfg(feature = "unstable_protocol_v2")]
1158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1159enum ClientProtocol {
1160 V1,
1161 V2,
1162}
1163
1164#[cfg(feature = "unstable_protocol_v2")]
1165impl ClientProtocol {
1166 fn name(self) -> &'static str {
1167 match self {
1168 Self::V1 => "1",
1169 Self::V2 => "2",
1170 }
1171 }
1172}
1173
1174#[cfg(feature = "unstable_protocol_v2")]
1175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1176struct SupportedClientProtocols {
1177 v1: bool,
1178 v2: bool,
1179}
1180
1181#[cfg(feature = "unstable_protocol_v2")]
1182impl SupportedClientProtocols {
1183 fn highest_configured(self) -> Option<ClientProtocol> {
1184 if self.v2 {
1185 return Some(ClientProtocol::V2);
1186 }
1187
1188 if self.v1 {
1189 return Some(ClientProtocol::V1);
1190 }
1191
1192 None
1193 }
1194}
1195
1196#[cfg(feature = "unstable_protocol_v2")]
1197async fn start_client_protocol(
1198 protocol: ClientProtocol,
1199 client: DynConnectTo<Agent>,
1200) -> Result<(RunningProtocolPeer, RawJsonRpcMessage), crate::Error> {
1201 let client = RunningProtocolPeer::new(client);
1202 let Some((initialize, client)) = client.next_message().await? else {
1203 return Err(crate::Error::invalid_request().data(format!(
1204 "ACP protocol version {} client implementation ended before initialize",
1205 protocol.name()
1206 )));
1207 };
1208 ensure_client_initialize_request(protocol, &initialize)?;
1209 Ok((client, initialize))
1210}
1211
1212#[cfg(feature = "unstable_protocol_v2")]
1213async fn send_initialize_and_receive(
1214 client: RunningProtocolPeer,
1215 agent: RunningProtocolPeer,
1216 initialize: RawJsonRpcMessage,
1217) -> Result<(RunningProtocolPeer, RunningProtocolPeer, InitializeResponse), crate::Error> {
1218 agent.send(initialize)?;
1219 let Some((response, agent)) = agent.next_message().await? else {
1220 return Err(crate::Error::internal_error().data("agent closed before initialize response"));
1221 };
1222 let response = InitializeResponse::from_message(response)?;
1223 Ok((client, agent, response))
1224}
1225
1226#[cfg(feature = "unstable_protocol_v2")]
1227async fn initialize_client_protocol(
1228 protocol: ClientProtocol,
1229 client: DynConnectTo<Agent>,
1230 agent: impl ConnectTo<Client>,
1231) -> Result<(RunningProtocolPeer, RunningProtocolPeer, InitializeResponse), crate::Error> {
1232 let agent = RunningProtocolPeer::new(agent);
1233 let (client, initialize) = start_client_protocol(protocol, client).await?;
1234 send_initialize_and_receive(client, agent, initialize).await
1235}
1236
1237#[cfg(feature = "unstable_protocol_v2")]
1238async fn connect_client_protocol(
1239 protocol: ClientProtocol,
1240 client: DynConnectTo<Agent>,
1241 agent: impl ConnectTo<Client>,
1242) -> Result<(), crate::Error> {
1243 let (client, agent, initialize_response) =
1244 initialize_client_protocol(protocol, client, agent).await?;
1245 client.send(initialize_response.into_message())?;
1246 pipe_protocol_peers_until_done(client, agent).await
1247}
1248
1249#[cfg(feature = "unstable_protocol_v2")]
1250fn ensure_client_initialize_request(
1251 protocol: ClientProtocol,
1252 message: &RawJsonRpcMessage,
1253) -> Result<(), crate::Error> {
1254 let RawJsonRpcMessage::Request(request) = message else {
1255 return Err(crate::Error::invalid_request().data(format!(
1256 "ACP protocol version {} client implementation must send initialize first",
1257 protocol.name()
1258 )));
1259 };
1260
1261 if request.method.as_ref() != "initialize" {
1262 return Err(crate::Error::invalid_request().data(format!(
1263 "ACP protocol version {} client implementation must send initialize first",
1264 protocol.name()
1265 )));
1266 }
1267
1268 Ok(())
1269}
1270
1271#[cfg(feature = "unstable_protocol_v2")]
1272fn initialize_request_id(message: &RawJsonRpcMessage) -> Option<RequestId> {
1273 let RawJsonRpcMessage::Request(request) = message else {
1274 return None;
1275 };
1276 Some(request.id.clone())
1277}
1278
1279#[cfg(feature = "unstable_protocol_v2")]
1280fn initialize_response_negotiated_v1(response: &InitializeResponse) -> bool {
1281 response.protocol_version() == Some(ProtocolVersion::V1)
1282}
1283
1284impl HasPeer<Agent> for Agent {
1285 fn remote_style(&self, _peer: Agent) -> RemoteStyle {
1286 RemoteStyle::Counterpart
1287 }
1288}
1289
1290#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1299pub struct Proxy;
1300
1301impl Role for Proxy {
1302 type Counterpart = Conductor;
1303
1304 fn default_handle_dispatch_from(
1305 &self,
1306 message: crate::Dispatch,
1307 _connection: crate::ConnectionTo<Self>,
1308 ) -> impl Future<Output = Result<crate::Handled<crate::Dispatch>, crate::Error>> + Send {
1309 std::future::ready(Ok(Handled::No {
1310 message,
1311 retry: false,
1312 }))
1313 }
1314
1315 fn role_id(&self) -> RoleId {
1316 RoleId::from_singleton(self)
1317 }
1318
1319 fn counterpart(&self) -> Self::Counterpart {
1320 Conductor
1321 }
1322}
1323
1324impl Proxy {
1325 pub fn builder(self) -> Builder<Proxy, NullHandler, NullRun> {
1332 Builder::new(self)
1333 }
1334
1335 #[cfg(feature = "unstable_protocol_v2")]
1344 pub fn v2(self) -> V2Builder<Proxy, NullHandler, NullRun> {
1345 self.builder().v2_proxy()
1346 }
1347
1348 #[cfg(feature = "unstable_protocol_v2")]
1359 #[must_use]
1360 pub fn protocol_router(self) -> ProxyProtocolRouter {
1361 ProxyProtocolRouter::new()
1362 }
1363}
1364
1365#[cfg(feature = "unstable_protocol_v2")]
1373#[derive(Debug, Default)]
1374pub struct ProxyProtocolRouter {
1375 v1: Option<DynConnectTo<Conductor>>,
1376 v2: Option<DynConnectTo<Conductor>>,
1377}
1378
1379#[cfg(feature = "unstable_protocol_v2")]
1380impl ProxyProtocolRouter {
1381 #[must_use]
1383 pub fn new() -> Self {
1384 Self::default()
1385 }
1386
1387 #[must_use]
1389 pub fn with_v1(mut self, proxy: impl ConnectTo<Conductor>) -> Self {
1390 self.v1 = Some(DynConnectTo::new(proxy));
1391 self
1392 }
1393
1394 #[must_use]
1396 pub fn with_v2(mut self, proxy: impl ConnectTo<Conductor>) -> Self {
1397 self.v2 = Some(DynConnectTo::new(proxy));
1398 self
1399 }
1400}
1401
1402#[cfg(feature = "unstable_protocol_v2")]
1403impl ConnectTo<Conductor> for ProxyProtocolRouter {
1404 async fn connect_to(self, conductor: impl ConnectTo<Proxy>) -> Result<(), crate::Error> {
1405 let supported = SupportedProtocols {
1406 v1: self.v1.is_some(),
1407 v2: self.v2.is_some(),
1408 };
1409 let mut conductor = RunningProtocolPeer::new(conductor);
1410 let (first_frame, conductor, selected) = loop {
1411 let Some((mut frame, next_conductor)) = conductor.next_frame().await? else {
1412 return Ok(());
1413 };
1414 let message = match initialize_message_mut(&mut frame) {
1415 Ok(Some(message)) => message,
1416 Ok(None) => {
1417 conductor = next_conductor;
1418 continue;
1419 }
1420 Err(error) => return reject_initialize(next_conductor, &frame, error).await,
1421 };
1422 let selected = match select_proxy_protocol(message, supported) {
1423 Ok(selected) => selected,
1424 Err(error) => return reject_initialize(next_conductor, &frame, error).await,
1425 };
1426 break (frame, next_conductor, selected);
1427 };
1428 let Some(proxy) = selected.take_proxy(self) else {
1429 let error = selected.unsupported_error(supported);
1430 return reject_initialize(conductor, &first_frame, error).await;
1431 };
1432
1433 let proxy = RunningProtocolPeer::new(proxy);
1434 proxy.send_frame(first_frame)?;
1435 pipe_protocol_peers_until_closed(conductor, proxy).await
1436 }
1437}
1438
1439#[cfg(feature = "unstable_protocol_v2")]
1440impl SelectedProtocol {
1441 fn take_proxy(self, proxy: ProxyProtocolRouter) -> Option<DynConnectTo<Conductor>> {
1442 match self {
1443 Self::V1 => proxy.v1,
1444 Self::V2 => proxy.v2,
1445 }
1446 }
1447}
1448
1449#[cfg(feature = "unstable_protocol_v2")]
1450fn select_proxy_protocol(
1451 message: &RawJsonRpcMessage,
1452 supported: SupportedProtocols,
1453) -> Result<SelectedProtocol, crate::Error> {
1454 let RawJsonRpcMessage::Request(request) = message else {
1455 return Err(crate::Error::invalid_request()
1456 .data("first ACP proxy message must be an `_proxy/initialize` request"));
1457 };
1458
1459 if request.method.as_ref() != METHOD_INITIALIZE_PROXY {
1460 return Err(crate::Error::invalid_request()
1461 .data("first ACP proxy request must be `_proxy/initialize`"));
1462 }
1463
1464 let Some(RawJsonRpcParams::Object(params)) = &request.params else {
1465 return Err(invalid_initialize_protocol_version());
1466 };
1467 let Some(protocol_version) = params.get("protocolVersion") else {
1468 return Err(invalid_initialize_protocol_version());
1469 };
1470 let requested = serde_json::from_value::<ProtocolVersion>(protocol_version.clone())
1471 .map_err(|_| invalid_initialize_protocol_version())?;
1472 let selected = supported.exact(requested).ok_or_else(|| {
1473 crate::Error::invalid_request().data(format!(
1474 "unsupported ACP protocol version {requested}; this proxy supports {}",
1475 supported.description()
1476 ))
1477 })?;
1478
1479 match selected {
1480 SelectedProtocol::V1 => {
1481 parse_initialize_params::<crate::schema::InitializeProxyRequest>(params)?;
1482 }
1483 SelectedProtocol::V2 => {
1484 parse_initialize_params::<v2::InitializeProxyRequest>(params)?;
1485 }
1486 }
1487 Ok(selected)
1488}
1489
1490impl HasPeer<Proxy> for Proxy {
1491 fn remote_style(&self, _peer: Proxy) -> RemoteStyle {
1492 RemoteStyle::Counterpart
1493 }
1494}
1495
1496#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1501pub struct Conductor;
1502
1503impl Role for Conductor {
1504 type Counterpart = Proxy;
1505
1506 fn role_id(&self) -> RoleId {
1507 RoleId::from_singleton(self)
1508 }
1509
1510 fn counterpart(&self) -> Self::Counterpart {
1511 Proxy
1512 }
1513
1514 async fn default_handle_dispatch_from(
1515 &self,
1516 message: Dispatch,
1517 cx: ConnectionTo<Conductor>,
1518 ) -> Result<Handled<Dispatch>, crate::Error> {
1519 #[cfg(not(feature = "unstable_protocol_v2"))]
1520 {
1521 MatchDispatchFrom::new(message, &cx)
1522 .if_request_from(Client, async |_req: InitializeRequest, responder| {
1523 responder.respond_with_error(crate::Error::invalid_request().data(format!(
1524 "proxies must be initialized with `{METHOD_INITIALIZE_PROXY}`"
1525 )))
1526 })
1527 .await
1528 .if_request_from(
1529 Client,
1530 async |request: InitializeProxyRequest, responder| {
1531 let InitializeProxyRequest { initialize } = request;
1532 cx.send_ordered_request_to(Agent, initialize)
1533 .forward_response_to(responder)
1534 },
1535 )
1536 .await
1537 .if_request_from(Client, async |request: NewSessionRequest, responder| {
1538 let sent = cx.send_ordered_request_to(Agent, request);
1539 let sent = sent.forward_cancellation_from(responder.cancellation());
1540 sent.on_receiving_result({
1541 let cx = cx.clone();
1542 async move |result| {
1543 if let Ok(NewSessionResponse { session_id, .. }) = &result {
1544 cx.add_dynamic_handler(ProxySessionMessages::new(
1545 session_id.clone(),
1546 ))?
1547 .detach();
1548 }
1549 responder.respond_with_result(result)
1550 }
1551 })
1552 })
1553 .await
1554 .if_dispatch_from(Client, async |message: Dispatch| {
1555 cx.send_proxied_message_to(Agent, message)
1556 })
1557 .await
1558 .if_dispatch_from(Agent, async |message: Dispatch| {
1559 cx.send_proxied_message_to(Client, message)
1560 })
1561 .await
1562 .done()
1563 }
1564
1565 #[cfg(feature = "unstable_protocol_v2")]
1566 {
1567 let message = match message {
1568 Dispatch::Request(request, responder) if request.method() == "initialize" => {
1569 responder.respond_with_error(crate::Error::invalid_request().data(format!(
1570 "proxies must be initialized with `{METHOD_INITIALIZE_PROXY}`"
1571 )))?;
1572 return Ok(Handled::Yes);
1573 }
1574 Dispatch::Request(mut request, responder)
1575 if request.method() == METHOD_INITIALIZE_PROXY =>
1576 {
1577 request.method = "initialize".to_string();
1578 cx.send_ordered_request_to(Agent, request)
1579 .forward_response_to(responder)?;
1580 return Ok(Handled::Yes);
1581 }
1582 Dispatch::Request(request, responder) if request.method() == "session/new" => {
1583 let sent = cx.send_ordered_request_to(Agent, request);
1584 let sent = sent.forward_cancellation_from(responder.cancellation());
1589 sent.on_receiving_result({
1590 let cx = cx.clone();
1591 async move |result| {
1592 let result = result.and_then(|response| {
1593 let envelope: NewSessionResponseEnvelope =
1594 crate::util::json_cast(response.clone())?;
1595 cx.add_dynamic_handler(ProxySessionMessages::new(
1596 envelope.session_id,
1597 ))?
1598 .detach();
1599 Ok(response)
1600 });
1601 responder.respond_with_result(result)
1602 }
1603 })?;
1604 return Ok(Handled::Yes);
1605 }
1606 message => message,
1607 };
1608
1609 MatchDispatchFrom::new(message, &cx)
1610 .if_dispatch_from(Client, async |message: Dispatch| {
1611 cx.send_proxied_message_to(Agent, message)
1612 })
1613 .await
1614 .if_dispatch_from(Agent, async |message: Dispatch| {
1615 cx.send_proxied_message_to(Client, message)
1616 })
1617 .await
1618 .done()
1619 }
1620 }
1621}
1622
1623impl Conductor {
1624 pub fn builder(self) -> Builder<Conductor, NullHandler, NullRun> {
1626 Builder::new(self)
1627 }
1628}
1629
1630impl HasPeer<Client> for Conductor {
1631 fn remote_style(&self, _peer: Client) -> RemoteStyle {
1632 RemoteStyle::Predecessor
1633 }
1634}
1635
1636impl HasPeer<Agent> for Conductor {
1637 fn remote_style(&self, _peer: Agent) -> RemoteStyle {
1638 RemoteStyle::Successor
1639 }
1640}
1641
1642pub(crate) struct ProxySessionMessages {
1647 session_id: SessionId,
1648}
1649
1650impl ProxySessionMessages {
1651 pub fn new(session_id: SessionId) -> Self {
1653 Self { session_id }
1654 }
1655}
1656
1657impl<Counterpart: Role> HandleDispatchFrom<Counterpart> for ProxySessionMessages
1658where
1659 Counterpart: HasPeer<Agent> + HasPeer<Client>,
1660{
1661 async fn handle_dispatch_from(
1662 &mut self,
1663 message: Dispatch,
1664 connection: ConnectionTo<Counterpart>,
1665 ) -> Result<Handled<Dispatch>, crate::Error> {
1666 MatchDispatchFrom::new(message, &connection)
1667 .if_dispatch_from(Agent, async |message| {
1668 if let Some(session_id) = message.get_session_id()?
1670 && session_id == self.session_id
1671 {
1672 connection.send_proxied_message_to(Client, message)?;
1673 return Ok(Handled::Yes);
1674 }
1675
1676 Ok(Handled::No {
1678 message,
1679 retry: false,
1680 })
1681 })
1682 .await
1683 .done()
1684 }
1685
1686 fn describe_chain(&self) -> impl std::fmt::Debug {
1687 format!("ProxySessionMessages({})", self.session_id)
1688 }
1689}