11use std:: io:: Write as _;
22use std:: marker:: PhantomData ;
33
4- use crossterm:: execute;
5- use crossterm:: style:: Print ;
4+ use crossterm:: style:: {
5+ self ,
6+ Print ,
7+ Stylize ,
8+ } ;
9+ use crossterm:: {
10+ execute,
11+ queue,
12+ } ;
613
14+ use crate :: legacy_ui_util:: ThemeSource ;
715use crate :: protocol:: {
816 Event ,
917 LegacyPassThroughOutput ,
18+ ToolCallStart ,
1019} ;
1120
21+ const TOOL_BULLET : & str = " ● " ;
22+ const CONTINUATION_LINE : & str = " ⋮ " ;
23+ const PURPOSE_ARROW : & str = " ↳ " ;
24+ const SUCCESS_TICK : & str = " ✓ " ;
25+ const ERROR_EXCLAMATION : & str = " ❗ " ;
26+ const DELEGATE_NOTIFIER : & str = "[BACKGROUND TASK READY]" ;
27+
1228#[ derive( thiserror:: Error , Debug ) ]
1329pub enum ConduitError {
1430 #[ error( transparent) ]
@@ -39,6 +55,7 @@ impl ViewEnd {
3955 /// This blocks the current thread and consumes the [ViewEnd]
4056 pub fn into_legacy_mode (
4157 self ,
58+ theme_source : impl ThemeSource ,
4259 mut stderr : std:: io:: Stderr ,
4360 mut stdout : std:: io:: Stdout ,
4461 ) -> Result < ( ) , ConduitError > {
@@ -140,50 +157,59 @@ impl ViewEnd {
140157 ) ;
141158 } ,
142159 Event :: ToolCallStart ( tool_call_start) => {
143- let parent_info = tool_call_start
144- . parent_message_id
145- . as_ref ( )
146- . map ( |p| format ! ( " (Parent: {})" , p) )
147- . unwrap_or_default ( ) ;
148- let _ = execute ! (
149- stderr,
160+ let ToolCallStart {
161+ tool_call_name,
162+ is_trusted,
163+ mcp_server_name,
164+ ..
165+ } = tool_call_start;
166+
167+ queue ! (
168+ stdout,
169+ theme_source. emphasis_fg( ) ,
150170 Print ( format!(
151- "Tool call started - ID: {}, Tool: {}{}\n " ,
152- tool_call_start. tool_call_id, tool_call_start. tool_call_name, parent_info
153- ) )
154- ) ;
171+ "🛠️ Using tool: {}{}" ,
172+ tool_call_name,
173+ if is_trusted {
174+ " (trusted)" . dark_green( )
175+ } else {
176+ "" . reset( )
177+ }
178+ ) ) ,
179+ theme_source. reset( ) ,
180+ ) ?;
181+
182+ if let Some ( server_name) = mcp_server_name {
183+ queue ! (
184+ stdout,
185+ theme_source. reset( ) ,
186+ Print ( " from mcp server " ) ,
187+ theme_source. emphasis_fg( ) ,
188+ Print ( & server_name) ,
189+ theme_source. reset( ) ,
190+ ) ?;
191+ }
192+
193+ execute ! (
194+ stdout,
195+ Print ( "\n " ) ,
196+ Print ( CONTINUATION_LINE ) ,
197+ Print ( "\n " ) ,
198+ Print ( TOOL_BULLET )
199+ ) ?;
155200 } ,
156201 Event :: ToolCallArgs ( tool_call_args) => {
157- let _ = execute ! (
158- stderr,
159- Print ( format!(
160- "Tool call args ({}): {}\n " ,
161- tool_call_args. tool_call_id, tool_call_args. delta
162- ) )
163- ) ;
202+ if let serde_json:: Value :: String ( content) = tool_call_args. delta {
203+ execute ! ( stdout, style:: Print ( content) ) ?;
204+ } else {
205+ execute ! ( stdout, style:: Print ( tool_call_args. delta) ) ?;
206+ }
164207 } ,
165- Event :: ToolCallEnd ( tool_call_end) => {
166- let _ = execute ! (
167- stderr,
168- Print ( format!( "Tool call ended - ID: {}\n " , tool_call_end. tool_call_id) )
169- ) ;
208+ Event :: ToolCallEnd ( _tool_call_end) => {
209+ // noop for now
170210 } ,
171- Event :: ToolCallResult ( tool_call_result) => {
172- let role_info = tool_call_result
173- . role
174- . as_ref ( )
175- . map ( |r| format ! ( " Role: {:?}" , r) )
176- . unwrap_or_default ( ) ;
177- let _ = execute ! (
178- stderr,
179- Print ( format!(
180- "Tool call result - Message: {}, Tool: {}{}\n Content: {}\n " ,
181- tool_call_result. message_id,
182- tool_call_result. tool_call_id,
183- role_info,
184- tool_call_result. content
185- ) )
186- ) ;
211+ Event :: ToolCallResult ( _tool_call_result) => {
212+ // noop for now (currently we don't show the tool call results to users)
187213 } ,
188214 Event :: StateSnapshot ( state_snapshot) => {
189215 let _ = execute ! (
@@ -307,6 +333,7 @@ impl ViewEnd {
307333 ) )
308334 ) ;
309335 } ,
336+ Event :: ToolCallRejection ( tool_call_rejection) => todo ! ( ) ,
310337 }
311338 }
312339
@@ -330,6 +357,10 @@ pub struct ControlEnd<T> {
330357 pub current_event : Option < Event > ,
331358 /// Used by the control to send state changes to the view
332359 pub sender : std:: sync:: mpsc:: Sender < Event > ,
360+ /// Flag indicating whether structured events should be sent through the conduit.
361+ /// When true, the control end will send structured event data in addition to
362+ /// raw pass-through content, enabling richer communication between layers.
363+ pub should_send_structured_event : bool ,
333364 /// Phantom data to specify the destination type for pass-through operations.
334365 /// This allows the type system to track whether this ControlEnd is configured
335366 /// for stdout or stderr output without runtime overhead.
@@ -341,6 +372,7 @@ impl<T> Clone for ControlEnd<T> {
341372 Self {
342373 current_event : self . current_event . clone ( ) ,
343374 sender : self . sender . clone ( ) ,
375+ should_send_structured_event : self . should_send_structured_event ,
344376 pass_through_destination : PhantomData ,
345377 }
346378 }
@@ -365,6 +397,7 @@ impl ControlEnd<DestinationStderr> {
365397 pub fn as_stdout ( & self ) -> ControlEnd < DestinationStdout > {
366398 ControlEnd {
367399 current_event : self . current_event . clone ( ) ,
400+ should_send_structured_event : self . should_send_structured_event ,
368401 sender : self . sender . clone ( ) ,
369402 pass_through_destination : PhantomData ,
370403 }
@@ -375,6 +408,7 @@ impl ControlEnd<DestinationStdout> {
375408 pub fn as_stderr ( & self ) -> ControlEnd < DestinationStderr > {
376409 ControlEnd {
377410 current_event : self . current_event . clone ( ) ,
411+ should_send_structured_event : self . should_send_structured_event ,
378412 sender : self . sender . clone ( ) ,
379413 pass_through_destination : PhantomData ,
380414 }
@@ -447,11 +481,16 @@ impl std::io::Write for ControlEnd<DestinationStdout> {
447481 }
448482}
449483
450- /// Creates a set of legacy conduits for communication between view and control layers.
484+ /// Creates a set of legacy conduits forcommunication between view and control layers.
451485///
452486/// This function establishes the communication channels needed for the legacy mode operation,
453487/// where the view layer and control layer can exchange events and byte data.
454488///
489+ /// # Parameters
490+ ///
491+ /// - `should_send_structured_event`: Flag indicating whether structured events should be sent
492+ /// through the conduit
493+ ///
455494/// # Returns
456495///
457496/// A tuple containing:
@@ -463,9 +502,11 @@ impl std::io::Write for ControlEnd<DestinationStdout> {
463502/// # Example
464503///
465504/// ```rust
466- /// let (view_end, input_receiver, stderr_control, stdout_control) = get_legacy_conduits();
505+ /// let (view_end, input_receiver, stderr_control, stdout_control) = get_legacy_conduits(true );
467506/// ```
468- pub fn get_legacy_conduits ( ) -> (
507+ pub fn get_legacy_conduits (
508+ should_send_structured_event : bool ,
509+ ) -> (
469510 ViewEnd ,
470511 InputReceiver ,
471512 ControlEnd < DestinationStderr > ,
@@ -482,11 +523,13 @@ pub fn get_legacy_conduits() -> (
482523 byte_rx,
483524 ControlEnd {
484525 current_event : None ,
526+ should_send_structured_event,
485527 sender : state_tx. clone ( ) ,
486528 pass_through_destination : PhantomData ,
487529 } ,
488530 ControlEnd {
489531 current_event : None ,
532+ should_send_structured_event,
490533 sender : state_tx,
491534 pass_through_destination : PhantomData ,
492535 } ,
0 commit comments