fix(codex): keep the default effort after a daemon restart (#5686)

A Codex agent created without a thinking option ran on the configured
default effort until the daemon restarted. The resumed session never
resolved the defaults, so it showed thinking auto and sent turns with no
effort. Resolve unset model and effort for a resumed thread the same way
a new thread does.

Closes #5673

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
paseo-bot[bot]
2026-09-29 17:53:43 +02:00
committed by GitHub
parent 23c4404b95
commit a8f5d969b8
2 changed files with 41 additions and 3 deletions
@@ -1170,6 +1170,36 @@ describe("Codex app-server provider", () => {
}
});
test("keeps the default effort when a session without one is resumed after a restart", async () => {
const beforeRestart = createFakeCodexAppServer();
const firstSession = await createProviderWithFakeAppServer(beforeRestart).createSession(
createConfig({ thinkingOptionId: undefined }),
);
await firstSession.startTurn("first turn");
await expect(beforeRestart.waitForTurnStart()).resolves.toMatchObject({ effort: "medium" });
const handle = firstSession.describePersistence()!;
await firstSession.close();
const afterRestart = createFakeCodexAppServer();
// The daemon resumes with the stored agent config, which never recorded an effort.
const resumed = await createProviderWithFakeAppServer(afterRestart).resumeSession(handle, {
model: "gpt-5.4",
thinkingOptionId: undefined,
});
try {
await expect(resumed.getRuntimeInfo()).resolves.toMatchObject({
thinkingOptionId: "medium",
});
await resumed.startTurn("turn after restart");
await expect(afterRestart.waitForTurnStart()).resolves.toMatchObject({ effort: "medium" });
beforeRestart.assertNoErrors();
afterRestart.assertNoErrors();
} finally {
await resumed.close();
}
});
test("preapproves only granted tools on the injected Codex MCP server", async () => {
const session = createSession({
modeId: undefined,
@@ -3611,6 +3611,7 @@ export class CodexAppServerAgentSession implements AgentSession {
if (this.currentThreadId) {
await this.ensureThreadLoaded();
await this.loadPersistedHistory(this.client);
await this.applyDefaultModelAndThinking();
}
if (this.closed) {
@@ -5208,13 +5209,20 @@ export class CodexAppServerAgentSession implements AgentSession {
return { model, thinkingOptionId };
}
// A session without a model or effort runs on Codex's configured defaults, whether its
// thread is new or resumed after a restart.
private async applyDefaultModelAndThinking(): Promise<string> {
const { model, thinkingOptionId } = await this.resolveModelAndThinking();
this.config.model = model;
this.config.thinkingOptionId = thinkingOptionId;
return model;
}
private async ensureThread(): Promise<void> {
if (!this.client) return;
if (this.currentThreadId) return;
const { model, thinkingOptionId } = await this.resolveModelAndThinking();
this.config.model = model;
this.config.thinkingOptionId = thinkingOptionId;
const model = await this.applyDefaultModelAndThinking();
const { params, approvalPolicy, sandbox } = this.buildThreadStartRequest(model);
const rawResponse = await this.client.request("thread/start", params);