1use rz_agent_protocol::{Envelope, MessageKind, SENTINEL};
4
5pub fn extract_messages(scrollback: &str) -> Vec<Envelope> {
10 let lines: Vec<&str> = scrollback.lines().collect();
11 let mut result = Vec::new();
12 let mut i = 0;
13
14 while i < lines.len() {
15 if let Some(idx) = lines[i].find(SENTINEL) {
16 let mut candidate = lines[i][idx..].to_string();
17 if let Ok(env) = Envelope::decode(&candidate) {
18 result.push(env);
19 i += 1;
20 continue;
21 }
22 for j in 1..20 {
24 if i + j >= lines.len() {
25 break;
26 }
27 candidate.push_str(lines[i + j]);
28 if let Ok(env) = Envelope::decode(&candidate) {
29 result.push(env);
30 i += j;
31 break;
32 }
33 }
34 }
35 i += 1;
36 }
37
38 result
39}
40
41pub fn format_message(envelope: &Envelope, own_id: Option<&str>) -> String {
45 let secs = envelope.ts / 1000;
46 let h = (secs / 3600) % 24;
47 let m = (secs % 3600) / 60;
48 let s = secs % 60;
49
50 let me = if own_id == Some(envelope.from.as_str()) { " (me)" } else { "" };
51
52 let text = match &envelope.kind {
53 MessageKind::Chat { text } => text.as_str(),
54 MessageKind::Ping => "ping",
55 MessageKind::Pong => "pong",
56 MessageKind::Error { message } => {
57 return format!("[{h:02}:{m:02}:{s:02}] {}{me}> error: {message}", envelope.from);
58 }
59 MessageKind::Timer { label } => {
60 return format!("[{h:02}:{m:02}:{s:02}] {}{me}> timer: {label}", envelope.from);
61 }
62 MessageKind::Status { state, detail } => {
63 return format!("[{h:02}:{m:02}:{s:02}] {}{me}> [{state}] {detail}", envelope.from);
64 }
65 MessageKind::ToolCall { name, .. } => {
66 return format!("[{h:02}:{m:02}:{s:02}] {}{me}> (calling tool: {name})", envelope.from);
67 }
68 MessageKind::ToolResult { name, result, is_error } => {
69 let prefix = if *is_error { "tool error" } else { "tool result" };
70 let short = if result.len() > 200 { &result[..200] } else { result.as_str() };
71 return format!("[{h:02}:{m:02}:{s:02}] {}{me}> {prefix} ({name}): {short}", envelope.from);
72 }
73 MessageKind::Delegate { task, .. } => {
74 let short = if task.len() > 200 { &task[..200] } else { task.as_str() };
75 return format!("[{h:02}:{m:02}:{s:02}] {}{me}> (delegating: {short})", envelope.from);
76 }
77 MessageKind::Hello { name } => {
78 return format!("[{h:02}:{m:02}:{s:02}] {}{me}> hello from {name}", envelope.from);
79 }
80 };
81
82 format!("[{h:02}:{m:02}:{s:02}] {}{me}> {text}", envelope.from)
83}