use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
use async_trait::async_trait;
use supercode::reduce::rehydrate::{
expand_reduction, sidecar_search, ExpandOutcome, SidecarSearchResult,
};
use supercode::reduce::{
export_session, project_messages, reduction_id, ReductionKind, ReductionLog, ReductionPolicy,
REDUCTION_SENTINEL,
};
use supercode::session::{Session, SessionFormat};
use supercode::sidecar::SidecarWriter;
use supercode::{
Agent, ChatMessage, ChatRequest, Config, FunctionCall, Provider, Role, ToolCall, Usage,
};
fn temp_dir(tag: &str) -> PathBuf {
static N: AtomicUsize = AtomicUsize::new(0);
let dir = std::env::temp_dir().join(format!(
"supercode-rehydrate-{tag}-{}-{}",
std::process::id(),
N.fetch_add(1, Ordering::SeqCst)
));
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn tool_call_msg(id: &str, name: &str, args: serde_json::Value) -> ChatMessage {
ChatMessage {
role: Role::Assistant,
content: None,
content_parts: None,
tool_calls: Some(vec![ToolCall {
id: id.to_string(),
kind: "function".to_string(),
function: FunctionCall {
name: name.to_string(),
arguments: args.to_string(),
},
}]),
tool_call_id: None,
name: None,
metadata: Default::default(),
}
}
fn big_output_with_needle(needle: &str) -> String {
let mut s = "Q".repeat(4096);
s.push_str(&"x".repeat(10_000));
s.push_str(needle);
s.push_str(&"y".repeat(20_000 - s.len()));
debug_assert_eq!(s.len(), 20_000);
s
}
struct BigOutputTool(String);
#[async_trait]
impl supercode::tools::Tool for BigOutputTool {
fn name(&self) -> &str {
"list_dir"
}
fn description(&self) -> &str {
"x"
}
fn parameters(&self) -> serde_json::Value {
serde_json::json!({"type": "object"})
}
async fn execute(
&self,
_a: serde_json::Value,
_c: &supercode::tools::ToolContext,
) -> supercode::Result<String> {
Ok(self.0.clone())
}
}
struct SearchThenExpand {
calls: AtomicUsize,
found_id: Mutex<Option<String>>,
}
#[async_trait]
impl Provider for SearchThenExpand {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode::Result<(ChatMessage, Usage)> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
match n {
0 => Ok((
tool_call_msg("c1", "list_dir", serde_json::json!({})),
Usage::default(),
)),
1 => {
assert!(
req.tools.iter().any(|t| t.name == "expand_reduction"),
"expand_reduction must be advertised once a ReductionPolicy is installed"
);
assert!(
req.tools.iter().any(|t| t.name == "sidecar_search"),
"sidecar_search must be advertised once a ReductionPolicy is installed"
);
let body = serde_json::to_string(&req.messages).unwrap();
assert!(body.contains(REDUCTION_SENTINEL));
assert!(!body.contains("NEEDLE-XYZ-123"));
Ok((
tool_call_msg(
"s1",
"sidecar_search",
serde_json::json!({"query": "NEEDLE-XYZ-123"}),
),
Usage::default(),
))
}
2 => {
let last = req.messages.last().unwrap();
assert_eq!(last.role, Role::Tool);
let result: SidecarSearchResult =
serde_json::from_str(last.content.as_deref().unwrap()).unwrap();
assert_eq!(
result.matches.len(),
1,
"expected exactly one match: {result:?}"
);
assert!(!result.truncated);
assert_eq!(result.matches[0].kind, "tool-output");
assert!(result.matches[0].snippet.contains("NEEDLE-XYZ-123"));
*self.found_id.lock().unwrap() = Some(result.matches[0].reduction_id.clone());
Ok((
tool_call_msg(
"e1",
"expand_reduction",
serde_json::json!({"reduction_id": result.matches[0].reduction_id}),
),
Usage::default(),
))
}
3 => {
let id = self.found_id.lock().unwrap().clone().unwrap();
Ok((
tool_call_msg(
"e2",
"expand_reduction",
serde_json::json!({"reduction_id": id, "byte_range": [0, 4]}),
),
Usage::default(),
))
}
_ => Ok((ChatMessage::assistant("done"), Usage::default())),
}
}
}
#[tokio::test]
async fn dev01_dev04_search_then_expand_whole_and_ranged() {
let dir = temp_dir("search-expand");
let original = big_output_with_needle("NEEDLE-XYZ-123");
let config = Config::builder().cwd(dir.clone()).build();
let mut reg = supercode::tools::ToolRegistry::new();
reg.register(BigOutputTool(original.clone()));
let mut agent = Agent::with_parts(
config,
Box::new(SearchThenExpand {
calls: AtomicUsize::new(0),
found_id: Mutex::new(None),
}),
reg,
);
agent.set_reduction_policy(ReductionPolicy {
tool_output_keep_bytes: 4096,
tool_output_trigger_bytes: 8192,
protect_last_n_tool_results: 0,
..ReductionPolicy::default()
});
let reply = agent.send("investigate").await.unwrap();
assert_eq!(reply, "done");
let result_for = |call_id: &str| {
agent
.history()
.iter()
.find(|m| m.role == Role::Tool && m.tool_call_id.as_deref() == Some(call_id))
.unwrap_or_else(|| panic!("no tool result for call `{call_id}` in history"))
.content
.clone()
.unwrap_or_default()
};
assert_eq!(
result_for("e1"),
original,
"expand_reduction with no byte_range must return the exact original bytes"
);
let ranged = result_for("e2");
let (header, body) = ranged
.split_once('\n')
.expect("ranged expand result should have a header line");
assert!(
header.contains("bytes 0..4 of 20000"),
"ranged expand header must name the slice and the total: {header}"
);
assert_eq!(
body, "QQQQ",
"expand_reduction with byte_range=[0,4] must return exactly that slice"
);
std::fs::remove_dir_all(&dir).ok();
}
fn read_call(id: &str, path: &Path) -> ChatMessage {
ChatMessage {
role: Role::Assistant,
content: None,
content_parts: None,
tool_calls: Some(vec![ToolCall {
id: id.to_string(),
kind: "function".to_string(),
function: FunctionCall {
name: "read_file".to_string(),
arguments: serde_json::json!({"path": path.to_string_lossy()}).to_string(),
},
}]),
tool_call_id: None,
name: None,
metadata: Default::default(),
}
}
fn stub_ids_in(text: &str) -> Vec<String> {
let re = regex::Regex::new(r"r\d{4}-[0-9a-f]{4}").unwrap();
re.find_iter(text).map(|m| m.as_str().to_string()).collect()
}
#[test]
fn dev02_stub_ids_are_sufficient_to_expand_every_kind() {
let dir = temp_dir("dev02-stub-ids");
let file_path = dir.join("f.txt");
let fresh_content = "b".repeat(4096); std::fs::write(&file_path, &fresh_content).unwrap();
let mut msgs = Vec::new();
for i in 0..4 {
msgs.push(ChatMessage::user(format!("filler {i}")));
msgs.push(ChatMessage::assistant(format!("filler reply {i}")));
}
let old_block_len = msgs.len();
msgs.push(read_call("rc1", &file_path));
msgs.push(ChatMessage::tool_result(
"rc1",
"read_file",
fresh_content.clone(),
));
let big_output = "z".repeat(50_000);
msgs.push(ChatMessage::tool_result(
"bc1",
"big_tool",
big_output.clone(),
));
msgs.push(ChatMessage::user("what did you find?"));
msgs.push(ChatMessage::assistant("let me check"));
let tail_len = msgs.len() - old_block_len;
let threshold = 10; assert_eq!(tail_len, 5);
let freshness = supercode::reduce::probe_read_freshness(&msgs);
let policy = ReductionPolicy {
tool_output_keep_bytes: 4096,
tool_output_trigger_bytes: 8192,
protect_last_n_tool_results: 0,
elide_stale_reads: true,
read_freshness: freshness,
clear_turns_older_than: Some(threshold),
..ReductionPolicy::default()
};
let (view, log) = project_messages(&msgs, &policy, &ReductionLog::default());
let kinds: Vec<&ReductionKind> = log.reductions.iter().map(|r| &r.kind).collect();
assert!(kinds
.iter()
.any(|k| matches!(k, ReductionKind::ToolOutputTruncated { .. })));
assert!(kinds
.iter()
.any(|k| matches!(k, ReductionKind::FileReadElided { .. })));
assert!(kinds
.iter()
.any(|k| matches!(k, ReductionKind::TurnsCleared { .. })));
assert_eq!(log.reductions.len(), 3, "{log:#?}");
let rendered: String = view
.iter()
.map(|m| m.content.clone().unwrap_or_default())
.collect::<Vec<_>>()
.join("\n");
let ids = stub_ids_in(&rendered);
assert_eq!(ids.len(), 3, "expected one stub id per kind: {rendered}");
for id in &ids {
let outcome = expand_reduction(&log, &msgs, None, id, None)
.unwrap_or_else(|e| panic!("expand_reduction({id}) failed: {e}"));
let r = log.reductions.iter().find(|r| &r.id == id).unwrap();
match &r.kind {
ReductionKind::ToolOutputTruncated { .. } => {
assert_eq!(outcome.content, big_output, "tool-output expand mismatch");
}
ReductionKind::FileReadElided { .. } => {
assert_eq!(outcome.content, fresh_content, "file-read expand mismatch");
}
ReductionKind::TurnsCleared { .. } => {
assert!(
outcome.content.contains("filler 0")
&& outcome.content.contains("filler reply 3"),
"turns-cleared expand should render the cleared range: {}",
outcome.content
);
}
other => panic!("unexpected kind: {other:?}"),
}
}
std::fs::remove_dir_all(&dir).ok();
}
struct ExpandThenAnswer {
calls: AtomicUsize,
}
#[async_trait]
impl Provider for ExpandThenAnswer {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode::Result<(ChatMessage, Usage)> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
match n {
0 => Ok((
tool_call_msg("c1", "list_dir", serde_json::json!({})),
Usage::default(),
)),
1 => {
let body = serde_json::to_string(&req.messages).unwrap();
let re = regex::Regex::new(r"r\d{4}-[0-9a-f]{4}").unwrap();
let id = re.find(&body).expect("a stub id must be present").as_str();
Ok((
tool_call_msg(
"e1",
"expand_reduction",
serde_json::json!({"reduction_id": id}),
),
Usage::default(),
))
}
_ => Ok((ChatMessage::assistant("done"), Usage::default())),
}
}
}
#[tokio::test]
async fn dev03_expand_reduction_call_result_exports_honestly() {
let dir = temp_dir("dev03-export");
let sidecar_path = dir.join("sess.sidecar.jsonl");
let original = "Q".repeat(20_000);
let config = Config::builder().cwd(dir.clone()).build();
let mut reg = supercode::tools::ToolRegistry::new();
reg.register(BigOutputTool(original.clone()));
let mut agent = Agent::with_parts(
config,
Box::new(ExpandThenAnswer {
calls: AtomicUsize::new(0),
}),
reg,
);
let empty_session = Session::from_claude_code_str("").unwrap();
let writer = SidecarWriter::create(&sidecar_path, &empty_session).unwrap();
agent.set_recorder(writer);
agent.set_reduction_policy(ReductionPolicy {
tool_output_keep_bytes: 4096,
tool_output_trigger_bytes: 8192,
protect_last_n_tool_results: 0,
..ReductionPolicy::default()
});
let reply = agent.send("go").await.unwrap();
assert_eq!(reply, "done");
let expand_call = agent
.history()
.iter()
.find_map(|m| {
m.tool_calls()
.iter()
.find(|c| c.function.name == "expand_reduction")
})
.expect("expand_reduction call must be in history");
let expand_result = agent
.history()
.iter()
.find(|m| {
m.role == Role::Tool && m.tool_call_id.as_deref() == Some(expand_call.id.as_str())
})
.expect("matching expand_reduction result must be in history");
assert_eq!(expand_result.content.as_deref(), Some(original.as_str()));
let sidecar_jsonl = std::fs::read_to_string(&sidecar_path).unwrap();
for format in [SessionFormat::ClaudeCode, SessionFormat::Codex] {
let exported = export_session(&sidecar_jsonl, format)
.unwrap_or_else(|e| panic!("export_session({format:?}) failed: {e}"));
assert!(
!exported.contains(REDUCTION_SENTINEL),
"export_session({format:?}) leaked the reduction sentinel — the intrinsic pair \
should need no leak-guard special case:\n{exported}"
);
let reloaded = Session::load_str(&exported, format)
.unwrap_or_else(|e| panic!("reloading export_session({format:?}) failed: {e}"));
let reloaded_call = reloaded
.messages
.iter()
.find_map(|m| {
m.tool_calls()
.iter()
.find(|c| c.function.name == "expand_reduction")
.cloned()
})
.unwrap_or_else(|| panic!("{format:?} reload lost the expand_reduction call"));
let reloaded_result = reloaded
.messages
.iter()
.find(|m| m.tool_call_id.as_deref() == Some(reloaded_call.id.as_str()))
.unwrap_or_else(|| panic!("{format:?} reload lost the matching tool result"));
assert_eq!(
reloaded_result.content.as_deref(),
Some(original.as_str()),
"{format:?}: exported/reloaded expand result must still be byte-exact"
);
}
std::fs::remove_dir_all(&dir).ok();
}
#[test]
fn dev05_oversized_expand_result_is_itself_a7_truncated_and_reexpandable() {
let big = "m".repeat(200_000);
let msgs = vec![ChatMessage::tool_result("call_1", "bash", big.clone())];
let policy = ReductionPolicy {
tool_output_keep_bytes: 4096,
tool_output_trigger_bytes: 8192,
protect_last_n_tool_results: 0,
..ReductionPolicy::default()
};
let (_view1, log1) = project_messages(&msgs, &policy, &ReductionLog::default());
assert_eq!(log1.reductions.len(), 1);
let original_id = log1.reductions[0].id.clone();
let ExpandOutcome { content, .. } =
expand_reduction(&log1, &msgs, None, &original_id, None).unwrap();
assert_eq!(
content, big,
"expand_reduction must return the exact original bytes"
);
let mut msgs2 = msgs.clone();
msgs2.push(ChatMessage::tool_result(
"call_expand",
"expand_reduction",
content.clone(),
));
let expand_result_idx = msgs2.len() - 1;
let (view2, log2) = project_messages(&msgs2, &policy, &log1);
assert_eq!(
log2.reductions.len(),
2,
"the original reduction reapplies verbatim, plus a fresh one over the expand result"
);
let fresh = log2
.reductions
.iter()
.find(|r| r.ptr.addr.index == expand_result_idx)
.expect("a fresh reduction must cover the just-expanded, still-oversized tool result");
assert_ne!(
fresh.id, original_id,
"the fresh reduction gets its own new id"
);
assert!(fresh.placeholder.starts_with(REDUCTION_SENTINEL));
assert!(
fresh.placeholder.contains(&fresh.id),
"the fresh stub carries its own id: {}",
fresh.placeholder
);
let reduced_copy = view2[expand_result_idx].content.clone().unwrap();
assert!(
reduced_copy.len() < 10_000,
"the re-truncated view must stay small: {} bytes",
reduced_copy.len()
);
let outcome2 = expand_reduction(&log2, &msgs2, None, &fresh.id, None).unwrap();
assert_eq!(
outcome2.content, big,
"re-expanding the fresh stub must be byte-exact"
);
}
const SECRET: &str = "the deploy key is DK-771-ZQ";
struct Tier2Demo {
calls: AtomicUsize,
}
#[async_trait]
impl Provider for Tier2Demo {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode::Result<(ChatMessage, Usage)> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
match n {
0 => Ok((
ChatMessage::assistant("Noted, I'll remember that."),
Usage::default(),
)),
1..=3 => Ok((ChatMessage::assistant(format!("ack {n}")), Usage::default())),
4 => {
assert!(req.tools.iter().any(|t| t.name == "sidecar_search"));
assert!(req.tools.iter().any(|t| t.name == "expand_reduction"));
let body = serde_json::to_string(&req.messages).unwrap();
assert!(
body.contains(REDUCTION_SENTINEL),
"the secret's turn must have been A10-cleared by now: {body}"
);
assert!(
!body.contains("DK-771-ZQ"),
"the secret must not be directly visible in the reduced view"
);
Ok((
tool_call_msg(
"s1",
"sidecar_search",
serde_json::json!({"query": "deploy key"}),
),
Usage::default(),
))
}
5 => {
let last = req.messages.last().unwrap();
assert_eq!(last.role, Role::Tool);
let result: SidecarSearchResult =
serde_json::from_str(last.content.as_deref().unwrap()).unwrap();
assert_eq!(result.matches.len(), 1, "{result:?}");
Ok((
tool_call_msg(
"e1",
"expand_reduction",
serde_json::json!({"reduction_id": result.matches[0].reduction_id}),
),
Usage::default(),
))
}
_ => {
let last = req.messages.last().unwrap();
assert_eq!(last.role, Role::Tool);
let expanded = last.content.clone().unwrap_or_default();
assert!(
expanded.contains("DK-771-ZQ"),
"expand_reduction must have recovered the secret: {expanded}"
);
Ok((
ChatMessage::assistant("The deploy key is DK-771-ZQ."),
Usage::default(),
))
}
}
}
}
#[tokio::test]
async fn dev06_tier2_demo_sidecar_search_then_expand_answers_from_cleared_turns() {
let dir = temp_dir("dev06-demo");
let sidecar_path = dir.join("demo.sidecar.jsonl");
let config = Config::builder()
.cwd(dir.clone())
.compact_after_messages(6) .build();
let mut agent = Agent::with_provider(
config,
Box::new(Tier2Demo {
calls: AtomicUsize::new(0),
}),
);
let empty_session = Session::from_claude_code_str("").unwrap();
let writer = SidecarWriter::create(&sidecar_path, &empty_session).unwrap();
agent.set_recorder(writer);
agent.set_reduction_policy(ReductionPolicy::default());
agent
.send(format!("Please remember this: {SECRET}"))
.await
.unwrap();
for i in 0..3 {
agent
.send(format!("filler turn {i}, just say ack"))
.await
.unwrap();
}
assert!(
agent
.reduction_log()
.reductions
.iter()
.any(|r| matches!(r.kind, ReductionKind::TurnsCleared { .. })),
"expected a TurnsCleared reduction before the question turn: {:?}",
agent.reduction_log()
);
let reply = agent.send("What was the deploy key?").await.unwrap();
assert_eq!(reply, "The deploy key is DK-771-ZQ.");
let workspace_target = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../target");
std::fs::create_dir_all(&workspace_target).ok();
let transcript_path = workspace_target.join("tr1-demo-transcript.jsonl");
agent.save_transcript(&transcript_path).unwrap();
assert!(transcript_path.exists());
eprintln!(
"dev/06 demo transcript archived at {}",
transcript_path.display()
);
std::fs::remove_dir_all(&dir).ok();
}
fn over_cap_output_with_needle(needle: &str) -> String {
let mut s = "Q".repeat(4096);
s.push_str(&"x".repeat(120_000 - s.len()));
s.push_str(needle);
s.push_str(&"y".repeat(150_000 - s.len()));
s
}
struct OverCapSearchThenExpand {
calls: AtomicUsize,
}
#[async_trait]
impl Provider for OverCapSearchThenExpand {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode::Result<(ChatMessage, Usage)> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
match n {
0 => Ok((
tool_call_msg("c1", "list_dir", serde_json::json!({})),
Usage::default(),
)),
1 => {
let body = serde_json::to_string(&req.messages).unwrap();
assert!(body.contains(REDUCTION_SENTINEL));
assert!(
!body.contains("TAIL-NEEDLE-B1"),
"the needle lives past the cap; it must not be visible in the view"
);
Ok((
tool_call_msg(
"s1",
"sidecar_search",
serde_json::json!({"query": "TAIL-NEEDLE-B1"}),
),
Usage::default(),
))
}
2 => {
let last = req.messages.last().unwrap();
assert_eq!(last.role, Role::Tool);
let result: SidecarSearchResult =
serde_json::from_str(last.content.as_deref().unwrap()).unwrap();
assert_eq!(
result.matches.len(),
1,
"search must reach past the cap into the recorded bytes: {result:?}"
);
assert!(result.matches[0].snippet.contains("TAIL-NEEDLE-B1"));
Ok((
tool_call_msg(
"e1",
"expand_reduction",
serde_json::json!({"reduction_id": result.matches[0].reduction_id}),
),
Usage::default(),
))
}
_ => Ok((ChatMessage::assistant("done"), Usage::default())),
}
}
}
#[tokio::test]
async fn b1_over_cap_output_expands_to_full_recorded_bytes() {
let dir = temp_dir("b1-over-cap");
let sidecar_path = dir.join("sess.sidecar.jsonl");
let original = over_cap_output_with_needle("TAIL-NEEDLE-B1");
assert_eq!(original.len(), 150_000);
let config = Config::builder().cwd(dir.clone()).build();
let mut reg = supercode::tools::ToolRegistry::new();
reg.register(BigOutputTool(original.clone()));
let mut agent = Agent::with_parts(
config,
Box::new(OverCapSearchThenExpand {
calls: AtomicUsize::new(0),
}),
reg,
);
let empty_session = Session::from_claude_code_str("").unwrap();
let writer = SidecarWriter::create(&sidecar_path, &empty_session).unwrap();
agent.set_recorder(writer);
agent.set_reduction_policy(ReductionPolicy {
tool_output_keep_bytes: 4096,
tool_output_trigger_bytes: 8192,
protect_last_n_tool_results: 0,
..ReductionPolicy::default()
});
let reply = agent.send("go").await.unwrap();
assert_eq!(reply, "done");
let history_copy = agent
.history()
.iter()
.find(|m| m.role == Role::Tool && m.tool_call_id.as_deref() == Some("c1"))
.and_then(|m| m.content.clone())
.unwrap();
assert_eq!(
history_copy.len(),
original.len(),
"the gate must keep the FULL bytes in history, not a capped prefix"
);
assert_eq!(history_copy, original);
assert!(
!history_copy.contains("bytes total, showing first"),
"no cap notice should ever be appended once the D6/A7 gate is active: {}",
&history_copy[history_copy.len().saturating_sub(200)..]
);
let sidecar_raw = std::fs::read_to_string(&sidecar_path).unwrap();
let recorded = Session::from_native_str(&sidecar_raw).unwrap();
let expand_result = recorded
.messages
.iter()
.find(|m| m.role == Role::Tool && m.tool_call_id.as_deref() == Some("e1"))
.and_then(|m| m.content.clone())
.expect("the expand result must be recorded in the sidecar");
assert_eq!(
expand_result, original,
"expand_reduction must return the FULL recorded bytes, not the capped history copy"
);
std::fs::remove_dir_all(&dir).ok();
}
struct MalformedRangeProbe {
calls: AtomicUsize,
found_id: Mutex<Option<String>>,
}
impl MalformedRangeProbe {
fn shapes() -> Vec<serde_json::Value> {
vec![
serde_json::json!([1]),
serde_json::json!([1, 2, 3]),
serde_json::json!(["a", "b"]),
serde_json::json!([-5, 10]),
serde_json::json!([1.5, 2]),
serde_json::json!([100, 5]),
]
}
}
#[async_trait]
impl Provider for MalformedRangeProbe {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode::Result<(ChatMessage, Usage)> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
let shapes = Self::shapes();
if n == 0 {
return Ok((
tool_call_msg("c1", "list_dir", serde_json::json!({})),
Usage::default(),
));
}
if n == 1 {
let body = serde_json::to_string(&req.messages).unwrap();
let re = regex::Regex::new(r"r\d{4}-[0-9a-f]{4}").unwrap();
let id = re.find(&body).expect("a stub id must be present").as_str();
*self.found_id.lock().unwrap() = Some(id.to_string());
} else {
let last = req.messages.last().unwrap();
assert_eq!(last.role, Role::Tool);
let content = last.content.as_deref().unwrap_or_default();
assert!(
content.starts_with("Error:"),
"probe {} must produce an error result: {content}",
n - 2
);
assert!(
content.contains("byte_range"),
"probe {} error must name byte_range: {content}",
n - 2
);
let is_reversed_probe = n - 2 == shapes.len() - 1;
if is_reversed_probe {
assert!(
content.contains("reversed"),
"the reversed-range error must say so: {content}"
);
}
assert!(
content.contains("20000"),
"probe {} error must name the true total: {content}",
n - 2
);
}
let probe = n - 1;
if probe < shapes.len() {
let id = self.found_id.lock().unwrap().clone().unwrap();
Ok((
tool_call_msg(
&format!("p{probe}"),
"expand_reduction",
serde_json::json!({"reduction_id": id, "byte_range": shapes[probe]}),
),
Usage::default(),
))
} else {
Ok((ChatMessage::assistant("survived"), Usage::default()))
}
}
}
#[tokio::test]
async fn b3_malformed_byte_ranges_error_recoverably_through_dispatch() {
let dir = temp_dir("b3-ranges");
let config = Config::builder().cwd(dir.clone()).build();
let mut reg = supercode::tools::ToolRegistry::new();
reg.register(BigOutputTool("Q".repeat(20_000)));
let mut agent = Agent::with_parts(
config,
Box::new(MalformedRangeProbe {
calls: AtomicUsize::new(0),
found_id: Mutex::new(None),
}),
reg,
);
agent.set_reduction_policy(ReductionPolicy {
tool_output_keep_bytes: 4096,
tool_output_trigger_bytes: 8192,
protect_last_n_tool_results: 0,
..ReductionPolicy::default()
});
let reply = agent.send("probe").await.unwrap();
assert_eq!(reply, "survived");
std::fs::remove_dir_all(&dir).ok();
}
struct ErrorPathProbe {
calls: AtomicUsize,
}
#[async_trait]
impl Provider for ErrorPathProbe {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode::Result<(ChatMessage, Usage)> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
let last_error = |hint: &str| {
let last = req.messages.last().unwrap();
assert_eq!(last.role, Role::Tool);
let content = last.content.as_deref().unwrap_or_default();
assert!(
content.starts_with("Error:"),
"expected an error result, got: {content}"
);
assert!(
content.contains(hint),
"error should mention `{hint}`: {content}"
);
};
match n {
0 => Ok((
tool_call_msg(
"x1",
"expand_reduction",
serde_json::json!({"reduction_id": "r9999-dead"}),
),
Usage::default(),
)),
1 => {
last_error("r9999-dead");
let call = ChatMessage {
role: Role::Assistant,
content: None,
content_parts: None,
tool_calls: Some(vec![ToolCall {
id: "x2".to_string(),
kind: "function".to_string(),
function: FunctionCall {
name: "expand_reduction".to_string(),
arguments: "not json".to_string(),
},
}]),
tool_call_id: None,
name: None,
metadata: Default::default(),
};
Ok((call, Usage::default()))
}
2 => {
last_error("expand_reduction");
Ok((
tool_call_msg("x3", "sidecar_search", serde_json::json!({})),
Usage::default(),
))
}
3 => {
last_error("query");
Ok((
tool_call_msg("x4", "sidecar_search", serde_json::json!({"query": " "})),
Usage::default(),
))
}
_ => {
last_error("query");
Ok((ChatMessage::assistant("recovered"), Usage::default()))
}
}
}
}
#[tokio::test]
async fn b2_f4_error_paths_are_recoverable_through_the_loop() {
let dir = temp_dir("error-paths");
let config = Config::builder().cwd(dir.clone()).build();
let mut agent = Agent::with_provider(
config,
Box::new(ErrorPathProbe {
calls: AtomicUsize::new(0),
}),
);
agent.set_reduction_policy(ReductionPolicy::default());
let reply = agent.send("probe the error paths").await.unwrap();
assert_eq!(reply, "recovered");
std::fs::remove_dir_all(&dir).ok();
}
#[test]
fn b4_cleared_tool_call_arguments_are_searchable_and_expandable() {
let mut msgs = vec![
ChatMessage::user("write my notes file"),
tool_call_msg(
"w1",
"write_file",
serde_json::json!({
"path": "notes.txt",
"content": "ARGS-ONLY-PAYLOAD-77: the real content of the file"
}),
),
ChatMessage::tool_result("w1", "write_file", "ok, wrote notes.txt"),
ChatMessage::assistant("Written."),
];
for i in 0..4 {
msgs.push(ChatMessage::user(format!("filler {i}")));
msgs.push(ChatMessage::assistant(format!("filler reply {i}")));
}
let policy = ReductionPolicy {
clear_turns_older_than: Some(8),
..ReductionPolicy::default()
};
let (_view, log) = project_messages(&msgs, &policy, &ReductionLog::default());
let cleared = log
.reductions
.iter()
.find(|r| matches!(r.kind, ReductionKind::TurnsCleared { .. }))
.expect("the old block must have been cleared");
let hits = sidecar_search(&log, &msgs, None, "ARGS-ONLY-PAYLOAD-77").unwrap();
assert_eq!(hits.matches.len(), 1, "{hits:?}");
assert_eq!(hits.matches[0].reduction_id, cleared.id);
assert!(hits.matches[0].snippet.contains("ARGS-ONLY-PAYLOAD-77"));
let outcome = expand_reduction(&log, &msgs, None, &cleared.id, None).unwrap();
assert!(
outcome.content.contains("ARGS-ONLY-PAYLOAD-77"),
"expansion must include argument-only content: {}",
outcome.content
);
assert!(outcome.content.contains("write_file"));
assert!(outcome.content.contains("w1"));
assert!(outcome.content.contains("tool_call_id=w1"));
assert!(outcome.content.contains("ok, wrote notes.txt"));
}
#[tokio::test]
async fn b5_intrinsics_absent_without_a_reduction_policy() {
let names = std::sync::Arc::new(Mutex::new(None));
struct Shared(std::sync::Arc<Mutex<Option<Vec<String>>>>);
#[async_trait]
impl Provider for Shared {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode::Result<(ChatMessage, Usage)> {
let mut names = self.0.lock().unwrap();
if names.is_none() {
*names = Some(req.tools.iter().map(|t| t.name.clone()).collect());
}
Ok((ChatMessage::assistant("ok"), Usage::default()))
}
}
let config = Config::builder().build();
let mut agent = Agent::with_provider(config, Box::new(Shared(names.clone())));
agent.send("hi").await.unwrap();
let seen = names.lock().unwrap().clone().unwrap();
assert!(
!seen.iter().any(|n| n == "expand_reduction"),
"expand_reduction must not be advertised without a policy: {seen:?}"
);
assert!(
!seen.iter().any(|n| n == "sidecar_search"),
"sidecar_search must not be advertised without a policy: {seen:?}"
);
let names2 = std::sync::Arc::new(Mutex::new(None));
let config = Config::builder().build();
let mut agent = Agent::with_provider(config, Box::new(Shared(names2.clone())));
agent.set_reduction_policy(ReductionPolicy::default());
agent.send("hi").await.unwrap();
let seen = names2.lock().unwrap().clone().unwrap();
assert!(seen.iter().any(|n| n == "expand_reduction"), "{seen:?}");
assert!(seen.iter().any(|n| n == "sidecar_search"), "{seen:?}");
}
#[test]
fn f1_image_reduction_id_reachable_from_view_and_expands() {
let data_url = format!("data:image/png;base64,{}", "A".repeat(20_000));
let msgs = vec![
ChatMessage::user_with_images("look at this screenshot", std::slice::from_ref(&data_url)),
ChatMessage::assistant("Looking."),
];
let policy = ReductionPolicy::default();
let (view, log) = project_messages(&msgs, &policy, &ReductionLog::default());
assert_eq!(log.reductions.len(), 1, "{log:#?}");
assert!(matches!(
log.reductions[0].kind,
ReductionKind::ImageRedacted { .. }
));
let rendered: String = view
.iter()
.map(|m| {
let mut s = m.content.clone().unwrap_or_default();
for part in m.content_parts.as_deref().unwrap_or_default() {
if let Some(text) = part.get("text").and_then(|t| t.as_str()) {
s.push('\n');
s.push_str(text);
}
}
s
})
.collect::<Vec<_>>()
.join("\n");
assert!(
!rendered.contains(&data_url),
"the data URL itself must be gone from the view"
);
let ids = stub_ids_in(&rendered);
assert_eq!(
ids.len(),
1,
"the image stub id must be visible in the model's view: {rendered}"
);
assert_eq!(ids[0], log.reductions[0].id);
let outcome = expand_reduction(&log, &msgs, None, &ids[0], None).unwrap();
assert_eq!(outcome.content, data_url);
assert_eq!(outcome.total_bytes, data_url.len());
}
#[test]
fn tr3_dev06_file_read_diffed_stub_carries_id_and_expands_to_full_re_read() {
use std::fmt::Write as _;
let file_path = PathBuf::from("/workspace/src/tr3_dev06.rs");
let mut base_content = String::with_capacity(5000);
for i in 0..500 {
writeln!(base_content, "line {i:04}").unwrap();
}
let mut new_content = String::with_capacity(base_content.len());
for (i, line) in base_content.lines().enumerate() {
if (250..252).contains(&i) {
writeln!(new_content, "CHANGED {i}").unwrap();
} else {
writeln!(new_content, "{line}").unwrap();
}
}
assert_ne!(base_content, new_content);
let msgs = vec![
ChatMessage::user("read the file"),
read_call("rd1", &file_path),
ChatMessage::tool_result("rd1", "read_file", base_content.clone()),
ChatMessage::user("small edit"),
ChatMessage::assistant("done"),
read_call("rd2", &file_path),
ChatMessage::tool_result("rd2", "read_file", new_content.clone()),
];
let new_idx = 6;
let policy = ReductionPolicy {
tool_output_trigger_bytes: usize::MAX, protect_last_n_tool_results: 0,
..ReductionPolicy::default() };
let (view, log) = project_messages(&msgs, &policy, &ReductionLog::default());
assert_eq!(log.reductions.len(), 1, "{log:#?}");
let r = &log.reductions[0];
assert!(
matches!(r.kind, ReductionKind::FileReadDiffed { .. }),
"expected FileReadDiffed, got {:?}",
r.kind
);
let rendered: String = view
.iter()
.map(|m| m.content.clone().unwrap_or_default())
.collect::<Vec<_>>()
.join("\n");
let ids = stub_ids_in(&rendered);
assert_eq!(ids.len(), 1, "{rendered}");
assert_eq!(ids[0], r.id);
assert_eq!(reduction_id(&view[new_idx]), Some(r.id.as_str()));
let outcome = expand_reduction(&log, &msgs, None, &ids[0], None).unwrap();
assert_eq!(outcome.content, new_content);
assert_eq!(outcome.total_bytes, new_content.len());
assert_ne!(outcome.content, base_content);
}