fix: verify lazy native provider launch during restored model selection
This commit is contained in:
parent
6f65d00732
commit
06ab95bb3c
|
|
@ -642,6 +642,9 @@ fn response_error_classification(error: &ResponseError) -> &'static str {
|
|||
_ => {}
|
||||
}
|
||||
match error.message.as_str() {
|
||||
"ACPX provider spawned after ownership admission was sealed" => {
|
||||
"provider_spawn_after_ownership_seal"
|
||||
}
|
||||
"ACPX recovery identity conflicts with the immutable session configuration" => {
|
||||
"recovery_configuration_mismatch"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1311,6 +1311,36 @@ describe("Codex ACPX runtime adapter", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("verifies a lazy recovered provider spawned by model selection before returning", async () => {
|
||||
const runtime = fakeRuntime();
|
||||
const command = fakeCommand();
|
||||
vi.mocked(command.spawn).mockReturnValue(fakeChild());
|
||||
let runtimeOptions: AcpRuntimeOptions | undefined;
|
||||
let acknowledgeOwnership!: () => void;
|
||||
const ownership = new Promise<void>((resolve) => { acknowledgeOwnership = resolve; });
|
||||
vi.mocked(runtime.setConfigOption!).mockImplementation(async () => {
|
||||
await Promise.resolve();
|
||||
runtimeOptions?.spawnAgent?.({ command: "ignored", args: ["--stdio"], options: {} });
|
||||
});
|
||||
const port = await openCodexAcpxRuntime(openOptions(command), {
|
||||
createRegistry: () => registry(), createStore: () => store(),
|
||||
awaitProviderOwnership: () => ownership,
|
||||
awaitProviderExit: providerOwnershipEstablished,
|
||||
createRuntime: (options) => { runtimeOptions = options; return runtime; },
|
||||
});
|
||||
let admitted = false;
|
||||
const selection = port.setModel!("gpt-5.6-sol").then(() => { admitted = true; });
|
||||
void selection.catch(() => undefined);
|
||||
await vi.waitFor(() => expect(command.spawn).toHaveBeenCalledOnce());
|
||||
expect(admitted).toBe(false);
|
||||
acknowledgeOwnership();
|
||||
await selection;
|
||||
expect(admitted).toBe(true);
|
||||
expect(() => runtimeOptions?.spawnAgent?.({ command: "ignored", args: [], options: {} }))
|
||||
.toThrow("provider spawned after ownership admission was sealed");
|
||||
await port.close({ reason: "test complete" });
|
||||
});
|
||||
|
||||
it("admits a verified provider that starts with the first recovered turn", async () => {
|
||||
const runtime = fakeRuntime();
|
||||
const child = fakeChild();
|
||||
|
|
|
|||
|
|
@ -1156,11 +1156,20 @@ function runtimePort(
|
|||
...(runtime.setConfigOption
|
||||
? {
|
||||
async setModel(model: string) {
|
||||
await runtime.setConfigOption?.({
|
||||
handle,
|
||||
key: "model",
|
||||
value: model,
|
||||
});
|
||||
// A restored handle can be lazy: selecting the pinned model may
|
||||
// launch its first provider before any prompt. Admit that spawn
|
||||
// only for this control call, and verify ownership before return.
|
||||
const finishOwnershipAdmission =
|
||||
children.beginLifetimeOwnershipAdmission();
|
||||
try {
|
||||
await runtime.setConfigOption?.({
|
||||
handle,
|
||||
key: "model",
|
||||
value: model,
|
||||
});
|
||||
} finally {
|
||||
await finishOwnershipAdmission();
|
||||
}
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
|
|
|
|||
Loading…
Reference in New Issue