Skip to content

Commit 201bf12

Browse files
Sunjae-ksunjae-1
andauthored
fix(session-store): rewrite generated transcript paths on rollover
Rewrite generated session transcript paths at the shared session-store merge boundary when a persisted session rolls from one session id to another. This prevents patches that carry a stale generated `sessionFile` from leaving a new logical session id attached to the old transcript file, while preserving custom transcript paths. Refs #65564. Proof: - `node scripts/run-vitest.mjs src/config/sessions/sessions.test.ts` - `node scripts/run-vitest.mjs src/agents/command/session-store.test.ts` - `git diff --check origin/main...HEAD` - `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main` - CI run 26719583889 attempt 2 Co-authored-by: Sunjae Kim <sunjaekim@bigvalue.co.kr>
1 parent db40fde commit 201bf12

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

src/config/sessions/sessions.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,60 @@ describe("session store writer queue", () => {
897897
expect(merged.modelProvider).toBeUndefined();
898898
});
899899

900+
it("rewrites generated sessionFile paths when session id changes", () => {
901+
const previousSessionId = "11111111-1111-4111-8111-111111111111";
902+
const nextSessionId = "22222222-2222-4222-8222-222222222222";
903+
const merged = mergeSessionEntry(
904+
{
905+
sessionId: previousSessionId,
906+
updatedAt: 100,
907+
sessionFile: `/tmp/openclaw/sessions/${previousSessionId}.jsonl`,
908+
},
909+
{
910+
sessionId: nextSessionId,
911+
updatedAt: 200,
912+
},
913+
);
914+
915+
expect(merged.sessionFile).toBe(`/tmp/openclaw/sessions/${nextSessionId}.jsonl`);
916+
});
917+
918+
it("rewrites stale generated sessionFile patches during session rollover", () => {
919+
const previousSessionId = "11111111-1111-4111-8111-111111111111";
920+
const nextSessionId = "22222222-2222-4222-8222-222222222222";
921+
const previousSessionFile = `/tmp/openclaw/sessions/${previousSessionId}-topic-456.jsonl`;
922+
const merged = mergeSessionEntry(
923+
{
924+
sessionId: previousSessionId,
925+
updatedAt: 100,
926+
sessionFile: previousSessionFile,
927+
},
928+
{
929+
sessionId: nextSessionId,
930+
updatedAt: 200,
931+
sessionFile: previousSessionFile,
932+
},
933+
);
934+
935+
expect(merged.sessionFile).toBe(`/tmp/openclaw/sessions/${nextSessionId}-topic-456.jsonl`);
936+
});
937+
938+
it("preserves custom sessionFile paths when session id changes", () => {
939+
const merged = mergeSessionEntry(
940+
{
941+
sessionId: "previous-session",
942+
updatedAt: 100,
943+
sessionFile: "/tmp/openclaw/sessions/custom-transcript.jsonl",
944+
},
945+
{
946+
sessionId: "next-session",
947+
updatedAt: 200,
948+
},
949+
);
950+
951+
expect(merged.sessionFile).toBe("/tmp/openclaw/sessions/custom-transcript.jsonl");
952+
});
953+
900954
it("caps future updatedAt values at the session merge boundary", () => {
901955
const now = 1_000;
902956
const merged = mergeSessionEntryWithPolicy(

src/config/sessions/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { ChannelRouteRef } from "../../plugin-sdk/channel-route.js";
1313
import type { Skill } from "../../skills/loading/skill-contract.js";
1414
import type { DeliveryContext } from "../../utils/delivery-context.types.js";
1515
import type { TtsAutoMode } from "../types.tts.js";
16+
import { rewriteSessionFileForNewSessionId } from "./session-file-rotation.js";
1617

1718
export type SessionScope = "per-sender" | "global";
1819

@@ -555,6 +556,19 @@ export function mergeSessionEntryWithPolicy(
555556
(existing.sessionId === sessionId ? existing.sessionStartedAt : updatedAt),
556557
};
557558

559+
if (existing.sessionId !== sessionId) {
560+
const patchHasSessionFile = Object.hasOwn(patch, "sessionFile");
561+
const candidateSessionFile = patchHasSessionFile ? patch.sessionFile : existing.sessionFile;
562+
const rewrittenSessionFile = rewriteSessionFileForNewSessionId({
563+
sessionFile: candidateSessionFile,
564+
previousSessionId: existing.sessionId,
565+
nextSessionId: sessionId,
566+
});
567+
if (rewrittenSessionFile) {
568+
next.sessionFile = rewrittenSessionFile;
569+
}
570+
}
571+
558572
// Guard against stale provider carry-over when callers patch runtime model
559573
// without also patching runtime provider.
560574
if (Object.hasOwn(patch, "model") && !Object.hasOwn(patch, "modelProvider")) {

0 commit comments

Comments
 (0)