1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//! P1 (Prolonged-Session Stack): tool deferral via Anthropic's native
//! `defer_loading`.
//!
//! Claude Code's ~80K-token request prefix is dominated by `tools[]` schemas
//! (many connected MCP servers), re-read on every turn. Most turns use 2-3
//! tools. Anthropic supports `defer_loading: true` on a tool definition: a
//! deferred tool is NOT rendered into the cached `tools[]` prefix; when the
//! model needs it, tool-search appends its schema as a `tool_reference` block
//! in `messages` (after the breakpoint), so the prefix cache is never broken,
//! even mid-session.
//!
//! This module marks every tool NOT in the recent working set with
//! `defer_loading: true`, keeping the working set always-loaded. It only ever
//! ADDS the flag -- names, order, and count are unchanged -- so the `tools[]`
//! array stays byte-stable turn-over-turn (the binding cache-safety rule)
//! while the cached prefix shrinks. See
//! docs/superpowers/plans/2026-07-11-prolonged-session-stack.md, step P1.
use std::collections::HashSet;
use serde_json::Value;
/// Tools always kept loaded regardless of recent use -- the core edit/inspect
/// loop a coding agent reaches for constantly, so deferring them would just
/// cause immediate reloads.
const CORE: [&str; 6] = ["Read", "Edit", "Write", "Bash", "Glob", "Grep"];
/// The working set for a session: every tool invoked (`tool_use`) in the last
/// `recent_k` turns, unioned with the always-keep [`CORE`] set.
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());
}
}
}
}
/// Does this request's `tools[]` overlap the working set at all? When it
/// does not -- not even the always-keep CORE builtins are present -- the
/// request is not the coding-agent main loop L-A was designed for (observed
/// live 2026-07-17: Claude Code's one-shot background utility calls carry a
/// bespoke tool catalog with zero CORE/recent overlap). On such a request
/// the working-set heuristic has no signal, and deferring 100% of a
/// bespoke toolset risks hiding schemas the call was built around. Zero
/// overlap => the caller must skip deferral entirely (fail closed).
pub fn has_working_set_overlap(tools: &[Value], keep: &HashSet<String>) -> bool {
tools
.iter()
.filter_map(|t| t.get("name").and_then(Value::as_str))
.any(|n| keep.contains(n))
}
/// Mark every tool whose `name` is NOT in `keep` with `defer_loading: true`,
/// preserving order and count (only a boolean flag is added, so the `tools[]`
/// bytes stay stable modulo the flag). Returns `(tools, deferred_count)`,
/// where the count covers only tools THIS call newly flagged -- a tool the
/// client already sent with `defer_loading: true` passes through
/// byte-identical and is not counted (the count feeds telemetry and must
/// reflect proxy-added deferrals only).
///
/// A tool that already carries a `cache_control` breakpoint keeps it; a tool
/// with no `name` is passed through untouched (defensive: never corrupt an
/// unexpected shape).
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) => {
if tool.get("defer_loading") == Some(&Value::Bool(true)) {
return tool.clone(); // client already deferred it
}
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 no_overlap_means_not_a_main_loop_request_and_no_deferral() {
// Live regression (2026-07-17): Claude Code's background utility
// calls (one-shot, msgs=1, model=sonnet-4-6) carry a bespoke 81-tool
// catalog with NO overlap with CORE or any recent tool_use. The
// working-set heuristic has zero signal on such a request; deferring
// 100% of its tools risks hiding schemas the call was built around.
// Zero overlap => leave the request alone entirely.
let keep = working_set(&[], 8); // CORE only
let bespoke = vec![
json!({"name":"CronCreate"}),
json!({"name":"Monitor"}),
json!({"name":"SendMessage"}),
];
assert!(!has_working_set_overlap(&bespoke, &keep), "no overlap -> skip deferral");
// The main-loop shape (builtins present) DOES overlap.
let main_loop = vec![json!({"name":"Read"}), json!({"name":"ObscureTool"})];
assert!(has_working_set_overlap(&main_loop, &keep), "core overlap -> defer eligible");
}
#[test]
fn mark_deferred_does_not_count_tools_already_deferred_by_the_client() {
// A client-side pre-deferred tool keeps its flag (byte-identical) and
// is not counted as OUR deferral -- the count feeds telemetry/logs
// and must reflect proxy-added flags only.
let keep: HashSet<String> = ["Read"].iter().map(|s| s.to_string()).collect();
let tools = vec![
json!({"name":"Read"}),
json!({"name":"AlreadyDeferred", "defer_loading": true}),
json!({"name":"FreshlyDeferred"}),
];
let (out, count) = mark_deferred(&tools, &keep);
assert_eq!(count, 1, "only the freshly-flagged tool counts");
assert_eq!(out[1]["defer_loading"], json!(true), "pre-deferred flag preserved");
assert_eq!(out[2]["defer_loading"], json!(true), "unused tool freshly deferred");
}
#[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() {
// An old tool_use outside the recent_k window is NOT in the set.
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);
// order + count unchanged (byte-stability rule)
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"));
// kept tool: no defer_loading; unused tools: defer_loading true
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() {
// Same inputs -> byte-identical output (cache-safety).
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());
}
}