From 7a4cc8423813c016998fdab9b23e3f4def32c0a0 Mon Sep 17 00:00:00 2001 From: tracycam Date: Wed, 22 Jul 2026 16:34:46 +0800 Subject: [PATCH 1/2] fix(context): remove silent age-based tool reclaim --- CONFIGURATION.md | 6 +- README.md | 2 +- assets/magic-context.schema.json | 2 +- crates/mc-module/gen/gen-selection-golden.ts | 35 +--- crates/mc-module/src/selection.rs | 192 +++++++----------- crates/mc-module/src/transform.rs | 77 ++----- .../mc-module/testdata/selection-golden.json | 157 -------------- .../content/docs/reference/configuration.md | 2 +- .../pi-plugin/src/context-handler.test.ts | 65 +++++- packages/pi-plugin/src/context-handler.ts | 92 ++++----- .../plugin/src/config/schema/magic-context.ts | 2 +- .../magic-context/storage-meta-session.ts | 14 -- .../features/magic-context/storage-meta.ts | 1 - .../src/features/magic-context/storage.ts | 1 - .../hooks/magic-context/apply-operations.ts | 2 +- .../magic-context/supersession-reclaim.ts | 11 +- .../src/hooks/magic-context/tool-reclaim.ts | 49 ----- .../transform-postprocess-phase.test.ts | 149 ++------------ .../transform-postprocess-phase.ts | 100 ++++----- 19 files changed, 255 insertions(+), 704 deletions(-) delete mode 100644 packages/plugin/src/hooks/magic-context/tool-reclaim.ts diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 96b6d0175..76a982631 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -585,7 +585,7 @@ Tier boundaries are hardcoded to keep behavior predictable and prevent cache-bus **Always compressed from the original.** The pristine pre-caveman text is persisted in `source_contents` per tag. When a tag shifts deeper (lite → full → ultra), caveman compresses the ORIGINAL text at the new target depth rather than the already-cavemaned intermediate, so repeated tier shifts converge to exactly the same output as direct compression at the final depth. -**Cache safety.** Runs only on execute-threshold heuristic passes (same gate as automatic tool drops), so the single cache-busting pass materializes both tool drops and caveman compression together. Defer passes don't run caveman, and tier assignments are persisted in `tags.caveman_depth` so the next pass re-compresses only the tags that have shifted tiers. +**Cache safety.** Runs only on execute-threshold heuristic passes, so a cache-busting pass can materialize queued tool drops and caveman compression together. Defer passes don't run caveman, and tier assignments are persisted in `tags.caveman_depth` so the next pass re-compresses only the tags that have shifted tiers. **Relationship to `ctx_reduce`.** Caveman compression is independent of the agent-driven `ctx_reduce` tool. `ctx_reduce` is still best for tool outputs the agent knows are spent; caveman targets old prose only, and dropped tags always win over caveman rewriting. To hide the reduce surface for a particular agent, remove or deny `ctx_reduce` in that agent's tool allow-list; Magic Context then omits `§N§` prefixes, nudges, and reduce guidance for that session. @@ -597,7 +597,7 @@ Tier boundaries are hardcoded to keep behavior predictable and prevent cache-bus |-----|------|---------| | `smart_drops` | `boolean` | `false` | -**Experimental, opt-in.** Content-aware reclaim of tool output that a later call has made obsolete, layered on top of the normal age-based auto-drop. The age-based drop reclaims the *oldest* tool outputs first; smart-drops additionally reclaims outputs that are dead by *supersession*, regardless of age: +**Experimental, opt-in.** Content-aware reclaim of tool output that a later call has made obsolete. This is separate from agent-controlled `ctx_reduce`: smart-drops targets outputs that are dead by *supersession*, never merely because they are old: | Class | Behavior | |---|---| @@ -606,7 +606,7 @@ Tier boundaries are hardcoded to keep behavior predictable and prevent cache-bus | Zero-value meta (`bash_status`, `bash_kill`, `ctx_note` read/dismiss) | Drop all (worthless once the call ran; `ctx_note` write/update carry intent and are never dropped). | | Superseded edits | When a file is edited more than once, the newest edit stays in full and each older edit is compressed to a marker that keeps its `filePath` and a short region hint of the diff, so the agent still sees which file and region it edited. This is the largest source of reclaimable bytes. | -**Cache safety.** Selection is age-independent, but smart-drops only *acts* during a transform pass that is already rewriting the message array (the same execute-threshold gate the age-based drop uses), so it never causes a prompt-cache miss on its own. Every drop resolves to the same deterministic placeholder as the normal drops, so defer passes replay byte-for-byte identically. **When `smart_drops` is off, the messages sent to the model are byte-identical to the age-based-only behavior** — the entire feature is inert. +**Cache safety.** Selection is age-independent, but smart-drops only *acts* during a transform pass that is already rewriting the message array, so it never causes a prompt-cache miss on its own. Every drop resolves to the same deterministic placeholder as an explicit drop, so defer passes replay byte-for-byte identically. **When `smart_drops` is off, the entire supersession feature is inert:** routine tool outputs are reclaimed only when the agent explicitly queues them with `ctx_reduce`, or by the emergency safety valve. **Cross-version note.** Once you enable `smart_drops`, all binaries that share your Magic Context database (e.g. multiple OpenCode instances, or OpenCode + Pi) should be on the release that introduced it. If a stale older binary co-runs with the feature on, the worst case is a one-time cache bust (the older binary fully drops what the newer one compressed); it never corrupts data. diff --git a/README.md b/README.md index baea5e410..0ca5cfae6 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Magic Context is **1 of the 3 plugins you'll ever need.** It remembers; AFT perc - **Historian compartmentalization**: a background historian compresses old raw history into **tiered compartments**, chronological summaries that stand in for older messages. Each carries an importance score, so the live window stays small without losing the thread. Summarizing doesn't need your primary's coding muscle, so you can run the historian on a cheap or even fully local model while your main agent stays top-tier. - **Decay rendering**: compartments render at the right fidelity for the moment, by a deterministic, no-LLM rule that self-tunes to the model's context window. Old history fades gracefully instead of dropping off a cliff, and because it is deterministic, the same history always renders the same way. -- **The agent hints what to drop, or it doesn't**: with agent-driven reduction on, the agent calls `ctx_reduce` to mark stale tool outputs or long messages for removal. Drops are **queued and cache-aware**, applied only at cache-safe moments so reduction never thrashes the cache. Switch it off and the agent stays out of context management entirely: stale output is shed automatically by age, with optional caveman compression of the oldest text. +- **Agent-controlled tool reduction**: the agent calls `ctx_reduce` to mark stale tool outputs or long messages for removal. Drops are **queued and cache-aware**, applied only at cache-safe moments so reduction never thrashes the cache. Magic Context does not silently drop tool output by age; when agent-driven reduction is unavailable, routine tool output stays whole until the emergency safety valve, while optional caveman compression can reduce the oldest text. - **Cache-stable layout**: all of this is structured so background work never invalidates the cached prefix of your prompt. Your cache survives the whole session. The result: one session runs for months, with no compaction pauses and low cost on cache-priced providers. You can watch it happen in OpenCode's TUI, where a live sidebar shows the context breakdown by source, historian status, and memory counts, updating after every message. diff --git a/assets/magic-context.schema.json b/assets/magic-context.schema.json index 26ad2777b..2307d8bc0 100644 --- a/assets/magic-context.schema.json +++ b/assets/magic-context.schema.json @@ -1292,7 +1292,7 @@ }, "smart_drops": { "default": false, - "description": "Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart.", + "description": "Content-aware reclaim of provably-superseded tool output. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Age alone never selects a tool. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off; when off the entire supersession feature is inert and routine tool output requires explicit ctx_reduce or the emergency safety valve. Requires a restart.", "type": "boolean" }, "caveman_text_compression": { diff --git a/crates/mc-module/gen/gen-selection-golden.ts b/crates/mc-module/gen/gen-selection-golden.ts index 539302acf..0bf0bfbbf 100644 --- a/crates/mc-module/gen/gen-selection-golden.ts +++ b/crates/mc-module/gen/gen-selection-golden.ts @@ -1,13 +1,13 @@ /** * Generate the differential SELECTION golden for the Rust mc-module port. * - * Drives the REAL OpenCode TS selectors (supersession, edit-supersession, two-pass, + * Drives the REAL OpenCode TS selectors (supersession, edit-supersession, * emergency) over tag fixtures, extracts the DECISION set (which tool → drop vs * edit_marker), and emits (a) the equivalent flat typed-CK tail (SelItem[]) and (b) * the expected ARC-LEVEL decisions. The Rust `selection_golden` test runs * `select_reductions` over the same tail, projects its per-block output back to * arc-level, and asserts equality — proving the SELECTOR logic (keep-N, tier - * ordering, reserve, headroom, watermark, file-supersession, ctx_note actions) is + * ordering, reserve, headroom, file-supersession, ctx_note actions) is * bit-faithful to TS. * * SCOPE: the golden is DECISION-LEVEL (which arcs, what intent) — that is the @@ -35,7 +35,6 @@ const resolve = (m: string) => Bun.resolveSync(m, pluginDir); const storage = await import(resolve("./src/features/magic-context/storage")); const supersession = await import(resolve("./src/hooks/magic-context/supersession-reclaim")); -const toolReclaim = await import(resolve("./src/hooks/magic-context/tool-reclaim")); const emergency = await import(resolve("./src/hooks/magic-context/emergency-drop")); const { openDatabase, closeDatabase, insertTag } = storage as { @@ -64,7 +63,7 @@ interface TagFixture { providerExecuted?: boolean; } -type SelectorKind = "supersession" | "edit" | "two_pass" | "emergency"; +type SelectorKind = "supersession" | "edit" | "emergency"; interface CaseSpec { label: string; @@ -73,8 +72,6 @@ interface CaseSpec { /** pass class for the Rust ctx. */ passClass: "Execute" | "EmergencyForce"; smartDrops: boolean; - /** two_pass: the watermark ordinal. */ - lastExecuteOrdinal?: number; /** emergency inputs. */ emergency?: { currentTotalInputTokens: number; @@ -276,17 +273,6 @@ function runTsSelector(spec: CaseSpec): Record { const arc = tagNumberToArc.get(op.tagId); if (arc) expected[arc] = "edit_marker"; } - } else if (spec.selector === "two_pass") { - const ops = toolReclaim.buildSyntheticToolReclaimOps({ - db, - sessionId: SES, - targets, - watermark: spec.lastExecuteOrdinal ?? 0, - }); - for (const op of ops) { - const arc = tagNumberToArc.get(op.tagId); - if (arc) expected[arc] = "drop"; - } } } finally { closeDatabase(); @@ -303,7 +289,6 @@ function buildCtx(spec: CaseSpec): Record { ceiling_tokens: em?.ceilingTokens ?? 0, // protected tail cutoff = maxTag − protectedTags (ordinal space == tag space). protected_cutoff_ordinal: em ? Math.max(maxN - em.protectedTags, 0) : 0, - last_execute_ordinal: spec.lastExecuteOrdinal ?? 0, prior_input_sample: em?.priorInputSample ?? 0, has_prior_drop: em?.hasPriorDrop ?? false, agent_drop_ids: [], @@ -379,20 +364,6 @@ const cases: CaseSpec[] = [ { id: "c2", toolName: "edit", n: 2, byteSize: 500, input: { oldString: "p", newString: "q" } }, ], }, - { - label: "two_pass: drop tools at/under watermark", - selector: "two_pass", - smartDrops: false, - passClass: "Execute", - lastExecuteOrdinal: 3, - tags: [ - { id: "c1", toolName: "bash", n: 1, byteSize: 200 }, - { id: "c2", toolName: "read", n: 2, byteSize: 200 }, - { id: "c3", toolName: "grep", n: 3, byteSize: 200 }, - { id: "c4", toolName: "bash", n: 4, byteSize: 200 }, - { id: "c5", toolName: "edit", n: 5, byteSize: 200 }, - ], - }, { label: "emergency: tier order T3→T2→T1 to headroom", selector: "emergency", diff --git a/crates/mc-module/src/selection.rs b/crates/mc-module/src/selection.rs index b0a9689ce..99c464e50 100644 --- a/crates/mc-module/src/selection.rs +++ b/crates/mc-module/src/selection.rs @@ -6,9 +6,9 @@ //! Determinism is the cache invariant: same (items, frozen_keys, ctx, cfg) → same //! decisions → the slice-3 freeze/replay stays byte-identical. //! -//! Faithful port of the five OpenCode selectors (differential-golden'd vs the TS +//! Faithful port of the four OpenCode selectors (differential-golden'd vs the TS //! source): control-plane supersession, edit supersession, emergency tiered drop, -//! age-based two-pass, and ctx_reduce agent-drop. +//! and ctx_reduce agent-drop. //! //! Cache-critical invariants (enforced structurally here): //! - **frozen_keys HARD FILTER**: a CK item stays LIVE with original bytes after @@ -144,9 +144,6 @@ pub struct SelectionContext { /// The protected-recent window: items with `ordinal > protected_cutoff_ordinal` /// are never emergency-evicted (0 = protect nothing). pub protected_cutoff_ordinal: u64, - /// The two-pass watermark: tool items with `ordinal <= last_execute_ordinal` are - /// age-drop candidates (0 = none). - pub last_execute_ordinal: u64, /// Emergency idempotence latch: the input-token reading at the prior emergency /// drop (0 if never), and whether any emergency drop has happened. pub prior_input_sample: f64, @@ -171,7 +168,7 @@ pub struct SelectionContext { /// Which scheduler class this pass is — gates which selectors run. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PassClass { - /// A normal execute+bust pass: control-plane / edit / two-pass / ctx_reduce run. + /// A normal execute+bust pass: control-plane / edit / ctx_reduce run. Execute, /// A ≥85% force pass: the emergency tiered drop runs (in addition). EmergencyForce, @@ -539,19 +536,7 @@ fn select_supersession(arcs: &[&ToolArc]) -> HashMap { intents } -/// 1.4 Age-based two-pass: tool arcs whose age (ToolCall ordinal) is at/under the -/// last-execute watermark. Add-only (the watermark advances forward). Returns arc ids. -fn select_two_pass(arcs: &[&ToolArc], last_execute_ordinal: u64) -> HashSet { - if last_execute_ordinal == 0 { - return HashSet::new(); - } - arcs.iter() - .filter(|a| a.ordinal <= last_execute_ordinal) - .map(|a| a.arc_id.clone()) - .collect() -} - -/// 1.5 ctx_reduce agent-drop: the caller-supplied marked ids (a control-plane side +/// 1.4 ctx_reduce agent-drop: the caller-supplied marked ids (a control-plane side /// input). These are already flat block ids; emitted directly as drops (arc-atomic /// isn't needed — the agent marks specific blocks). Frozen/absent filtered by caller. fn select_agent_drops( @@ -745,17 +730,9 @@ pub fn select_reductions( } } PassClass::Execute => { - // Order = two-pass (drop) → control-plane (drop) → edit (edit_marker). - // drop wins: a later edit_marker never overrides an assigned drop. - for arc_id in select_two_pass(&active_arcs, ctx.last_execute_ordinal) { - arc_shapes.insert(arc_id, ArcShape::FullDrop); - } if cfg.smart_drops { let intents = select_supersession(&active_arcs); for (arc_id, intent) in intents { - if arc_shapes.contains_key(&arc_id) { - continue; // already a drop (two-pass) → drop wins - } arc_shapes.insert( arc_id, if intent.edit_marker { @@ -957,7 +934,6 @@ mod tests { current_total_input_tokens: 0.0, ceiling_tokens: 0.0, protected_cutoff_ordinal: 0, - last_execute_ordinal: 0, prior_input_sample: 0.0, has_prior_drop: false, agent_drop_ids: Vec::new(), @@ -1024,7 +1000,6 @@ mod tests { current_total_input_tokens: f64, ceiling_tokens: f64, protected_cutoff_ordinal: u64, - last_execute_ordinal: u64, prior_input_sample: f64, has_prior_drop: bool, agent_drop_ids: Vec, @@ -1133,7 +1108,6 @@ mod tests { current_total_input_tokens: case.ctx.current_total_input_tokens, ceiling_tokens: case.ctx.ceiling_tokens, protected_cutoff_ordinal: case.ctx.protected_cutoff_ordinal, - last_execute_ordinal: case.ctx.last_execute_ordinal, prior_input_sample: case.ctx.prior_input_sample, has_prior_drop: case.ctx.has_prior_drop, agent_drop_ids: case.ctx.agent_drop_ids.clone(), @@ -1170,12 +1144,12 @@ mod tests { #[test] fn provider_executed_arc_never_targeted() { - // A server-side tool arc must be skipped even under the two-pass watermark. + // A server-side tool arc must be skipped by automatic smart-drops. let mut items = vec![ - tool_call("c1", 1, "bash", serde_json::json!({}), 200), - tool_result("c1", 1, "bash", 200), - tool_call("c2", 2, "web_search", serde_json::json!({}), 200), - tool_result("c2", 2, "web_search", 200), + tool_call("c1", 1, "bash_status", serde_json::json!({}), 200), + tool_result("c1", 1, "bash_status", 200), + tool_call("c2", 2, "bash_status", serde_json::json!({}), 200), + tool_result("c2", 2, "bash_status", 200), ]; let c2_arc = call_block_id("c2"); for it in items.iter_mut() { @@ -1183,9 +1157,13 @@ mod tests { it.provider_executed = true; } } - let mut ctx = base_ctx(PassClass::Execute); - ctx.last_execute_ordinal = 2; // both arcs at/under watermark - let out = select_reductions(&items, &HashSet::new(), &ctx, &SelectionConfig::default()); + let ctx = base_ctx(PassClass::Execute); + let out = select_reductions( + &items, + &HashSet::new(), + &ctx, + &SelectionConfig { smart_drops: true }, + ); let arcs = arc_decisions(&items, &out); assert_eq!( arcs.get(&call_block_id("c1")).map(String::as_str), @@ -1201,15 +1179,19 @@ mod tests { fn frozen_arc_blocks_never_re_emitted() { // c1's result already frozen → the arc is inactive; no decision for ANY c1 block. let items = vec![ - tool_call("c1", 1, "bash", serde_json::json!({}), 200), - tool_result("c1", 1, "bash", 200), - tool_call("c2", 2, "bash", serde_json::json!({}), 200), - tool_result("c2", 2, "bash", 200), + tool_call("c1", 1, "bash_status", serde_json::json!({}), 200), + tool_result("c1", 1, "bash_status", 200), + tool_call("c2", 2, "bash_status", serde_json::json!({}), 200), + tool_result("c2", 2, "bash_status", 200), ]; - let mut ctx = base_ctx(PassClass::Execute); - ctx.last_execute_ordinal = 2; + let ctx = base_ctx(PassClass::Execute); let frozen: HashSet = [result_block_id("c1")].into_iter().collect(); - let out = select_reductions(&items, &frozen, &ctx, &SelectionConfig::default()); + let out = select_reductions( + &items, + &frozen, + &ctx, + &SelectionConfig { smart_drops: true }, + ); assert!( out.iter().all(|d| !d.target_id.starts_with("c1")), "no c1 block may be re-emitted once the arc is frozen: {out:?}" @@ -1223,18 +1205,22 @@ mod tests { #[test] fn dynamic_block_protection_filters_automatic_and_agent_drop_decisions() { let items = vec![ - tool_call("c1", 1, "bash", serde_json::json!({}), 200), - tool_result("c1", 1, "bash", 200), - tool_call("c2", 2, "bash", serde_json::json!({}), 200), - tool_result("c2", 2, "bash", 200), + tool_call("c1", 1, "bash_status", serde_json::json!({}), 200), + tool_result("c1", 1, "bash_status", 200), + tool_call("c2", 2, "bash_status", serde_json::json!({}), 200), + tool_result("c2", 2, "bash_status", 200), ]; let protected = result_block_id("c1"); let mut ctx = base_ctx(PassClass::Execute); - ctx.last_execute_ordinal = 2; ctx.agent_drop_ids = vec![protected.clone()]; ctx.protected_block_ids.insert(protected.clone()); - let out = select_reductions(&items, &HashSet::new(), &ctx, &SelectionConfig::default()); + let out = select_reductions( + &items, + &HashSet::new(), + &ctx, + &SelectionConfig { smart_drops: true }, + ); assert!(out.iter().all(|decision| decision.target_id != protected)); assert!( out.iter() @@ -1251,12 +1237,16 @@ mod tests { // permanently fences the session to raw serving. let items = vec![ reasoning("c1", 1, 100), - tool_call("c1", 1, "bash", serde_json::json!({}), 50), - tool_result("c1", 1, "bash", 300), + tool_call("c1", 1, "bash_status", serde_json::json!({}), 50), + tool_result("c1", 1, "bash_status", 300), ]; - let mut ctx = base_ctx(PassClass::Execute); - ctx.last_execute_ordinal = 1; - let out = select_reductions(&items, &HashSet::new(), &ctx, &SelectionConfig::default()); + let ctx = base_ctx(PassClass::Execute); + let out = select_reductions( + &items, + &HashSet::new(), + &ctx, + &SelectionConfig { smart_drops: true }, + ); let ids: HashSet<&str> = out.iter().map(|d| d.target_id.as_str()).collect(); assert!( ids.contains(call_block_id("c1").as_str()) @@ -1279,7 +1269,6 @@ mod tests { tool_result("c1", 1, "bash", 300), ]; let mut ctx = base_ctx(PassClass::Execute); - ctx.last_execute_ordinal = 1; ctx.agent_drop_ids = vec![reasoning_block_id("c1")]; let out = select_reductions(&items, &HashSet::new(), &ctx, &SelectionConfig::default()); assert!( @@ -1298,24 +1287,28 @@ mod tests { "turn1#0", "turn1#0", 1, - "bash", + "bash_status", serde_json::json!({"provider_call_id":"call_0"}), 50, ), - tool_result_with_ids("turn1-tool#0", "turn1#0", 1, "bash", 300), + tool_result_with_ids("turn1-tool#0", "turn1#0", 1, "bash_status", 300), tool_call_with_ids( "turn2#0", "turn2#0", 2, - "bash", + "bash_status", serde_json::json!({"provider_call_id":"call_0"}), 50, ), - tool_result_with_ids("turn2-tool#0", "turn2#0", 2, "bash", 300), + tool_result_with_ids("turn2-tool#0", "turn2#0", 2, "bash_status", 300), ]; - let mut ctx = base_ctx(PassClass::Execute); - ctx.last_execute_ordinal = 2; - let out = select_reductions(&items, &HashSet::new(), &ctx, &SelectionConfig::default()); + let ctx = base_ctx(PassClass::Execute); + let out = select_reductions( + &items, + &HashSet::new(), + &ctx, + &SelectionConfig { smart_drops: true }, + ); let arcs = arc_decisions(&items, &out); assert_eq!(arcs.get("turn1#0").map(String::as_str), Some("drop")); assert_eq!(arcs.get("turn2#0").map(String::as_str), Some("drop")); @@ -1339,15 +1332,19 @@ mod tests { items.push(tool_call( &format!("c{n}"), n, - "bash", + "bash_status", serde_json::json!({"cmd": "x".repeat(20)}), 50, )); - items.push(tool_result(&format!("c{n}"), n, "bash", 200)); + items.push(tool_result(&format!("c{n}"), n, "bash_status", 200)); } - let mut ctx = base_ctx(PassClass::Execute); - ctx.last_execute_ordinal = 22; // all arcs are drop candidates - let out = select_reductions(&items, &HashSet::new(), &ctx, &SelectionConfig::default()); + let ctx = base_ctx(PassClass::Execute); + let out = select_reductions( + &items, + &HashSet::new(), + &ctx, + &SelectionConfig { smart_drops: true }, + ); let call_kind = |arc: &str| -> String { out.iter() .find(|d| d.target_id == call_block_id(arc)) @@ -1360,54 +1357,6 @@ mod tests { assert_eq!(call_kind("c3"), "skeleton", "inside the window → skeleton"); } - #[test] - fn drop_wins_over_edit_marker() { - // c1 is an older edit to a.ts (edit_marker candidate) AND under the two-pass - // watermark (drop candidate). Drop must win. - let items = vec![ - tool_call( - "c1", - 1, - "edit", - serde_json::json!({"filePath":"a.ts","oldString":"x".repeat(80)}), - 500, - ), - tool_result("c1", 1, "edit", 100), - tool_call( - "c2", - 2, - "edit", - serde_json::json!({"filePath":"a.ts","oldString":"y".repeat(80)}), - 500, - ), - tool_result("c2", 2, "edit", 100), - ]; - let mut ctx = base_ctx(PassClass::Execute); - ctx.last_execute_ordinal = 1; // c1 under watermark → two-pass drop - let out = select_reductions( - &items, - &HashSet::new(), - &ctx, - &SelectionConfig { smart_drops: true }, - ); - let c1_call = out - .iter() - .find(|d| d.target_id == call_block_id("c1")) - .map(|d| d.kind.clone()); - // Drop wins: the arc is on the DROP path (skeleton in-window, or full-drop - // older), NEVER edit_marker. With only 2 arcs c1 is in the skeleton window, so - // the winning drop shapes to "skeleton" — a drop variant, not an edit_marker. - assert!( - matches!(c1_call.as_deref(), Some("drop") | Some("skeleton")), - "drop beats edit_marker for c1 (got {c1_call:?})" - ); - assert_ne!( - c1_call.as_deref(), - Some("edit_marker"), - "edit_marker must NOT win" - ); - } - #[test] fn payload_purity_independent_of_pressure() { // The cache-critical monotonicity pin: a payload = f(id, immutable block bytes) @@ -1418,9 +1367,8 @@ mod tests { // c1/c2/c3 are all edits to a.ts; c3 (newest) stays full, c1+c2 are older → // edit_marker candidates. Context B varies agent_drop_ids (drops UNRELATED c9, // so the produced set genuinely differs) plus the pressure/latch fields, chosen - // so c1 stays an edit_marker in BOTH (last_execute_ordinal stays 0, so c1 is - // never a two-pass FullDrop that would change its shape). A payload fn that - // accidentally read ctx would diverge here; a pure one cannot. + // so c1 stays an edit_marker in BOTH. A payload fn that accidentally read + // ctx would diverge here; a pure one cannot. let items = vec![ tool_call( "c1", @@ -1464,8 +1412,7 @@ mod tests { // Context A: no agent drops, zero pressure fields. let ctx_a = base_ctx(PassClass::Execute); // Context B: a genuinely different produced set — an unrelated agent drop (c9) - // plus non-zero pressure/latch fields. last_execute_ordinal stays 0 so c1 is NOT - // a two-pass drop candidate (keeps it an edit_marker in both). + // plus non-zero pressure/latch fields. let ctx_b = SelectionContext { agent_drop_ids: vec![result_block_id("c9")], current_total_input_tokens: 123_456.0, @@ -1622,7 +1569,6 @@ mod tests { tool_result("c1", 1, "bash", 300), ]; let mut ctx = base_ctx(PassClass::Defer); - ctx.last_execute_ordinal = 1; ctx.agent_drop_ids = vec![result_block_id("c1")]; let out = select_reductions( &items, diff --git a/crates/mc-module/src/transform.rs b/crates/mc-module/src/transform.rs index 4a43f2df6..660d57d6c 100644 --- a/crates/mc-module/src/transform.rs +++ b/crates/mc-module/src/transform.rs @@ -1238,11 +1238,6 @@ fn apply_once( * ctx.execute_threshold_percentage.clamp(1.0, 100.0) / 100.0, protected_cutoff_ordinal: 0, - last_execute_ordinal: if loaded.core.reconcile_pending { - 0 - } else { - loaded.meta.last_execute_ordinal - }, prior_input_sample: loaded.meta.last_emergency_input_sample, has_prior_drop: loaded.meta.has_prior_emergency_drop, agent_drop_ids, @@ -1457,10 +1452,6 @@ fn apply_once( }, estimate_tokens, )?; - meta.last_execute_ordinal = meta - .last_execute_ordinal - .min(comp.coverage_ordinal.unwrap_or(0)); - if let Some(stray) = first_uncovered_live_block( &recut_compartments, &live, @@ -1641,26 +1632,6 @@ fn apply_once( } } - // The two-pass watermark advances on EVERY scheduler execute-class decision - // (Execute/Force85/Emergency95), not only on passes that froze reductions: this - // execute's tail is the NEXT execute's age-drop candidate set, so a zero-drop execute - // must still stamp the max ordinal or completed arcs never age in. It does NOT advance - // when the producer gate opened via a hard advisory on a scheduler-Defer pass - // (first-fold, render-config change): those busts are not execute cadence, and - // stamping there would age the current tail into the very next execute. The write may - // be the only meta change on the pass — a metadata-only commit with byte-identical - // output, not a cache bust. Held back under reconcile (the watermark may be stale-high - // against a store about to be re-cut; the re-cut arm re-clamps it). - let scheduler_execute_class = - tail_reclaim_enabled && !matches!(scheduler_outcome.pass, scheduler::PassDecision::Defer); - if scheduler_execute_class && !loaded.core.reconcile_pending { - meta.last_execute_ordinal = tail_for_selection - .iter() - .map(|item| item.ordinal) - .max() - .unwrap_or(0) - .max(meta.last_execute_ordinal); - } if is_bust_pass && reductions_pending_now && selection_class == PassClass::EmergencyForce { meta.last_emergency_input_sample = usage_input_tokens; meta.has_prior_emergency_drop = true; @@ -6263,7 +6234,7 @@ mod tests { } #[test] - fn reconcile_pending_disables_two_pass_selector() { + fn reconcile_pending_ignores_legacy_reclaim_watermark() { let dir = tempfile::tempdir().unwrap(); let s = store(dir.path()); bootstrap_covering_a(&s); @@ -6309,15 +6280,12 @@ mod tests { let mut ctx = pctx("git:proj", "/nonexistent-docs", 0); ctx.observed_last_response_at_ms = Some(0); let execute = transform(&s, &boot_req, &ctx).unwrap(); - // A zero-drop execute is byte-identical (no bust) but MAY commit metadata: the - // two-pass watermark stamps this tail as the next execute's candidate set. + // A zero-drop execute is byte-identical and does not revive the retired + // positional reclaim watermark. assert_eq!(execute.action, "SOFT+"); assert_eq!(serde_json::to_vec(&execute.ck_messages).unwrap(), before); let meta = s.load("ses").unwrap().meta; - assert_eq!( - meta.last_execute_ordinal, 1, - "zero-drop execute still advances the two-pass watermark" - ); + assert_eq!(meta.last_execute_ordinal, 0); // And the pass after it, with an unchanged tail, is a true no-write defer. let again = transform(&s, &boot_req, &ctx).unwrap(); assert!(!again.committed); @@ -6325,13 +6293,11 @@ mod tests { } #[test] - fn zero_drop_execute_watermark_ages_arcs_into_next_execute() { + fn legacy_reclaim_watermark_never_ages_arcs_into_a_later_execute() { let dir = tempfile::tempdir().unwrap(); let s = store(dir.path()); - // Two-pass aging requires a FOLDED session: a new reduction can only freeze on - // a SOFT pass, and the classifier admits a SOFT only when a boundary is present. - // Seed one compartment covering ordinal 1 and fold it, then drive executes over - // the post-coverage tail. + // Seed one compartment covering ordinal 1 and fold it, then persist a + // pre-removal watermark to model an upgraded session. s.replace_compartments("ses", &[comp(1, 1, 1, "a", "SUMMARY")]) .unwrap(); let msgs = vec![ @@ -6342,10 +6308,14 @@ mod tests { ]; let boot = run(&s, &req("ses", "cfg0", msgs.clone()), &spine()); assert_eq!(boot.action, "HARD"); - // Execute pass 1 over the completed tool arc: nothing to drop yet (the arc is - // newer than the 0 watermark), but the watermark must stamp its ordinal. - // 70% usage: above the execute threshold (65) but below Force85, so the - // scheduler classes the pass Execute and the two-pass selector runs. + let loaded = s.load("ses").unwrap(); + let mut meta = loaded.meta.clone(); + meta.last_execute_ordinal = 9; + s.commit("ses", loaded.row_version, &loaded.core, &meta) + .unwrap(); + + // 70% usage is execute-class. Neither this pass nor the next may select + // an unrequested tool from the inert legacy watermark. let exec_req = with_usage(req("ses", "cfg0", msgs.clone()), 70, 100); let mut ctx = pctx("git:proj", "/nonexistent-docs", 0); ctx.observed_last_response_at_ms = Some(0); @@ -6357,13 +6327,9 @@ mod tests { .frozen_units .iter() .all(|unit| !unit.key.starts_with("red:m1#") && !unit.key.starts_with("red:m2#")), - "first execute has no candidates old enough to drop" + "execute must not synthesize an unrequested drop" ); - assert!( - after_first.meta.last_execute_ordinal >= 9, - "watermark stamped from the execute tail" - ); - // Execute pass 2: the same arc is now at-or-below the watermark → age-drops. + assert_eq!(after_first.meta.last_execute_ordinal, 9); let _ = transform(&s, &exec_req, &ctx).unwrap(); let after_second = s.load("ses").unwrap(); assert!( @@ -6371,9 +6337,10 @@ mod tests { .core .frozen_units .iter() - .any(|unit| unit.key.starts_with("red:m1#") || unit.key.starts_with("red:m2#")), - "completed arc aged in by the prior execute's watermark must freeze a drop" + .all(|unit| !unit.key.starts_with("red:m1#") && !unit.key.starts_with("red:m2#")), + "later execute must still preserve the unrequested tool" ); + assert_eq!(after_second.meta.last_execute_ordinal, 9); } #[test] @@ -7439,7 +7406,7 @@ mod tests { .unwrap() .contains("dropped seq 2")); assert_eq!(loaded.meta.folded_compartment_seq, 1); - assert_eq!(loaded.meta.last_execute_ordinal, 1); + assert_eq!(loaded.meta.last_execute_ordinal, 99); assert_eq!(loaded.row_version.unwrap(), before_recut + 2); assert_eq!(s.load_compartments("ses").unwrap().len(), 1); @@ -11788,7 +11755,7 @@ mod tests { let tail = vec![ item("m1", 1, "covered raw"), assistant_tool_call("m2", 2, "call_old"), - tool_result("m3", 3, "call_old", "big old tool output that age-drops"), + tool_result("m3", 3, "call_old", "big old tool output marked for drop"), todowrite_call( "m4", 4, diff --git a/crates/mc-module/testdata/selection-golden.json b/crates/mc-module/testdata/selection-golden.json index 0946c301a..2d17872e3 100644 --- a/crates/mc-module/testdata/selection-golden.json +++ b/crates/mc-module/testdata/selection-golden.json @@ -83,7 +83,6 @@ "current_total_input_tokens": 0, "ceiling_tokens": 0, "protected_cutoff_ordinal": 0, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] @@ -279,7 +278,6 @@ "current_total_input_tokens": 0, "ceiling_tokens": 0, "protected_cutoff_ordinal": 0, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] @@ -375,7 +373,6 @@ "current_total_input_tokens": 0, "ceiling_tokens": 0, "protected_cutoff_ordinal": 0, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] @@ -478,7 +475,6 @@ "current_total_input_tokens": 0, "ceiling_tokens": 0, "protected_cutoff_ordinal": 0, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] @@ -585,7 +581,6 @@ "current_total_input_tokens": 0, "ceiling_tokens": 0, "protected_cutoff_ordinal": 0, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] @@ -661,7 +656,6 @@ "current_total_input_tokens": 0, "ceiling_tokens": 0, "protected_cutoff_ordinal": 0, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] @@ -670,153 +664,6 @@ "frozen": [], "expected": {} }, - { - "label": "two_pass: drop tools at/under watermark", - "items": [ - { - "id": "c1#0", - "ordinal": 1, - "kind": { - "ToolCall": { - "name": "bash", - "input": {} - } - }, - "provider_executed": false, - "byte_size": 0, - "arc_id": "c1#0" - }, - { - "id": "c1#1", - "ordinal": 1, - "kind": { - "ToolResult": { - "tool_name": "bash" - } - }, - "provider_executed": false, - "byte_size": 200, - "arc_id": "c1#0" - }, - { - "id": "c2#0", - "ordinal": 2, - "kind": { - "ToolCall": { - "name": "read", - "input": {} - } - }, - "provider_executed": false, - "byte_size": 0, - "arc_id": "c2#0" - }, - { - "id": "c2#1", - "ordinal": 2, - "kind": { - "ToolResult": { - "tool_name": "read" - } - }, - "provider_executed": false, - "byte_size": 200, - "arc_id": "c2#0" - }, - { - "id": "c3#0", - "ordinal": 3, - "kind": { - "ToolCall": { - "name": "grep", - "input": {} - } - }, - "provider_executed": false, - "byte_size": 0, - "arc_id": "c3#0" - }, - { - "id": "c3#1", - "ordinal": 3, - "kind": { - "ToolResult": { - "tool_name": "grep" - } - }, - "provider_executed": false, - "byte_size": 200, - "arc_id": "c3#0" - }, - { - "id": "c4#0", - "ordinal": 4, - "kind": { - "ToolCall": { - "name": "bash", - "input": {} - } - }, - "provider_executed": false, - "byte_size": 0, - "arc_id": "c4#0" - }, - { - "id": "c4#1", - "ordinal": 4, - "kind": { - "ToolResult": { - "tool_name": "bash" - } - }, - "provider_executed": false, - "byte_size": 200, - "arc_id": "c4#0" - }, - { - "id": "c5#0", - "ordinal": 5, - "kind": { - "ToolCall": { - "name": "edit", - "input": {} - } - }, - "provider_executed": false, - "byte_size": 0, - "arc_id": "c5#0" - }, - { - "id": "c5#1", - "ordinal": 5, - "kind": { - "ToolResult": { - "tool_name": "edit" - } - }, - "provider_executed": false, - "byte_size": 200, - "arc_id": "c5#0" - } - ], - "ctx": { - "pass_class": "Execute", - "current_total_input_tokens": 0, - "ceiling_tokens": 0, - "protected_cutoff_ordinal": 0, - "last_execute_ordinal": 3, - "prior_input_sample": 0, - "has_prior_drop": false, - "agent_drop_ids": [] - }, - "smart_drops": false, - "frozen": [], - "expected": { - "c1#0": "drop", - "c2#0": "drop", - "c3#0": "drop" - } - }, { "label": "emergency: tier order T3→T2→T1 to headroom", "items": [ @@ -926,7 +773,6 @@ "current_total_input_tokens": 200000, "ceiling_tokens": 160000, "protected_cutoff_ordinal": 4, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] @@ -1047,7 +893,6 @@ "current_total_input_tokens": 200000, "ceiling_tokens": 160000, "protected_cutoff_ordinal": 2, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] @@ -1093,7 +938,6 @@ "current_total_input_tokens": 200000, "ceiling_tokens": 160000, "protected_cutoff_ordinal": 1, - "last_execute_ordinal": 0, "prior_input_sample": 200000, "has_prior_drop": true, "agent_drop_ids": [] @@ -1136,7 +980,6 @@ "current_total_input_tokens": 160500, "ceiling_tokens": 160000, "protected_cutoff_ordinal": 1, - "last_execute_ordinal": 0, "prior_input_sample": 0, "has_prior_drop": false, "agent_drop_ids": [] diff --git a/packages/docs/src/content/docs/reference/configuration.md b/packages/docs/src/content/docs/reference/configuration.md index c99f30149..feee1d3d3 100644 --- a/packages/docs/src/content/docs/reference/configuration.md +++ b/packages/docs/src/content/docs/reference/configuration.md @@ -251,4 +251,4 @@ Behavior tuning most installs never need to touch. |---|---|---|---| | `toast_duration_ms` | number (0–60000) | `5000` | TUI toast lifetime in milliseconds for Magic Context notifications. Set to 0 to disable Magic Context toasts entirely (min: 0, max: 60000, default: 5000) | | `subc.connection_file` | string | — | Path to the owner-only subc connection file. | -| `smart_drops` | boolean | `false` | Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart. | +| `smart_drops` | boolean | `false` | Content-aware reclaim of provably-superseded tool output. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Age alone never selects a tool. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off; when off the entire supersession feature is inert and routine tool output requires explicit ctx_reduce or the emergency safety valve. Requires a restart. | diff --git a/packages/pi-plugin/src/context-handler.test.ts b/packages/pi-plugin/src/context-handler.test.ts index 5db50a52c..c0026d3cc 100644 --- a/packages/pi-plugin/src/context-handler.test.ts +++ b/packages/pi-plugin/src/context-handler.test.ts @@ -148,8 +148,8 @@ describe("applyForwardPressureFloor", () => { expect(src).toContain("usagePercentage = Math.max(usagePercentage, 95)"); expect(src).not.toContain("usagePercentage = 95;"); }); - describe("two-pass tool reclaim source invariants", () => { - it("uses confirmed mutation booleans rather than executedWorkThisPass for the reclaim gate", () => { + describe("explicit-only tool reclaim source invariants", () => { + it("retains the smart-drops gate without positional synthetic reclaim", () => { const src = readFileSync( join(import.meta.dir, "context-handler.ts"), "utf8", @@ -160,7 +160,9 @@ describe("applyForwardPressureFloor", () => { "const alreadyMutatingThisPass =\n\t\tpendingOpsDidMutate || heuristicOrReasoningDidMutate", ); expect(src).toContain("heuristicsResult.droppedStaleReduceCalls"); - expect(src).toContain("buildSyntheticToolReclaimOps"); + expect(src).toContain("args.smartDrops &&"); + expect(src).not.toContain("buildSyntheticToolReclaimOps"); + expect(src).not.toContain("advanceToolReclaimWatermarkToCurrentMax"); expect(src).not.toContain( "const alreadyMutatingThisPass = executedWorkThisPass", ); @@ -1316,6 +1318,63 @@ describe("registerPiContextHandler", () => { } }); + it("does not use a legacy watermark to drop an unrequested tool", async () => { + const db = createTestDb(); + const sessionId = "ses-pi-explicit-only"; + try { + const fake = createFakePi(); + registerPiContextHandler(fake.pi as never, { + db, + protectedTags: 0, + scheduler: { executeThresholdPercentage: 65 }, + }); + const handler = fake.handlers.get("context") as ( + event: { messages: never[] }, + ctx: never, + ) => Promise<{ messages: never[] }>; + const messages = [ + userMessage("start", 1), + assistantToolCall("call-1", "read", {}, 2), + toolResultMessage("call-1", "explicit output", 3), + assistantToolCall("call-2", "read", {}, 4), + toolResultMessage("call-2", "unrequested output", 5), + userMessage("continue", 6), + ] as never[]; + const executeCtx = { + ...fakeContext(sessionId), + getContextUsage: () => ({ + tokens: 70_000, + percent: 70, + contextWindow: 100_000, + }), + }; + + await handler({ messages }, executeCtx as never); + const toolTags = getTagsBySession(db, sessionId).filter( + (tag) => tag.type === "tool", + ); + expect(toolTags).toHaveLength(2); + queuePendingOp(db, sessionId, toolTags[0].tagNumber, "drop"); + updateSessionMeta(db, sessionId, { + toolReclaimWatermark: toolTags[1].tagNumber, + }); + + const result = await handler({ messages }, executeCtx as never); + const statuses = new Map( + getTagsBySession(db, sessionId).map((tag) => [tag.tagNumber, tag.status]), + ); + expect(statuses.get(toolTags[0].tagNumber)).toBe("dropped"); + expect(statuses.get(toolTags[1].tagNumber)).toBe("active"); + expect(textOf(result.messages[4] as never)).toContain("unrequested output"); + expect(getOrCreateSessionMeta(db, sessionId).toolReclaimWatermark).toBe( + toolTags[1].tagNumber, + ); + } finally { + clearContextHandlerSession(sessionId); + closeQuietly(db); + } + }); + it("injects deferred-note text into the latest new user message", async () => { const db = createTestDb(); try { diff --git a/packages/pi-plugin/src/context-handler.ts b/packages/pi-plugin/src/context-handler.ts index 44c11098f..5c3c739eb 100644 --- a/packages/pi-plugin/src/context-handler.ts +++ b/packages/pi-plugin/src/context-handler.ts @@ -147,10 +147,6 @@ import { buildEditSupersessionReclaim, buildSupersessionReclaimOps, } from "@magic-context/core/hooks/magic-context/supersession-reclaim"; -import { - advanceToolReclaimWatermarkToCurrentMax, - buildSyntheticToolReclaimOps, -} from "@magic-context/core/hooks/magic-context/tool-reclaim"; import { log, sessionLog } from "@magic-context/core/shared/logger"; import { isSaneLimit } from "@magic-context/core/shared/models-dev-cache"; import type { SubagentRunner } from "@magic-context/core/shared/subagent-runner"; @@ -4004,7 +4000,7 @@ async function runPipeline(args: RunPipelineArgs): Promise { let droppedCount = 0; const droppedTokens = 0; let emergency = false; - let autoReclaimDidMutateThisPass = false; + let smartDropsDidMutateThisPass = false; let suppressDeferredHistoryDrain = false; let deferredMaterializationConsumedThisPass = false; let casLost = false; @@ -4740,78 +4736,61 @@ async function runPipeline(args: RunPipelineArgs): Promise { } } - const toolReclaimExecutePass = args.schedulerDecision === "execute"; + const smartDropsExecutePass = args.schedulerDecision === "execute"; const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate; const emergencyDropEligible = args.forceMaterialization === true || args.contextUsage.percentage >= FORCE_MATERIALIZATION_PERCENTAGE; - let autoReclaimTargetCount = 0; - let autoReclaimDidMutate = false; + let smartDropTargetCount = 0; + let smartDropsDidMutate = false; if ( - toolReclaimExecutePass && + args.smartDrops && + smartDropsExecutePass && alreadyMutatingThisPass && !emergencyDropEligible ) { - const reclaimMeta = args.sessionMeta; - const syntheticPendingOps = buildSyntheticToolReclaimOps({ + // Smart-drops are an independently configured feature. They select + // outputs that a later call superseded; age or a prior execute watermark + // cannot select a tool. + const smartDropOps = buildSupersessionReclaimOps({ db: args.db, sessionId: args.sessionId, targets, - watermark: reclaimMeta.toolReclaimWatermark ?? 0, pendingOps, }); - // Smart-drops: also reclaim older todowrite/ctx_reduce/meta outputs that - // a later call supersedes, and compress superseded edits to an - // edit_marker (keep filePath + region hint). Merged into the same - // already-gated drop apply as the age-based sweep above. Dedupe (a tag - // can qualify under more than one rule). const editMarkerTagIds = new Set(); - if (args.smartDrops) { - const selectedIds = new Set(syntheticPendingOps.map((op) => op.tagId)); - const supersessionOps = buildSupersessionReclaimOps({ - db: args.db, - sessionId: args.sessionId, - targets, - pendingOps, - }); - for (const op of supersessionOps) { - if (!selectedIds.has(op.tagId)) { - syntheticPendingOps.push(op); - selectedIds.add(op.tagId); - } - } - const editReclaim = buildEditSupersessionReclaim({ - db: args.db, - sessionId: args.sessionId, - targets, - pendingOps, - }); - for (const op of editReclaim.ops) { - // Drop wins over compress: only compress an edit no earlier rule - // already selected for a full/skeleton drop. - if (!selectedIds.has(op.tagId)) { - syntheticPendingOps.push(op); - selectedIds.add(op.tagId); - editMarkerTagIds.add(op.tagId); - } + const selectedIds = new Set(smartDropOps.map((op) => op.tagId)); + const editReclaim = buildEditSupersessionReclaim({ + db: args.db, + sessionId: args.sessionId, + targets, + pendingOps, + }); + for (const op of editReclaim.ops) { + // A superseded edit only compresses if the control-plane rule did + // not already select it for a full drop. + if (!selectedIds.has(op.tagId)) { + smartDropOps.push(op); + selectedIds.add(op.tagId); + editMarkerTagIds.add(op.tagId); } } - autoReclaimTargetCount = syntheticPendingOps.length; - if (syntheticPendingOps.length > 0) { - autoReclaimDidMutate = applyPendingOperations( + smartDropTargetCount = smartDropOps.length; + if (smartDropOps.length > 0) { + smartDropsDidMutate = applyPendingOperations( args.sessionId, args.db, targets, args.protectedTags, undefined, [], - syntheticPendingOps, + smartDropOps, editMarkerTagIds, ); - if (autoReclaimDidMutate) { - droppedCount += syntheticPendingOps.length; - autoReclaimDidMutateThisPass = true; + if (smartDropsDidMutate) { + droppedCount += smartDropOps.length; + smartDropsDidMutateThisPass = true; } } } @@ -4859,13 +4838,10 @@ async function runPipeline(args: RunPipelineArgs): Promise { const tTranscriptCommit = performance.now(); transcript.commit(); logTransformTiming(args.sessionId, "transcriptCommit", tTranscriptCommit); - if (toolReclaimExecutePass) { - advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId); - } - if (autoReclaimTargetCount > 0) { + if (smartDropTargetCount > 0) { sessionLog( args.sessionId, - `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`, + `smart-drops reclaim: targets=${smartDropTargetCount} mutated=${smartDropsDidMutate}`, ); } @@ -5181,7 +5157,7 @@ async function runPipeline(args: RunPipelineArgs): Promise { didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || - autoReclaimDidMutateThisPass || + smartDropsDidMutateThisPass || materialized || historyWasConsumedThisPass; diff --git a/packages/plugin/src/config/schema/magic-context.ts b/packages/plugin/src/config/schema/magic-context.ts index ddc0a30bb..1a871e585 100644 --- a/packages/plugin/src/config/schema/magic-context.ts +++ b/packages/plugin/src/config/schema/magic-context.ts @@ -730,7 +730,7 @@ export const MagicContextConfigSchema = z .boolean() .default(false) .describe( - "Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart.", + "Content-aware reclaim of provably-superseded tool output. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Age alone never selects a tool. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off; when off the entire supersession feature is inert and routine tool output requires explicit ctx_reduce or the emergency safety valve. Requires a restart.", ), caveman_text_compression: z .object({ diff --git a/packages/plugin/src/features/magic-context/storage-meta-session.ts b/packages/plugin/src/features/magic-context/storage-meta-session.ts index ee6b0a2da..6d2699763 100644 --- a/packages/plugin/src/features/magic-context/storage-meta-session.ts +++ b/packages/plugin/src/features/magic-context/storage-meta-session.ts @@ -148,20 +148,6 @@ export function updateSessionMeta( })(); } -export function advanceToolReclaimWatermark( - db: Database, - sessionId: string, - maxTagNumber: number, -): void { - if (maxTagNumber <= 0) return; - db.transaction(() => { - ensureSessionMetaRow(db, sessionId); - db.prepare( - "UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?", - ).run(maxTagNumber, sessionId); - })(); -} - export function clearSession(db: Database, sessionId: string): void { // Every session-scoped table must be cleared here; the structural storage-db // test discovers tables with session_id and seeds each one to enforce this list. diff --git a/packages/plugin/src/features/magic-context/storage-meta.ts b/packages/plugin/src/features/magic-context/storage-meta.ts index b1f82635f..caff50389 100644 --- a/packages/plugin/src/features/magic-context/storage-meta.ts +++ b/packages/plugin/src/features/magic-context/storage-meta.ts @@ -97,7 +97,6 @@ export { type WrapupInProgressState, } from "./storage-meta-persisted"; export { - advanceToolReclaimWatermark, clearSession, getOrCreateSessionMeta, updateSessionMeta, diff --git a/packages/plugin/src/features/magic-context/storage.ts b/packages/plugin/src/features/magic-context/storage.ts index 66630ae6c..2425a4ec0 100644 --- a/packages/plugin/src/features/magic-context/storage.ts +++ b/packages/plugin/src/features/magic-context/storage.ts @@ -101,7 +101,6 @@ export { acquireWrapupInProgress, addProcessedImageStrippedIds, addStaleReduceStrippedIds, - advanceToolReclaimWatermark, appendAutoSearchHintDecision, appendNoteNudgeAnchor, applyStrippedPlaceholderDelta, diff --git a/packages/plugin/src/hooks/magic-context/apply-operations.ts b/packages/plugin/src/hooks/magic-context/apply-operations.ts index cddb2135d..bc3631846 100644 --- a/packages/plugin/src/hooks/magic-context/apply-operations.ts +++ b/packages/plugin/src/hooks/magic-context/apply-operations.ts @@ -116,7 +116,7 @@ export function applyPendingOperations( const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool"; if (synthetic) { - // Synthetic two-pass reclaim must never persist a DB-only drop for + // A smart-drop must never persist a DB-only drop for // a tag that is absent/incomplete on this pass's visible wire. It // only rides an already-mutating pass when the target can actually // reclaim bytes right now; real pending ops keep their legacy diff --git a/packages/plugin/src/hooks/magic-context/supersession-reclaim.ts b/packages/plugin/src/hooks/magic-context/supersession-reclaim.ts index 83a43a3c2..41260e0db 100644 --- a/packages/plugin/src/hooks/magic-context/supersession-reclaim.ts +++ b/packages/plugin/src/hooks/magic-context/supersession-reclaim.ts @@ -4,8 +4,7 @@ import { isEditTool } from "./edit-marker"; import type { TagTarget } from "./tag-messages"; // Smart-drops Phase 1: select provably-superseded "spent control-plane" tool -// outputs for reclaim, on top of the positional watermark sweep. These classes -// are dead by SUPERSESSION (not age), so selection ignores the watermark — but +// outputs for reclaim. These classes are dead by SUPERSESSION (not age), and // the caller only ACTS on the result inside the existing // execute + already-mutating gate, so this never originates a cache bust. // @@ -26,8 +25,7 @@ const CTX_NOTE_ZERO_VALUE_ACTIONS = new Set(["read", "dismiss"]); /** * Build synthetic drop ops for superseded spent-control-plane tool outputs. - * Mirrors `buildSyntheticToolReclaimOps`'s op shape. The caller merges these - * into the same gated `applyPendingOperations` call as the positional sweep. + * The caller merges these into a gated `applyPendingOperations` call. */ export function buildSupersessionReclaimOps(input: { db: ContextDatabase; @@ -86,9 +84,8 @@ export function buildSupersessionReclaimOps(input: { /** * Select superseded edit/write tool calls for COMPRESSION (not full drop). * Among active edit/write tags grouped by their `filePath`, the newest stays - * full; every older edit to the same file is an edit_marker target. Like the - * control-plane selector, supersession is age-independent so the watermark is - * ignored, but the caller only acts inside the gated pass. + * full; every older edit to the same file is an edit_marker target. The caller + * only acts inside the gated pass. * * Returns both the drop ops AND the set of tag ids that must be compressed as * edit_marker (the caller passes the set to applyPendingOperations). diff --git a/packages/plugin/src/hooks/magic-context/tool-reclaim.ts b/packages/plugin/src/hooks/magic-context/tool-reclaim.ts deleted file mode 100644 index a6ee62898..000000000 --- a/packages/plugin/src/hooks/magic-context/tool-reclaim.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - advanceToolReclaimWatermark, - type ContextDatabase, - getActiveTagsBySession, - getMaxTagNumberBySession, -} from "../../features/magic-context/storage"; -import type { PendingOp } from "../../features/magic-context/types"; -import type { TagTarget } from "./tag-messages"; - -export function buildSyntheticToolReclaimOps(input: { - db: ContextDatabase; - sessionId: string; - targets: Map; - watermark: number; - pendingOps?: readonly PendingOp[]; -}): PendingOp[] { - const watermark = Math.max(0, input.watermark); - if (watermark <= 0) return []; - - const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId)); - const tags = getActiveTagsBySession(input.db, input.sessionId); - const synthetic: PendingOp[] = []; - - for (const tag of tags) { - if (tag.type !== "tool") continue; - if (tag.status !== "active") continue; - if (tag.tagNumber > watermark) continue; - if (realPendingTagIds.has(tag.tagNumber)) continue; - if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true) continue; - synthetic.push({ - id: 0, - sessionId: input.sessionId, - tagId: tag.tagNumber, - operation: "drop", - queuedAt: 0, - }); - } - - return synthetic; -} - -export function advanceToolReclaimWatermarkToCurrentMax( - db: ContextDatabase, - sessionId: string, -): number { - const maxTagNumber = getMaxTagNumberBySession(db, sessionId); - advanceToolReclaimWatermark(db, sessionId, maxTagNumber); - return maxTagNumber; -} diff --git a/packages/plugin/src/hooks/magic-context/transform-postprocess-phase.test.ts b/packages/plugin/src/hooks/magic-context/transform-postprocess-phase.test.ts index 73f11408e..434e47676 100644 --- a/packages/plugin/src/hooks/magic-context/transform-postprocess-phase.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-postprocess-phase.test.ts @@ -8,7 +8,6 @@ import { appendCompartments } from "../../features/magic-context/compartment-sto import { addProcessedImageStrippedIds, addStaleReduceStrippedIds, - advanceToolReclaimWatermark, getActiveTagsBySession, getOrCreateSessionMeta, getPendingCompactionMarkerState, @@ -1372,44 +1371,21 @@ describe("postprocess emergency drop accounting", () => { }); }); -describe("two-pass tool reclaim", () => { +describe("explicit-only tool reclaim", () => { function tagStatuses(sessionId: string): Map { return new Map(getTagsBySession(db, sessionId).map((tag) => [tag.tagNumber, tag.status])); } - it("does not auto-drop on an execute pass with no confirmed wire mutation", async () => { + it("does not use a legacy watermark to drop an unrequested tool on execute", async () => { db = new Database(":memory:"); initializeDatabase(db); - const sessionId = "ses-reclaim-noop"; - const message = makeToolMessage("tool-1"); - insertTag(db, sessionId, "tool-1", "tool", 4000, 1, 0, "bash"); - advanceToolReclaimWatermark(db, sessionId, 1); - - await runPostTransformPhase( - basePostTransformArgs(db, sessionId, [message], { - schedulerDecision: "execute", - tags: getActiveTagsBySession(db, sessionId), - targets: new Map([[1, makeDropTarget(message)]]), - sessionMeta: getOrCreateSessionMeta(db, sessionId), - }), - ); - - expect(tagStatuses(sessionId).get(1)).toBe("active"); - expect((message.parts[0] as { state?: { output?: string } }).state?.output).not.toBe( - "[dropped]", - ); - }); - - it("auto-drops eligible old visible tools only when another confirmed mutation already happened", async () => { - db = new Database(":memory:"); - initializeDatabase(db); - const sessionId = "ses-reclaim-mutating"; + const sessionId = "ses-explicit-only-execute"; const first = makeToolMessage("tool-1"); const second = makeToolMessage("tool-2"); insertTag(db, sessionId, "tool-1", "tool", 4000, 1, 0, "bash"); insertTag(db, sessionId, "tool-2", "tool", 4000, 2, 0, "read"); queuePendingOp(db, sessionId, 1, "drop", 1); - advanceToolReclaimWatermark(db, sessionId, 2); + updateSessionMeta(db, sessionId, { toolReclaimWatermark: 2 }); await runPostTransformPhase( basePostTransformArgs(db, sessionId, [first, second], { @@ -1425,105 +1401,12 @@ describe("two-pass tool reclaim", () => { const statuses = tagStatuses(sessionId); expect(statuses.get(1)).toBe("dropped"); - expect(statuses.get(2)).toBe("dropped"); - expect((second.parts[0] as { state?: { output?: string } }).state?.output).toBe( + expect(statuses.get(2)).toBe("active"); + expect((second.parts[0] as { state?: { output?: string } }).state?.output).not.toBe( "[dropped]", ); - }); - - it("does not persist a synthetic drop for an absent old DB tag", async () => { - db = new Database(":memory:"); - initializeDatabase(db); - const sessionId = "ses-reclaim-absent"; - const visible = makeToolMessage("tool-2"); - insertTag(db, sessionId, "tool-1", "tool", 4000, 1, 0, "bash"); - insertTag(db, sessionId, "tool-2", "tool", 4000, 2, 0, "bash"); - queuePendingOp(db, sessionId, 2, "drop", 1); - advanceToolReclaimWatermark(db, sessionId, 1); - - await runPostTransformPhase( - basePostTransformArgs(db, sessionId, [visible], { - schedulerDecision: "execute", - tags: getActiveTagsBySession(db, sessionId), - targets: new Map([[2, makeDropTarget(visible)]]), - sessionMeta: getOrCreateSessionMeta(db, sessionId), - }), - ); - - const statuses = tagStatuses(sessionId); - expect(statuses.get(1)).toBe("active"); - expect(statuses.get(2)).toBe("dropped"); - }); - - it("suppresses two-pass reclaim in the emergency band but still advances the watermark on execute", async () => { - db = new Database(":memory:"); - initializeDatabase(db); - const sessionId = "ses-reclaim-emergency"; - const first = makeToolMessage("tool-1"); - const second = makeToolMessage("tool-2"); - insertTag(db, sessionId, "tool-1", "tool", 4000, 1, 0, "bash"); - insertTag(db, sessionId, "tool-2", "tool", 4000, 2, 0, "read"); - queuePendingOp(db, sessionId, 1, "drop", 1); - advanceToolReclaimWatermark(db, sessionId, 2); - - await runPostTransformPhase( - basePostTransformArgs(db, sessionId, [first, second], { - schedulerDecision: "execute", - contextUsage: { percentage: 90, inputTokens: 9000 }, - tags: getActiveTagsBySession(db, sessionId), - targets: new Map([ - [1, makeDropTarget(first)], - [2, makeDropTarget(second)], - ]), - sessionMeta: getOrCreateSessionMeta(db, sessionId), - }), - ); - - const statuses = tagStatuses(sessionId); - expect(statuses.get(1)).toBe("dropped"); - expect(statuses.get(2)).toBe("active"); expect(getOrCreateSessionMeta(db, sessionId).toolReclaimWatermark).toBe(2); }); - - it("advances the watermark on execute even when the auto-drop gate is closed", async () => { - db = new Database(":memory:"); - initializeDatabase(db); - const sessionId = "ses-reclaim-advance"; - const message = makeToolMessage("tool-1"); - insertTag(db, sessionId, "tool-1", "tool", 4000, 1, 0, "bash"); - - await runPostTransformPhase( - basePostTransformArgs(db, sessionId, [message], { - schedulerDecision: "execute", - tags: getActiveTagsBySession(db, sessionId), - targets: new Map([[1, makeDropTarget(message)]]), - sessionMeta: getOrCreateSessionMeta(db, sessionId), - }), - ); - - expect(getOrCreateSessionMeta(db, sessionId).toolReclaimWatermark).toBe(1); - expect(tagStatuses(sessionId).get(1)).toBe("active"); - }); - - it("does not advance the watermark on a non-execute force-materialization pass", async () => { - db = new Database(":memory:"); - initializeDatabase(db); - const sessionId = "ses-reclaim-force-defer"; - const message = makeToolMessage("tool-1"); - insertTag(db, sessionId, "tool-1", "tool", 4000, 1, 0, "bash"); - - await runPostTransformPhase( - basePostTransformArgs(db, sessionId, [message], { - schedulerDecision: "defer", - contextUsage: { percentage: 90, inputTokens: 9000 }, - tags: getActiveTagsBySession(db, sessionId), - targets: new Map([[1, makeDropTarget(message)]]), - sessionMeta: getOrCreateSessionMeta(db, sessionId), - }), - ); - - expect(getOrCreateSessionMeta(db, sessionId).toolReclaimWatermark).toBe(0); - }); }); describe("smart-drops supersession reclaim (flag-gated)", () => { @@ -1531,10 +1414,9 @@ describe("smart-drops supersession reclaim (flag-gated)", () => { return new Map(getTagsBySession(db, sessionId).map((tag) => [tag.tagNumber, tag.status])); } - // tag 1 performs a real drop, which enables the reclaim block this pass; + // tag 1 performs a real drop, which enables the smart-drops block this pass; // tags 2 & 3 are todowrite where the older (2) is superseded by the newer - // (3). watermark=1 makes the age-based sweep skip tags 2/3, so only the - // smart-drops supersession path can touch them. + // (3), so only the independently configured supersession rule can touch it. function seedTodowriteSession(sessionId: string): { trigger: MessageLike; older: MessageLike; @@ -1547,7 +1429,6 @@ describe("smart-drops supersession reclaim (flag-gated)", () => { insertTag(db, sessionId, "tool-2", "tool", 4000, 2, 0, "todowrite"); insertTag(db, sessionId, "tool-3", "tool", 4000, 3, 0, "todowrite"); queuePendingOp(db, sessionId, 1, "drop", 1); - advanceToolReclaimWatermark(db, sessionId, 1); return { trigger, older, newer }; } @@ -1737,7 +1618,7 @@ describe("known m[0] hard-fold folds the execute pass in", () => { ); }); - it("drains two-pass reclaim and advances its watermark on a DEFER scheduler pass when m[0] HARD-folds", async () => { + it("does not use a legacy watermark to drop an unrequested tool when m[0] HARD-folds", async () => { db = new Database(":memory:"); initializeDatabase(db); const sessionId = "ses-hardfold-reclaim-drain"; @@ -1750,7 +1631,7 @@ describe("known m[0] hard-fold folds the execute pass in", () => { insertTag(db, sessionId, "tool-2", "tool", 4000, 2, 0, "bash"); insertTag(db, sessionId, "tool-3", "tool", 4000, 3, 0, "read"); queuePendingOp(db, sessionId, 1, "drop", 1); - advanceToolReclaimWatermark(db, sessionId, 2); + updateSessionMeta(db, sessionId, { toolReclaimWatermark: 2 }); const messages = [trigger, reclaimable, newer]; const targets = new Map([ [1, makeDropTarget(trigger)], @@ -1781,9 +1662,9 @@ describe("known m[0] hard-fold folds the execute pass in", () => { getTagsBySession(db, sessionId).map((tag) => [tag.tagNumber, tag.status]), ); expect(statuses.get(1)).toBe("dropped"); - expect(statuses.get(2)).toBe("dropped"); + expect(statuses.get(2)).toBe("active"); expect(statuses.get(3)).toBe("active"); - expect(getOrCreateSessionMeta(db, sessionId).toolReclaimWatermark).toBe(3); + expect(getOrCreateSessionMeta(db, sessionId).toolReclaimWatermark).toBe(2); const deferReplayBytes = JSON.stringify(messages); await runPostTransformPhase( @@ -2107,7 +1988,7 @@ describe("postprocess empty-sentinel provider gate", () => { }); describe("final message representation", () => { - it("serializes a late auto-reclaim clear identically on execute and defer", async () => { + it("serializes a late explicit reclaim clear identically on execute and defer", async () => { db = new Database(":memory:"); initializeDatabase(db); const sessionId = "ses-final-representation-late-clear"; @@ -2147,7 +2028,7 @@ describe("final message representation", () => { insertTag(db, sessionId, "call-survivor", "tool", 100, 3, 0, "read"); padRecentToolSkeletonWindow(sessionId, 3); queuePendingOp(db, sessionId, 1, "drop", 1); - advanceToolReclaimWatermark(db, sessionId, 2); + queuePendingOp(db, sessionId, 2, "drop", 2); const foldMessages = cloneMessages(template); const foldBatch = new ToolMutationBatch(foldMessages); @@ -2291,7 +2172,7 @@ describe("final message representation", () => { insertTag(db, sessionId, "call-predecessor", "tool", 100, 2, 0, "read"); padRecentToolSkeletonWindow(sessionId, 2); queuePendingOp(db, sessionId, 1, "drop", 1); - advanceToolReclaimWatermark(db, sessionId, 2); + queuePendingOp(db, sessionId, 2, "drop", 2); const foldMessages = cloneMessages(template); const foldBatch = new ToolMutationBatch(foldMessages); diff --git a/packages/plugin/src/hooks/magic-context/transform-postprocess-phase.ts b/packages/plugin/src/hooks/magic-context/transform-postprocess-phase.ts index bcc08bf10..d5427cccf 100644 --- a/packages/plugin/src/hooks/magic-context/transform-postprocess-phase.ts +++ b/packages/plugin/src/hooks/magic-context/transform-postprocess-phase.ts @@ -68,10 +68,6 @@ import { import { buildEditSupersessionReclaim, buildSupersessionReclaimOps } from "./supersession-reclaim"; import { byteSize, prependTag } from "./tag-content-primitives"; import { buildSyntheticTodoPart, type SyntheticTodoPart } from "./todo-view"; -import { - advanceToolReclaimWatermarkToCurrentMax, - buildSyntheticToolReclaimOps, -} from "./tool-reclaim"; import { appendReminderToUserMessageById, findLastUserMessageId, @@ -689,7 +685,7 @@ export async function runPostTransformPhase( let m0RematerializedThisPass = false; let m0MaterializeReason: string | null = null; let m0M1InjectedThisPass = false; - let autoReclaimDidMutateThisPass = false; + let smartDropsDidMutateThisPass = false; try { if (shouldApplyPendingOps) { const applyReason = isExplicitFlush @@ -867,85 +863,65 @@ export async function runPostTransformPhase( updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() }); } - const toolReclaimExecutePass = args.schedulerDecision === "execute" || m0HardFoldThisPass; + const smartDropsExecutePass = args.schedulerDecision === "execute" || m0HardFoldThisPass; const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate; - let autoReclaimTargetCount = 0; - let autoReclaimDidMutate = false; - if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) { - const syntheticPendingOps = buildSyntheticToolReclaimOps({ + let smartDropTargetCount = 0; + let smartDropsDidMutate = false; + if ( + args.smartDrops && + smartDropsExecutePass && + alreadyMutatingThisPass && + !emergencyDropEligible + ) { + // Smart-drops are an independently configured feature. They select + // outputs that a later call superseded; unlike the removed positional + // reclaim, age or a prior execute watermark cannot select a tool. + const smartDropOps = buildSupersessionReclaimOps({ db: args.db, sessionId: args.sessionId, targets: args.targets, - watermark: args.sessionMeta.toolReclaimWatermark ?? 0, pendingOps, }); - // Smart-drops: reclaim spent control-plane outputs that a later - // call supersedes (older todowrite/ctx_reduce/meta), and compress - // superseded edits to an edit_marker (keep filePath + region hint). - // Merged into the same gated apply as the age-based sweep. Dedupe - // against those ops (a tag can qualify under more than one rule). const editMarkerTagIds = new Set(); - if (args.smartDrops) { - const selectedIds = new Set(syntheticPendingOps.map((op) => op.tagId)); - const supersessionOps = buildSupersessionReclaimOps({ - db: args.db, - sessionId: args.sessionId, - targets: args.targets, - pendingOps, - }); - for (const op of supersessionOps) { - if (!selectedIds.has(op.tagId)) { - syntheticPendingOps.push(op); - selectedIds.add(op.tagId); - } - } - const editReclaim = buildEditSupersessionReclaim({ - db: args.db, - sessionId: args.sessionId, - targets: args.targets, - pendingOps, - }); - for (const op of editReclaim.ops) { - // A superseded edit only compresses if no earlier rule already - // selected it for a full/skeleton drop (drop wins; it reclaims - // strictly more). - if (!selectedIds.has(op.tagId)) { - syntheticPendingOps.push(op); - selectedIds.add(op.tagId); - editMarkerTagIds.add(op.tagId); - } + const selectedIds = new Set(smartDropOps.map((op) => op.tagId)); + const editReclaim = buildEditSupersessionReclaim({ + db: args.db, + sessionId: args.sessionId, + targets: args.targets, + pendingOps, + }); + for (const op of editReclaim.ops) { + // A superseded edit only compresses if the control-plane rule did + // not already select it for a full drop. + if (!selectedIds.has(op.tagId)) { + smartDropOps.push(op); + selectedIds.add(op.tagId); + editMarkerTagIds.add(op.tagId); } } - autoReclaimTargetCount = syntheticPendingOps.length; - if (syntheticPendingOps.length > 0) { - autoReclaimDidMutate = applyPendingOperations( + smartDropTargetCount = smartDropOps.length; + if (smartDropOps.length > 0) { + smartDropsDidMutate = applyPendingOperations( args.sessionId, args.db, args.targets, args.protectedTags, undefined, [], - syntheticPendingOps, + smartDropOps, editMarkerTagIds, ); - if (autoReclaimDidMutate) { - droppedCount += syntheticPendingOps.length; - autoReclaimDidMutateThisPass = true; + if (smartDropsDidMutate) { + droppedCount += smartDropOps.length; + smartDropsDidMutateThisPass = true; } } } args.batch?.finalize(); - if (toolReclaimExecutePass) { - const maxTagNumber = advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId); - args.sessionMeta.toolReclaimWatermark = Math.max( - args.sessionMeta.toolReclaimWatermark ?? 0, - maxTagNumber, - ); - } - if (autoReclaimTargetCount > 0) { + if (smartDropTargetCount > 0) { sessionLog( args.sessionId, - `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`, + `smart-drops reclaim: targets=${smartDropTargetCount} mutated=${smartDropsDidMutate}`, ); } logTransformTiming(args.sessionId, "batchFinalize:heuristics", performance.now()); @@ -1608,7 +1584,7 @@ export async function runPostTransformPhase( args.didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || - autoReclaimDidMutateThisPass || + smartDropsDidMutateThisPass || m0RematerializedThisPass || (m0M1InjectedThisPass && historyWasConsumedThisPass) || historyWasConsumedThisPass; From c46d1b8ad0015a122c4053084e52865c0efb8f58 Mon Sep 17 00:00:00 2001 From: tracycam Date: Thu, 23 Jul 2026 15:26:58 +0800 Subject: [PATCH 2/2] test(pi): format explicit reclaim regression --- packages/pi-plugin/src/context-handler.test.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/pi-plugin/src/context-handler.test.ts b/packages/pi-plugin/src/context-handler.test.ts index c0026d3cc..e82d67c9d 100644 --- a/packages/pi-plugin/src/context-handler.test.ts +++ b/packages/pi-plugin/src/context-handler.test.ts @@ -1361,11 +1361,16 @@ describe("registerPiContextHandler", () => { const result = await handler({ messages }, executeCtx as never); const statuses = new Map( - getTagsBySession(db, sessionId).map((tag) => [tag.tagNumber, tag.status]), + getTagsBySession(db, sessionId).map((tag) => [ + tag.tagNumber, + tag.status, + ]), ); expect(statuses.get(toolTags[0].tagNumber)).toBe("dropped"); expect(statuses.get(toolTags[1].tagNumber)).toBe("active"); - expect(textOf(result.messages[4] as never)).toContain("unrequested output"); + expect(textOf(result.messages[4] as never)).toContain( + "unrequested output", + ); expect(getOrCreateSessionMeta(db, sessionId).toolReclaimWatermark).toBe( toolTags[1].tagNumber, );