Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .pr/live_priority_fast_mode.ts
Original file line number Diff line number Diff line change
@@ -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();
}
2 changes: 1 addition & 1 deletion src/CodexAcpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ export type SessionMetadata = {
currentModelId: string,
models: Model[],
modelProvider?: string | null,
currentServiceTier?: ServiceTier | null,
currentServiceTier?: string | null,
additionalDirectories: string[],
}

Expand Down
5 changes: 3 additions & 2 deletions src/CodexAcpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import {
FAST_MODE_CONFIG_ID,
FAST_MODE_OFF,
FAST_MODE_ON,
isFastServiceTier,
modelSupportsFast,
resolveFastServiceTier,
} from "./FastModeConfig";
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions src/FastModeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
15 changes: 9 additions & 6 deletions src/__tests__/CodexACPAgent/fast-mode-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
) {
Expand Down Expand Up @@ -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, {
Expand Down