import type { AgentTool } from "@earendil-works/pi-agent-core"; import { fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai"; import { Type } from "typebox"; import { afterEach, describe, expect, it } from "vitest"; import { createHarness, type Harness } from "../harness.ts"; function createNoopTool(): AgentTool { return { name: "noop", label: "No-op", description: "Return immediately", parameters: Type.Object({}), execute: async () => ({ content: [{ type: "text", text: "done" }], details: {} }), }; } describe("issue #7253: manual compaction during an active response", () => { const harnesses: Harness[] = []; afterEach(() => { while (harnesses.length > 0) { harnesses.pop()?.cleanup(); } }); it("persists the aborted response before running the requested manual compaction", async () => { let markSecondResponseStarted = () => {}; const secondResponseStarted = new Promise((resolve) => { markSecondResponseStarted = resolve; }); let releaseSecondResponse = () => {}; const secondResponseReleased = new Promise((resolve) => { releaseSecondResponse = resolve; }); const harness = await createHarness({ models: [{ id: "faux-1", contextWindow: 1000, maxTokens: 1000 }], settings: { compaction: { enabled: true, reserveTokens: 200, keepRecentTokens: 2 } }, tools: [createNoopTool()], extensionFactories: [ (pi) => { pi.on("session_before_compact", async (event) => ({ compaction: { summary: `${event.reason} summary`, firstKeptEntryId: event.preparation.firstKeptEntryId, tokensBefore: event.preparation.tokensBefore, details: {}, }, })); }, ], }); harnesses.push(harness); harness.setResponses([ fauxAssistantMessage(fauxToolCall("noop", {}), { stopReason: "toolUse" }), async () => { markSecondResponseStarted(); await secondResponseReleased; return fauxAssistantMessage(`second response:${"x".repeat(4000)}`); }, ]); const promptPromise = harness.session.prompt("Run the tool, then continue responding."); await secondResponseStarted; const compactPromise = harness.session.compact(); const compactExpectation = expect(compactPromise).resolves.toMatchObject({ summary: "manual summary" }); releaseSecondResponse(); await Promise.all([promptPromise, compactExpectation]); expect(harness.eventsOfType("compaction_start").map((event) => event.reason)).toEqual(["manual"]); expect(harness.eventsOfType("compaction_end").map((event) => event.reason)).toEqual(["manual"]); const entries = harness.sessionManager.getEntries(); const abortedResponseIndex = entries.findIndex( (entry) => entry.type === "message" && entry.message.role === "assistant" && entry.message.stopReason === "aborted", ); const compactionIndex = entries.findIndex((entry) => entry.type === "compaction"); expect(abortedResponseIndex).toBeGreaterThan(-1); expect(compactionIndex).toBeGreaterThan(abortedResponseIndex); expect(entries.filter((entry) => entry.type === "compaction")).toHaveLength(1); }); });