Skip to main content

agent_client_protocol_conductor/
conductor.rs

1//! # Conductor: ACP Proxy Chain Orchestrator
2//!
3//! This module implements the Conductor conductor, which orchestrates a chain of
4//! proxy components that sit between an editor and an agent, transforming the
5//! Agent-Client Protocol (ACP) stream bidirectionally.
6//!
7//! ## Architecture Overview
8//!
9//! The conductor builds and manages a chain of components:
10//!
11//! ```text
12//! Editor <-ACP-> [Component 0] <-ACP-> [Component 1] <-ACP-> ... <-ACP-> Agent
13//! ```
14//!
15//! Each component receives ACP messages, can transform them, and forwards them
16//! to the next component in the chain. The conductor:
17//!
18//! 1. Spawns each component as a subprocess
19//! 2. Establishes bidirectional JSON-RPC connections with each component
20//! 3. Routes messages between editor, components, and agent
21//! 4. Distinguishes proxy vs agent components via distinct request types
22//!
23//! ## Recursive Chain Building
24//!
25//! The chain is built recursively through the `_proxy/successor` envelope:
26//!
27//! 1. Editor connects to Component 0 via the conductor
28//! 2. When Component 0 wants to communicate with its successor, it sends
29//!    a `_proxy/successor` request or notification containing the inner method
30//!    and params
31//! 3. The conductor unwraps the inner message and forwards it to Component 1
32//! 4. Component 1 does the same for Component 2, and so on
33//! 5. The last component receives the unwrapped ACP message directly
34//!
35//! This allows each component to be written as if it's talking to a single successor,
36//! without knowing about the full chain.
37//!
38//! ## Proxy vs Agent Initialization
39//!
40//! Components discover whether they're a proxy or agent via the initialization request they receive:
41//!
42//! - **Proxy components**: Receive `InitializeProxyRequest` (`_proxy/initialize` method)
43//! - **Agent component**: Receives standard `InitializeRequest` (`initialize` method)
44//!
45//! The conductor sends `InitializeProxyRequest` to all proxy components in the chain,
46//! and `InitializeRequest` only to the final agent component. This allows proxies to
47//! know they should forward messages to a successor, while agents know they are the
48//! terminal component
49//!
50//! ## Message Routing
51//!
52//! The conductor runs an event loop processing messages from:
53//!
54//! - **Editor to first component**: Standard ACP messages
55//! - **Component to successor**: Via the `_proxy/successor` envelope
56//! - **Component responses**: Via futures channels back to requesters
57//!
58//! The message flow ensures bidirectional communication while maintaining the
59//! abstraction that each component only knows about its immediate successor.
60//!
61//! ## Lazy Component Initialization
62//!
63//! Components are instantiated lazily when the first `initialize` request is received
64//! from the editor. This enables dynamic proxy chain construction based on client capabilities.
65//!
66//! ### Fixed Chains
67//!
68//! Use [`ProxiesAndAgent`] to assemble a conductor that presents as an agent:
69//!
70//! ```ignore
71//! use agent_client_protocol_conductor::{ConductorImpl, ProxiesAndAgent};
72//!
73//! let conductor = ConductorImpl::new_agent(
74//!     "my-conductor",
75//!     ProxiesAndAgent::new(agent)
76//!         .proxy(proxy1)
77//!         .proxy(proxy2),
78//! );
79//! ```
80//!
81//! A conductor that presents as a proxy takes only its internal proxies; its
82//! final successor is supplied when the conductor is connected:
83//!
84//! ```ignore
85//! use agent_client_protocol_conductor::ConductorImpl;
86//!
87//! let conductor = ConductorImpl::new_proxy("my-proxy-conductor", vec![proxy]);
88//! ```
89//!
90//! ### Dynamic Chain Selection
91//!
92//! Both constructors also accept an instantiator closure. The closure receives
93//! the `InitializeRequest` and returns the possibly modified request together
94//! with type-erased connectors for the selected chain:
95//!
96//! ```ignore
97//! use agent_client_protocol::{Client, Conductor, DynConnectTo};
98//! use agent_client_protocol_conductor::ConductorImpl;
99//!
100//! let conductor = ConductorImpl::new_agent("my-conductor", |init_req| async move {
101//!     let mut proxies: Vec<DynConnectTo<Conductor>> = Vec::new();
102//!     if has_auth_capability(&init_req) {
103//!         proxies.push(DynConnectTo::new(make_auth_proxy()));
104//!     }
105//!
106//!     let agent: DynConnectTo<Client> = DynConnectTo::new(make_agent());
107//!     Ok((init_req, proxies, agent))
108//! });
109//! ```
110
111use std::sync::Arc;
112
113use agent_client_protocol::{
114    Agent, BoxFuture, Client, Conductor, ConnectTo, Dispatch, DynConnectTo, Error, JsonRpcMessage,
115    Proxy, Role, RunWithConnectionTo, role::HasPeer, util::MatchDispatch,
116};
117use agent_client_protocol::{
118    Builder, ConnectionTo, JsonRpcNotification, JsonRpcRequest, SentRequest,
119};
120use agent_client_protocol::{
121    HandleDispatchFrom,
122    schema::{InitializeProxyRequest, v1::InitializeRequest},
123    util::MatchDispatchFrom,
124};
125use agent_client_protocol::{Handled, schema::SuccessorMessage};
126use futures::{
127    SinkExt, StreamExt,
128    channel::mpsc::{self},
129};
130use tracing::{debug, info};
131
132/// The conductor manages the proxy chain lifecycle and message routing.
133///
134/// It maintains connections to all components in the chain and routes messages
135/// bidirectionally between the editor, components, and agent.
136///
137#[derive(Debug)]
138pub struct ConductorImpl<Host: ConductorHostRole> {
139    host: Host,
140    name: String,
141    instantiator: Host::Instantiator,
142    trace_writer: Option<crate::trace::TraceWriter>,
143}
144
145impl<Host: ConductorHostRole> ConductorImpl<Host> {
146    pub fn new(host: Host, name: impl ToString, instantiator: Host::Instantiator) -> Self {
147        ConductorImpl {
148            name: name.to_string(),
149            host,
150            instantiator,
151            trace_writer: None,
152        }
153    }
154}
155
156impl ConductorImpl<Agent> {
157    /// Create a conductor in agent mode (the last component is an agent).
158    pub fn new_agent(
159        name: impl ToString,
160        instantiator: impl InstantiateProxiesAndAgent + 'static,
161    ) -> Self {
162        ConductorImpl::new(Agent, name, Box::new(instantiator))
163    }
164}
165
166impl ConductorImpl<Proxy> {
167    /// Create a conductor in proxy mode (forwards to another conductor).
168    pub fn new_proxy(name: impl ToString, instantiator: impl InstantiateProxies + 'static) -> Self {
169        ConductorImpl::new(Proxy, name, Box::new(instantiator))
170    }
171}
172
173impl<Host: ConductorHostRole> ConductorImpl<Host> {
174    /// Enable trace logging to a custom destination.
175    ///
176    /// Use `agent-client-protocol-trace-viewer` to view the trace as an interactive sequence diagram.
177    #[must_use]
178    pub fn trace_to(mut self, dest: impl crate::trace::WriteEvent) -> Self {
179        self.trace_writer = Some(crate::trace::TraceWriter::new(dest));
180        self
181    }
182
183    /// Enable trace logging to a file path.
184    ///
185    /// Events will be written as newline-delimited JSON (`.jsons` format).
186    /// Use `agent-client-protocol-trace-viewer` to view the trace as an interactive sequence diagram.
187    pub fn trace_to_path(mut self, path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
188        self.trace_writer = Some(crate::trace::TraceWriter::from_path(path)?);
189        Ok(self)
190    }
191
192    /// Enable trace logging with an existing TraceWriter.
193    #[must_use]
194    pub fn with_trace_writer(mut self, writer: crate::trace::TraceWriter) -> Self {
195        self.trace_writer = Some(writer);
196        self
197    }
198
199    /// Run the conductor with a transport.
200    pub async fn run(
201        self,
202        transport: impl ConnectTo<Host>,
203    ) -> Result<(), agent_client_protocol::Error> {
204        let (conductor_tx, conductor_rx) = mpsc::channel(128 /* chosen arbitrarily */);
205
206        // Set up tracing if enabled - spawn writer task and get handle
207        let trace_handle;
208        let trace_future: BoxFuture<'static, Result<(), agent_client_protocol::Error>>;
209        if let Some((h, f)) = self.trace_writer.map(super::trace::TraceWriter::spawn) {
210            trace_handle = Some(h);
211            trace_future = Box::pin(f);
212        } else {
213            trace_handle = None;
214            trace_future = Box::pin(std::future::ready(Ok(())));
215        }
216
217        let runner = ConductorRunner {
218            conductor_rx,
219            conductor_tx: conductor_tx.clone(),
220            instantiator: Some(self.instantiator),
221            proxies: Vec::default(),
222            successor: Arc::new(agent_client_protocol::util::internal_error(
223                "successor not initialized",
224            )),
225            trace_handle,
226            host: self.host.clone(),
227        };
228
229        Builder::new_with(
230            self.host.clone(),
231            ConductorMessageHandler {
232                conductor_tx,
233                host: self.host.clone(),
234            },
235        )
236        .name(self.name)
237        .with_runner(runner)
238        .with_spawned(|_cx| trace_future)
239        .connect_to(transport)
240        .await
241    }
242
243    async fn incoming_message_from_client(
244        conductor_tx: &mut mpsc::Sender<ConductorMessage>,
245        message: Dispatch,
246    ) -> Result<(), agent_client_protocol::Error> {
247        conductor_tx
248            .send(ConductorMessage::LeftToRight {
249                target_component_index: 0,
250                message,
251            })
252            .await
253            .map_err(agent_client_protocol::util::internal_error)
254    }
255
256    async fn incoming_message_from_agent(
257        conductor_tx: &mut mpsc::Sender<ConductorMessage>,
258        message: Dispatch,
259    ) -> Result<(), agent_client_protocol::Error> {
260        conductor_tx
261            .send(ConductorMessage::RightToLeft {
262                source_component_index: SourceComponentIndex::Successor,
263                message,
264            })
265            .await
266            .map_err(agent_client_protocol::util::internal_error)
267    }
268}
269
270impl<Host: ConductorHostRole> ConnectTo<Host::Counterpart> for ConductorImpl<Host> {
271    async fn connect_to(
272        self,
273        client: impl ConnectTo<Host>,
274    ) -> Result<(), agent_client_protocol::Error> {
275        self.run(client).await
276    }
277}
278
279struct ConductorMessageHandler<Host: ConductorHostRole> {
280    conductor_tx: mpsc::Sender<ConductorMessage>,
281    host: Host,
282}
283
284impl<Host: ConductorHostRole> HandleDispatchFrom<Host::Counterpart>
285    for ConductorMessageHandler<Host>
286{
287    async fn handle_dispatch_from(
288        &mut self,
289        message: Dispatch,
290        connection: agent_client_protocol::ConnectionTo<Host::Counterpart>,
291    ) -> Result<agent_client_protocol::Handled<Dispatch>, agent_client_protocol::Error> {
292        self.host
293            .handle_dispatch(message, connection, &mut self.conductor_tx)
294            .await
295    }
296
297    fn describe_chain(&self) -> impl std::fmt::Debug {
298        "ConductorMessageHandler"
299    }
300}
301
302/// The conductor manages the proxy chain lifecycle and message routing.
303///
304/// It maintains connections to all components in the chain and routes messages
305/// bidirectionally between the editor, components, and agent.
306///
307pub struct ConductorRunner<Host>
308where
309    Host: ConductorHostRole,
310{
311    conductor_rx: mpsc::Receiver<ConductorMessage>,
312
313    conductor_tx: mpsc::Sender<ConductorMessage>,
314
315    /// The instantiator for lazy initialization.
316    /// Set to None after components are instantiated.
317    instantiator: Option<Host::Instantiator>,
318
319    /// The chain of proxies before the agent (if any).
320    ///
321    /// Populated lazily when the first Initialize request is received.
322    proxies: Vec<ConnectionTo<Proxy>>,
323
324    /// If the conductor is operating in agent mode, this will direct messages to the agent.
325    /// If the conductor is operating in proxy mode, this will direct messages to the successor.
326    /// Populated lazily when the first Initialize request is received; the initial value just returns errors.
327    successor: Arc<dyn ConductorSuccessor<Host>>,
328
329    /// Optional trace handle for sequence diagram visualization.
330    trace_handle: Option<crate::trace::TraceHandle>,
331
332    /// Defines what sort of link we have
333    host: Host,
334}
335
336impl<Host> std::fmt::Debug for ConductorRunner<Host>
337where
338    Host: ConductorHostRole,
339{
340    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341        f.debug_struct("ConductorRunner")
342            .field("conductor_rx", &self.conductor_rx)
343            .field("conductor_tx", &self.conductor_tx)
344            .field("proxies", &self.proxies)
345            .field("trace_handle", &self.trace_handle)
346            .field("host", &self.host)
347            .finish_non_exhaustive()
348    }
349}
350
351impl<Host> RunWithConnectionTo<Host::Counterpart> for ConductorRunner<Host>
352where
353    Host: ConductorHostRole,
354{
355    async fn run_with_connection_to(
356        mut self,
357        connection: ConnectionTo<Host::Counterpart>,
358    ) -> Result<(), agent_client_protocol::Error> {
359        // Components are now spawned lazily in forward_initialize_request
360        // when the first Initialize request is received.
361
362        // This is the "central actor" of the conductor. Most other things forward messages
363        // via `conductor_tx` into this loop. This lets us serialize the conductor's activity.
364        while let Some(message) = self.conductor_rx.next().await {
365            self.handle_conductor_message(connection.clone(), message)
366                .await?;
367        }
368        Ok(())
369    }
370}
371
372impl<Host> ConductorRunner<Host>
373where
374    Host: ConductorHostRole,
375{
376    /// Recursively spawns components and builds the proxy chain.
377    ///
378    /// This function implements the recursive chain building pattern:
379    /// 1. Pop the next component from the `providers` list
380    /// 2. Create the component (either spawn subprocess or use mock)
381    /// 3. Set up JSON-RPC connection and message handlers
382    /// 4. Recursively call itself to spawn the next component
383    /// 5. When no components remain, continue in the central runner's message-routing loop
384    ///
385    /// Central message handling logic for the conductor.
386    /// The conductor routes all [`ConductorMessage`] messages through to this function.
387    /// Each message corresponds to a request or notification from one component to another.
388    /// The conductor ferries messages from one place to another, sometimes making modifications along the way.
389    /// Note that *responses to requests* are sent *directly* without going through this loop.
390    ///
391    /// The names we use are
392    ///
393    /// * The *client* is the originator of all ACP traffic, typically an editor or GUI.
394    /// * Then there is a sequence of *components* consisting of:
395    ///     * Zero or more *proxies*, which receive messages and forward them to the next component in the chain.
396    ///     * And finally the *agent*, which is the final component in the chain and handles the actual work.
397    ///
398    /// For the most part, we pass messages through the chain without modification. The initialization
399    /// handshake is the exception:
400    ///
401    /// * We send `InitializeProxyRequest` to proxy components and `InitializeRequest` to the agent component.
402    async fn handle_conductor_message(
403        &mut self,
404        client: ConnectionTo<Host::Counterpart>,
405        message: ConductorMessage,
406    ) -> Result<(), agent_client_protocol::Error> {
407        tracing::debug!(?message, "handle_conductor_message");
408
409        match message {
410            ConductorMessage::LeftToRight {
411                target_component_index,
412                message,
413            } => {
414                // Tracing happens inside forward_client_to_agent_message, after initialization,
415                // so that component_name() has access to the populated proxies list.
416                self.forward_client_to_agent_message(target_component_index, message, client)
417                    .await
418            }
419
420            ConductorMessage::RightToLeft {
421                source_component_index,
422                message,
423            } => {
424                tracing::debug!(
425                    ?source_component_index,
426                    message_method = ?message.method(),
427                    "Conductor: AgentToClient received"
428                );
429                self.send_message_to_predecessor_of(client, source_component_index, message)
430            }
431        }
432    }
433
434    /// Send a message (request or notification) to the predecessor of the given component.
435    ///
436    /// This is a bit subtle because the relationship of the conductor
437    /// is different depending on who will be receiving the message:
438    /// * If the message is going to the conductor's client, then no changes
439    ///   are needed, as the conductor is sending an agent-to-client message and
440    ///   the conductor is acting as the agent.
441    /// * If the message is going to a proxy component, then we have to wrap
442    ///   it in a "from successor" wrapper, because the conductor is the
443    ///   proxy's client.
444    fn send_message_to_predecessor_of<Req: JsonRpcRequest, N: JsonRpcNotification>(
445        &mut self,
446        client: ConnectionTo<Host::Counterpart>,
447        source_component_index: SourceComponentIndex,
448        message: Dispatch<Req, N>,
449    ) -> Result<(), agent_client_protocol::Error>
450    where
451        Req::Response: Send,
452    {
453        let source_component_index = match source_component_index {
454            SourceComponentIndex::Successor => self.proxies.len(),
455            SourceComponentIndex::Proxy(index) => index,
456        };
457
458        match message {
459            Dispatch::Request(request, responder) => self
460                .send_request_to_predecessor_of(client, source_component_index, request)
461                .forward_response_to(responder),
462            Dispatch::Notification(notification) => {
463                // `$/cancel_request` is connection-scoped: its `requestId` was
464                // allocated on the connection the notification arrived over
465                // and means nothing on the predecessor's connection. The SDK
466                // already propagates the cancellation hop by hop through the
467                // `forward_response_to` calls above, so drop the raw
468                // notification instead of tunneling a meaningless ID.
469                if agent_client_protocol::is_cancel_request_notification(&notification) {
470                    tracing::debug!(
471                        "not forwarding hop-scoped `$/cancel_request` notification to predecessor"
472                    );
473                    return Ok(());
474                }
475                self.send_notification_to_predecessor_of(
476                    client,
477                    source_component_index,
478                    notification,
479                )
480            }
481            Dispatch::Response(result, router) => router.route_with_result(result),
482        }
483    }
484
485    fn send_request_to_predecessor_of<Req: JsonRpcRequest>(
486        &mut self,
487        client_connection: ConnectionTo<Host::Counterpart>,
488        source_component_index: usize,
489        request: Req,
490    ) -> SentRequest<Req::Response> {
491        if source_component_index == 0 {
492            client_connection.send_request_to(Client, request)
493        } else {
494            self.proxies[source_component_index - 1].send_request(SuccessorMessage {
495                message: request,
496                meta: None,
497            })
498        }
499    }
500
501    /// Send a notification to the predecessor of the given component.
502    ///
503    /// This is a bit subtle because the relationship of the conductor
504    /// is different depending on who will be receiving the message:
505    /// * If the notification is going to the conductor's client, then no changes
506    ///   are needed, as the conductor is sending an agent-to-client message and
507    ///   the conductor is acting as the agent.
508    /// * If the notification is going to a proxy component, then we have to wrap
509    ///   it in a "from successor" wrapper, because the conductor is the
510    ///   proxy's client.
511    fn send_notification_to_predecessor_of<N: JsonRpcNotification>(
512        &mut self,
513        client: ConnectionTo<Host::Counterpart>,
514        source_component_index: usize,
515        notification: N,
516    ) -> Result<(), agent_client_protocol::Error> {
517        tracing::debug!(
518            source_component_index,
519            proxies_len = self.proxies.len(),
520            "send_notification_to_predecessor_of"
521        );
522        if source_component_index == 0 {
523            tracing::debug!("Sending notification directly to client");
524            client.send_notification_to(Client, notification)
525        } else {
526            tracing::debug!(
527                target_proxy = source_component_index - 1,
528                "Sending notification wrapped as SuccessorMessage to proxy"
529            );
530            self.proxies[source_component_index - 1].send_notification(SuccessorMessage {
531                message: notification,
532                meta: None,
533            })
534        }
535    }
536
537    /// Send a message (request or notification) from 'left to right'.
538    /// Left-to-right means from the client or an intermediate proxy to the component
539    /// at `target_component_index` (could be a proxy or the agent).
540    /// Ensures the component chain is initialized before forwarding the message.
541    async fn forward_client_to_agent_message(
542        &mut self,
543        target_component_index: usize,
544        message: Dispatch,
545        client: ConnectionTo<Host::Counterpart>,
546    ) -> Result<(), agent_client_protocol::Error> {
547        tracing::trace!(
548            target_component_index,
549            ?message,
550            "forward_client_to_agent_message"
551        );
552
553        // Ensure components are initialized before processing any message.
554        let message = self.ensure_initialized(client.clone(), message).await?;
555
556        // In proxy mode, if the target is beyond our component chain,
557        // forward to the conductor's own successor (via client connection)
558        if target_component_index < self.proxies.len() {
559            self.forward_message_from_client_to_proxy(target_component_index, message)
560                .await
561        } else {
562            assert_eq!(target_component_index, self.proxies.len());
563
564            debug!(
565                target_component_index,
566                proxies_count = self.proxies.len(),
567                "Proxy mode: forwarding successor message to conductor's successor"
568            );
569            let successor = self.successor.clone();
570            successor.send_message(message, client, self).await
571        }
572    }
573
574    /// Ensures components are initialized before processing messages.
575    ///
576    /// If components haven't been initialized yet, this expects the first message
577    /// to be an `initialize` request and uses it to spawn the component chain.
578    ///
579    /// Returns:
580    /// - `Ok(Some(message))` - Components are initialized, continue processing this message
581    /// - `Ok(None)` - An error response was sent, caller should return early
582    /// - `Err(_)` - A fatal error occurred
583    async fn ensure_initialized(
584        &mut self,
585        client: ConnectionTo<Host::Counterpart>,
586        message: Dispatch,
587    ) -> Result<Dispatch, Error> {
588        // Already initialized - pass through
589        let Some(instantiator) = self.instantiator.take() else {
590            return Ok(message);
591        };
592
593        let host = self.host.clone();
594        let message = host.initialize(message, client, instantiator, self).await?;
595        Ok(message)
596    }
597
598    /// Wrap a proxy component with tracing if tracing is enabled.
599    ///
600    /// Returns the component unchanged if tracing is disabled.
601    fn trace_proxy(
602        &self,
603        proxy_index: ComponentIndex,
604        successor_index: ComponentIndex,
605        component: impl ConnectTo<Conductor>,
606    ) -> DynConnectTo<Conductor> {
607        match &self.trace_handle {
608            Some(trace_handle) => {
609                trace_handle.bridge_component(proxy_index, successor_index, component)
610            }
611            None => DynConnectTo::new(component),
612        }
613    }
614
615    /// Spawn proxy components and add them to the proxies list.
616    fn spawn_proxies(
617        &mut self,
618        client: ConnectionTo<Host::Counterpart>,
619        proxy_components: Vec<DynConnectTo<Conductor>>,
620    ) -> Result<(), agent_client_protocol::Error> {
621        assert!(self.proxies.is_empty());
622
623        let num_proxies = proxy_components.len();
624        info!(proxy_count = num_proxies, "spawn_proxies");
625
626        // Special case: if there are no user-defined proxies
627        // but tracing is enabled, we make a dummy proxy that just
628        // passes through messages but which can trigger the
629        // tracing events.
630        if self.trace_handle.is_some() && num_proxies == 0 {
631            self.connect_to_proxy(
632                &client,
633                0,
634                ComponentIndex::Client,
635                ComponentIndex::Agent,
636                Proxy.builder(),
637            )?;
638        } else {
639            // Spawn each proxy component
640            for (component_index, dyn_component) in proxy_components.into_iter().enumerate() {
641                debug!(component_index, "spawning proxy");
642
643                self.connect_to_proxy(
644                    &client,
645                    component_index,
646                    ComponentIndex::Proxy(component_index),
647                    ComponentIndex::successor_of(component_index, num_proxies),
648                    dyn_component,
649                )?;
650            }
651        }
652
653        info!(proxy_count = self.proxies.len(), "Proxies spawned");
654
655        Ok(())
656    }
657
658    /// Create a connection to the proxy with index `component_index` implemented in `component`.
659    ///
660    /// If tracing is enabled, the proxy's index is `trace_proxy_index` and its successor is `trace_successor_index`.
661    fn connect_to_proxy(
662        &mut self,
663        client: &ConnectionTo<Host::Counterpart>,
664        component_index: usize,
665        trace_proxy_index: ComponentIndex,
666        trace_successor_index: ComponentIndex,
667        component: impl ConnectTo<Conductor>,
668    ) -> Result<(), Error> {
669        let connection_builder = self.connection_to_proxy(component_index);
670        let connect_component =
671            self.trace_proxy(trace_proxy_index, trace_successor_index, component);
672        let proxy_connection = client.spawn_connection(connection_builder, connect_component)?;
673        self.proxies.push(proxy_connection);
674        Ok(())
675    }
676
677    /// Create the conductor's connection to the proxy with index `component_index`.
678    ///
679    /// Outgoing messages received from the proxy are sent to `self.conductor_tx` as either
680    /// left-to-right or right-to-left messages depending on whether they are wrapped
681    /// in `SuccessorMessage`.
682    fn connection_to_proxy(
683        &mut self,
684        component_index: usize,
685    ) -> Builder<Conductor, impl HandleDispatchFrom<Proxy> + 'static> {
686        type SuccessorDispatch = Dispatch<SuccessorMessage, SuccessorMessage>;
687        let mut conductor_tx = self.conductor_tx.clone();
688        Conductor
689            .builder()
690            .name(format!("conductor-to-component({component_index})"))
691            // Intercept messages sent by the proxy.
692            .on_receive_dispatch(
693                async move |dispatch: Dispatch, _connection| {
694                    MatchDispatch::new(dispatch)
695                        .if_dispatch(async |dispatch: SuccessorDispatch| {
696                            //                         ------------------
697                            // SuccessorMessages sent by the proxy go to its successor.
698                            //
699                            // Subtle point:
700                            //
701                            // `ConductorToProxy` has only a single peer, `Agent`. This means that we see
702                            // "successor messages" in their "desugared form". So when we intercept an *outgoing*
703                            // message that matches `SuccessorMessage`, it could be one of three things
704                            //
705                            // - A request being sent by the proxy to its successor (hence going left->right)
706                            // - A notification being sent by the proxy to its successor (hence going left->right)
707                            // - A response to a request sent to the proxy *by* its successor. Here, the *request*
708                            //   was going right->left, but the *response* (the message we are processing now)
709                            //   is going left->right.
710                            //
711                            // So, in all cases, we forward as a left->right message.
712
713                            conductor_tx
714                                .send(ConductorMessage::LeftToRight {
715                                    target_component_index: component_index + 1,
716                                    message: dispatch.map(|r, cx| (r.message, cx), |n| n.message),
717                                })
718                                .await
719                                .map_err(agent_client_protocol::util::internal_error)
720                        })
721                        .await
722                        .otherwise(async |dispatch| {
723                            // Other messagrs send by the proxy go its predecessor.
724                            // As in the previous handler:
725                            //
726                            // Messages here are seen in their "desugared form", so we are seeing
727                            // one of three things
728                            //
729                            // - A request being sent by the proxy to its predecessor (hence going right->left)
730                            // - A notification being sent by the proxy to its predecessor (hence going right->left)
731                            // - A response to a request sent to the proxy *by* its predecessor. Here, the *request*
732                            //   was going left->right, but the *response* (the message we are processing now)
733                            //   is going right->left.
734                            //
735                            // So, in all cases, we forward as a right->left message.
736
737                            let message = ConductorMessage::RightToLeft {
738                                source_component_index: SourceComponentIndex::Proxy(
739                                    component_index,
740                                ),
741                                message: dispatch,
742                            };
743                            conductor_tx
744                                .send(message)
745                                .await
746                                .map_err(agent_client_protocol::util::internal_error)
747                        })
748                        .await
749                },
750                agent_client_protocol::on_receive_dispatch!(),
751            )
752    }
753
754    async fn forward_message_from_client_to_proxy(
755        &mut self,
756        target_component_index: usize,
757        message: Dispatch,
758    ) -> Result<(), agent_client_protocol::Error> {
759        tracing::debug!(?message, "forward_message_to_proxy");
760
761        MatchDispatch::new(message)
762            .if_request(async |_request: InitializeProxyRequest, responder| {
763                responder.respond_with_error(
764                    agent_client_protocol::Error::invalid_request()
765                        .data("initialize/proxy requests are only sent by the conductor"),
766                )
767            })
768            .await
769            .if_request(async |request: InitializeRequest, responder| {
770                // The pattern for `Initialize` messages is a bit subtle.
771                // Proxy receive incoming `Initialize` messages as if they
772                // were a client. The conductor (us) intercepts these and
773                // converts them to an `InitializeProxyRequest`.
774                //
775                // The proxy will then initialize itself and forward an `Initialize`
776                // request to its successor.
777                let sent = self.proxies[target_component_index]
778                    .send_request(InitializeProxyRequest::from(request));
779                // The request is rewritten, so `forward_response_to` cannot be
780                // used here; wire up cancellation forwarding explicitly to
781                // keep `initialize` cancellable like every other forwarded
782                // request.
783                let sent = sent.forward_cancellation_from(responder.cancellation());
784                sent.on_receiving_result(async move |result| {
785                    tracing::debug!(?result, "got initialize_proxy response from proxy");
786                    responder.respond_with_result(result)
787                })
788            })
789            .await
790            .otherwise(async |message| {
791                // Otherwise, just send the message along "as is".
792                self.proxies[target_component_index].send_proxied_message(message)
793            })
794            .await
795    }
796
797    /// Invoked when sending a message from the conductor to the agent that it manages.
798    /// This is called by `self.successor`'s [`ConductorSuccessor::send_message`]
799    /// method when `Link = ConductorToClient` (i.e., the conductor is not itself
800    /// running as a proxy).
801    async fn forward_message_to_agent(
802        &mut self,
803        _client_connection: ConnectionTo<Host::Counterpart>,
804        message: Dispatch,
805        agent_connection: ConnectionTo<Agent>,
806    ) -> Result<(), Error> {
807        MatchDispatch::new(message)
808            .if_request(async |_request: InitializeProxyRequest, responder| {
809                responder.respond_with_error(
810                    agent_client_protocol::Error::invalid_request()
811                        .data("initialize/proxy requests are only sent by the conductor"),
812                )
813            })
814            .await
815            .otherwise(async |message| {
816                // Forward all other messages to the agent as-is.
817                agent_connection.send_proxied_message_to(Agent, message)
818            })
819            .await
820    }
821}
822
823/// Identifies a component in the conductor's chain for tracing purposes.
824///
825/// Used to track message sources and destinations through the proxy chain.
826#[derive(Debug, Clone, Copy)]
827pub enum ComponentIndex {
828    /// The client (editor) at the start of the chain.
829    Client,
830
831    /// A proxy component at the given index.
832    Proxy(usize),
833
834    /// The successor (agent in agent mode, outer conductor in proxy mode).
835    Agent,
836}
837
838impl ComponentIndex {
839    /// Return the index for the predecessor of `proxy_index`, which might be `Client`.
840    #[must_use]
841    pub fn predecessor_of(proxy_index: usize) -> Self {
842        match proxy_index.checked_sub(1) {
843            Some(p_i) => ComponentIndex::Proxy(p_i),
844            None => ComponentIndex::Client,
845        }
846    }
847
848    /// Return the index for the predecessor of `proxy_index`, which might be `Client`.
849    #[must_use]
850    pub fn successor_of(proxy_index: usize, num_proxies: usize) -> Self {
851        if proxy_index == num_proxies {
852            ComponentIndex::Agent
853        } else {
854            ComponentIndex::Proxy(proxy_index + 1)
855        }
856    }
857}
858
859/// Identifies the source of an agent-to-client message.
860///
861/// This enum handles the fact that the conductor may receive messages from two different sources:
862/// 1. From one of its managed components (identified by index)
863/// 2. From the conductor's own successor in a larger proxy chain (when in proxy mode)
864#[derive(Debug, Clone, Copy)]
865pub enum SourceComponentIndex {
866    /// Message from a specific component at the given index in the managed chain.
867    Proxy(usize),
868
869    /// Message from the conductor's agent or successor.
870    Successor,
871}
872
873/// Trait for lazy proxy instantiation (proxy mode).
874///
875/// Used by conductors in proxy mode (`ConductorToConductor`) where all components
876/// are proxies that forward to an outer conductor.
877pub trait InstantiateProxies: Send {
878    /// Instantiate proxy components based on the Initialize request.
879    ///
880    /// Returns proxy components typed as `DynConnectTo<Conductor>` since proxies
881    /// communicate with the conductor.
882    fn instantiate_proxies(
883        self: Box<Self>,
884        req: InitializeRequest,
885    ) -> futures::future::BoxFuture<
886        'static,
887        Result<(InitializeRequest, Vec<DynConnectTo<Conductor>>), agent_client_protocol::Error>,
888    >;
889}
890
891/// Simple implementation: provide all proxy components unconditionally.
892///
893/// Requires `T: ConnectTo<Conductor>`.
894impl<T> InstantiateProxies for Vec<T>
895where
896    T: ConnectTo<Conductor> + 'static,
897{
898    fn instantiate_proxies(
899        self: Box<Self>,
900        req: InitializeRequest,
901    ) -> futures::future::BoxFuture<
902        'static,
903        Result<(InitializeRequest, Vec<DynConnectTo<Conductor>>), agent_client_protocol::Error>,
904    > {
905        Box::pin(async move {
906            let components: Vec<DynConnectTo<Conductor>> =
907                (*self).into_iter().map(|c| DynConnectTo::new(c)).collect();
908            Ok((req, components))
909        })
910    }
911}
912
913/// Dynamic implementation: closure receives the Initialize request and returns proxies.
914impl<F, Fut> InstantiateProxies for F
915where
916    F: FnOnce(InitializeRequest) -> Fut + Send + 'static,
917    Fut: std::future::Future<
918            Output = Result<
919                (InitializeRequest, Vec<DynConnectTo<Conductor>>),
920                agent_client_protocol::Error,
921            >,
922        > + Send
923        + 'static,
924{
925    fn instantiate_proxies(
926        self: Box<Self>,
927        req: InitializeRequest,
928    ) -> futures::future::BoxFuture<
929        'static,
930        Result<(InitializeRequest, Vec<DynConnectTo<Conductor>>), agent_client_protocol::Error>,
931    > {
932        Box::pin(async move { (*self)(req).await })
933    }
934}
935
936/// Trait for lazy proxy and agent instantiation (agent mode).
937///
938/// Used by conductors in agent mode (`ConductorToClient`) where there are
939/// zero or more proxies followed by an agent component.
940pub trait InstantiateProxiesAndAgent: Send {
941    /// Instantiate proxy and agent components based on the Initialize request.
942    ///
943    /// Returns the (possibly modified) request, a vector of proxy components
944    /// (typed as `DynConnectTo<Conductor>`), and the agent component
945    /// (typed as `DynConnectTo<Client>`).
946    fn instantiate_proxies_and_agent(
947        self: Box<Self>,
948        req: InitializeRequest,
949    ) -> futures::future::BoxFuture<
950        'static,
951        Result<
952            (
953                InitializeRequest,
954                Vec<DynConnectTo<Conductor>>,
955                DynConnectTo<Client>,
956            ),
957            agent_client_protocol::Error,
958        >,
959    >;
960}
961
962/// Wrapper to convert a single agent component (no proxies) into InstantiateProxiesAndAgent.
963#[derive(Debug)]
964pub struct AgentOnly<A>(pub A);
965
966impl<A: ConnectTo<Client> + 'static> InstantiateProxiesAndAgent for AgentOnly<A> {
967    fn instantiate_proxies_and_agent(
968        self: Box<Self>,
969        req: InitializeRequest,
970    ) -> futures::future::BoxFuture<
971        'static,
972        Result<
973            (
974                InitializeRequest,
975                Vec<DynConnectTo<Conductor>>,
976                DynConnectTo<Client>,
977            ),
978            agent_client_protocol::Error,
979        >,
980    > {
981        Box::pin(async move { Ok((req, Vec::new(), DynConnectTo::new(self.0))) })
982    }
983}
984
985/// Builder for creating proxies and agent components.
986///
987/// # Example
988/// ```ignore
989/// ProxiesAndAgent::new(ElizaAgent::new())
990///     .proxy(LoggingProxy::new())
991///     .proxy(AuthProxy::new())
992/// ```
993#[derive(Debug)]
994pub struct ProxiesAndAgent {
995    proxies: Vec<DynConnectTo<Conductor>>,
996    agent: DynConnectTo<Client>,
997}
998
999impl ProxiesAndAgent {
1000    /// Create a new builder with the given agent component.
1001    pub fn new(agent: impl ConnectTo<Client> + 'static) -> Self {
1002        Self {
1003            proxies: vec![],
1004            agent: DynConnectTo::new(agent),
1005        }
1006    }
1007
1008    /// Add a single proxy component.
1009    #[must_use]
1010    pub fn proxy(mut self, proxy: impl ConnectTo<Conductor> + 'static) -> Self {
1011        self.proxies.push(DynConnectTo::new(proxy));
1012        self
1013    }
1014
1015    /// Add multiple proxy components.
1016    #[must_use]
1017    pub fn proxies<P, I>(mut self, proxies: I) -> Self
1018    where
1019        P: ConnectTo<Conductor> + 'static,
1020        I: IntoIterator<Item = P>,
1021    {
1022        self.proxies
1023            .extend(proxies.into_iter().map(DynConnectTo::new));
1024        self
1025    }
1026}
1027
1028impl InstantiateProxiesAndAgent for ProxiesAndAgent {
1029    fn instantiate_proxies_and_agent(
1030        self: Box<Self>,
1031        req: InitializeRequest,
1032    ) -> futures::future::BoxFuture<
1033        'static,
1034        Result<
1035            (
1036                InitializeRequest,
1037                Vec<DynConnectTo<Conductor>>,
1038                DynConnectTo<Client>,
1039            ),
1040            agent_client_protocol::Error,
1041        >,
1042    > {
1043        Box::pin(async move { Ok((req, self.proxies, self.agent)) })
1044    }
1045}
1046
1047/// Dynamic implementation: closure receives the Initialize request and returns proxies + agent.
1048impl<F, Fut> InstantiateProxiesAndAgent for F
1049where
1050    F: FnOnce(InitializeRequest) -> Fut + Send + 'static,
1051    Fut: std::future::Future<
1052            Output = Result<
1053                (
1054                    InitializeRequest,
1055                    Vec<DynConnectTo<Conductor>>,
1056                    DynConnectTo<Client>,
1057                ),
1058                agent_client_protocol::Error,
1059            >,
1060        > + Send
1061        + 'static,
1062{
1063    fn instantiate_proxies_and_agent(
1064        self: Box<Self>,
1065        req: InitializeRequest,
1066    ) -> futures::future::BoxFuture<
1067        'static,
1068        Result<
1069            (
1070                InitializeRequest,
1071                Vec<DynConnectTo<Conductor>>,
1072                DynConnectTo<Client>,
1073            ),
1074            agent_client_protocol::Error,
1075        >,
1076    > {
1077        Box::pin(async move { (*self)(req).await })
1078    }
1079}
1080
1081/// Messages sent to the conductor's main event loop for routing.
1082///
1083/// These messages enable the conductor to route communication between:
1084/// - The editor and the first component
1085/// - Components and their successors in the chain
1086/// - Components and their clients (editor or predecessor)
1087///
1088/// All spawned tasks send messages via this enum through a shared channel,
1089/// allowing centralized routing logic in the conductor runner's event loop.
1090#[derive(Debug)]
1091pub enum ConductorMessage {
1092    /// If this message is a request or notification, then it is going "left-to-right"
1093    /// (e.g., a component making a request of its successor).
1094    ///
1095    /// If this message is a response, then it is going right-to-left
1096    /// (i.e., the successor answering a request made by its predecessor).
1097    LeftToRight {
1098        target_component_index: usize,
1099        message: Dispatch,
1100    },
1101
1102    /// If this message is a request or notification, then it is going "right-to-left"
1103    /// (e.g., a component making a request of its predecessor).
1104    ///
1105    /// If this message is a response, then it is going "left-to-right"
1106    /// (i.e., the predecessor answering a request made by its successor).
1107    RightToLeft {
1108        source_component_index: SourceComponentIndex,
1109        message: Dispatch,
1110    },
1111}
1112
1113/// Trait implemented for the two links the conductor can use:
1114///
1115/// * ConductorToClient -- conductor is acting as an agent, so when its last proxy sends to its successor, the conductor sends that message to its agent component
1116/// * ConductorToConductor -- conductor is acting as a proxy, so when its last proxy sends to its successor, the (inner) conductor sends that message to its successor, via the outer conductor
1117pub trait ConductorHostRole: Role<Counterpart: HasPeer<Client>> {
1118    /// The type used to instantiate components for this link type.
1119    type Instantiator: Send;
1120
1121    /// Handle initialization: parse the init request, instantiate components, and spawn them.
1122    ///
1123    /// Takes ownership of the instantiator and returns the (possibly modified) init request
1124    /// wrapped in a Dispatch for forwarding.
1125    fn initialize(
1126        &self,
1127        message: Dispatch,
1128        connection: ConnectionTo<Self::Counterpart>,
1129        instantiator: Self::Instantiator,
1130        runner: &mut ConductorRunner<Self>,
1131    ) -> impl Future<Output = Result<Dispatch, agent_client_protocol::Error>> + Send;
1132
1133    /// Handle an incoming message from the client or conductor, depending on `Self`
1134    fn handle_dispatch(
1135        &self,
1136        message: Dispatch,
1137        connection: ConnectionTo<Self::Counterpart>,
1138        conductor_tx: &mut mpsc::Sender<ConductorMessage>,
1139    ) -> impl Future<Output = Result<Handled<Dispatch>, agent_client_protocol::Error>> + Send;
1140}
1141
1142/// Conductor acting as an agent
1143impl ConductorHostRole for Agent {
1144    type Instantiator = Box<dyn InstantiateProxiesAndAgent>;
1145
1146    async fn initialize(
1147        &self,
1148        message: Dispatch,
1149        client_connection: ConnectionTo<Client>,
1150        instantiator: Self::Instantiator,
1151        runner: &mut ConductorRunner<Self>,
1152    ) -> Result<Dispatch, agent_client_protocol::Error> {
1153        let invalid_request = || Error::invalid_request().data("expected `initialize` request");
1154
1155        // Not yet initialized - expect an initialize request.
1156        // Error if we get anything else.
1157        let Dispatch::Request(request, init_responder) = message else {
1158            if let Dispatch::Response(_, router) = message {
1159                router.route_with_error(invalid_request())?;
1160            }
1161            return Err(invalid_request());
1162        };
1163        if !InitializeRequest::matches_method(request.method()) {
1164            init_responder.respond_with_error(invalid_request())?;
1165            return Err(invalid_request());
1166        }
1167
1168        let init_request =
1169            match InitializeRequest::parse_message(request.method(), request.params()) {
1170                Ok(r) => r,
1171                Err(error) => {
1172                    init_responder.respond_with_error(error)?;
1173                    return Err(invalid_request());
1174                }
1175            };
1176
1177        // Instantiate proxies and agent
1178        let (modified_req, proxy_components, agent_component) = instantiator
1179            .instantiate_proxies_and_agent(init_request)
1180            .await?;
1181
1182        // Spawn the agent component
1183        debug!(?agent_component, "spawning agent");
1184
1185        let connection_to_agent = client_connection.spawn_connection(
1186            Client
1187                .builder()
1188                .name("conductor-to-agent")
1189                // Intercept agent-to-client messages from the agent.
1190                .on_receive_dispatch(
1191                    {
1192                        let mut conductor_tx = runner.conductor_tx.clone();
1193                        async move |dispatch: Dispatch, _cx| {
1194                            conductor_tx
1195                                .send(ConductorMessage::RightToLeft {
1196                                    source_component_index: SourceComponentIndex::Successor,
1197                                    message: dispatch,
1198                                })
1199                                .await
1200                                .map_err(agent_client_protocol::util::internal_error)
1201                        }
1202                    },
1203                    agent_client_protocol::on_receive_dispatch!(),
1204                ),
1205            agent_component,
1206        )?;
1207        runner.successor = Arc::new(connection_to_agent);
1208
1209        // Spawn the proxy components
1210        runner.spawn_proxies(client_connection.clone(), proxy_components)?;
1211
1212        Ok(Dispatch::Request(
1213            modified_req.to_untyped_message()?,
1214            init_responder,
1215        ))
1216    }
1217
1218    async fn handle_dispatch(
1219        &self,
1220        message: Dispatch,
1221        client_connection: ConnectionTo<Client>,
1222        conductor_tx: &mut mpsc::Sender<ConductorMessage>,
1223    ) -> Result<Handled<Dispatch>, agent_client_protocol::Error> {
1224        tracing::debug!(
1225            method = ?message.method(),
1226            "ConductorToClient::handle_dispatch"
1227        );
1228        MatchDispatchFrom::new(message, &client_connection)
1229            // Any incoming messages from the client are client-to-agent messages targeting the first component.
1230            .if_dispatch_from(Client, async move |message: Dispatch| {
1231                tracing::debug!(
1232                    method = ?message.method(),
1233                    "ConductorToClient::handle_dispatch - matched Client"
1234                );
1235                ConductorImpl::<Self>::incoming_message_from_client(conductor_tx, message).await
1236            })
1237            .await
1238            .done()
1239    }
1240}
1241
1242/// Conductor acting as a proxy
1243impl ConductorHostRole for Proxy {
1244    type Instantiator = Box<dyn InstantiateProxies>;
1245
1246    async fn initialize(
1247        &self,
1248        message: Dispatch,
1249        client_connection: ConnectionTo<Conductor>,
1250        instantiator: Self::Instantiator,
1251        runner: &mut ConductorRunner<Self>,
1252    ) -> Result<Dispatch, agent_client_protocol::Error> {
1253        let invalid_request = || Error::invalid_request().data("expected `initialize` request");
1254
1255        // Not yet initialized - expect an InitializeProxy request.
1256        // Error if we get anything else.
1257        let Dispatch::Request(request, init_responder) = message else {
1258            if let Dispatch::Response(_, router) = message {
1259                router.route_with_error(invalid_request())?;
1260            }
1261            return Err(invalid_request());
1262        };
1263        if !InitializeProxyRequest::matches_method(request.method()) {
1264            init_responder.respond_with_error(invalid_request())?;
1265            return Err(invalid_request());
1266        }
1267
1268        let InitializeProxyRequest { initialize } =
1269            match InitializeProxyRequest::parse_message(request.method(), request.params()) {
1270                Ok(r) => r,
1271                Err(error) => {
1272                    init_responder.respond_with_error(error)?;
1273                    return Err(invalid_request());
1274                }
1275            };
1276
1277        tracing::debug!("ensure_initialized: InitializeProxyRequest (proxy mode)");
1278
1279        // Instantiate proxies (no agent in proxy mode)
1280        let (modified_req, proxy_components) = instantiator.instantiate_proxies(initialize).await?;
1281
1282        // In proxy mode, our successor is the outer conductor (via our client connection)
1283        runner.successor = Arc::new(GrandSuccessor);
1284
1285        // Spawn the proxy components
1286        runner.spawn_proxies(client_connection.clone(), proxy_components)?;
1287
1288        Ok(Dispatch::Request(
1289            modified_req.to_untyped_message()?,
1290            init_responder,
1291        ))
1292    }
1293
1294    async fn handle_dispatch(
1295        &self,
1296        message: Dispatch,
1297        client_connection: ConnectionTo<Conductor>,
1298        conductor_tx: &mut mpsc::Sender<ConductorMessage>,
1299    ) -> Result<Handled<Dispatch>, agent_client_protocol::Error> {
1300        tracing::debug!(
1301            method = ?message.method(),
1302            ?message,
1303            "ConductorToConductor::handle_dispatch"
1304        );
1305        MatchDispatchFrom::new(message, &client_connection)
1306            .if_dispatch_from(Agent, {
1307                // Messages from our successor arrive already unwrapped
1308                // (RemoteRoleStyle::Successor strips the SuccessorMessage envelope).
1309                async |message: Dispatch| {
1310                    tracing::debug!(
1311                        method = ?message.method(),
1312                        "ConductorToConductor::handle_dispatch - matched Agent"
1313                    );
1314                    let mut conductor_tx = conductor_tx.clone();
1315                    ConductorImpl::<Self>::incoming_message_from_agent(&mut conductor_tx, message)
1316                        .await
1317                }
1318            })
1319            .await
1320            // Any incoming messages from the client are client-to-agent messages targeting the first component.
1321            .if_dispatch_from(Client, async |message: Dispatch| {
1322                tracing::debug!(
1323                    method = ?message.method(),
1324                    "ConductorToConductor::handle_dispatch - matched Client"
1325                );
1326                let mut conductor_tx = conductor_tx.clone();
1327                ConductorImpl::<Self>::incoming_message_from_client(&mut conductor_tx, message)
1328                    .await
1329            })
1330            .await
1331            .done()
1332    }
1333}
1334
1335pub trait ConductorSuccessor<Host: ConductorHostRole>: Send + Sync + 'static {
1336    /// Send a message to the successor.
1337    fn send_message<'a>(
1338        &self,
1339        message: Dispatch,
1340        connection_to_conductor: ConnectionTo<Host::Counterpart>,
1341        runner: &'a mut ConductorRunner<Host>,
1342    ) -> BoxFuture<'a, Result<(), agent_client_protocol::Error>>;
1343}
1344
1345impl<Host: ConductorHostRole> ConductorSuccessor<Host> for agent_client_protocol::Error {
1346    fn send_message<'a>(
1347        &self,
1348        _message: Dispatch,
1349        _connection_to_conductor: ConnectionTo<Host::Counterpart>,
1350        _runner: &'a mut ConductorRunner<Host>,
1351    ) -> BoxFuture<'a, Result<(), agent_client_protocol::Error>> {
1352        let error = self.clone();
1353        Box::pin(std::future::ready(Err(error)))
1354    }
1355}
1356
1357/// A dummy type handling messages sent to the conductor's
1358/// successor when it is acting as a proxy.
1359struct GrandSuccessor;
1360
1361/// When the conductor is acting as an proxy, messages sent by
1362/// the last proxy go to the conductor's successor.
1363///
1364/// ```text
1365/// client --> Conductor -----------------------------> GrandSuccessor
1366///            |                                  |
1367///            +-> Proxy[0] -> ... -> Proxy[n-1] -+
1368/// ```
1369impl ConductorSuccessor<Proxy> for GrandSuccessor {
1370    fn send_message<'a>(
1371        &self,
1372        message: Dispatch,
1373        connection: ConnectionTo<Conductor>,
1374        _runner: &'a mut ConductorRunner<Proxy>,
1375    ) -> BoxFuture<'a, Result<(), agent_client_protocol::Error>> {
1376        Box::pin(async move {
1377            debug!("Proxy mode: forwarding successor message to conductor's successor");
1378            connection.send_proxied_message_to(Agent, message)
1379        })
1380    }
1381}
1382
1383/// When the conductor is acting as an agent, messages sent by
1384/// the last proxy to its successor go to the internal agent
1385/// (`self`).
1386impl ConductorSuccessor<Agent> for ConnectionTo<Agent> {
1387    fn send_message<'a>(
1388        &self,
1389        message: Dispatch,
1390        connection: ConnectionTo<Client>,
1391        runner: &'a mut ConductorRunner<Agent>,
1392    ) -> BoxFuture<'a, Result<(), agent_client_protocol::Error>> {
1393        let connection_to_agent = self.clone();
1394        Box::pin(async move {
1395            debug!("Proxy mode: forwarding successor message to conductor's successor");
1396            runner
1397                .forward_message_to_agent(connection, message, connection_to_agent)
1398                .await
1399        })
1400    }
1401}