Skip to content

Commit f1cc8f0

Browse files
vincentkocsteipete
authored andcommitted
fix(codex): reuse bound auth profile for app-server startup
1 parent b2ca265 commit f1cc8f0

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

extensions/codex/src/app-server/run-attempt.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,4 +558,54 @@ describe("runCodexAppServerAttempt", () => {
558558

559559
expect(binding.authProfileId).toBe("openai-codex:bound");
560560
});
561+
562+
it("reuses the bound auth profile for app-server startup when params omit it", async () => {
563+
const sessionFile = path.join(tempDir, "session.jsonl");
564+
const workspaceDir = path.join(tempDir, "workspace");
565+
await writeCodexAppServerBinding(sessionFile, {
566+
threadId: "thread-existing",
567+
cwd: workspaceDir,
568+
authProfileId: "openai-codex:bound",
569+
model: "gpt-5.4-codex",
570+
modelProvider: "openai",
571+
dynamicToolsFingerprint: "[]",
572+
});
573+
const seenAuthProfileIds: Array<string | undefined> = [];
574+
let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;
575+
__testing.setCodexAppServerClientFactoryForTests(async (_startOptions, authProfileId) => {
576+
seenAuthProfileIds.push(authProfileId);
577+
return {
578+
request: async (method: string) => {
579+
if (method === "thread/resume") {
580+
return { thread: { id: "thread-existing" }, modelProvider: "openai" };
581+
}
582+
if (method === "turn/start") {
583+
return { turn: { id: "turn-1", status: "inProgress" } };
584+
}
585+
throw new Error(`unexpected method: ${method}`);
586+
},
587+
addNotificationHandler: (handler: typeof notify) => {
588+
notify = handler;
589+
return () => undefined;
590+
},
591+
addRequestHandler: () => () => undefined,
592+
} as never;
593+
});
594+
const params = createParams(sessionFile, workspaceDir);
595+
delete params.authProfileId;
596+
597+
const run = runCodexAppServerAttempt(params);
598+
await vi.waitFor(() => expect(seenAuthProfileIds).toEqual(["openai-codex:bound"]));
599+
await notify({
600+
method: "turn/completed",
601+
params: {
602+
threadId: "thread-existing",
603+
turnId: "turn-1",
604+
turn: { id: "turn-1", status: "completed" },
605+
},
606+
});
607+
await run;
608+
609+
expect(seenAuthProfileIds).toEqual(["openai-codex:bound"]);
610+
});
561611
});

extensions/codex/src/app-server/run-attempt.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
type JsonObject,
3131
type JsonValue,
3232
} from "./protocol.js";
33-
import type { CodexAppServerThreadBinding } from "./session-binding.js";
33+
import { readCodexAppServerBinding, type CodexAppServerThreadBinding } from "./session-binding.js";
3434
import { clearSharedCodexAppServerClient, getSharedCodexAppServerClient } from "./shared-client.js";
3535
import { buildTurnStartParams, startOrResumeThread } from "./thread-lifecycle.js";
3636
import { mirrorCodexAppServerTranscript } from "./transcript-mirror.js";
@@ -79,6 +79,8 @@ export async function runCodexAppServerAttempt(
7979
agentId: params.agentId,
8080
});
8181
let yieldDetected = false;
82+
const startupBinding = await readCodexAppServerBinding(params.sessionFile);
83+
const startupAuthProfileId = params.authProfileId ?? startupBinding?.authProfileId;
8284
const tools = await buildDynamicTools({
8385
params,
8486
resolvedWorkspace,
@@ -102,7 +104,7 @@ export async function runCodexAppServerAttempt(
102104
timeoutMs: params.timeoutMs,
103105
signal: runAbortController.signal,
104106
operation: async () => {
105-
const startupClient = await clientFactory(appServer.start, params.authProfileId);
107+
const startupClient = await clientFactory(appServer.start, startupAuthProfileId);
106108
const startupThread = await startOrResumeThread({
107109
client: startupClient,
108110
params,

0 commit comments

Comments
 (0)