steer_tui/tui/widgets/formatters/
dispatch_agent.rs

1use super::{ToolFormatter, helpers::*};
2use crate::tui::theme::Theme;
3use ratatui::{
4    style::Style,
5    text::{Line, Span},
6};
7use serde_json::Value;
8use steer_core::app::conversation::ToolResult;
9use steer_core::tools::dispatch_agent::DispatchAgentParams;
10
11pub struct DispatchAgentFormatter;
12
13impl ToolFormatter for DispatchAgentFormatter {
14    fn compact(
15        &self,
16        params: &Value,
17        result: &Option<ToolResult>,
18        _wrap_width: usize,
19        theme: &Theme,
20    ) -> Vec<Line<'static>> {
21        let mut lines = Vec::new();
22
23        let Ok(params) = serde_json::from_value::<DispatchAgentParams>(params.clone()) else {
24            return vec![Line::from(Span::styled(
25                "Invalid agent params",
26                theme.error_text(),
27            ))];
28        };
29
30        let preview = if params.prompt.len() > 60 {
31            format!("{}...", &params.prompt[..57])
32        } else {
33            params.prompt.clone()
34        };
35
36        let info = match result {
37            Some(ToolResult::Agent(agent_result)) => {
38                let line_count = agent_result.content.lines().count();
39                format!("{line_count} lines")
40            }
41            Some(ToolResult::Error(_)) => "failed".to_string(),
42            Some(_) => "unexpected result type".to_string(),
43            None => "running...".to_string(),
44        };
45
46        lines.push(Line::from(vec![
47            Span::styled(format!("task='{preview}' "), Style::default()),
48            Span::styled(format!("({info})"), theme.subtle_text()),
49        ]));
50
51        lines
52    }
53
54    fn detailed(
55        &self,
56        params: &Value,
57        result: &Option<ToolResult>,
58        wrap_width: usize,
59        theme: &Theme,
60    ) -> Vec<Line<'static>> {
61        let mut lines = Vec::new();
62
63        let Ok(params) = serde_json::from_value::<DispatchAgentParams>(params.clone()) else {
64            return vec![Line::from(Span::styled(
65                "Invalid agent params",
66                theme.error_text(),
67            ))];
68        };
69
70        lines.push(Line::from(Span::styled("Agent Task:", theme.text())));
71        for line in params.prompt.lines() {
72            for wrapped_line in textwrap::wrap(line, wrap_width) {
73                lines.push(Line::from(Span::styled(
74                    wrapped_line.to_string(),
75                    Style::default(),
76                )));
77            }
78        }
79
80        // Show output if we have results
81        if let Some(result) = result {
82            match result {
83                ToolResult::Agent(agent_result) => {
84                    if !agent_result.content.trim().is_empty() {
85                        lines.push(separator_line(wrap_width, theme.dim_text()));
86
87                        const MAX_OUTPUT_LINES: usize = 30;
88                        let (output_lines, truncated) =
89                            truncate_lines(&agent_result.content, MAX_OUTPUT_LINES);
90
91                        for line in output_lines {
92                            for wrapped in textwrap::wrap(line, wrap_width) {
93                                lines.push(Line::from(Span::raw(wrapped.to_string())));
94                            }
95                        }
96
97                        if truncated {
98                            lines.push(Line::from(Span::styled(
99                                format!(
100                                    "... ({} more lines)",
101                                    agent_result.content.lines().count() - MAX_OUTPUT_LINES
102                                ),
103                                theme.subtle_text(),
104                            )));
105                        }
106                    }
107                }
108                ToolResult::Error(error) => {
109                    lines.push(separator_line(wrap_width, theme.dim_text()));
110                    lines.push(Line::from(Span::styled(
111                        error.to_string(),
112                        theme.error_text(),
113                    )));
114                }
115                _ => {
116                    lines.push(Line::from(Span::styled(
117                        "Unexpected result type",
118                        theme.error_text(),
119                    )));
120                }
121            }
122        }
123
124        lines
125    }
126}