fix(rpc): stripped snapcompact archive from auto compaction event too

The auto_compaction_end event and its frame-rescue variant emitted the unstripped snapcompact preserveData, forcing the RPC shrink ladder on every unattended pass and risking a silently dropped event. Strip the archive from both event payloads while keeping it in the persisted compaction entry.

Fixes #8168
This commit is contained in:
roboomp
2026-08-10 16:00:22 +00:00
parent 6f2c931012
commit 1451f9a1b9
3 changed files with 44 additions and 4 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
### Fixed
- Fixed RPC v1 manual snapcompact compaction reporting a transport failure after the compaction had already persisted by omitting the redundant frame archive from the response ([#8168](https://github.com/can1357/oh-my-pi/issues/8168)).
- Fixed snapcompact compaction shipping its redundant frame archive out of `SessionMaintenance.compact()` on both the manual RPC response (which hard-failed protocol v1 with a transport error after the compaction had already persisted) and the `auto_compaction_end` event payload (which forced the shrink ladder on every unattended pass); the archive is now stripped from both exits while the persisted compaction entry keeps it ([#8168](https://github.com/can1357/oh-my-pi/issues/8168)).
## [17.2.12] - 2026-08-08
@@ -2390,7 +2390,10 @@ export class SessionMaintenance {
await this.#host.emitSessionEvent({
type: "auto_compaction_end",
action,
result: frameRescueResult,
result: frameRescueResult && {
...frameRescueResult,
preserveData: snapcompact.stripPreservedArchive(frameRescueResult.preserveData),
},
aborted: false,
willRetry: false,
skipped: frameRescueResult === undefined,
@@ -2790,7 +2793,7 @@ export class SessionMaintenance {
firstKeptEntryId,
tokensBefore,
details,
preserveData,
preserveData: snapcompact.stripPreservedArchive(preserveData),
};
// Post-maintenance progress guard — evaluated BEFORE emitting
// auto_compaction_end so the TUI rebuild triggered by that event
@@ -27,7 +27,7 @@ import { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
import { encodeRpcFrame, MAX_RPC_FRAME_BYTES } from "@oh-my-pi/pi-coding-agent/modes/rpc/rpc-frame";
import { computeNonMessageTokens } from "@oh-my-pi/pi-coding-agent/modes/utils/context-usage";
import { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
import { AgentSession, type AgentSessionEvent } from "@oh-my-pi/pi-coding-agent/session/agent-session";
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
import { TempDir } from "@oh-my-pi/pi-utils";
@@ -328,4 +328,41 @@ describe("AgentSession snapcompact frame-budget sizing", () => {
[snapcompact.PRESERVE_KEY]: archive,
});
});
it("keeps the frame archive out of the auto_compaction_end event after persisting it", async () => {
const branchEntries = sessionManager.getBranch();
const lastEntry = branchEntries[branchEntries.length - 1];
if (!lastEntry?.id) throw new Error("Expected branch entry with id");
// A zero-frame archive clears the payload/projection gates on the auto
// path while still carrying PRESERVE_KEY, so the strip is what removes it.
const archive = { frames: [], totalChars: 1000, truncatedChars: 0 };
vi.spyOn(snapcompact, "compact").mockResolvedValue({
summary: "stubbed snapcompact",
shortSummary: "stub",
firstKeptEntryId: lastEntry.id,
tokensBefore: 100_000,
details: { readFiles: [], modifiedFiles: [] },
preserveData: {
extensionState: "keep-me",
[snapcompact.PRESERVE_KEY]: archive,
},
});
const events: AgentSessionEvent[] = [];
session.subscribe(event => events.push(event));
await session.runIdleCompaction();
const endEvent = events.find(
(event): event is Extract<AgentSessionEvent, { type: "auto_compaction_end" }> =>
event.type === "auto_compaction_end" && event.result !== undefined,
);
if (!endEvent?.result) throw new Error("Expected a result-carrying auto_compaction_end event");
expect(endEvent.result.preserveData).toEqual({ extensionState: "keep-me" });
const compactionEntry = sessionManager.getEntries().find(entry => entry.type === "compaction");
if (compactionEntry?.type !== "compaction") throw new Error("Expected persisted compaction entry");
expect(compactionEntry.preserveData).toEqual({
extensionState: "keep-me",
[snapcompact.PRESERVE_KEY]: archive,
});
});
});