diff --git a/cli/src/cursor/cursorAcpRemoteLauncher.test.ts b/cli/src/cursor/cursorAcpRemoteLauncher.test.ts index c039ce04..3aeb7473 100644 --- a/cli/src/cursor/cursorAcpRemoteLauncher.test.ts +++ b/cli/src/cursor/cursorAcpRemoteLauncher.test.ts @@ -12,7 +12,9 @@ const harness = vi.hoisted(() => ({ backendArgs: null as { command: string; args?: string[] } | null, setConfigOptionCalls: [] as Array<{ sessionId: string; configId: string; value: string }>, deferSetConfigOption: null as Promise | null, - releaseSetConfigOption: null as (() => void) | null + releaseSetConfigOption: null as (() => void) | null, + deferLoadSession: null as Promise | null, + releaseLoadSession: null as (() => void) | null })); const legacyLauncher = vi.hoisted(() => vi.fn()); @@ -38,6 +40,9 @@ vi.mock('./utils/cursorAcpBackend', () => ({ supportsLoadSession: vi.fn(() => harness.supportsLoadSession), loadSession: vi.fn(async () => { harness.loadSessionCalled = true; + if (harness.deferLoadSession) { + await harness.deferLoadSession; + } if (harness.loadSessionError) throw harness.loadSessionError; return 'loaded-acp-session'; }), @@ -174,6 +179,8 @@ describe('cursorAcpRemoteLauncher', () => { harness.setConfigOptionCalls = []; harness.deferSetConfigOption = null; harness.releaseSetConfigOption = null; + harness.deferLoadSession = null; + harness.releaseLoadSession = null; legacyLauncher.mockClear(); process.stdin.isTTY = false; process.stdout.isTTY = false; @@ -204,6 +211,27 @@ describe('cursorAcpRemoteLauncher', () => { expect(harness.newSessionCalled).toBe(false); }); + it('registers cursorSessionId before session/load completes', async () => { + let releaseLoadSession!: () => void; + harness.deferLoadSession = new Promise((resolve) => { + harness.releaseLoadSession = resolve; + releaseLoadSession = resolve; + }); + + const session = makeSession('resume-thread-1'); + const launchPromise = cursorAcpRemoteLauncher(session); + + await vi.waitFor(() => { + expect(session.onSessionFoundWithProtocol).toHaveBeenCalledWith('resume-thread-1', 'acp'); + }); + expect(harness.loadSessionCalled).toBe(true); + + releaseLoadSession(); + await launchPromise; + + expect(session.onSessionFoundWithProtocol).toHaveBeenCalledWith('loaded-acp-session', 'acp'); + }); + it('throws when session/load fails instead of falling back to stream-json', async () => { harness.loadSessionError = new Error('session not found'); const session = makeSession('old-stream-json-id'); diff --git a/cli/src/cursor/cursorAcpRemoteLauncher.ts b/cli/src/cursor/cursorAcpRemoteLauncher.ts index ba6aef50..58b611be 100644 --- a/cli/src/cursor/cursorAcpRemoteLauncher.ts +++ b/cli/src/cursor/cursorAcpRemoteLauncher.ts @@ -96,6 +96,8 @@ class CursorAcpRemoteLauncher extends RemoteLauncherBase { let acpSessionId: string; if (resumeSessionId && backend.supportsLoadSession()) { + // Register pending cursorSessionId before awaiting session/load (Zed PR #54431). + session.onSessionFoundWithProtocol(resumeSessionId, 'acp'); try { acpSessionId = await backend.loadSession({ sessionId: resumeSessionId, @@ -119,7 +121,9 @@ class CursorAcpRemoteLauncher extends RemoteLauncherBase { }); } - session.onSessionFoundWithProtocol(acpSessionId, 'acp'); + if (acpSessionId !== resumeSessionId) { + session.onSessionFoundWithProtocol(acpSessionId, 'acp'); + } syncCursorModelsFromAcp(backend, acpSessionId);