fix(runner): bind provider model before terminal

This commit is contained in:
Dotta 2026-09-03 03:27:49 -05:00
parent 7462d142dd
commit 848b8296d0
4 changed files with 28 additions and 6 deletions

View File

@ -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 }),
}),
);
},

View File

@ -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 } }
: {}),

View File

@ -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({

View File

@ -1532,6 +1532,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport {
#providerIdentity: Record<string, unknown> | null = null;
#turnId = "";
#turnStartResponsePending = false;
#turnStartResponseEpoch = 0;
#durableTurnId = "";
#authorizedTools: Record<string, unknown> | 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();
}
}
}