fix(session-memory): fallback to rotated transcript after /new

When /new rotates <session>.jsonl to <session>.jsonl.reset.*, the session-memory hook may read an empty active transcript and write header-only memory entries.

Add fallback logic to read the latest .jsonl.reset.* sibling when the primary file has no usable content.

Also add a unit test covering the rotated transcript path.

Fixes #18088
Refs #17563
This commit is contained in:
Tomas Hajek
2026-02-16 22:38:30 +00:00
committed by Peter Steinberger
parent 769f7631d5
commit 19ae7a4e17
2 changed files with 87 additions and 2 deletions

View File

@@ -67,6 +67,46 @@ async function getRecentSessionContent(
}
}
/**
* Try the active transcript first; if /new already rotated it,
* fallback to the latest .jsonl.reset.* sibling.
*/
async function getRecentSessionContentWithResetFallback(
sessionFilePath: string,
messageCount: number = 15,
): Promise<string | null> {
const primary = await getRecentSessionContent(sessionFilePath, messageCount);
if (primary) {
return primary;
}
try {
const dir = path.dirname(sessionFilePath);
const base = path.basename(sessionFilePath);
const resetPrefix = `${base}.reset.`;
const files = await fs.readdir(dir);
const resetCandidates = files.filter((name) => name.startsWith(resetPrefix)).toSorted();
if (resetCandidates.length === 0) {
return primary;
}
const latestResetPath = path.join(dir, resetCandidates[resetCandidates.length - 1]);
const fallback = await getRecentSessionContent(latestResetPath, messageCount);
if (fallback) {
log.debug("Loaded session content from reset fallback", {
sessionFilePath,
latestResetPath,
});
}
return fallback || primary;
} catch {
return primary;
}
}
/**
* Save session context to memory when /new command is triggered
*/
@@ -119,8 +159,8 @@ const saveSessionToMemory: HookHandler = async (event) => {
let sessionContent: string | null = null;
if (sessionFile) {
// Get recent conversation content
sessionContent = await getRecentSessionContent(sessionFile, messageCount);
// Get recent conversation content, with fallback to rotated reset transcript.
sessionContent = await getRecentSessionContentWithResetFallback(sessionFile, messageCount);
log.debug("Session content loaded", {
length: sessionContent?.length ?? 0,
messageCount,