From 338451ffdb779cdd05d2c46f9943a094294839d4 Mon Sep 17 00:00:00 2001 From: roboomp Date: Wed, 5 Aug 2026 06:22:22 +0000 Subject: [PATCH] fix(session): migrate hashed session dirs back to legacy names The 17.2.9 revert (172ce9b) restored the legacy path-based session directory names but removed all migration, including the reverse path. Sessions written under the short-lived hashed scheme (17.2.5-17.2.8, `--`) were left orphaned, so `omp -r` current-folder scope reported no sessions. computeDefaultSessionDir now reconstructs the hashed dir name for the cwd and performs a one-way best-effort migration into the legacy name, alongside the existing legacy-absolute migration. Fixes #7677 --- packages/coding-agent/CHANGELOG.md | 4 ++ .../coding-agent/src/session/session-paths.ts | 66 ++++++++++++++++--- .../session-manager/file-operations.test.ts | 27 ++++++++ 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 5bcee1054..cd0c7bbfa 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed `omp -r` current-folder scope missing sessions written under the short-lived hashed project-directory scheme (17.2.5-17.2.8): the 17.2.9 revert restored the legacy path-based names but dropped all migration, stranding those sessions. `computeDefaultSessionDir` now performs a one-way migration of the hashed dir back into its legacy name ([#7677](https://github.com/can1357/oh-my-pi/issues/7677)). + ## [17.2.9] - 2026-08-05 ### Breaking Changes diff --git a/packages/coding-agent/src/session/session-paths.ts b/packages/coding-agent/src/session/session-paths.ts index f76d6e183..f0c80f294 100644 --- a/packages/coding-agent/src/session/session-paths.ts +++ b/packages/coding-agent/src/session/session-paths.ts @@ -42,7 +42,28 @@ function encodeRelativeSessionDirName(prefix: string, relative: string): string return encoded ? (prefix.endsWith("-") ? `${prefix}${encoded}` : `${prefix}-${encoded}`) : prefix; } -function getDefaultSessionDirName(cwd: string): { encodedDirName: string; resolvedCwd: string } { +/** + * Reconstruct the short-lived hashed session dir name used by 17.2.5-17.2.8 + * (reverted PR #7397): `--` keyed by the canonical + * cwd. Kept only so {@link migrateHashedSessionDir} can recover sessions + * stranded when 17.2.9 restored the legacy names without a reverse migration. + */ +function encodeHashedSessionDirName(canonicalCwd: string, scope: "home" | "tmp" | "abs"): string { + const normalized = canonicalCwd.replaceAll("\\", "/"); + const readable = path + .basename(canonicalCwd) + .replace(/[^a-zA-Z0-9._-]+/g, "-") + .replace(/^-+|-+$/g, "") + .slice(-80); + const digest = Bun.SHA256.hash(normalized, "hex"); + return `${scope}-${readable || "project"}-${digest}`; +} + +function getDefaultSessionDirName(cwd: string): { + encodedDirName: string; + hashedDirName: string; + resolvedCwd: string; +} { const resolvedCwd = path.resolve(cwd); const canonicalCwd = resolveEquivalentPath(resolvedCwd); const home = os.homedir(); @@ -51,13 +72,19 @@ function getDefaultSessionDirName(cwd: string): { encodedDirName: string; resolv const canonicalTempRoot = resolveEquivalentPath(tempRoot); const homeRelative = path.relative(canonicalHome, canonicalCwd); const tempRelative = path.relative(canonicalTempRoot, canonicalCwd); - const encodedDirName = - homeRelative === "" || (!homeRelative.startsWith("..") && !path.isAbsolute(homeRelative)) - ? encodeRelativeSessionDirName("-", homeRelative) - : tempRelative === "" || (!tempRelative.startsWith("..") && !path.isAbsolute(tempRelative)) - ? encodeRelativeSessionDirName("-tmp", tempRelative) - : encodeLegacyAbsoluteSessionDirName(canonicalCwd); - return { encodedDirName, resolvedCwd }; + let encodedDirName: string; + let scope: "home" | "tmp" | "abs"; + if (homeRelative === "" || (!homeRelative.startsWith("..") && !path.isAbsolute(homeRelative))) { + encodedDirName = encodeRelativeSessionDirName("-", homeRelative); + scope = "home"; + } else if (tempRelative === "" || (!tempRelative.startsWith("..") && !path.isAbsolute(tempRelative))) { + encodedDirName = encodeRelativeSessionDirName("-tmp", tempRelative); + scope = "tmp"; + } else { + encodedDirName = encodeLegacyAbsoluteSessionDirName(canonicalCwd); + scope = "abs"; + } + return { encodedDirName, hashedDirName: encodeHashedSessionDirName(canonicalCwd, scope), resolvedCwd }; } /** @@ -121,6 +148,26 @@ function migrateLegacyAbsoluteSessionDir(cwd: string, sessionDir: string, sessio } } +/** + * Migrate a 17.2.5-17.2.8 hashed session dir back into its legacy path-based + * directory. The 17.2.9 revert restored the legacy names but dropped migration, + * stranding sessions written under the hashed scheme (issue #7677). Best-effort. + */ +function migrateHashedSessionDir(hashedDirName: string, sessionDir: string, sessionsRoot: string): void { + const hashedDir = path.join(sessionsRoot, hashedDirName); + if (hashedDir === sessionDir || !fs.existsSync(hashedDir)) return; + + try { + migrateSessionDirPath(hashedDir, sessionDir); + } catch (error) { + logger.warn("Failed to migrate hashed session directory", { + oldPath: hashedDir, + newPath: sessionDir, + error: String(error), + }); + } +} + export function resolveManagedSessionRoot(sessionDir: string, cwd: string): string | undefined { const currentDirName = path.basename(sessionDir); const { encodedDirName } = getDefaultSessionDirName(cwd); @@ -140,10 +187,11 @@ export function computeDefaultSessionDir( storage: SessionStorage, sessionsRoot: string = getSessionsDir(), ): string { - const { encodedDirName, resolvedCwd } = getDefaultSessionDirName(cwd); + const { encodedDirName, hashedDirName, resolvedCwd } = getDefaultSessionDirName(cwd); migrateHomeSessionDirs(sessionsRoot); const sessionDir = path.join(sessionsRoot, encodedDirName); migrateLegacyAbsoluteSessionDir(resolvedCwd, sessionDir, sessionsRoot); + migrateHashedSessionDir(hashedDirName, sessionDir, sessionsRoot); storage.ensureDirSync(sessionDir); return sessionDir; } diff --git a/packages/coding-agent/test/session-manager/file-operations.test.ts b/packages/coding-agent/test/session-manager/file-operations.test.ts index 7c3abe61f..92d3540dc 100644 --- a/packages/coding-agent/test/session-manager/file-operations.test.ts +++ b/packages/coding-agent/test/session-manager/file-operations.test.ts @@ -217,6 +217,33 @@ describe("SessionManager temp cwd session dirs", () => { expect(path.dirname(sessionFile)).toBe(expectedDir); expect(fs.existsSync(path.join(expectedDir, "carried.jsonl"))).toBe(true); }); + + it("migrates hashed-scheme session dirs back into legacy names", () => { + const tempCwd = path.join(testAgentDir, `hashed-cwd-${Snowflake.next()}`); + fs.mkdirSync(tempCwd, { recursive: true }); + + // Reconstruct the 17.2.5-17.2.8 hashed dir name (reverted PR #7397). + const canonicalCwd = path.resolve(tempCwd); + const normalized = canonicalCwd.replaceAll("\\", "/"); + const readable = path + .basename(canonicalCwd) + .replace(/[^a-zA-Z0-9._-]+/g, "-") + .replace(/^-+|-+$/g, "") + .slice(-80); + const digest = Bun.SHA256.hash(normalized, "hex"); + const hashedDir = path.join(getSessionsDir(), `tmp-${readable || "project"}-${digest}`); + fs.mkdirSync(hashedDir, { recursive: true }); + fs.writeFileSync(path.join(hashedDir, "stranded.jsonl"), "stranded\n"); + + const session = SessionManager.create(tempCwd); + const sessionFile = session.getSessionFile(); + if (!sessionFile) throw new Error("Expected session file path"); + + const expectedDir = path.join(getSessionsDir(), expectedTempSessionDirName(tempCwd)); + expect(fs.existsSync(hashedDir)).toBe(false); + expect(path.dirname(sessionFile)).toBe(expectedDir); + expect(fs.existsSync(path.join(expectedDir, "stranded.jsonl"))).toBe(true); + }); }); describe("SessionManager legacy session migration persistence", () => {