agent_core/agent/router.rs
1// Message router for TUI-Controller communication
2//
3// InputRouter reads user input from the TUI and forwards it to the controller.
4// The controller-to-TUI direction is handled by the event callback in the agent.
5
6use std::sync::Arc;
7
8use crate::controller::LLMController;
9use tokio_util::sync::CancellationToken;
10
11use super::messages::channels::ToControllerRx;
12
13/// Routes messages from the TUI to the controller.
14///
15/// This component runs as an async task and reads from the `from_tui` channel,
16/// forwarding each message to the controller via `send_input()`.
17pub struct InputRouter {
18 controller: Arc<LLMController>,
19 from_tui: ToControllerRx,
20 cancel_token: CancellationToken,
21}
22
23impl InputRouter {
24 /// Creates a new input router.
25 ///
26 /// # Arguments
27 /// * `controller` - The LLM controller to forward messages to
28 /// * `from_tui` - Channel receiver for messages from the TUI
29 /// * `cancel_token` - Token for graceful shutdown
30 pub fn new(
31 controller: Arc<LLMController>,
32 from_tui: ToControllerRx,
33 cancel_token: CancellationToken,
34 ) -> Self {
35 Self {
36 controller,
37 from_tui,
38 cancel_token,
39 }
40 }
41
42 /// Runs the input router event loop.
43 ///
44 /// This method will run until either:
45 /// - The cancel token is triggered
46 /// - The `from_tui` channel is closed
47 pub async fn run(mut self) {
48 tracing::info!("InputRouter started");
49
50 loop {
51 tokio::select! {
52 _ = self.cancel_token.cancelled() => {
53 tracing::info!("InputRouter cancelled");
54 break;
55 }
56 msg = self.from_tui.recv() => {
57 match msg {
58 Some(payload) => {
59 if let Err(e) = self.controller.send_input(payload).await {
60 tracing::error!("Failed to send input to controller: {}", e);
61 }
62 }
63 None => {
64 tracing::info!("TUI channel closed");
65 break;
66 }
67 }
68 }
69 }
70 }
71
72 tracing::info!("InputRouter stopped");
73 }
74}