vtcode_core/core/agent/
display.rs1use crate::config::constants::tools;
2use crate::tools::tool_intent;
3use serde_json::Value;
4
5#[inline]
8pub fn format_tool_result_for_display(tool_name: &str, result: &Value) -> String {
9 let display_tool_name =
10 tool_intent::canonical_unified_exec_tool_name(tool_name).unwrap_or(tool_name);
11 let is_command_session_tool = display_tool_name == tools::UNIFIED_EXEC;
12
13 if display_tool_name == tools::UNIFIED_SEARCH {
14 return format_unified_search_result_for_display(display_tool_name, result);
15 }
16
17 if is_command_session_tool {
18 if let Some(obj) = result.as_object()
20 && let Some(stdout) = obj
21 .get("stdout")
22 .or_else(|| obj.get("output"))
23 .and_then(|v| v.as_str())
24 && stdout.len() > 2000
25 && (stdout.contains("error") || stdout.contains("Error"))
26 {
27 let lines: Vec<&str> = stdout.lines().collect();
28 let mut extracted = Vec::new();
29 for (i, line) in lines.iter().enumerate() {
30 if line.to_lowercase().contains("error") {
31 let start = i.saturating_sub(2);
32 let end = (i + 3).min(lines.len());
33 extracted.extend_from_slice(&lines[start..end]);
34 extracted.push("...");
35 }
36 }
37 if !extracted.is_empty() {
38 let compact = serde_json::json!({
39 "exit_code": obj.get("exit_code"),
40 "errors": extracted.join("\n"),
41 "note": "Showing error lines + context only"
42 });
43 return format!("Tool {} result: {}", display_tool_name, compact);
44 }
45 }
46 return format!("Tool {} result: {}", display_tool_name, result);
47 }
48
49 format!("Tool {} result: {}", display_tool_name, result)
50}
51
52fn format_unified_search_result_for_display(tool_name: &str, result: &Value) -> String {
53 if let Some(obj) = result.as_object()
54 && obj.get("url").is_some()
55 && obj.get("content").is_some()
56 {
57 if obj.contains_key("error") {
58 return format!(
59 "Tool {} result: {{\"error\": {}}}",
60 tool_name,
61 obj.get("error")
62 .map(|v| v.to_string())
63 .unwrap_or_else(|| "unknown error".into())
64 );
65 }
66
67 let status = serde_json::json!({
68 "status": "fetched",
69 "content_length": obj.get("content_length"),
70 "truncated": obj.get("truncated"),
71 "url": obj.get("url")
72 });
73 return format!("Tool {} result: {}", tool_name, status);
74 }
75
76 if let Some(obj) = result.as_object()
77 && let Some(matches) = obj.get("matches").and_then(|v| v.as_array())
78 && matches.len() > 5
79 {
80 let truncated: Vec<_> = matches.iter().take(5).cloned().collect();
81 let overflow = matches.len().saturating_sub(5);
82 let summary = serde_json::json!({
83 "matches": truncated,
84 "overflow": format!("[+{} more matches]", overflow),
85 "total": matches.len()
86 });
87 return format!("Tool {} result: {}", tool_name, summary);
88 }
89
90 if let Some(obj) = result.as_object()
91 && let Some(files) = obj.get("files").and_then(|v| v.as_array())
92 && files.len() > 50
93 {
94 let sample: Vec<_> = files.iter().take(5).cloned().collect();
95 let summary = serde_json::json!({
96 "total_files": files.len(),
97 "sample": sample,
98 "note": format!("Showing 5 of {} files", files.len())
99 });
100 return format!("Tool {} result: {}", tool_name, summary);
101 }
102
103 format!("Tool {} result: {}", tool_name, result)
104}