use std::cell::RefCell;
use std::collections::BTreeMap;
use super::ccr;
use super::prose_compress::compress_prose;
use lean_ctx::core::archive::{RecoveryId, RecoveryRefusal, delivered};
use lean_ctx::core::tokens::{COUNTING_FAMILY, TokenizerFamily, count_tokens_for};
use lean_ctx::core::web::distill;
#[path = "compress_gate.rs"]
pub(crate) mod gate;
const RESEARCH_PROSE_CAP: usize = 20_000;
const RESEARCH_PROSE_CAP_ENV: &str = "LEAN_CTX_RESEARCH_PROSE_CAP";
fn research_prose_cap() -> usize {
std::env::var(RESEARCH_PROSE_CAP_ENV)
.ok()
.and_then(|v| v.trim().parse::<usize>().ok())
.filter(|cap| *cap > 0)
.unwrap_or(RESEARCH_PROSE_CAP)
}
pub fn compress_tool_result(content: &str, tool_name: Option<&str>) -> String {
compress_tool_result_for(content, tool_name, COUNTING_FAMILY)
}
pub fn compress_tool_result_with_policy(
content: &str,
tool_name: Option<&str>,
policy: lean_ctx::core::adaptive_policy::CompressionPolicy,
) -> String {
if !policy.compress_tool_output || policy.log_compression_level == 0 {
return content.to_owned();
}
if policy.code_preserve && looks_like_code(content) {
return content.to_owned();
}
compress_tool_result(content, tool_name)
}
pub fn compress_tool_result_gateway_with_policy(
content: &str,
tool_name: Option<&str>,
family: TokenizerFamily,
policy: lean_ctx::core::adaptive_policy::CompressionPolicy,
) -> String {
if !policy.compress_tool_output || policy.log_compression_level == 0 {
return content.to_owned();
}
if policy.code_preserve && looks_like_code(content) {
return content.to_owned();
}
compress_tool_result_gateway_for(content, tool_name, family)
}
fn looks_like_code(content: &str) -> bool {
content.lines().any(|line| {
let line = line.trim_start();
["fn ", "struct ", "class ", "function ", "def ", "impl "]
.iter()
.any(|p| line.starts_with(p))
|| line.contains('{')
&& line.contains('}')
&& (line.contains(';') || line.contains("=>"))
})
}
pub fn compress_tool_result_for(
content: &str,
tool_name: Option<&str>,
family: TokenizerFamily,
) -> String {
if carries_recovery_stub(content) {
return content.to_owned();
}
let compressed = compress_inner(content, tool_name, family);
attach_ccr(content, compressed, CcrAudience::Local)
}
pub fn shape_command_output(command: &str, output: &str, exit_code: Option<i32>) -> String {
if output.trim().is_empty() || output.len() < 200 {
return output.to_string();
}
if carries_recovery_stub(output) {
return output.to_string();
}
let compressed = match exit_code {
Some(code) => {
lean_ctx::shell::compress::engine::compress_for_outcome(command, output, code)
}
None => lean_ctx::shell::compress::engine::compress_if_beneficial_pub(command, output),
};
super::sticky_tools::in_pass(|| attach_ccr(output, compressed, CcrAudience::Local))
}
pub fn shape_tool_output(output: &str, tool_name: Option<&str>) -> String {
super::sticky_tools::in_pass(|| compress_tool_result(output, tool_name))
}
pub fn compress_tool_result_gateway(content: &str, tool_name: Option<&str>) -> String {
compress_tool_result_gateway_for(content, tool_name, COUNTING_FAMILY)
}
pub fn compress_tool_result_gateway_for(
content: &str,
tool_name: Option<&str>,
family: TokenizerFamily,
) -> String {
if carries_recovery_stub(content) {
return content.to_owned();
}
let compressed = compress_inner(content, tool_name, family);
let clean = lean_ctx::core::protocol::strip_trailing_savings_footer(&compressed).to_string();
attach_ccr(content, clean, CcrAudience::Gateway)
}
#[derive(Clone, Copy, PartialEq)]
enum CcrAudience {
Local,
Gateway,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Reader {
#[cfg(feature = "http-server")]
Model,
GatewayCaller,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub(crate) struct KeptWhole {
blocks: BTreeMap<[u8; 32], &'static str>,
}
impl KeptWhole {
fn note(&mut self, block: &str, code: &'static str) {
self.blocks
.entry(*blake3::hash(block.as_bytes()).as_bytes())
.or_insert(code);
}
#[cfg(feature = "http-server")]
pub(crate) fn merge(&mut self, other: Self) {
for (block, code) in other.blocks {
self.blocks.entry(block).or_insert(code);
}
}
#[cfg(feature = "http-server")]
pub(crate) fn is_empty(&self) -> bool {
self.blocks.is_empty()
}
#[cfg(feature = "http-server")]
pub(crate) fn counts(&self) -> BTreeMap<&'static str, usize> {
let mut counts = BTreeMap::new();
for code in self.blocks.values() {
*counts.entry(*code).or_insert(0) += 1;
}
counts
}
#[cfg(feature = "http-server")]
pub(crate) fn codes(&self) -> Option<String> {
let counts = self.counts();
(!counts.is_empty()).then(|| counts.keys().copied().collect::<Vec<_>>().join(","))
}
#[cfg(feature = "http-server")]
pub(crate) fn summary(&self) -> String {
self.counts()
.iter()
.map(|(code, count)| format!("{code}={count}"))
.collect::<Vec<_>>()
.join(", ")
}
}
struct Observation {
reader: Reader,
kept: KeptWhole,
}
thread_local! {
static OBSERVATION: RefCell<Option<Observation>> = const { RefCell::new(None) };
}
#[cfg(feature = "http-server")]
pub(crate) fn observing<T>(reader: Reader, compression: impl FnOnce() -> T) -> (T, KeptWhole) {
struct Restore(Option<Observation>);
impl Drop for Restore {
fn drop(&mut self) {
let outer = self.0.take();
OBSERVATION.with(|slot| *slot.borrow_mut() = outer);
}
}
let restore = Restore(OBSERVATION.with(|slot| {
slot.replace(Some(Observation {
reader,
kept: KeptWhole::default(),
}))
}));
let result = compression();
let kept = OBSERVATION
.with(|slot| slot.borrow_mut().take())
.map_or_else(KeptWhole::default, |observation| observation.kept);
drop(restore);
(result, kept)
}
fn observed_reader() -> Option<Reader> {
OBSERVATION.with(|slot| slot.borrow().as_ref().map(|observation| observation.reader))
}
pub(crate) fn note_kept_whole(block: &str, code: &'static str) {
OBSERVATION.with(|slot| {
if let Some(observation) = slot.borrow_mut().as_mut() {
observation.kept.note(block, code);
} else {
tracing::debug!("lean-ctx proxy: block kept whole, no recovery copy ({code})");
}
});
}
pub(crate) fn protected() -> bool {
lean_ctx::core::context_admission::stored::source_authority_required()
}
const RECOVERY_STUB_OPEN: &str = "[lean-ctx recovery: ";
const EXPAND_TOOL: &str = "ctx_expand";
pub(crate) fn recovery_stub_for(tool: &str, id: &RecoveryId) -> String {
format!(
"{RECOVERY_STUB_OPEN}full original archived — \
{tool}(id=\"{}\", head=N|search=\"…\"|json_path=\"…\") for a slice]",
id.as_str()
)
}
#[cfg(test)]
pub(crate) fn recovery_stub(id: &RecoveryId) -> String {
recovery_stub_for(EXPAND_TOOL, id)
}
fn reference_stub(id: &RecoveryId) -> String {
format!(
"[lean-ctx CCR: full original archived — GET /v1/references/{} on this gateway \
returns it while its sources are admitted]",
id.as_str()
)
}
const STUB_MAX_BYTES: usize = 256;
fn carries_recovery_stub(content: &str) -> bool {
let last = content
.trim_end_matches('\n')
.rsplit('\n')
.next()
.unwrap_or_default();
last.ends_with(']')
&& [
RECOVERY_STUB_OPEN,
"[lean-ctx CCR: full original ",
"[lean-ctx: full original ",
]
.iter()
.any(|open| last.starts_with(open))
}
fn attach_ccr(original: &str, result: String, audience: CcrAudience) -> String {
use super::sticky_tools;
if result == original {
return result;
}
let shrink = original.len().saturating_sub(result.len());
if original.len() < ccr::MIN_TEE_BYTES || shrink < ccr::MIN_TEE_BYTES {
if !protected() {
return result;
}
if shrink <= STUB_MAX_BYTES {
return original.to_string();
}
}
let tool = (audience == CcrAudience::Local)
.then(|| sticky_tools::expand_tool_for(original))
.flatten();
let followable =
tool.is_some() || observed_reader() == Some(Reader::GatewayCaller) || !protected();
if !followable {
note_kept_whole(original, HANDLE_NOT_FOLLOWABLE);
return original.to_string();
}
match recovery_copy(original) {
Some(RecoveryCopy::Bound {
id,
lineage,
project,
}) => {
let stub = if audience == CcrAudience::Local {
recovery_stub_for(tool.as_deref().unwrap_or(EXPAND_TOOL), &id)
} else {
reference_stub(&id)
};
let sent = format!("{result}\n{stub}");
if sent.len() >= original.len() {
return original.to_string();
}
let recorded = super::request_project::in_project(&project, || {
delivered::record_derived(&delivered::digest(&sent), &lineage)
});
if recorded {
if audience == CcrAudience::Local {
gate::note_issued(original, &sent);
}
sent
} else {
note_kept_whole(original, DERIVED_NOT_RECORDED);
original.to_string()
}
}
Some(RecoveryCopy::Tee(handle)) if audience == CcrAudience::Gateway => {
sticky_tools::mark_ccr_active(0);
let hash = ccr::litellm_hash(original);
format!(
"{result}\n[lean-ctx CCR: full original elided to save tokens — call the \
retrieve tool with hash={hash}, or read {handle} locally]"
)
}
Some(RecoveryCopy::Tee(handle)) => {
sticky_tools::mark_ccr_active(0);
match ccr::inband_locator(&handle) {
Some(marker) => format!(
"{result}\n[lean-ctx: full original elided to save tokens — echo {marker} \
on your next turn to get the verbatim original spliced back inline]"
),
None => format!(
"{result}\n[lean-ctx: full original at {handle} — read it directly (no MCP), or \
ctx_expand(id=\"{handle}\", head=N|search=\"…\"|json_path=\"…\") for a slice]"
),
}
}
None => original.to_string(),
}
}
fn recovery_copy(original: &str) -> Option<RecoveryCopy> {
if !lean_ctx::core::context_admission::stored::source_authority_required() {
return ccr::persist(original).map(RecoveryCopy::Tee);
}
match bound_copy(original) {
Ok(copy) => Some(copy),
Err(code) => {
note_kept_whole(original, code);
None
}
}
}
pub(crate) fn shorten_with_copy(original: &str, shortened: &str) -> Option<String> {
let Some(tool) = super::sticky_tools::expand_tool_for(original) else {
note_kept_whole(original, HANDLE_NOT_FOLLOWABLE);
return None;
};
let copy = match bound_copy(original) {
Ok(copy) => copy,
Err(code) => {
note_kept_whole(original, code);
return None;
}
};
let RecoveryCopy::Bound {
id,
lineage,
project,
} = copy
else {
return None;
};
let sent = format!("{shortened}\n{}", recovery_stub_for(&tool, &id));
if sent.len() >= original.len() {
return None;
}
let recorded = super::request_project::in_project(&project, || {
delivered::record_derived(&delivered::digest(&sent), &lineage)
});
if recorded {
gate::note_issued(original, &sent);
Some(sent)
} else {
note_kept_whole(original, DERIVED_NOT_RECORDED);
None
}
}
pub(crate) const DEDUP_WITHOUT_COPY: &str = "recovery.dedup_without_copy";
#[cfg(feature = "http-server")]
pub(crate) const SUMMARY_WITHOUT_COPY: &str = "recovery.summary_without_copy";
const HANDLE_NOT_FOLLOWABLE: &str = "recovery.handle_not_followable";
const DERIVED_NOT_RECORDED: &str = "recovery.derived_not_recorded";
const NO_PROJECT: &str = "recovery.no_project";
const UNKNOWN_LINEAGE: &str = "recovery.unknown_lineage";
fn bound_copy(original: &str) -> Result<RecoveryCopy, &'static str> {
let project = super::request_project::current_source_project().ok_or(NO_PROJECT)?;
super::request_project::in_project(&project, || {
let lineage = delivered::lineage_of(&delivered::digest(original))
.filter(delivered::Lineage::is_complete)
.ok_or(UNKNOWN_LINEAGE)?;
let id = lean_ctx::core::archive::store_recovery_with_lineage(original, &lineage)
.map_err(recovery_code)?;
Ok((id, lineage))
})
.map(|(id, lineage)| RecoveryCopy::Bound {
id,
lineage: Box::new(lineage),
project,
})
}
pub(crate) fn recovery_code(refusal: RecoveryRefusal) -> &'static str {
match refusal {
RecoveryRefusal::UnknownLineage => UNKNOWN_LINEAGE,
RecoveryRefusal::SourceNotPresented => "recovery.source_not_presented",
RecoveryRefusal::SourceNoLongerAdmitted => "recovery.source_no_longer_admitted",
RecoveryRefusal::NotStorable => "recovery.not_storable",
RecoveryRefusal::Missing => "recovery.missing",
}
}
#[cfg(feature = "http-server")]
pub(crate) fn resolve_recovery(id: &str) -> Option<Result<String, &'static str>> {
let id = RecoveryId::parse(id)?;
let Some(project) = super::request_project::current_source_project() else {
return lean_ctx::core::context_admission::stored::source_authority_required()
.then_some(Err(NO_PROJECT));
};
Some(super::request_project::in_project(&project, || {
lean_ctx::core::archive::retrieve_recovery_checked(&id).map_err(recovery_code)
}))
}
enum RecoveryCopy {
Tee(String),
Bound {
id: RecoveryId,
lineage: Box<delivered::Lineage>,
project: std::path::PathBuf,
},
}
fn compress_inner(content: &str, tool_name: Option<&str>, family: TokenizerFamily) -> String {
if content.trim().is_empty() || content.len() < 200 {
return content.to_string();
}
if tool_name.is_some_and(is_lean_ctx_tool) {
return content.to_string();
}
if lean_ctx::core::protect::has_markers(content) {
return lean_ctx::core::protect::compress_preserving(content, |seg| {
compress_inner(seg, tool_name, family)
});
}
if is_cited_research_output(content) {
if looks_like_markdown(content) {
let prose = compress_prose(content, None);
if prose.compressed_tokens < prose.original_tokens {
return prose.compressed;
}
}
return content.to_string();
}
if extract_command_hint(content).is_none()
&& looks_like_prose(content)
&& let Some(out) = squeeze_research_prose(content, family)
{
return out;
}
let cmd = infer_command(content, tool_name);
let generic_command = cmd.is_empty() || cmd == "shell";
if generic_command
&& (output_looks_like_test_run(content) || output_looks_like_build_failure(content))
{
return lean_ctx::shell::compress::engine::preserve_verbatim_pub_for(content, family);
}
lean_ctx::shell::compress::engine::compress_if_beneficial_for(&cmd, content, family)
}
fn output_looks_like_test_run(content: &str) -> bool {
const NEEDLES: &[&str] = &[
"test result:", "short test summary info", " passed in ", " failed in ", "=== RUN", "--- FAIL:", "--- PASS:", "Test Suites:", " examples, ", "FAILED", ];
NEEDLES.iter().any(|n| content.contains(n))
}
fn output_looks_like_build_failure(content: &str) -> bool {
const NEEDLES: &[&str] = &[
"error[", ": error:", "fatal error:", "undefined reference to", "panicked at", "could not compile", "Traceback (most recent call last)", "AssertionError", "make: ***", "Build FAILED",
"BUILD FAILED",
"Segmentation fault",
];
NEEDLES.iter().any(|n| content.contains(n))
}
fn is_cited_research_output(content: &str) -> bool {
content.contains("· Retrieved: ") && content.contains("\nSource: ")
}
const CODE_SYMBOLS: &str = "{}<>;=|\\$`";
fn looks_like_prose(content: &str) -> bool {
let sample: String = content.chars().take(4000).collect();
let total = sample.chars().count();
if total < 600 {
return false;
}
let total_f = total as f32;
let alpha = sample.chars().filter(|c| c.is_alphabetic()).count() as f32;
let spaces = sample.chars().filter(|c| *c == ' ').count() as f32;
let symbols = sample.chars().filter(|c| CODE_SYMBOLS.contains(*c)).count() as f32;
if alpha / total_f < 0.6 || spaces / total_f < 0.12 || symbols / total_f > 0.06 {
return false;
}
if sample.matches(['.', '!', '?']).count() < 4 {
return false;
}
let non_empty: Vec<&str> = sample.lines().filter(|l| !l.trim().is_empty()).collect();
if non_empty.is_empty() {
return false;
}
let avg_len =
non_empty.iter().map(|l| l.chars().count()).sum::<usize>() as f32 / non_empty.len() as f32;
avg_len >= 40.0
}
fn looks_like_markdown(content: &str) -> bool {
content.lines().any(|line| {
let trimmed = line.trim_start();
trimmed.starts_with("# ")
|| trimmed.starts_with("## ")
|| trimmed.starts_with("```")
|| trimmed.starts_with("~~~")
|| matches!(trimmed.as_bytes(), [b'-' | b'*' | b'+', b' ', ..])
|| trimmed.matches('|').count() >= 2
})
}
fn squeeze_research_prose(content: &str, family: TokenizerFamily) -> Option<String> {
let before = count_tokens_for(content, family);
let squeezed = squeeze_research_prose_body(content);
if squeezed.trim().is_empty() {
return None;
}
let after = count_tokens_for(&squeezed, family);
if after + 2 >= before {
return None;
}
Some(lean_ctx::core::protocol::append_savings_with_info(
&squeezed,
before,
after,
Some("research"),
None,
))
}
fn squeeze_research_prose_body(content: &str) -> String {
let cap = research_prose_cap();
if content.len() > cap {
return super::prose_ranker::squeeze(content, cap);
}
distill::squeeze_prose(content, cap)
}
fn is_lean_ctx_tool(name: &str) -> bool {
bare_tool_name(name).starts_with("ctx_") || name.starts_with("ctx_")
}
pub(crate) fn bare_tool_name(name: &str) -> &str {
name.rsplit("__")
.next()
.unwrap_or(name)
.rsplit([':', '/', '.'])
.next()
.unwrap_or(name)
}
#[cfg(feature = "http-server")]
pub(crate) fn expand_tool_name(name: &str) -> bool {
bare_tool_name(name) == EXPAND_TOOL
&& name.len() <= 128
&& name
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || b"_.:/-".contains(&byte))
}
fn infer_command(content: &str, tool_name: Option<&str>) -> String {
if let Some(cmd) = extract_command_hint(content) {
return cmd;
}
if let Some(name) = tool_name {
let nl = name.to_lowercase();
if nl.contains("bash") || nl.contains("shell") || nl.contains("terminal") {
return "shell".to_string();
}
if nl.contains("search") || nl.contains("grep") || nl.contains("find") {
return "grep".to_string();
}
}
String::new()
}
fn extract_command_hint(content: &str) -> Option<String> {
for line in content.lines().take(3) {
let trimmed = line.trim();
if let Some(cmd) = trimmed.strip_prefix("$ ") {
return Some(cmd.to_string());
}
if let Some(cmd) = trimmed.strip_prefix("% ") {
return Some(cmd.to_string());
}
}
None
}
#[cfg(test)]
#[path = "compress_tests.rs"]
mod tests;
#[cfg(all(test, feature = "http-server"))]
#[path = "compress_recovery_tests.rs"]
mod recovery_tests;