From 848b8296d0be61634ccd323bcd109e2c85d597e2 Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 3 Sep 2026 03:27:49 -0500 Subject: [PATCH] fix(runner): bind provider model before terminal --- .../acpx/codex-runtime-adapter.test.ts | 6 +++--- .../src/drivers/acpx/codex-runtime-adapter.ts | 6 +++++- .../src/drivers/acpx/runtime-host.test.ts | 2 +- .../src/live/runnerd-codex-transport.ts | 20 ++++++++++++++++++- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts index 391af18bd4..721cf47547 100644 --- a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.test.ts @@ -101,9 +101,9 @@ describe("Codex ACPX runtime adapter", () => { }); }); - it.each([["claude" as const, "claude-sonnet-5"]])( + it.each([["claude" as const, "claude-sonnet-5", "sonnet"]])( "opens the qualified %s session through the verified lease", - async (agent, model) => { + async (agent, model, providerModel) => { const runtime = fakeRuntime(); const command = fakeCommand(); const options = openOptions(command); @@ -132,7 +132,7 @@ describe("Codex ACPX runtime adapter", () => { expect(runtime.ensureSession).toHaveBeenCalledWith( expect.objectContaining({ agent, - sessionOptions: expect.objectContaining({ model }), + sessionOptions: expect.objectContaining({ model: providerModel }), }), ); }, diff --git a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts index 0c9834f94a..a3049f2045 100644 --- a/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts +++ b/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts @@ -296,7 +296,11 @@ export async function openQualifiedAcpxRuntime( mode: "persistent", cwd: options.cwd, sessionOptions: { - model: options.profile.qualificationModel, + // ACP session construction receives the provider-native selector. + // The caller-facing canonical model was already pinned when the + // qualified profile was resolved and is restored at the status + // boundary after the provider reports this selector. + model: options.profile.reportedModelId, ...(options.systemInstructions ? { systemPrompt: { append: options.systemInstructions } } : {}), diff --git a/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts b/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts index 8ec2e7cbdc..26a592ac51 100644 --- a/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts @@ -419,7 +419,7 @@ describe("ACPX runtime host", () => { const fixture = await hostFixture(); let selected = false; const setModel = vi.fn(async (model: string) => { - expect(model).toBe("claude-sonnet-5"); + expect(model).toBe("sonnet"); selected = true; }); const runtime = runtimePort({ diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts index 70e1f53d10..f232645c9b 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts @@ -1532,6 +1532,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { #providerIdentity: Record | null = null; #turnId = ""; #turnStartResponsePending = false; + #turnStartResponseEpoch = 0; #durableTurnId = ""; #authorizedTools: Record | null = null; #closed = false; @@ -2766,7 +2767,9 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { .join("\n"); const pendingTurnId = `turn_lab_${randomUUID().replaceAll("-", "")}`; this.#turnId = pendingTurnId; + const responseEpoch = ++this.#turnStartResponseEpoch; this.#turnStartResponsePending = true; + let responseReady = false; try { await this.#command("turn.start", { text: message }); // Command completion only means runnerd accepted the command. Codex assigns @@ -2783,9 +2786,24 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { } if (this.#turnId === pendingTurnId) throw new Error("runnerd did not report the provider turn identity"); + responseReady = true; return { turn: { id: this.#turnId, status: "inProgress" } }; } finally { - this.#turnStartResponsePending = false; + if (!responseReady) { + if (this.#turnStartResponseEpoch === responseEpoch) + this.#turnStartResponsePending = false; + } else { + // Resolving this async method schedules the strict driver's response + // continuation as a microtask. Keep terminal frames held until the + // following task so the driver can bind and emit turn.accepted first. + // The epoch prevents a late release from clearing a newer turn fence. + const release = setTimeout(() => { + if (this.#turnStartResponseEpoch !== responseEpoch) return; + this.#turnStartResponsePending = false; + if (!this.#closed) this.#pumpEventsSafely(); + }, 0); + release.unref(); + } } }