@@ -116,7 +116,7 @@ use futures::{
116116 SinkExt , StreamExt ,
117117 channel:: mpsc:: { self } ,
118118} ;
119- use sacp:: role:: { ConductorToAgent , ConductorToClient , ConductorToProxy } ;
119+ use sacp:: { ChainResponder , JrResponder , NullResponder , role:: { ConductorToAgent , ConductorToClient , ConductorToProxy } } ;
120120use sacp:: schema:: {
121121 McpConnectRequest , McpConnectResponse , McpDisconnectNotification , McpOverAcpMessage ,
122122 SuccessorMessage ,
@@ -193,11 +193,13 @@ impl Conductor {
193193
194194 pub fn into_connection_builder (
195195 self ,
196- ) -> JrConnectionBuilder < ConductorMessageHandler , impl sacp :: JrResponder < ConductorToClient > >
196+ ) -> JrConnectionBuilder < ConductorMessageHandler , ChainResponder < NullResponder , ConductorResponder > >
197197 {
198- let ( mut conductor_tx, mut conductor_rx) = mpsc:: channel ( 128 /* chosen arbitrarily */ ) ;
198+ let ( conductor_tx, conductor_rx) = mpsc:: channel ( 128 /* chosen arbitrarily */ ) ;
199199
200- let mut state = ConductorHandlerState {
200+ let responder = ConductorResponder {
201+ conductor_rx,
202+ conductor_tx : conductor_tx. clone ( ) ,
201203 component_list : Some ( self . component_list ) ,
202204 bridge_listeners : Default :: default ( ) ,
203205 bridge_connections : Default :: default ( ) ,
@@ -209,22 +211,10 @@ impl Conductor {
209211 } ;
210212
211213 JrConnectionBuilder :: new_with ( ConductorMessageHandler {
212- conductor_tx : conductor_tx . clone ( ) ,
214+ conductor_tx,
213215 } )
214216 . name ( self . name )
215- . with_spawned ( async move |cx| {
216- // Components are now spawned lazily in forward_initialize_request
217- // when the first Initialize request is received.
218-
219- // This is the "central actor" of the conductor. Most other things forward messages
220- // via `conductor_tx` into this loop. This lets us serialize the conductor's activity.
221- while let Some ( message) = conductor_rx. next ( ) . await {
222- state
223- . handle_conductor_message ( & cx, message, & mut conductor_tx)
224- . await ?;
225- }
226- Ok ( ( ) )
227- } )
217+ . with_responder ( responder)
228218 }
229219
230220 /// Convenience method to run the conductor with a transport.
@@ -254,42 +244,6 @@ pub struct ConductorMessageHandler {
254244 conductor_tx : mpsc:: Sender < ConductorMessage > ,
255245}
256246
257- /// The conductor manages the proxy chain lifecycle and message routing.
258- ///
259- /// It maintains connections to all components in the chain and routes messages
260- /// bidirectionally between the editor, components, and agent.
261- ///
262- struct ConductorHandlerState {
263- /// Manages the TCP listeners for MCP connections that will be proxied over ACP.
264- bridge_listeners : McpBridgeListeners ,
265-
266- /// Manages active connections to MCP clients.
267- bridge_connections : HashMap < String , McpBridgeConnection > ,
268-
269- /// The component list for lazy initialization.
270- /// Set to None after components are instantiated.
271- component_list : Option < Box < dyn ComponentList > > ,
272-
273- /// The chain of proxies before the agent (if any).
274- ///
275- /// Populated lazily when the first Initialize request is received.
276- proxies : Vec < JrConnectionCx < ConductorToProxy > > ,
277-
278- /// If the conductor is operating in agent mode, this will be the agent.
279- /// If the conductor is operating in proxy mode, this will be None.
280- ///
281- /// Populated lazily when the first Initialize request is received.
282- agent : Option < JrConnectionCx < ConductorToAgent > > ,
283-
284- /// Mode for the MCP bridge (determines how to spawn bridge processes).
285- mcp_bridge_mode : crate :: McpBridgeMode ,
286-
287- /// Optional trace writer for sequence diagram visualization.
288- trace_writer : Option < crate :: trace:: TraceWriter > ,
289-
290- /// Tracks pending requests for response tracing: id -> (from, to)
291- pending_requests : HashMap < String , ( String , String ) > ,
292- }
293247
294248impl JrMessageHandler for ConductorMessageHandler {
295249 type Role = ConductorToClient ;
@@ -336,7 +290,66 @@ impl JrMessageHandler for ConductorMessageHandler {
336290 }
337291}
338292
339- impl ConductorHandlerState {
293+ /// The conductor manages the proxy chain lifecycle and message routing.
294+ ///
295+ /// It maintains connections to all components in the chain and routes messages
296+ /// bidirectionally between the editor, components, and agent.
297+ ///
298+ pub struct ConductorResponder {
299+ conductor_rx : mpsc:: Receiver < ConductorMessage > ,
300+
301+ conductor_tx : mpsc:: Sender < ConductorMessage > ,
302+
303+ /// Manages the TCP listeners for MCP connections that will be proxied over ACP.
304+ bridge_listeners : McpBridgeListeners ,
305+
306+ /// Manages active connections to MCP clients.
307+ bridge_connections : HashMap < String , McpBridgeConnection > ,
308+
309+ /// The component list for lazy initialization.
310+ /// Set to None after components are instantiated.
311+ component_list : Option < Box < dyn ComponentList > > ,
312+
313+ /// The chain of proxies before the agent (if any).
314+ ///
315+ /// Populated lazily when the first Initialize request is received.
316+ proxies : Vec < JrConnectionCx < ConductorToProxy > > ,
317+
318+ /// If the conductor is operating in agent mode, this will be the agent.
319+ /// If the conductor is operating in proxy mode, this will be None.
320+ ///
321+ /// Populated lazily when the first Initialize request is received.
322+ agent : Option < JrConnectionCx < ConductorToAgent > > ,
323+
324+ /// Mode for the MCP bridge (determines how to spawn bridge processes).
325+ mcp_bridge_mode : crate :: McpBridgeMode ,
326+
327+ /// Optional trace writer for sequence diagram visualization.
328+ trace_writer : Option < crate :: trace:: TraceWriter > ,
329+
330+ /// Tracks pending requests for response tracing: id -> (from, to)
331+ pending_requests : HashMap < String , ( String , String ) > ,
332+ }
333+
334+ impl JrResponder < ConductorToClient > for ConductorResponder {
335+ async fn run ( mut self , cx : JrConnectionCx < ConductorToClient > ) -> Result < ( ) , sacp:: Error > {
336+ // Components are now spawned lazily in forward_initialize_request
337+ // when the first Initialize request is received.
338+
339+ let mut conductor_tx = self . conductor_tx . clone ( ) ;
340+
341+ // This is the "central actor" of the conductor. Most other things forward messages
342+ // via `conductor_tx` into this loop. This lets us serialize the conductor's activity.
343+ while let Some ( message) = self . conductor_rx . next ( ) . await {
344+ self
345+ . handle_conductor_message ( & cx, message, & mut conductor_tx)
346+ . await ?;
347+ }
348+ Ok ( ( ) )
349+ }
350+ }
351+
352+ impl ConductorResponder {
340353 /// Convert a component index to a trace-friendly name.
341354 fn component_name ( & self , index : usize ) -> String {
342355 if self . is_agent_component ( index) {
0 commit comments