Skip to main content

vtcode_commons/ui_protocol/
tool_summary.rs

1//! Data exchanged by compact per-call tool summaries.
2
3use crate::tool_types::CompactStr;
4
5/// Session-local key for a complete tool-output capture.
6///
7/// This is deliberately a UI protocol identifier. It is not persisted in
8/// `ThreadEvent` data and must not be treated as a tool-call identity outside
9/// the live terminal session.
10pub type ToolOutputId = u64;
11
12/// Presentation metadata for a compact command activity row.
13///
14/// The complete command output is sent through `RecordToolOutput` separately.
15/// Keeping this small metadata object independent means compact rendering can
16/// replace a row without truncating, reordering, or otherwise changing the
17/// captured stdout, stderr, PTY, or spool-backed transcript.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct CompactActivityMetadata {
20    /// Identifier shared by all rows in one contiguous successful-command group.
21    pub group_id: u64,
22    /// Number of successful commands represented by the row.
23    pub command_count: usize,
24    /// The command text for a single-command row. Grouped rows leave this empty.
25    pub command: Option<CompactStr>,
26    /// Number of complete output lines hidden behind the review affordance.
27    pub hidden_line_count: usize,
28    /// Optional status or artifact text that remains visible in the row.
29    pub suffix: Option<CompactStr>,
30    /// First complete capture represented by the row, used as the review focus.
31    pub review_anchor: Option<ToolOutputId>,
32    /// All complete captures represented by the row, in render order.
33    ///
34    /// `review_anchor` remains the first capture for compatibility with the
35    /// click protocol; this list lets the UI re-anchor every member of a
36    /// grouped row without guessing from transcript text.
37    pub review_anchors: Vec<ToolOutputId>,
38}
39
40impl CompactActivityMetadata {
41    /// Return the compact row text without the UI-only review affordance.
42    pub fn display_text(&self) -> String {
43        let mut text = if self.command_count > 1 {
44            format!("• Ran {} commands", self.command_count)
45        } else {
46            format!("• Ran {}", self.command.as_deref().unwrap_or("command"))
47        };
48
49        if self.command_count == 1 && self.hidden_line_count > 0 {
50            text.push_str(&format!(" · … +{} lines", self.hidden_line_count));
51        }
52        if let Some(suffix) = self.suffix.as_deref().filter(|suffix| !suffix.is_empty()) {
53            text.push_str(" · ");
54            text.push_str(suffix);
55        }
56        text
57    }
58}
59
60/// UI-only payload that reopens a completed-edit diff as full-viewport review.
61///
62/// Not part of `ThreadEvent`. The transcript notice text is the activation
63/// target; `unified` is the retained preview used when before/after are absent.
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct DiffReviewAnchor {
66    /// Workspace-visible file path for the review header.
67    pub file_path: String,
68    /// Retained unified preview body.
69    pub unified: String,
70    /// Rows omitted from the compact transcript body, when known.
71    pub omitted_lines: u64,
72    /// Exact notice text that should activate this review.
73    pub notice: String,
74}
75
76#[cfg(test)]
77mod tests {
78    use super::CompactActivityMetadata;
79
80    #[test]
81    fn compact_activity_display_includes_single_command_output_count() {
82        let activity = CompactActivityMetadata {
83            group_id: 1,
84            command_count: 1,
85            command: Some("cargo check".into()),
86            hidden_line_count: 3,
87            suffix: None,
88            review_anchor: Some(7),
89            review_anchors: vec![7],
90        };
91
92        assert_eq!(activity.display_text(), "• Ran cargo check · … +3 lines");
93    }
94
95    #[test]
96    fn compact_activity_display_collapses_grouped_commands() {
97        let activity = CompactActivityMetadata {
98            group_id: 2,
99            command_count: 4,
100            command: None,
101            hidden_line_count: 12,
102            suffix: Some("output retained".into()),
103            review_anchor: Some(9),
104            review_anchors: vec![9],
105        };
106
107        assert_eq!(activity.display_text(), "• Ran 4 commands · output retained");
108    }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct CompactToolSummaryLine {
113    pub kind: CompactToolSummaryLineKind,
114    pub text: String,
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum CompactToolSummaryLineKind {
119    Info,
120    Detail,
121}