#![allow(dead_code)]
use crate::constants::xml_tags::{STATUS_TAG, SUMMARY_TAG, TASK_NOTIFICATION_TAG};
use crate::types::message::{Message, MessageBase, UserMessage};
const BACKGROUND_BASH_SUMMARY_PREFIX: &str = "background command";
fn extract_tag(text: &str, tag: &str) -> Option<String> {
let open = format!("<{tag}>");
let close = format!("</{tag}>");
let start = text.find(&open)?;
let end = text.find(&close)?;
if end > start + open.len() {
Some(text[start + open.len()..end].to_string())
} else {
Some(String::new())
}
}
fn is_completed_background_bash(msg: &Message) -> bool {
let text = match msg {
Message::User(user) => match &user.message.content {
crate::types::message::UserContent::Text(t) => t.clone(),
crate::types::message::UserContent::Blocks(blocks) => blocks
.first()
.and_then(|b| b.text.as_ref())
.cloned()
.unwrap_or_default(),
},
_ => return false,
};
if extract_tag(&text, STATUS_TAG).as_deref() != Some("completed") {
return false;
}
extract_tag(&text, SUMMARY_TAG)
.map(|s| s.starts_with(BACKGROUND_BASH_SUMMARY_PREFIX))
.unwrap_or(false)
}
pub fn collapse_background_bash_notifications(
messages: &[Message],
verbose: bool,
) -> Vec<Message> {
if !is_fullscreen_env_enabled() {
return messages.to_vec();
}
if verbose {
return messages.to_vec();
}
let mut result = Vec::new();
let mut i = 0;
while i < messages.len() {
let msg = &messages[i];
if is_completed_background_bash(msg) {
let mut count = 0;
while i < messages.len()
&& is_completed_background_bash(&messages[i])
{
count += 1;
i += 1;
}
if count == 1 {
result.push(msg.clone());
} else {
if let Message::User(user) = msg {
let new_text = format!(
"<{TASK_NOTIFICATION_TAG}><{STATUS_TAG}>completed</{STATUS_TAG}><{SUMMARY_TAG}>{count} background commands completed</{SUMMARY_TAG}></{TASK_NOTIFICATION_TAG}>"
);
result.push(Message::User(UserMessage {
message: crate::types::message::UserMessageContent {
content: crate::types::message::UserContent::Text(new_text),
extra: user.message.extra.clone(),
},
is_meta: user.is_meta,
is_visible_in_transcript_only: user.is_visible_in_transcript_only,
is_virtual: user.is_virtual,
parent_uuid: user.parent_uuid.clone(),
timestamp: user.timestamp,
tool_use_result: user.tool_use_result.clone(),
}));
}
}
} else {
result.push(msg.clone());
i += 1;
}
}
result
}
fn is_fullscreen_env_enabled() -> bool {
crate::utils::fullscreen::is_fullscreen_env_enabled()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::message::{UserContent, UserMessageContent};
fn make_background_bash(summary: &str) -> Message {
let text = format!(
"<{TASK_NOTIFICATION_TAG}><{STATUS_TAG}>completed</{STATUS_TAG}><{SUMMARY_TAG}>{summary}</{SUMMARY_TAG}></{TASK_NOTIFICATION_TAG}>"
);
Message::User(UserMessage {
message: UserMessageContent {
content: UserContent::Text(text),
extra: Default::default(),
},
is_meta: None,
is_visible_in_transcript_only: None,
is_virtual: None,
parent_uuid: None,
timestamp: None,
tool_use_result: None,
})
}
fn make_other_message() -> Message {
Message::User(UserMessage {
message: UserMessageContent {
content: UserContent::Text("some other message".to_string()),
extra: Default::default(),
},
is_meta: None,
is_visible_in_transcript_only: None,
is_virtual: None,
parent_uuid: None,
timestamp: None,
tool_use_result: None,
})
}
#[test]
fn test_collapse_multiple_background_bash() {
let messages = vec![
make_background_bash("background command ls"),
make_background_bash("background command cat"),
];
let result = collapse_background_bash_notifications(&messages, false);
assert_eq!(result.len(), 2);
}
#[test]
fn test_verbose_mode_passes_through() {
let messages = vec![
make_background_bash("background command ls"),
make_background_bash("background command cat"),
];
let result = collapse_background_bash_notifications(&messages, true);
assert_eq!(result.len(), 2);
}
#[test]
fn test_non_background_messages_pass_through() {
let messages = vec![make_other_message()];
let result = collapse_background_bash_notifications(&messages, false);
assert_eq!(result.len(), 1);
}
}