From 07bb0ac51f5c7dec6be7eaf128932a2c4a9439d7 Mon Sep 17 00:00:00 2001 From: roboomp Date: Thu, 6 Aug 2026 02:08:49 +0000 Subject: [PATCH] fix(acp): resolve session/load across legacy session directories session/load and session/resume resolved a session only within the directory re-derived from cwd (SessionManager.list(cwd)), so sessions stored under the legacy/hashed project-directory scheme (17.2.5+, reverted in #7656) were unreachable and threw "ACP session not found" despite existing on disk. #findStoredSession now falls back to a global by-id scan (listAll, already used by the fork path) when the cwd-scoped lookup misses. The session id is globally unique and #openStoredSession reopens the file with the request cwd, so no directory-scheme knowledge is needed. Fixes #7779 --- packages/coding-agent/CHANGELOG.md | 4 ++ .../coding-agent/src/modes/acp/acp-agent.ts | 11 ++++- packages/coding-agent/test/acp-agent.test.ts | 42 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 456e88609..af935ff50 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed ACP `session/load` and `session/resume` failing with `ACP session not found` for sessions created under the legacy/hashed project-directory scheme (17.2.5+, reverted in #7656): the lookup only scanned the directory re-derived from `cwd`, so sessions stored under a differently-named directory were unreachable. It now falls back to a global by-id scan (the same one the fork path already uses) when the cwd-scoped lookup misses ([#7779](https://github.com/can1357/oh-my-pi/issues/7779)). + ## [17.2.9] - 2026-08-05 ### Breaking Changes diff --git a/packages/coding-agent/src/modes/acp/acp-agent.ts b/packages/coding-agent/src/modes/acp/acp-agent.ts index d3d480564..b38ea614a 100644 --- a/packages/coding-agent/src/modes/acp/acp-agent.ts +++ b/packages/coding-agent/src/modes/acp/acp-agent.ts @@ -2019,7 +2019,16 @@ export class AcpAgent implements Agent { async #findStoredSession(sessionId: string, cwd: string): Promise { const sessions = await this.#listStoredSessions(cwd); - return sessions.find(session => session.id === sessionId); + const scoped = sessions.find(session => session.id === sessionId); + if (scoped) { + return scoped; + } + // The cwd-derived directory only covers sessions stored under the current + // naming scheme. Sessions written under a legacy/hashed project directory + // (the 17.2.5+ scheme reverted in #7656) live elsewhere, so fall back to a + // global by-id scan: the session id is globally unique, and + // #openStoredSession reopens the file with the request cwd. See #7779. + return this.#findStoredSessionById(sessionId); } async #findStoredSessionById(sessionId: string): Promise { diff --git a/packages/coding-agent/test/acp-agent.test.ts b/packages/coding-agent/test/acp-agent.test.ts index b42cecd51..f12fa39db 100644 --- a/packages/coding-agent/test/acp-agent.test.ts +++ b/packages/coding-agent/test/acp-agent.test.ts @@ -1104,6 +1104,48 @@ describe("ACP agent", () => { await Bun.sleep(0); }); + it("loads a session stored under a legacy/hashed project directory (#7779)", async () => { + const harness = await createHarness(); + const stored = new FakeAgentSession(harness.cwdA); + harness.sessions.push(stored); + stored.sessionManager.appendMessage({ role: "user", content: "legacy hello", timestamp: Date.now() }); + stored.sessionManager.appendMessage(makeAssistantMessage("legacy reply")); + await stored.sessionManager.ensureOnDisk(); + await stored.sessionManager.flush(); + + const sessionFile = stored.sessionManager.getSessionFile(); + if (!sessionFile) throw new Error("session file not persisted"); + const sessionId = stored.sessionId; + // Release the writer so the directory can be renamed out from under it. + await stored.dispose(); + + // Simulate the hashed-directory era (#7397, reverted in #7656): the + // session file lives under a project directory whose name the current + // cwd->dir scheme would never produce, so the cwd-scoped scan misses it. + const cwdDerivedDir = path.dirname(sessionFile); + const sessionsRoot = path.dirname(cwdDerivedDir); + const hashedDir = path.join(sessionsRoot, `home-cwd-a-${"a".repeat(64)}`); + await fs.promises.rename(cwdDerivedDir, hashedDir); + + const loaded = await harness.agent.loadSession({ + sessionId, + cwd: harness.cwdA, + mcpServers: [], + }); + expectAcpStructure(zLoadSessionResponse, loaded); + + const replayChunks = harness.updates.filter( + update => + update.sessionId === sessionId && + (update.update.sessionUpdate === "user_message_chunk" || + update.update.sessionUpdate === "agent_message_chunk"), + ); + expect(replayChunks.length).toBeGreaterThan(0); + + harness.abortController.abort(); + await Bun.sleep(0); + }); + it("delivers the final visible answer when agent_end overtakes the assistant message_end (#4902)", async () => { const harness = await createHarness(); const created = await harness.agent.newSession({ cwd: harness.cwdA, mcpServers: [] });