From e58f995faebbe9eb29b83048a26ff74c1e0f0b09 Mon Sep 17 00:00:00 2001 From: ytahdn <1294726970@qq.com> Date: Thu, 23 Jul 2026 16:37:35 +0800 Subject: [PATCH] fix(core): avoid empty transcript history pages (#7582) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 钉萁 --- .../session-transcript-reader.test.ts | 26 +++++++++++++ .../src/services/session-transcript-reader.ts | 38 +++++++++++++------ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/packages/core/src/services/session-transcript-reader.test.ts b/packages/core/src/services/session-transcript-reader.test.ts index e0435446d4..7eed032326 100644 --- a/packages/core/src/services/session-transcript-reader.test.ts +++ b/packages/core/src/services/session-transcript-reader.test.ts @@ -329,6 +329,32 @@ describe('SessionTranscriptReader', () => { }); }); + it('includes leading session metadata with the first backward page', async () => { + const sessionSource = { + ...record('source', null, 'session source'), + type: 'system' as const, + subtype: 'session_source' as const, + }; + await writeRecords([ + sessionSource, + record('u1', 'source', 'first prompt'), + record('a1', 'u1', 'first answer'), + ]); + + const page = await new SessionTranscriptReader(workspaceDir).readPage( + sessionId, + { direction: 'backward', limit: 100 }, + ); + + expect(page.records.map((item) => item.uuid)).toEqual([ + 'source', + 'u1', + 'a1', + ]); + expect(page.hasMore).toBe(false); + expect(page.nextCursorState).toBeUndefined(); + }); + it('keeps backward pages within a normal user turn boundary', async () => { const toolCall = record('a-tool', 'u1', 'call tool'); const toolResult = { diff --git a/packages/core/src/services/session-transcript-reader.ts b/packages/core/src/services/session-transcript-reader.ts index 14a0e3673b..e3ef8ffcf3 100644 --- a/packages/core/src/services/session-transcript-reader.ts +++ b/packages/core/src/services/session-transcript-reader.ts @@ -517,24 +517,38 @@ function selectBackwardPageUuids( break; } } - if (!alignedToReplayBoundary) { + let expandedSelection = false; + if (alignedToReplayBoundary && selectedStart > 0) { + let previousTurnStart = selectedStart - 1; + while ( + previousTurnStart >= 0 && + !isReplayTurnStart(index, index.activeUuids[previousTurnStart]!) + ) { + previousTurnStart--; + } + if (previousTurnStart < 0) { + selectedStart = 0; + expandedSelection = true; + } + } else if (!alignedToReplayBoundary) { while ( selectedStart > 0 && !isReplayTurnStart(index, index.activeUuids[selectedStart]!) ) { selectedStart--; } - if (maxBytes !== undefined) { - const alignedBytes = index.activeUuids - .slice(selectedStart, position) - .reduce((total, uuid) => total + recordSegmentBytes(index, uuid), 0); - if (alignedBytes > maxBytes) { - throw new SessionTranscriptPageTooLargeError( - sessionId, - alignedBytes, - maxBytes, - ); - } + expandedSelection = true; + } + if (expandedSelection && maxBytes !== undefined) { + const alignedBytes = index.activeUuids + .slice(selectedStart, position) + .reduce((total, uuid) => total + recordSegmentBytes(index, uuid), 0); + if (alignedBytes > maxBytes) { + throw new SessionTranscriptPageTooLargeError( + sessionId, + alignedBytes, + maxBytes, + ); } }