mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-29 21:42:06 +00:00
More permissive load session (#301)
Adds a lookup that checks the expected cwd path first, then scans all project directories to locate the session JSONL file. Ultimately matches the behavior we allow for resume
This commit is contained in:
@@ -294,10 +294,49 @@ export class ClaudeAcpAgent implements Agent {
|
||||
return response;
|
||||
}
|
||||
|
||||
async loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse> {
|
||||
/**
|
||||
* Find a session file by ID, first checking the given cwd's project directory,
|
||||
* then falling back to scanning all project directories.
|
||||
* Returns the absolute file path if found, or null if not found.
|
||||
*/
|
||||
private async findSessionFile(sessionId: string, cwd: string): Promise<string | null> {
|
||||
const fileName = `${sessionId}.jsonl`;
|
||||
|
||||
// Fast path: check the expected location based on cwd
|
||||
const expectedPath = sessionFilePath(cwd, sessionId);
|
||||
try {
|
||||
await fs.promises.access(sessionFilePath(params.cwd, params.sessionId));
|
||||
await fs.promises.access(expectedPath);
|
||||
return expectedPath;
|
||||
} catch {
|
||||
// Not found at expected path, scan all project directories
|
||||
}
|
||||
|
||||
const claudeDir = path.join(CLAUDE_CONFIG_DIR, "projects");
|
||||
try {
|
||||
const projectDirs = await fs.promises.readdir(claudeDir);
|
||||
for (const encodedPath of projectDirs) {
|
||||
const projectDir = path.join(claudeDir, encodedPath);
|
||||
const stat = await fs.promises.stat(projectDir);
|
||||
if (!stat.isDirectory()) continue;
|
||||
|
||||
const candidatePath = path.join(projectDir, fileName);
|
||||
try {
|
||||
await fs.promises.access(candidatePath);
|
||||
return candidatePath;
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// projects directory doesn't exist or isn't readable
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse> {
|
||||
const filePath = await this.findSessionFile(params.sessionId, params.cwd);
|
||||
if (!filePath) {
|
||||
throw new Error("Session not found");
|
||||
}
|
||||
|
||||
@@ -312,7 +351,7 @@ export class ClaudeAcpAgent implements Agent {
|
||||
},
|
||||
);
|
||||
|
||||
await this.replaySessionHistory(params.sessionId, params.cwd);
|
||||
await this.replaySessionHistory(params.sessionId, filePath);
|
||||
|
||||
return {
|
||||
modes: response.modes,
|
||||
@@ -710,8 +749,7 @@ export class ClaudeAcpAgent implements Agent {
|
||||
}
|
||||
}
|
||||
|
||||
private async replaySessionHistory(sessionId: string, cwd: string): Promise<void> {
|
||||
const filePath = sessionFilePath(cwd, sessionId);
|
||||
private async replaySessionHistory(sessionId: string, filePath: string): Promise<void> {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const stream = fs.createReadStream(filePath, { encoding: "utf-8" });
|
||||
const reader = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
||||
|
||||
@@ -230,6 +230,34 @@ describe("loadSession", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("finds session file when cwd differs from the original session cwd", async () => {
|
||||
const originalCwd = "/Users/test/original-project";
|
||||
const differentCwd = "/Users/test/different-project";
|
||||
const sessionId = "cross-cwd-session";
|
||||
|
||||
// Session was created in originalCwd
|
||||
writeSessionFile(originalCwd, sessionId, [
|
||||
{
|
||||
type: "user",
|
||||
sessionId,
|
||||
cwd: originalCwd,
|
||||
message: { role: "user", content: "Hello from original" },
|
||||
},
|
||||
]);
|
||||
|
||||
// Load with a different cwd
|
||||
await agent.loadSession({ cwd: differentCwd, sessionId, mcpServers: [] });
|
||||
|
||||
expect(sessionUpdates).toHaveLength(1);
|
||||
expect(sessionUpdates[0]?.update).toMatchObject({
|
||||
sessionUpdate: "user_message_chunk",
|
||||
content: { type: "text", text: "Hello from original" },
|
||||
});
|
||||
expect(createSessionSpy).toHaveBeenCalledWith(expect.objectContaining({ cwd: differentCwd }), {
|
||||
resume: sessionId,
|
||||
});
|
||||
});
|
||||
|
||||
it("handles Windows-style paths", async () => {
|
||||
const cwd = "C:\\Users\\test\\project";
|
||||
const sessionId = "win-session";
|
||||
|
||||
Reference in New Issue
Block a user