From bfb2147621231252da9240d4efc0fad473b2a3e9 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Thu, 23 Apr 2026 13:23:34 +0200 Subject: [PATCH 1/3] Advertise auth logout capability Route logout through unstable ACP auth support while keeping the legacy authentication/logout extension method as a compatibility shim. --- src/CodexAcpClient.ts | 5 ++--- src/CodexAcpServer.ts | 15 +++++++++++++-- .../CodexACPAgent/CodexAcpClient.test.ts | 12 +++++++++++- src/__tests__/CodexACPAgent/initialize.test.ts | 3 +++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/CodexAcpClient.ts b/src/CodexAcpClient.ts index 514e53b5..58c11953 100644 --- a/src/CodexAcpClient.ts +++ b/src/CodexAcpClient.ts @@ -35,7 +35,7 @@ import type { UserInput, } from "./app-server/v2"; import packageJson from "../package.json"; -import type {AuthenticationLogoutResponse, AuthenticationStatusResponse} from "./AcpExtensions"; +import type {AuthenticationStatusResponse} from "./AcpExtensions"; /** * API for accessing the Codex App Server using ACP requests. @@ -176,11 +176,10 @@ export class CodexAcpClient { return settingsModelProvider.config.model_provider; } - async logout(): Promise { + async logout(): Promise { const accountUpdatedPromise = this.awaitNextAccountUpdated(); await this.codexClient.accountLogout(); await accountUpdatedPromise; - return {}; } async authRequired(): Promise { diff --git a/src/CodexAcpServer.ts b/src/CodexAcpServer.ts index 4159656a..3df015ee 100644 --- a/src/CodexAcpServer.ts +++ b/src/CodexAcpServer.ts @@ -96,6 +96,9 @@ export class CodexAcpServer implements acp.Agent { return { protocolVersion: acp.PROTOCOL_VERSION, agentCapabilities: { + auth: { + logout: {}, + }, loadSession: true, promptCapabilities: { image: true @@ -121,8 +124,10 @@ export class CodexAcpServer implements acp.Agent { switch (methodRequest.method) { case "authentication/status": return await this.runWithProcessCheck(() => this.codexAcpClient.getAuthenticationStatus()); - case "authentication/logout": - return await this.runWithProcessCheck(() => this.codexAcpClient.logout()); + case "authentication/logout": { + await this.unstable_logout({}); + return {}; + } } } @@ -270,6 +275,12 @@ export class CodexAcpServer implements acp.Agent { return { }; } + async unstable_logout(_params: acp.LogoutRequest): Promise { + logger.log("Logout request received"); + await this.runWithProcessCheck(() => this.codexAcpClient.logout()); + logger.log("Logout request completed"); + } + async setSessionMode( _params: acp.SetSessionModeRequest, ): Promise { diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index 826165ca..095dfeec 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -103,7 +103,7 @@ describe('ACP server test', { timeout: 40_000 }, () => { const authenticatedResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); expect(authenticatedResponse).toEqual({type: "api-key"}); - await keyFixture.getCodexAcpAgent().extMethod("authentication/logout", {}); + await keyFixture.getCodexAcpAgent().unstable_logout({}); const logoutResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); expect(logoutResponse).toEqual({type: "unauthenticated"}); }); @@ -146,6 +146,16 @@ describe('ACP server test', { timeout: 40_000 }, () => { expect(newSessionResponse.sessionId).toBeDefined() }) + it('supports legacy authentication/logout ext method', async () => { + const mockFixture = createCodexMockTestFixture(); + const codexAcpAgent = mockFixture.getCodexAcpAgent(); + + const logoutSpy = vi.spyOn(codexAcpAgent, "unstable_logout").mockResolvedValue({}); + + await expect(codexAcpAgent.extMethod("authentication/logout", {})).resolves.toEqual({}); + expect(logoutSpy).toHaveBeenCalledWith({}); + }); + it('prefetches session additional skill roots before thread start', async () => { const mockFixture = createCodexMockTestFixture(); const codexAcpClient = mockFixture.getCodexAcpClient(); diff --git a/src/__tests__/CodexACPAgent/initialize.test.ts b/src/__tests__/CodexACPAgent/initialize.test.ts index 8ca6b432..372fee82 100644 --- a/src/__tests__/CodexACPAgent/initialize.test.ts +++ b/src/__tests__/CodexACPAgent/initialize.test.ts @@ -32,6 +32,9 @@ describe('CodexACPAgent - initialize', () => { expect(result).toEqual({ protocolVersion: acp.PROTOCOL_VERSION, agentCapabilities: { + auth: { + logout: {}, + }, loadSession: true, promptCapabilities: { image: true From 7facf76d646442d876ae47ca110dbff80feef756 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Thu, 23 Apr 2026 13:38:12 +0200 Subject: [PATCH 2/3] Fix test --- src/__tests__/CodexACPAgent/CodexAcpClient.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index 095dfeec..242e1771 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -150,7 +150,7 @@ describe('ACP server test', { timeout: 40_000 }, () => { const mockFixture = createCodexMockTestFixture(); const codexAcpAgent = mockFixture.getCodexAcpAgent(); - const logoutSpy = vi.spyOn(codexAcpAgent, "unstable_logout").mockResolvedValue({}); + const logoutSpy = vi.spyOn(codexAcpAgent, "unstable_logout"); await expect(codexAcpAgent.extMethod("authentication/logout", {})).resolves.toEqual({}); expect(logoutSpy).toHaveBeenCalledWith({}); @@ -575,7 +575,7 @@ describe('ACP server test', { timeout: 40_000 }, () => { const sessionState: SessionState = createTestSessionState(); - const logoutSpy = vi.spyOn(mockFixture.getCodexAcpClient(), "logout").mockResolvedValue({}); + const logoutSpy = vi.spyOn(mockFixture.getCodexAcpClient(), "logout"); // @ts-expect-error - exercising private helper const handled = await codexAcpAgent.availableCommands.handleCommand({ name: "logout", input: null }, sessionState); From f580aa597c542c8ff65a9961e39918cfae976629 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Thu, 23 Apr 2026 13:42:31 +0200 Subject: [PATCH 3/3] Add back resolves --- src/__tests__/CodexACPAgent/CodexAcpClient.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index 242e1771..2427b9ec 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -150,7 +150,7 @@ describe('ACP server test', { timeout: 40_000 }, () => { const mockFixture = createCodexMockTestFixture(); const codexAcpAgent = mockFixture.getCodexAcpAgent(); - const logoutSpy = vi.spyOn(codexAcpAgent, "unstable_logout"); + const logoutSpy = vi.spyOn(codexAcpAgent, "unstable_logout").mockResolvedValue(); await expect(codexAcpAgent.extMethod("authentication/logout", {})).resolves.toEqual({}); expect(logoutSpy).toHaveBeenCalledWith({}); @@ -575,7 +575,7 @@ describe('ACP server test', { timeout: 40_000 }, () => { const sessionState: SessionState = createTestSessionState(); - const logoutSpy = vi.spyOn(mockFixture.getCodexAcpClient(), "logout"); + const logoutSpy = vi.spyOn(mockFixture.getCodexAcpClient(), "logout").mockResolvedValue(); // @ts-expect-error - exercising private helper const handled = await codexAcpAgent.availableCommands.handleCommand({ name: "logout", input: null }, sessionState);