From 13ca829e2e328e994bcfb3615aab82a4db5bf004 Mon Sep 17 00:00:00 2001 From: Wynne668 Date: Fri, 17 Jul 2026 06:09:06 +0800 Subject: [PATCH] fix(sessions): preserve lifecycle headers across short reads (#109205) --- src/config/sessions/lifecycle.ts | 3 ++- src/config/sessions/sessions.test.ts | 36 +++++++++++++++++++++------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/config/sessions/lifecycle.ts b/src/config/sessions/lifecycle.ts index f7a12488c649..74111034c4c4 100644 --- a/src/config/sessions/lifecycle.ts +++ b/src/config/sessions/lifecycle.ts @@ -1,5 +1,6 @@ // Session lifecycle timestamps prefer store metadata and fall back to transcript headers. import fs from "node:fs"; +import { readFileWindowFullySync } from "../../infra/file-read.js"; import { asDateTimestampMs } from "../../shared/number-coercion.js"; import { canonicalizeMainSessionAlias } from "./main-session.js"; import { @@ -111,7 +112,7 @@ function readFirstLine(filePath: string): string | undefined { const fd = fs.openSync(filePath, "r"); try { const buffer = Buffer.alloc(8192); - const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0); + const bytesRead = readFileWindowFullySync(fd, buffer, 0); if (bytesRead <= 0) { return undefined; } diff --git a/src/config/sessions/sessions.test.ts b/src/config/sessions/sessions.test.ts index bb186ecf078d..d4fd57314975 100644 --- a/src/config/sessions/sessions.test.ts +++ b/src/config/sessions/sessions.test.ts @@ -302,16 +302,34 @@ describe("session lifecycle timestamps", () => { "utf8", ); - const timestamps = resolveSessionLifecycleTimestamps({ - storePath, - entry: { - sessionId: "legacy-session", - sessionFile, - updatedAt: Date.parse("2026-04-25T08:00:00.000Z"), - }, - }); + const realReadSync = fs.readSync.bind(fs); + let shortReadCalls = 0; + const readSpy = vi.spyOn(fs, "readSync").mockImplementation((( + fd: number, + buffer: NodeJS.ArrayBufferView, + offset: number, + length: number, + position: fs.ReadPosition | null, + ) => { + shortReadCalls += 1; + return realReadSync(fd, buffer, offset, Math.min(length, 16), position); + }) as typeof fs.readSync); - expect(timestamps.sessionStartedAt).toBe(Date.parse(headerTimestamp)); + try { + const timestamps = resolveSessionLifecycleTimestamps({ + storePath, + entry: { + sessionId: "legacy-session", + sessionFile, + updatedAt: Date.parse("2026-04-25T08:00:00.000Z"), + }, + }); + + expect(timestamps.sessionStartedAt).toBe(Date.parse(headerTimestamp)); + expect(shortReadCalls).toBeGreaterThan(1); + } finally { + readSpy.mockRestore(); + } } finally { await fsPromises.rm(dir, { recursive: true, force: true }); }