use super::*;
#[derive(Debug, Clone)]
pub(crate) enum AgentRender<'a> {
Kept(&'a AgentMsg),
Placeholder(PlaceholderSpan),
Superseded {
msg: &'a AgentMsg,
by_line: usize,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct PlaceholderSpan {
pub(crate) messages: usize,
pub(crate) tool_calls: usize,
pub(crate) failed: usize,
pub(crate) first_line: usize,
pub(crate) last_line: usize,
pub(crate) chars: usize,
pub(crate) previews: Vec<CollapsedPreview>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CollapsedPreview {
pub(crate) line: usize,
pub(crate) excerpt: String,
}
pub(crate) fn select_agent_messages<'a>(
turn: &'a TurnSlice,
cfg: &RichnessCfg,
) -> Vec<AgentRender<'a>> {
let mut lane = select_kept(turn, cfg);
mark_resends(&mut lane);
lane
}
fn select_kept<'a>(turn: &'a TurnSlice, cfg: &RichnessCfg) -> Vec<AgentRender<'a>> {
let agents = &turn.agents;
if agents.is_empty() {
return Vec::new();
}
match cfg.mode {
AgentMsgMode::Longest => {
if agents.len() == 1 {
return vec![AgentRender::Kept(&agents[0])];
}
let longest = agents
.iter()
.enumerate()
.max_by_key(|(_, a)| a.unit.full_chars)
.map(|(i, _)| i)
.expect("non-empty");
let last = agents.len() - 1;
let keep = |i: usize, a: &AgentMsg| -> bool {
if i == longest {
return true; }
if i == 0 {
return a.unit.full_chars >= cfg.rich_min_chars; }
if i == last {
return agent_msg_is_rich(&a.unit.text, cfg); }
agent_msg_is_rich(&a.unit.text, cfg) };
collapse_unkept(agents, keep)
}
AgentMsgMode::EotOnly => {
vec![AgentRender::Kept(agents.last().expect("non-empty"))]
}
AgentMsgMode::All => agents.iter().map(AgentRender::Kept).collect(),
AgentMsgMode::Rich => {
if agents.len() <= cfg.run_threshold {
return agents.iter().map(AgentRender::Kept).collect();
}
let last = agents.len() - 1;
let keep = |i: usize, a: &AgentMsg| -> bool {
if i == last {
return true; }
if i == 0 && cfg.keep_first {
return true; }
!agent_msg_is_droppable(&a.unit.text, cfg)
};
collapse_unkept(agents, keep)
}
}
}
pub(crate) fn collapse_unkept<'a>(
agents: &'a [AgentMsg],
keep: impl Fn(usize, &AgentMsg) -> bool,
) -> Vec<AgentRender<'a>> {
let mut out: Vec<AgentRender> = Vec::new();
let mut span: Option<PlaceholderSpan> = None;
for (i, a) in agents.iter().enumerate() {
if keep(i, a) {
if let Some(s) = span.take() {
out.push(AgentRender::Placeholder(s));
}
out.push(AgentRender::Kept(a));
} else {
let line = a.unit.line_no;
let preview = collapsed_preview(&a.unit);
match span.as_mut() {
Some(s) => {
s.messages += 1;
s.tool_calls += a.preceding_tool_calls;
s.failed += a.preceding_failed;
s.last_line = line;
s.chars += a.unit.full_chars;
s.previews.extend(preview);
}
None => {
span = Some(PlaceholderSpan {
messages: 1,
tool_calls: a.preceding_tool_calls,
failed: a.preceding_failed,
first_line: line,
last_line: line,
chars: a.unit.full_chars,
previews: preview.into_iter().collect(),
});
}
}
}
}
if let Some(s) = span.take() {
out.push(AgentRender::Placeholder(s));
}
out
}
pub(crate) fn resend_head(unit: &TurnUnit) -> Option<String> {
if unit.text.chars().count() < DEDUP_PREFIX {
return None;
}
Some(
unit.text
.chars()
.take(DEDUP_PREFIX)
.collect::<String>()
.to_lowercase(),
)
}
pub(crate) fn mark_resends(lane: &mut [AgentRender<'_>]) {
let mut groups: std::collections::HashMap<String, Vec<usize>> =
std::collections::HashMap::new();
for (i, entry) in lane.iter().enumerate() {
if let AgentRender::Kept(a) = entry {
if let Some(head) = resend_head(&a.unit) {
groups.entry(head).or_default().push(i);
}
}
}
let mut marks: Vec<(usize, usize)> = Vec::new(); for idxs in groups.values() {
if idxs.len() < 2 {
continue;
}
let keys: Vec<String> = idxs
.iter()
.map(|&i| match &lane[i] {
AgentRender::Kept(a) => a.unit.text.to_lowercase(),
_ => String::new(),
})
.collect();
for (pos, &i) in idxs.iter().enumerate() {
for later in (pos + 1..idxs.len()).rev() {
if keys[later].starts_with(&keys[pos]) {
if let AgentRender::Kept(a) = &lane[idxs[later]] {
marks.push((i, a.unit.line_no));
}
break;
}
}
}
}
for (i, by_line) in marks {
if let AgentRender::Kept(a) = lane[i] {
lane[i] = AgentRender::Superseded { msg: a, by_line };
}
}
}
pub(crate) fn superseded_line(msg: &AgentMsg, by_line: usize) -> String {
format!(
"△ L{} [superseded by the same-prefix re-send at L{by_line}, {} chars]",
msg.unit.line_no, msg.unit.full_chars
)
}
pub(crate) fn superseded_cost(msg: &AgentMsg, by_line: usize) -> usize {
superseded_line(msg, by_line).chars().count() + NEWLINE_COST
}
pub(crate) fn plural(n: usize, noun: &str) -> String {
if n == 1 {
format!("1 {noun}")
} else {
format!("{n} {noun}s")
}
}
pub(crate) fn collapsed_preview(unit: &TurnUnit) -> Option<CollapsedPreview> {
(unit.full_chars >= COLLAPSED_PREVIEW_MIN_CHARS).then(|| CollapsedPreview {
line: unit.line_no,
excerpt: crate::text::truncate_excerpt(&unit.text, COLLAPSED_PREVIEW_CHARS),
})
}
pub(crate) fn agent_placeholder_line(span: &PlaceholderSpan) -> String {
let range = if span.first_line == span.last_line {
format!("L{}", span.first_line)
} else {
format!("L{}–L{}", span.first_line, span.last_line)
};
let msgs = plural(span.messages, "agent message");
let tools = plural(span.tool_calls, "tool call");
let body = if span.failed == 0 {
format!("[{msgs} collapsed, {} chars, {tools}]", span.chars)
} else {
format!(
"[{msgs} collapsed, {} chars, {tools}, {} failed]",
span.chars, span.failed
)
};
format!("△ {range} {body}")
}
pub(crate) fn collapsed_preview_line(p: &CollapsedPreview) -> String {
format!(" L{} {}", p.line, p.excerpt)
}
pub(crate) fn agent_placeholder_lines(span: &PlaceholderSpan) -> Vec<String> {
let mut out = vec![agent_placeholder_line(span)];
out.extend(span.previews.iter().map(collapsed_preview_line));
out
}
pub(crate) fn agent_placeholder_cost(span: &PlaceholderSpan) -> usize {
agent_placeholder_lines(span)
.iter()
.map(|l| l.chars().count() + NEWLINE_COST)
.sum()
}