use std::collections::HashSet;
use serde_json::Value;
const CORE: [&str; 6] = ["Read", "Edit", "Write", "Bash", "Glob", "Grep"];
pub fn working_set(messages: &[Value], recent_k: usize) -> HashSet<String> {
let mut keep: HashSet<String> = CORE.iter().map(|s| s.to_string()).collect();
let start = messages.len().saturating_sub(recent_k);
for msg in &messages[start..] {
collect_tool_use_names(msg.get("content"), &mut keep);
}
keep
}
fn collect_tool_use_names(content: Option<&Value>, out: &mut HashSet<String>) {
let Some(blocks) = content.and_then(Value::as_array) else {
return;
};
for block in blocks {
if block.get("type").and_then(Value::as_str) == Some("tool_use") {
if let Some(name) = block.get("name").and_then(Value::as_str) {
out.insert(name.to_string());
}
}
}
}
pub fn mark_deferred(tools: &[Value], keep: &HashSet<String>) -> (Vec<Value>, usize) {
let mut deferred = 0usize;
let out = tools
.iter()
.map(|tool| {
let name = tool.get("name").and_then(Value::as_str);
match name {
Some(n) if !keep.contains(n) => {
let mut t = tool.clone();
if let Some(obj) = t.as_object_mut() {
obj.insert("defer_loading".to_string(), Value::Bool(true));
}
deferred += 1;
t
}
_ => tool.clone(),
}
})
.collect();
(out, deferred)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn working_set_keeps_recently_invoked_tools_plus_core() {
let messages = vec![json!({"role":"assistant","content":[
{"type":"tool_use","name":"WebFetch","id":"t1","input":{}}]})];
let ws = working_set(&messages, 8);
assert!(ws.contains("WebFetch"), "recently used tool kept");
assert!(ws.contains("Read"), "core tool always kept");
}
#[test]
fn working_set_only_scans_the_recent_window() {
let mut messages = vec![json!({"role":"assistant","content":[
{"type":"tool_use","name":"AncientTool","id":"a","input":{}}]})];
for i in 0..10 {
messages.push(json!({"role":"user","content":format!("turn {i}")}));
}
let ws = working_set(&messages, 3);
assert!(!ws.contains("AncientTool"), "tool outside window dropped");
}
#[test]
fn mark_deferred_sets_flag_on_unused_tools_preserving_order() {
let tools = vec![
json!({"name":"Read"}),
json!({"name":"WebFetch"}),
json!({"name":"ObscureTool"}),
];
let mut keep = HashSet::new();
keep.insert("Read".to_string());
let (out, deferred) = mark_deferred(&tools, &keep);
assert_eq!(deferred, 2);
assert_eq!(out.len(), 3);
assert_eq!(out[0]["name"], json!("Read"));
assert_eq!(out[1]["name"], json!("WebFetch"));
assert_eq!(out[2]["name"], json!("ObscureTool"));
assert!(out[0].get("defer_loading").is_none());
assert_eq!(out[1]["defer_loading"], json!(true));
assert_eq!(out[2]["defer_loading"], json!(true));
}
#[test]
fn mark_deferred_is_deterministic_across_calls() {
let tools = vec![json!({"name":"A"}), json!({"name":"B"})];
let keep: HashSet<String> = ["A".to_string()].into_iter().collect();
let a = mark_deferred(&tools, &keep).0;
let b = mark_deferred(&tools, &keep).0;
assert_eq!(serde_json::to_string(&a).unwrap(), serde_json::to_string(&b).unwrap());
}
#[test]
fn mark_deferred_passes_through_nameless_tools_untouched() {
let tools = vec![json!({"description":"no name here"})];
let (out, deferred) = mark_deferred(&tools, &HashSet::new());
assert_eq!(deferred, 0);
assert!(out[0].get("defer_loading").is_none());
}
}