fix(coding-agent): avoid pre-prompt compaction continue
This commit is contained in:
@@ -1075,17 +1075,11 @@ export class AgentSession {
|
|||||||
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
throw new Error(formatNoApiKeyFoundMessage(this.model.provider));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to compact before sending (catches aborted responses)
|
// Check if we need to compact before sending (catches aborted responses).
|
||||||
|
// The user's new prompt is sent below, so do not call agent.continue() here.
|
||||||
const lastAssistant = this._findLastAssistantMessage();
|
const lastAssistant = this._findLastAssistantMessage();
|
||||||
if (lastAssistant && (await this._checkCompaction(lastAssistant, false))) {
|
if (lastAssistant) {
|
||||||
try {
|
await this._checkCompaction(lastAssistant, false);
|
||||||
await this.agent.continue();
|
|
||||||
while (await this._handlePostAgentRun()) {
|
|
||||||
await this.agent.continue();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this._flushPendingBashMessages();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build messages array (custom message if any, then user message)
|
// Build messages array (custom message if any, then user message)
|
||||||
|
|||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
import { type AssistantMessage, fauxAssistantMessage } from "@earendil-works/pi-ai";
|
||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { createHarness, getUserTexts, type Harness } from "../harness.ts";
|
||||||
|
|
||||||
|
function createUsage(totalTokens: number) {
|
||||||
|
return {
|
||||||
|
input: totalTokens,
|
||||||
|
output: 0,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
totalTokens,
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("pre-prompt compaction regression", () => {
|
||||||
|
const harnesses: Harness[] = [];
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
while (harnesses.length > 0) {
|
||||||
|
harnesses.pop()?.cleanup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("compacts length-stop overflow before a new prompt without continuing from an assistant message", async () => {
|
||||||
|
const harness = await createHarness({
|
||||||
|
models: [{ id: "faux-1", contextWindow: 100, maxTokens: 100 }],
|
||||||
|
settings: { compaction: { enabled: true, keepRecentTokens: 1, reserveTokens: 0 } },
|
||||||
|
extensionFactories: [
|
||||||
|
(pi) => {
|
||||||
|
pi.on("session_before_compact", async (event) => ({
|
||||||
|
compaction: {
|
||||||
|
summary: "pre-prompt summary",
|
||||||
|
firstKeptEntryId: event.preparation.firstKeptEntryId,
|
||||||
|
tokensBefore: event.preparation.tokensBefore,
|
||||||
|
details: {},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
harnesses.push(harness);
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
const model = harness.getModel();
|
||||||
|
harness.sessionManager.appendMessage({
|
||||||
|
role: "user",
|
||||||
|
content: [{ type: "text", text: "previous prompt" }],
|
||||||
|
timestamp: now - 1000,
|
||||||
|
});
|
||||||
|
const lengthStopAssistant: AssistantMessage = {
|
||||||
|
...fauxAssistantMessage("length-stop assistant response", { stopReason: "length", timestamp: now - 500 }),
|
||||||
|
api: model.api,
|
||||||
|
provider: model.provider,
|
||||||
|
model: model.id,
|
||||||
|
usage: createUsage(100),
|
||||||
|
};
|
||||||
|
harness.sessionManager.appendMessage(lengthStopAssistant);
|
||||||
|
harness.session.agent.state.messages = harness.sessionManager.buildSessionContext().messages;
|
||||||
|
harness.setResponses([fauxAssistantMessage("answered next prompt")]);
|
||||||
|
const continueSpy = vi.spyOn(harness.session.agent, "continue");
|
||||||
|
|
||||||
|
await expect(harness.session.prompt("next prompt")).resolves.toBeUndefined();
|
||||||
|
|
||||||
|
expect(continueSpy).not.toHaveBeenCalled();
|
||||||
|
expect(harness.eventsOfType("compaction_end").at(-1)).toMatchObject({
|
||||||
|
reason: "overflow",
|
||||||
|
aborted: false,
|
||||||
|
willRetry: true,
|
||||||
|
});
|
||||||
|
expect(getUserTexts(harness)).toContain("next prompt");
|
||||||
|
expect(harness.faux.state.callCount).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user