From 9a9992b14a3369b90f17bc869205fc5ba0ac493a Mon Sep 17 00:00:00 2001 From: Emre K <110906681+kocaemre@users.noreply.github.com> Date: Sat, 12 Sep 2026 06:54:10 +0200 Subject: [PATCH] fix: honor plan mode reasoning effort Signed-off-by: Emre K <110906681+kocaemre@users.noreply.github.com> --- src/CodexAcpClient.ts | 6 +++ src/CodexAcpServer.ts | 23 +++++++++++ .../session-config-options.test.ts | 39 +++++++++++++++++++ src/__tests__/acp-test-utils.ts | 1 + 4 files changed, 69 insertions(+) diff --git a/src/CodexAcpClient.ts b/src/CodexAcpClient.ts index 7040057f..65a44d6d 100644 --- a/src/CodexAcpClient.ts +++ b/src/CodexAcpClient.ts @@ -1083,6 +1083,12 @@ export class CodexAcpClient { }); } + async getPlanModeReasoningEffort(cwd: string): Promise { + const effectiveConfig = await this.codexClient.configRead({includeLayers: false, cwd}); + const effort = effectiveConfig?.config?.["plan_mode_reasoning_effort"] ?? this.config["plan_mode_reasoning_effort"]; + return typeof effort === "string" ? effort : null; + } + private getCollaborationMode(sessionId: string): ModeKind { return this.codexClient.getThreadSettings(sessionId)?.collaborationMode.mode ?? "default"; } diff --git a/src/CodexAcpServer.ts b/src/CodexAcpServer.ts index 4ff0e1b4..18ae5755 100644 --- a/src/CodexAcpServer.ts +++ b/src/CodexAcpServer.ts @@ -156,6 +156,7 @@ import { export interface SessionState { sessionId: string, currentModelId: string, + defaultModeModelId: string | null, availableModels: Array, supportedReasoningEfforts: Array, supportedInputModalities: Array, @@ -662,6 +663,7 @@ export class CodexAcpServer { const sessionState: SessionState = { sessionId: sessionId, currentModelId: currentModelId, + defaultModeModelId: null, availableModels: models, supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [], supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"], @@ -1385,10 +1387,30 @@ export class CodexAcpServer { if (mode === null) { throw RequestError.invalidParams(); } + if (mode === PLAN_COLLABORATION_MODE && sessionState.collaborationMode !== PLAN_COLLABORATION_MODE) { + sessionState.defaultModeModelId = sessionState.currentModelId; + sessionState.currentModelId = await this.createPlanModeModelId(sessionState); + } else if (mode === DEFAULT_COLLABORATION_MODE && sessionState.defaultModeModelId !== null) { + sessionState.currentModelId = sessionState.defaultModeModelId; + sessionState.defaultModeModelId = null; + } await this.codexAcpClient.setCollaborationMode(sessionState.sessionId, mode, sessionState.currentModelId); sessionState.collaborationMode = mode; } + private async createPlanModeModelId(sessionState: SessionState): Promise { + const planEffort = await this.codexAcpClient.getPlanModeReasoningEffort(sessionState.cwd); + if (!planEffort) { + return sessionState.currentModelId; + } + const effort = findSupportedEffort(sessionState.supportedReasoningEfforts, planEffort); + if (!effort) { + return sessionState.currentModelId; + } + const {model} = ModelId.fromString(sessionState.currentModelId); + return ModelId.create(model, effort).toString(); + } + private applyModelChange(sessionState: SessionState, value: string): void { const model = sessionState.availableModels.find(m => m.id === value); if (!model) { @@ -1919,6 +1941,7 @@ export class CodexAcpServer { const sessionState: SessionState = { sessionId: sessionId, currentModelId: currentModelId, + defaultModeModelId: null, availableModels: models, supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [], supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"], diff --git a/src/__tests__/CodexACPAgent/session-config-options.test.ts b/src/__tests__/CodexACPAgent/session-config-options.test.ts index fe1f5006..bb8711ba 100644 --- a/src/__tests__/CodexACPAgent/session-config-options.test.ts +++ b/src/__tests__/CodexACPAgent/session-config-options.test.ts @@ -254,6 +254,45 @@ describe("Session config options", () => { expect(result.configOptions?.find(o => o.id === COLLABORATION_MODE_CONFIG_ID)).toMatchObject({currentValue: "plan"}); }); + it("uses plan_mode_reasoning_effort while Plan mode is active", async () => { + const {fast} = buildModels(); + const {codexAcpAgent, codexAcpClient} = await createSession("fast-model[low]", [fast]); + vi.spyOn(codexAcpClient, "getPlanModeReasoningEffort").mockResolvedValue("high"); + const update = vi.spyOn((codexAcpClient as any).codexClient, "threadSettingsUpdate").mockResolvedValue(undefined); + + const enabled = await codexAcpAgent.setSessionConfigOption({ + sessionId: "session-id", + configId: COLLABORATION_MODE_CONFIG_ID, + value: PLAN_COLLABORATION_MODE, + }); + + expect(update).toHaveBeenCalledWith(expect.objectContaining({ + threadId: "session-id", + collaborationMode: expect.objectContaining({ + mode: "plan", + settings: expect.objectContaining({reasoning_effort: "high"}), + }), + })); + expect(codexAcpAgent.getSessionState("session-id").currentModelId).toBe("fast-model[high]"); + expect(enabled.configOptions?.find(o => o.id === REASONING_EFFORT_CONFIG_ID)).toMatchObject({currentValue: "high"}); + + const disabled = await codexAcpAgent.setSessionConfigOption({ + sessionId: "session-id", + configId: COLLABORATION_MODE_CONFIG_ID, + value: "default", + }); + + expect(update).toHaveBeenLastCalledWith(expect.objectContaining({ + threadId: "session-id", + collaborationMode: expect.objectContaining({ + mode: "default", + settings: expect.objectContaining({reasoning_effort: "low"}), + }), + })); + expect(codexAcpAgent.getSessionState("session-id").currentModelId).toBe("fast-model[low]"); + expect(disabled.configOptions?.find(o => o.id === REASONING_EFFORT_CONFIG_ID)).toMatchObject({currentValue: "low"}); + }); + it("toggles collaboration mode with /plan without starting a model turn", async () => { const {fast} = buildModels(); const {fixture, codexAcpAgent, codexAcpClient} = await createSession("fast-model[medium]", [fast]); diff --git a/src/__tests__/acp-test-utils.ts b/src/__tests__/acp-test-utils.ts index f358664c..ab477d33 100644 --- a/src/__tests__/acp-test-utils.ts +++ b/src/__tests__/acp-test-utils.ts @@ -410,6 +410,7 @@ export function createTestSessionState(overrides?: Partial): Sessi additionalDirectories: [], sessionId, currentModelId: "model-id[effort]", + defaultModeModelId: null, availableModels: [], supportedReasoningEfforts: [], supportedInputModalities: ["text", "image"],