-
Notifications
You must be signed in to change notification settings - Fork 699
Fix/1984 synthetic compaction #2036
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -299,6 +299,69 @@ describe("AgentSessionProvider — tree-structured messages", () => { | |
| expect(compactions).toHaveLength(1); | ||
| }); | ||
|
|
||
| it("does not persist synthetic compaction overlays echoed back by a client (#1984)", async () => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For completeness, it might be good to add a test for the postgres provider as well |
||
| const agent = await getAgent(name); | ||
| for (let i = 0; i < 6; i++) { | ||
| await agent.appendMessage({ | ||
| id: `m${i}`, | ||
| role: i % 2 === 0 ? "user" : "assistant", | ||
| parts: [{ type: "text", text: `msg ${i}` }] | ||
| }); | ||
| } | ||
|
|
||
| await agent.addCompaction("Summary of m1-m3", "m1", "m3"); | ||
|
|
||
| // getHistory substitutes a synthetic compaction_<id> overlay on read. | ||
| const first = await agent.getHistory(); | ||
| const overlay = first.find((m) => m.id.startsWith("compaction_")); | ||
| expect(overlay).toBeDefined(); | ||
|
|
||
| // A browser transport echoes the full transcript back on the next turn, | ||
| // so the synthetic overlay arrives as an incoming message and the host | ||
| // tries to persist it. It must NOT become a real row. | ||
| await agent.appendMessage(overlay!, "m0"); | ||
|
|
||
| // No compaction-prefixed row should exist in storage. | ||
| expect(await agent.getMessage(overlay!.id)).toBeNull(); | ||
|
|
||
| // And the overlay must appear exactly once in the projection. | ||
| const after = await agent.getHistory(); | ||
| const overlayCount = after.filter((m) => | ||
| m.id.startsWith("compaction_") | ||
| ).length; | ||
| expect(overlayCount).toBe(1); | ||
| expect(after.map((m) => m.id)).toEqual(["m0", overlay!.id, "m4", "m5"]); | ||
| }); | ||
|
|
||
| it("filters a pre-filed compaction row out of getHistory (existing sessions, #1984)", async () => { | ||
| const agent = await getAgent(name); | ||
| for (let i = 0; i < 6; i++) { | ||
| await agent.appendMessage({ | ||
| id: `m${i}`, | ||
| role: i % 2 === 0 ? "user" : "assistant", | ||
| parts: [{ type: "text", text: `msg ${i}` }] | ||
| }); | ||
| } | ||
| await agent.addCompaction("Summary of m1-m3", "m1", "m3"); | ||
|
|
||
| const overlay = (await agent.getHistory()).find((m) => | ||
| m.id.startsWith("compaction_") | ||
| ); | ||
| expect(overlay).toBeDefined(); | ||
|
|
||
| // Simulate an already-corrupted session: a synthetic overlay was filed as | ||
| // a real row on a prior turn (before the intake guard existed), parented | ||
| // into the live chain via the raw insert seam. | ||
| await agent.rawInsertChildForTest("m5", overlay!.id); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For completeness, I might test that a child of the overlay message is restored and re-parented correctly |
||
|
|
||
| // The read projection must still show the overlay exactly once. | ||
| const history = await agent.getHistory(); | ||
| const overlayCount = history.filter((m) => | ||
| m.id.startsWith("compaction_") | ||
| ).length; | ||
| expect(overlayCount).toBe(1); | ||
| }); | ||
|
|
||
| it("iterative compaction — new overlay supersedes old one at same fromId", async () => { | ||
| const agent = await getAgent(name); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? Receiving a compaction message from the browser is expected, so we’re logging a warning under normal, happy-path conditions (potentially even on every single turn after a compaction?)