From 4825fd10649ed2005910b2ba7b8d0e3a3b300924 Mon Sep 17 00:00:00 2001 From: neubig Date: Sun, 12 Jul 2026 10:04:04 +0000 Subject: [PATCH 1/2] fix: preserve priority fast mode --- src/CodexAcpClient.ts | 2 +- src/CodexAcpServer.ts | 5 +++-- src/FastModeConfig.ts | 4 ++++ .../CodexACPAgent/fast-mode-config.test.ts | 15 +++++++++------ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/CodexAcpClient.ts b/src/CodexAcpClient.ts index 12593a65..27befc67 100644 --- a/src/CodexAcpClient.ts +++ b/src/CodexAcpClient.ts @@ -842,7 +842,7 @@ export type SessionMetadata = { currentModelId: string, models: Model[], modelProvider?: string | null, - currentServiceTier?: ServiceTier | null, + currentServiceTier?: string | null, additionalDirectories: string[], } diff --git a/src/CodexAcpServer.ts b/src/CodexAcpServer.ts index 18fdf195..19a569f8 100644 --- a/src/CodexAcpServer.ts +++ b/src/CodexAcpServer.ts @@ -61,6 +61,7 @@ import { FAST_MODE_CONFIG_ID, FAST_MODE_OFF, FAST_MODE_ON, + isFastServiceTier, modelSupportsFast, resolveFastServiceTier, } from "./FastModeConfig"; @@ -412,7 +413,7 @@ export class CodexAcpServer { authProvider: authProvider, cwd: request.cwd, additionalDirectories: sessionMetadata.additionalDirectories, - fastModeEnabled: sessionMetadata.currentServiceTier === "fast", + fastModeEnabled: isFastServiceTier(sessionMetadata.currentServiceTier), currentModelSupportsFast: currentModelSupportsFast, sessionMcpServers: sessionMcpServers, terminalOutputMode: this.terminalOutputMode, @@ -941,7 +942,7 @@ export class CodexAcpServer { authProvider: authProvider, cwd: request.cwd, additionalDirectories: sessionMetadata.additionalDirectories, - fastModeEnabled: sessionMetadata.currentServiceTier === "fast", + fastModeEnabled: isFastServiceTier(sessionMetadata.currentServiceTier), currentModelSupportsFast: currentModelSupportsFast, sessionMcpServers: sessionMcpServers, terminalOutputMode: this.terminalOutputMode, diff --git a/src/FastModeConfig.ts b/src/FastModeConfig.ts index 8e681c24..d40593b5 100644 --- a/src/FastModeConfig.ts +++ b/src/FastModeConfig.ts @@ -18,6 +18,10 @@ export function resolveFastServiceTier(fastModeEnabled: boolean, currentModelSup return fastModeEnabled && currentModelSupportsFast ? "fast" : null; } +export function isFastServiceTier(serviceTier: string | null | undefined): boolean { + return serviceTier === "fast" || serviceTier === "priority"; +} + export function clientSupportsBooleanConfigOptions(clientCapabilities?: acp.ClientCapabilities | null): boolean { return clientCapabilities?.session?.configOptions?.boolean != null; } diff --git a/src/__tests__/CodexACPAgent/fast-mode-config.test.ts b/src/__tests__/CodexACPAgent/fast-mode-config.test.ts index 07d42444..16fa5302 100644 --- a/src/__tests__/CodexACPAgent/fast-mode-config.test.ts +++ b/src/__tests__/CodexACPAgent/fast-mode-config.test.ts @@ -24,7 +24,7 @@ describe("Fast mode session config", () => { }; async function createSession( - currentServiceTier: "fast" | "flex" | null = null, + currentServiceTier: "fast" | "priority" | "flex" | null = null, clientInfo: acp.Implementation | null = null, clientCapabilities?: acp.ClientCapabilities, ) { @@ -98,12 +98,15 @@ describe("Fast mode session config", () => { expect(response.configOptions).toContainEqual(createFastModeConfigOption(false)); }); - it("initializes Fast mode as On when the app-server session tier is fast", async () => { - const {response, codexAcpAgent} = await createSession("fast"); + it.each(["fast", "priority"] as const)( + "initializes Fast mode as On when the app-server session tier is %s", + async (serviceTier) => { + const {response, codexAcpAgent} = await createSession(serviceTier); - expect(response.configOptions).toContainEqual(createFastModeConfigOption(true)); - expect(codexAcpAgent.getSessionState("session-id").fastModeEnabled).toBe(true); - }); + expect(response.configOptions).toContainEqual(createFastModeConfigOption(true)); + expect(codexAcpAgent.getSessionState("session-id").fastModeEnabled).toBe(true); + }, + ); it("omits Fast mode config options for JetBrains 2026.1 IntelliJ clients", async () => { const {response} = await createSession(null, { From dee5214f2bb53fcd7f8f8a383826de47228c56d7 Mon Sep 17 00:00:00 2001 From: neubig Date: Mon, 13 Jul 2026 08:11:46 +0000 Subject: [PATCH 2/2] test: add live priority fast-mode evidence Co-authored-by: openhands --- .pr/live_priority_fast_mode.ts | 106 +++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 .pr/live_priority_fast_mode.ts diff --git a/.pr/live_priority_fast_mode.ts b/.pr/live_priority_fast_mode.ts new file mode 100644 index 00000000..3a361bf0 --- /dev/null +++ b/.pr/live_priority_fast_mode.ts @@ -0,0 +1,106 @@ +import fs from "node:fs"; +import path from "node:path"; +import {pathToFileURL} from "node:url"; + +const sourceRoot = process.env.SOURCE_ROOT; +if (!sourceRoot) { + throw new Error("SOURCE_ROOT must point to the codex-acp checkout under test"); +} + +const importSource = async (relativePath: string) => { + return import(pathToFileURL(path.join(sourceRoot, relativePath)).href); +}; + +const {CodexAcpClient} = await importSource("src/CodexAcpClient.ts"); +const {CodexAppServerClient} = await importSource("src/CodexAppServerClient.ts"); +const {startCodexConnection} = await importSource("src/CodexJsonRpcConnection.ts"); +const {CodexAcpServer} = await importSource("src/CodexAcpServer.ts"); + +const codexPath = path.join( + sourceRoot, + "node_modules", + ".bin", + process.platform === "win32" ? "codex.cmd" : "codex", +); +if (!fs.existsSync(codexPath)) { + throw new Error(`Codex binary not found at ${codexPath}`); +} + +const transportEvents: any[] = []; +const acpEvents: Array<{method: string; args: unknown[]}> = []; +const acpConnection = new Proxy({} as any, { + get(_, property) { + return (...args: unknown[]) => { + acpEvents.push({method: String(property), args}); + return Promise.resolve({}); + }; + }, +}); + +const codexConnection = startCodexConnection(codexPath); +const appServer = new CodexAppServerClient(codexConnection.connection); +appServer.onClientTransportEvent((event: any) => transportEvents.push(event)); + +// Exercise the same production configuration path as CODEX_CONFIG. +const client = new CodexAcpClient(appServer, {service_tier: "priority"}); +const agent = new CodexAcpServer( + acpConnection, + client, + undefined, + () => codexConnection.process.exitCode, +); + +try { + await agent.initialize({protocolVersion: 1}); + if (await client.authRequired()) { + throw new Error("Codex authentication is required"); + } + + const session = await agent.newSession({cwd: sourceRoot, mcpServers: []}); + const fastOption = session.configOptions?.find((option: any) => option.id === "fast-mode"); + const stateAfterStart = agent.getSessionState(session.sessionId); + const promptResponse = await agent.prompt({ + sessionId: session.sessionId, + prompt: [{type: "text", text: "Reply with exactly: priority-fast-live-ok"}], + }); + + const threadStartRequest = transportEvents.find( + (event) => event.eventType === "request" && event.method === "thread/start", + ); + const threadStartIndex = transportEvents.indexOf(threadStartRequest); + const threadStartResponse = transportEvents + .slice(threadStartIndex + 1) + .find((event) => event.eventType === "response" && event.thread?.id === session.sessionId); + const turnStartRequest = transportEvents.find( + (event) => event.eventType === "request" && event.method === "turn/start", + ); + const finalMessage = transportEvents + .filter( + (event) => + event.eventType === "notification" && + event.method === "item/completed" && + event.params?.item?.type === "agentMessage", + ) + .at(-1)?.params?.item?.text; + + console.log(JSON.stringify({ + testedCommit: process.env.TESTED_COMMIT ?? null, + codexVersion: threadStartResponse?.thread?.cliVersion ?? null, + configuredServiceTier: threadStartRequest?.params?.config?.service_tier ?? null, + appServerServiceTier: threadStartResponse?.serviceTier ?? null, + fastConfigOption: fastOption + ? {type: fastOption.type, currentValue: fastOption.currentValue} + : null, + fastModeEnabledAfterStart: stateAfterStart.fastModeEnabled, + firstPromptServiceTier: turnStartRequest?.params?.serviceTier ?? null, + promptStopReason: promptResponse.stopReason, + finalMessage, + requestSequence: transportEvents + .filter((event) => event.eventType === "request") + .map((event) => event.method), + acpEventCount: acpEvents.length, + }, null, 2)); +} finally { + codexConnection.connection.end(); + codexConnection.process.kill(); +}