fix(sessions): fall back when lastDirectory is a deleted worktree

New chats inherited a persisted lastDirectory even after that worktree
was removed, so the first message saved but the prompt never started.
Validate the implicit draft directory, fall back to the active project
only when OpenCode confirms the path is missing, and leave explicit
worktree targets and unknown probes unchanged.

Co-authored-by: serkraser <serkraser@gmail.com>
This commit is contained in:
Cursor Agent
2026-08-15 04:26:39 +00:00
co-authored by serkraser
parent 6b1e677aaf
commit 90e79b04a4
7 changed files with 246 additions and 10 deletions
@@ -9,6 +9,7 @@ let configCalls = 0;
let runtimeKey = 'test-runtime';
const promptAsyncCalls: unknown[][] = [];
const promptAsyncResults: Array<unknown> = [];
const pathGetResults: Array<unknown> = [];
const promptAsyncMock = mock(async (...args: unknown[]) => {
promptAsyncCalls.push(args);
@@ -17,6 +18,12 @@ const promptAsyncMock = mock(async (...args: unknown[]) => {
return next ?? { response: new Response(null, { status: 200 }) };
});
const pathGetMock = mock(async () => {
const next = pathGetResults.shift();
if (next instanceof Error) throw next;
return next ?? { data: { directory: '/workspace/project' } };
});
mock.module('@opencode-ai/sdk/v2', () => ({
createOpencodeClient: mock(() => ({
config: {
@@ -30,6 +37,9 @@ mock.module('@opencode-ai/sdk/v2', () => ({
session: {
promptAsync: promptAsyncMock,
},
path: {
get: pathGetMock,
},
})),
}));
@@ -64,6 +74,17 @@ beforeEach(() => {
runtimeKey = 'test-runtime';
promptAsyncCalls.length = 0;
promptAsyncResults.length = 0;
pathGetResults.length = 0;
});
describe('opencodeClient directory availability', () => {
test('distinguishes a missing directory from an unavailable path probe', async () => {
pathGetResults.push({ error: { code: 'ENOENT', message: 'no such file or directory' } });
expect(await opencodeClient.getDirectoryAvailability('/private/deleted-worktree')).toBe('missing');
pathGetResults.push(new Error('offline'));
expect(await opencodeClient.getDirectoryAvailability('/private/deleted-worktree')).toBe('unknown');
});
});
describe('opencodeClient getConfig cache', () => {