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
This commit is contained in:
roboomp
2026-08-06 02:08:49 +00:00
parent 1e492d6ff9
commit 07bb0ac51f
3 changed files with 56 additions and 1 deletions
+4
View File
@@ -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
@@ -2019,7 +2019,16 @@ export class AcpAgent implements Agent {
async #findStoredSession(sessionId: string, cwd: string): Promise<StoredSessionInfo | undefined> {
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<StoredSessionInfo | undefined> {
@@ -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: [] });