Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/core/src/services/session-transcript-reader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
38 changes: 26 additions & 12 deletions packages/core/src/services/session-transcript-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] expandedSelection = true is set unconditionally here, even when the while loop didn't move selectedStart (already aligned to a turn boundary). Functionally equivalent to prior behavior, but the name implies expansion occurred. Consider renaming to needsByteCheck or adding a comment: // always check bytes in the unaligned path, matching prior behavior.

— qwen3.8-max-preview via Qwen Code /review

}
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,
);
}
}

Expand Down
Loading