const MIN_SUMMARY_CHARS: usize = 80;
pub(crate) fn entry_is_summary_only(entry: &crate::value::DictMap) -> bool {
matches!(
entry.get("compact"),
Some(crate::value::VmValue::Bool(true))
)
}
pub(crate) fn tool_summary(description: &str) -> String {
let head = description
.split("\n\n")
.next()
.unwrap_or(description)
.trim();
if head.is_empty() {
return description.trim().to_string();
}
let mut taken = 0usize;
for end in sentence_ends(head) {
taken = end;
let prefix = head.get(..end).unwrap_or(head);
if prefix.trim().chars().count() >= MIN_SUMMARY_CHARS {
break;
}
}
if taken == 0 {
return head.to_string();
}
head.get(..taken).unwrap_or(head).trim().to_string()
}
fn sentence_ends(text: &str) -> Vec<usize> {
let bytes = text.as_bytes();
let mut ends = Vec::new();
for (index, ch) in text.char_indices() {
if !matches!(ch, '.' | '!' | '?') {
continue;
}
let next = index + ch.len_utf8();
let mut end = next;
while end < bytes.len() && matches!(bytes[end], b'.' | b'!' | b'?' | b'"' | b')' | b'\'') {
end += 1;
}
let Some(rest) = text.get(end..) else {
continue;
};
match rest.chars().next() {
None => ends.push(end),
Some(following) if following.is_whitespace() => ends.push(end),
Some(_) => {}
}
}
ends
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keeps_enough_sentences_to_say_when_to_call_the_tool() {
let summary = tool_summary(
"Eval-only stop cord. Call this instead of continuing when the eval \
fixture, harness, or provided context is broken. Do not use it for \
ordinary uncertainty.",
);
assert!(
summary.starts_with("Eval-only stop cord. Call this"),
"a sub-floor first sentence must pull in the next one, got {summary:?}"
);
assert!(
!summary.contains("ordinary uncertainty"),
"the floor must stop once it is satisfied, got {summary:?}"
);
}
#[test]
fn stops_at_one_sentence_when_that_sentence_carries_the_contract() {
let description = "If a tool result confused you, an error message was \
unhelpful, or a tool you needed did not exist, report \
it here. This is telemetry-only and never changes task \
success.";
assert_eq!(
tool_summary(description),
"If a tool result confused you, an error message was unhelpful, or a \
tool you needed did not exist, report it here."
);
}
#[test]
fn never_serves_half_a_sentence() {
let one = "Read a slice of a file and return it with line numbers so \
later edits can quote exact lines without re-reading";
assert_eq!(tool_summary(one), one);
}
#[test]
fn keeps_a_dotted_token_intact_inside_the_summary() {
let summary = tool_summary(
"Match a pattern across the workspace, e.g. a symbol or an import \
path, honouring .gitignore. Results are capped.",
);
assert!(
summary.contains("honouring .gitignore"),
"`e.g.` and `.gitignore` are not sentence boundaries, got {summary:?}"
);
}
#[test]
fn cuts_at_a_paragraph_break_before_any_sentence_floor() {
let summary = tool_summary("Run a shell command.\n\nLong usage notes follow.");
assert_eq!(summary, "Run a shell command.");
}
#[test]
fn an_empty_description_stays_empty() {
assert_eq!(tool_summary(" "), "");
}
#[test]
fn multi_byte_characters_survive_the_cut() {
let summary = tool_summary(
"Résumé a paused run — pick up where the agent stopped, naïvely. \
Prefer it over restarting when the transcript is intact. \
SENTINEL: this sentence must be dropped.",
);
assert!(
summary.starts_with("Résumé a paused run — pick up"),
"a multi-byte opener must survive intact, got {summary:?}"
);
assert!(
!summary.contains("SENTINEL"),
"the tail must still be dropped, got {summary:?}"
);
assert!(
summary.is_char_boundary(summary.len()),
"the summary must be valid UTF-8 through its end"
);
}
}