use crate::core::tokens::count_tokens;
const SUMMARY_LINES: usize = 20;
#[must_use]
pub fn maybe_spill(server: &str, tool: &str, text: &str, budget_tokens: usize) -> Option<String> {
let tokens = count_tokens(text);
if tokens <= budget_tokens {
return None;
}
let source = format!("gateway:{server}::{tool}");
let id = crate::core::archive::store(&source, tool, text, None)?;
Some(format_handle(&id, text, tokens))
}
fn format_handle(id: &str, text: &str, tokens: usize) -> String {
let head: String = text
.lines()
.take(SUMMARY_LINES)
.collect::<Vec<_>>()
.join("\n");
let hint = crate::core::archive::format_hint(id, text.len(), tokens);
format!("{head}\n…\n{hint}")
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt::Write as _;
#[test]
fn under_budget_does_not_spill() {
assert!(maybe_spill("s", "t", "tiny", 1000).is_none());
}
#[test]
fn over_budget_spills_and_returns_handle() {
let _lock = crate::core::data_dir::test_env_lock();
let tmp = tempfile::tempdir().unwrap();
crate::test_env::set_var("LEAN_CTX_DATA_DIR", tmp.path());
crate::test_env::set_var("LEAN_CTX_ARCHIVE", "1");
let big = (0..5000).fold(String::new(), |mut s, i| {
let _ = writeln!(s, "payload line {i}");
s
});
let out = maybe_spill("repomix", "pack_codebase", &big, 100)
.expect("oversized output should spill when archive is enabled");
assert!(out.contains("ctx_expand"), "must offer a retrieval handle");
assert!(
out.contains("payload line 0"),
"must include a head summary"
);
let again = maybe_spill("repomix", "pack_codebase", &big, 100).unwrap();
assert_eq!(out, again);
crate::test_env::remove_var("LEAN_CTX_ARCHIVE");
crate::test_env::remove_var("LEAN_CTX_DATA_DIR");
}
}