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:
@@ -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: [] });
|
||||
|
||||
Reference in New Issue
Block a user