From c3da093a1d066b3da302b6783f6e849b6aac01f6 Mon Sep 17 00:00:00 2001 From: left-to-right <2201218482@qq.com> Date: Sun, 9 Aug 2026 18:09:17 +0800 Subject: [PATCH 1/3] fix(compaction): manual /shake keeps a recent tail of tool results Manual /shake used protectTokens: 0, stripping every eligible tool result including the ones the agent is still working from. Keep a small 4k-token recent window (matching the automatic shake mechanism, at a quarter of its 16k budget) so the full escape hatch stays aggressive without destroying the live tail. Two matcher-focused tests that implicitly relied on the zero window now pin protectTokens: 0 explicitly. Fixes #7776 --- packages/agent/CHANGELOG.md | 4 ++++ packages/agent/src/compaction/shake.ts | 8 +++++--- packages/agent/test/shake.test.ts | 15 +++++++++++++-- packages/agent/test/tool-protection.test.ts | 4 +++- .../test/plan-mode/plan-protection.test.ts | 1 + 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/agent/CHANGELOG.md b/packages/agent/CHANGELOG.md index 56c6c404d..e43f4e15d 100644 --- a/packages/agent/CHANGELOG.md +++ b/packages/agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Manual `/shake` now keeps a small recent tail of tool results instead of stripping every eligible result, so the agent does not lose the context it is currently working from ([#7776](https://github.com/can1357/oh-my-pi/issues/7776)). + ## [17.2.10] - 2026-08-06 ### Fixed diff --git a/packages/agent/src/compaction/shake.ts b/packages/agent/src/compaction/shake.ts index 3d5289b7d..1286d7b1f 100644 --- a/packages/agent/src/compaction/shake.ts +++ b/packages/agent/src/compaction/shake.ts @@ -52,11 +52,13 @@ export const DEFAULT_SHAKE_CONFIG: ShakeConfig = { }; /** - * Manual `/shake`: aggressive — drops every eligible region across history, - * artifact recovery reads included (the user's full escape hatch). + * Manual `/shake`: aggressive — no savings threshold and drops eligible + * regions across history, artifact recovery reads included (the user's full + * escape hatch). Still keeps a small recent tail so it cannot strip the tool + * results the agent is currently working from (#7776). */ export const AGGRESSIVE_SHAKE_CONFIG: ShakeConfig = { - protectTokens: 0, + protectTokens: 4_000, minSavings: 0, protectedTools: ["skill", isSkillReadToolResult], fenceMinTokens: 400, diff --git a/packages/agent/test/shake.test.ts b/packages/agent/test/shake.test.ts index 86978f24f..dee8926c1 100644 --- a/packages/agent/test/shake.test.ts +++ b/packages/agent/test/shake.test.ts @@ -204,12 +204,23 @@ describe("applyShakeRegions — multi-region ordering", () => { }); describe("shake config presets", () => { - test("aggressive preset protects skill and drops everything else", () => { - expect(AGGRESSIVE_SHAKE_CONFIG.protectTokens).toBe(0); + test("aggressive preset protects skill and keeps a small recent tail", () => { + expect(AGGRESSIVE_SHAKE_CONFIG.protectTokens).toBeGreaterThan(0); expect(AGGRESSIVE_SHAKE_CONFIG.minSavings).toBe(0); expect(AGGRESSIVE_SHAKE_CONFIG.protectedTools).toContain("skill"); }); + test("manual shake preserves the recent tool-result tail instead of stripping everything", () => { + const older = messageEntry(toolResultMessage("bash", "old-result ".repeat(300))); + const recent = messageEntry(toolResultMessage("bash", "recent-result ".repeat(3000))); + const regions = collectShakeRegions([older, recent], AGGRESSIVE_SHAKE_CONFIG); + + // The recent result sits inside the preserved tail; the older one is + // still shaken aggressively. + expect(regions).toHaveLength(1); + expect(regions[0].entry).toBe(older); + }); + test("default preset keeps a protect window", () => { expect(DEFAULT_SHAKE_CONFIG.protectTokens).toBeGreaterThan(0); expect(DEFAULT_SHAKE_CONFIG.protectedTools).toContain("skill"); diff --git a/packages/agent/test/tool-protection.test.ts b/packages/agent/test/tool-protection.test.ts index 0d5281dae..48c61e9ee 100644 --- a/packages/agent/test/tool-protection.test.ts +++ b/packages/agent/test/tool-protection.test.ts @@ -84,7 +84,9 @@ describe("conditional tool-result protection", () => { fileResult, ]; - const regions = collectShakeRegions(entries, AGGRESSIVE_SHAKE_CONFIG); + // protectTokens: 0 isolates the matcher behavior from the aggressive + // preset's recent-tail window (covered by shake.test.ts). + const regions = collectShakeRegions(entries, { ...AGGRESSIVE_SHAKE_CONFIG, protectTokens: 0 }); expect(regions).toHaveLength(1); expect(regions[0]?.kind).toBe("toolResult"); diff --git a/packages/coding-agent/test/plan-mode/plan-protection.test.ts b/packages/coding-agent/test/plan-mode/plan-protection.test.ts index ab234b102..fa8424307 100644 --- a/packages/coding-agent/test/plan-mode/plan-protection.test.ts +++ b/packages/coding-agent/test/plan-mode/plan-protection.test.ts @@ -155,6 +155,7 @@ describe("plan-read protection in compaction", () => { const regions = collectShakeRegions(entries, { ...AGGRESSIVE_SHAKE_CONFIG, + protectTokens: 0, protectedTools: [...AGGRESSIVE_SHAKE_CONFIG.protectedTools, matcher], }); From 835a7db15a5d307824432953ce2178969e955e79 Mon Sep 17 00:00:00 2001 From: left-to-right <2201218482@qq.com> Date: Tue, 11 Aug 2026 22:33:40 +0800 Subject: [PATCH 2/3] fix(compaction): keep rescue shake able to elide the newest result RESCUE_SHAKE_CONFIG spreads AGGRESSIVE_SHAKE_CONFIG, so the new 4k manual tail leaked into dead-end recovery and could block eliding the very result that caused the dead end. Override protectTokens back to 0 in the rescue preset and add the coding-agent changelog entry for the manual /shake behavior change. Addresses review on #8067. --- packages/agent/src/compaction/shake.ts | 4 ++++ packages/agent/test/shake.test.ts | 8 ++++++++ packages/coding-agent/CHANGELOG.md | 1 + 3 files changed, 13 insertions(+) diff --git a/packages/agent/src/compaction/shake.ts b/packages/agent/src/compaction/shake.ts index 1286d7b1f..43cc42f6b 100644 --- a/packages/agent/src/compaction/shake.ts +++ b/packages/agent/src/compaction/shake.ts @@ -67,6 +67,10 @@ export const AGGRESSIVE_SHAKE_CONFIG: ShakeConfig = { /** Compaction dead-end rescue: aggressive reach, but artifact recovery reads stay protected. */ export const RESCUE_SHAKE_CONFIG: ShakeConfig = { ...AGGRESSIVE_SHAKE_CONFIG, + // Rescue must be able to elide the newest oversized result even inside the + // manual preset's recent-tail window (#7776) — a dead-end recovery that + // cannot drop its blocker is not a recovery. + protectTokens: 0, protectedTools: [...AGGRESSIVE_SHAKE_CONFIG.protectedTools, isArtifactRecoveryToolResult], }; diff --git a/packages/agent/test/shake.test.ts b/packages/agent/test/shake.test.ts index dee8926c1..b9da194b9 100644 --- a/packages/agent/test/shake.test.ts +++ b/packages/agent/test/shake.test.ts @@ -8,6 +8,7 @@ import { collectShakeRegions, DEFAULT_SHAKE_CONFIG, estimateTokens, + RESCUE_SHAKE_CONFIG, } from "@oh-my-pi/pi-agent-core/compaction"; import type { AssistantMessage, TextContent, ToolCall, ToolResultMessage } from "@oh-my-pi/pi-ai"; @@ -226,6 +227,13 @@ describe("shake config presets", () => { expect(DEFAULT_SHAKE_CONFIG.protectedTools).toContain("skill"); }); + test("rescue preset overrides the manual tail so it can elide the newest result", () => { + const recent = messageEntry(toolResultMessage("bash", "oversized-result ".repeat(2000))); + const regions = collectShakeRegions([recent], RESCUE_SHAKE_CONFIG); + expect(regions).toHaveLength(1); + expect(regions[0].entry).toBe(recent); + }); + test("empty branch yields no regions", () => { expect(collectShakeRegions([] as SessionEntry[], AGGRESSIVE_SHAKE_CONFIG)).toHaveLength(0); }); diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index a4d66e8d2..395bd517f 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -8,6 +8,7 @@ - Fixed shell minimization replacing meaningful `rustc --print` output with `OK`. - Fixed shell minimization altering outputs shorter than 1,000 characters; these now pass through unchanged. +- Manual `/shake` now keeps a small recent tail of tool results instead of stripping every eligible result, so the agent does not lose the context it is currently working from ([#7776](https://github.com/can1357/oh-my-pi/issues/7776)). - Fixed primary and advisor Codex sessions falling back to another provider before trying sibling accounts when an account lacks Trusted Access for Cyber approval. - Fixed task subagent assistant turns being omitted from the per-model TPS/TTFT aggregates shown by `/models`. ([#8022](https://github.com/can1357/oh-my-pi/issues/8022)) - Fixed terminal-title spinner writes consuming CPU during WSL/ConPTY agent waits by using the same static working separator as native Windows ([#8012](https://github.com/can1357/oh-my-pi/issues/8012)). From cd0050a62e9ad8480491f238a5d9e58eb450c955 Mon Sep 17 00:00:00 2001 From: left-to-right <2201218482@qq.com> Date: Thu, 13 Aug 2026 01:11:56 +0800 Subject: [PATCH 3/3] chore(changelog): move /shake entry to Unreleased The #7776 entry landed under the already-released 17.2.12 section; move it to the coding-agent Unreleased Fixed section (roboomp, codex). Co-Authored-By: Claude Opus 4.7 --- packages/coding-agent/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 395bd517f..be3103172 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,13 +2,16 @@ ## [Unreleased] +### Fixed + +- Manual `/shake` now keeps a small recent tail of tool results instead of stripping every eligible result, so the agent does not lose the context it is currently working from ([#7776](https://github.com/can1357/oh-my-pi/issues/7776)). + ## [17.2.12] - 2026-08-08 ### Fixed - Fixed shell minimization replacing meaningful `rustc --print` output with `OK`. - Fixed shell minimization altering outputs shorter than 1,000 characters; these now pass through unchanged. -- Manual `/shake` now keeps a small recent tail of tool results instead of stripping every eligible result, so the agent does not lose the context it is currently working from ([#7776](https://github.com/can1357/oh-my-pi/issues/7776)). - Fixed primary and advisor Codex sessions falling back to another provider before trying sibling accounts when an account lacks Trusted Access for Cyber approval. - Fixed task subagent assistant turns being omitted from the per-model TPS/TTFT aggregates shown by `/models`. ([#8022](https://github.com/can1357/oh-my-pi/issues/8022)) - Fixed terminal-title spinner writes consuming CPU during WSL/ConPTY agent waits by using the same static working separator as native Windows ([#8012](https://github.com/can1357/oh-my-pi/issues/8012)).