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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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],
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user