diff --git a/src/config/sessions/session-accessor.test.ts b/src/config/sessions/session-accessor.test.ts index 6ef67e05f92..8ea2afc10ce 100644 --- a/src/config/sessions/session-accessor.test.ts +++ b/src/config/sessions/session-accessor.test.ts @@ -1036,6 +1036,92 @@ describe("session accessor file-backed seam", () => { }); }); + it("branches from the newest matching compaction checkpoint without sorting all checkpoints", async () => { + const sourceSessionId = "11111111-1111-4111-8111-111111111111"; + const branchSessionId = "22222222-2222-4222-8222-222222222222"; + const branchPath = path.join(tempDir, "branch-newest.jsonl"); + fs.writeFileSync(branchPath, `{"type":"session","id":"${branchSessionId}"}\n`, "utf8"); + const oldMatchingCheckpoint = { + checkpointId: "checkpoint-1", + sessionKey: "agent:main:main", + sessionId: sourceSessionId, + createdAt: 10, + reason: "manual", + preCompaction: { + sessionId: sourceSessionId, + leafId: "old-leaf", + }, + postCompaction: { sessionId: "33333333-3333-4333-8333-333333333333" }, + } satisfies NonNullable[number]; + const newestMatchingCheckpoint = { + ...oldMatchingCheckpoint, + createdAt: 20, + preCompaction: { + sessionId: sourceSessionId, + leafId: "new-leaf", + }, + postCompaction: { sessionId: "44444444-4444-4444-8444-444444444444" }, + } satisfies NonNullable[number]; + const newestDifferentCheckpoint = { + ...oldMatchingCheckpoint, + checkpointId: "checkpoint-2", + createdAt: 30, + preCompaction: { + sessionId: sourceSessionId, + leafId: "different-leaf", + }, + postCompaction: { sessionId: "55555555-5555-4555-8555-555555555555" }, + } satisfies NonNullable[number]; + fs.writeFileSync( + storePath, + JSON.stringify( + { + main: { + sessionId: sourceSessionId, + updatedAt: 30, + compactionCheckpoints: [ + oldMatchingCheckpoint, + newestDifferentCheckpoint, + newestMatchingCheckpoint, + ], + }, + } satisfies Record, + null, + 2, + ), + "utf8", + ); + + const result = await branchSessionFromCompactionCheckpoint({ + storePath, + sourceKey: "agent:main:main", + sourceStoreKey: "main", + nextKey: "agent:main:branch-newest", + checkpointId: "checkpoint-1", + forkTranscriptFromCheckpoint: async (selectedCheckpoint) => { + expect(selectedCheckpoint).toEqual(newestMatchingCheckpoint); + return { + status: "created", + transcript: { + sessionFile: branchPath, + sessionId: branchSessionId, + }, + }; + }, + buildEntry: ({ currentEntry, forkedTranscript }) => ({ + ...currentEntry, + sessionFile: forkedTranscript.sessionFile, + sessionId: forkedTranscript.sessionId, + }), + }); + + expect(result).toMatchObject({ + status: "created", + key: "agent:main:branch-newest", + checkpoint: newestMatchingCheckpoint, + }); + }); + it("does not persist checkpoint restores when the transcript boundary is missing", async () => { fs.writeFileSync( storePath, diff --git a/src/config/sessions/session-accessor.ts b/src/config/sessions/session-accessor.ts index af53aa512d9..7f1741335f0 100644 --- a/src/config/sessions/session-accessor.ts +++ b/src/config/sessions/session-accessor.ts @@ -1355,9 +1355,16 @@ function findSessionCompactionCheckpoint(params: { if (!checkpointId || !Array.isArray(params.entry.compactionCheckpoints)) { return undefined; } - return [...params.entry.compactionCheckpoints] - .toSorted((a, b) => b.createdAt - a.createdAt) - .find((checkpoint) => checkpoint.checkpointId === checkpointId); + let newest: SessionCompactionCheckpoint | undefined; + for (const checkpoint of params.entry.compactionCheckpoints) { + if (checkpoint.checkpointId !== checkpointId) { + continue; + } + if (!newest || checkpoint.createdAt > newest.createdAt) { + newest = checkpoint; + } + } + return newest; } type ApplySessionCompactionCheckpointMutationParams = {