fix(runner): select qualified ACP model alias

This commit is contained in:
Dotta 2026-09-03 03:17:13 -05:00
parent f5af8b2482
commit cbfb4f1bef
2 changed files with 40 additions and 10 deletions

View File

@ -24,7 +24,7 @@ describe("ACPX qualified model verification", () => {
expect(setModel).not.toHaveBeenCalled();
});
it("selects Claude's canonical model and normalizes its ACP selector", async () => {
it("accepts and normalizes Claude's qualified ACP selector", async () => {
const setModel = vi.fn(async () => undefined);
const getStatus = vi.fn(async () => ({
models: {
@ -33,6 +33,34 @@ describe("ACPX qualified model verification", () => {
},
}));
await expect(
requireVerifiedAcpxModel(
{ getStatus, setModel },
resolveQualifiedAcpxProfile("claude", "claude-sonnet-5"),
),
).resolves.toMatchObject({
models: {
currentModelId: "claude-sonnet-5",
availableModelIds: ["default", "claude-sonnet-5", "opus"],
},
});
expect(setModel).not.toHaveBeenCalled();
expect(getStatus).toHaveBeenCalledTimes(1);
});
it("selects Claude's profile-pinned ACP selector from a stale default", async () => {
let selected = false;
const setModel = vi.fn(async (model: string) => {
expect(model).toBe("sonnet");
selected = true;
});
const getStatus = vi.fn(async () => ({
models: {
currentModelId: selected ? "sonnet" : "default",
availableModelIds: ["default", "sonnet", "opus"],
},
}));
await expect(
requireVerifiedAcpxModel(
{ getStatus, setModel },
@ -45,7 +73,7 @@ describe("ACPX qualified model verification", () => {
},
});
expect(setModel).toHaveBeenCalledTimes(1);
expect(setModel).toHaveBeenCalledWith("claude-sonnet-5");
expect(setModel).toHaveBeenCalledWith("sonnet");
expect(getStatus).toHaveBeenCalledTimes(2);
});

View File

@ -28,24 +28,26 @@ export async function requireVerifiedAcpxModel(
);
}
const requestedModel = profile.qualificationModel;
const providerModel = profile.reportedModelId;
let status = await control.getStatus();
const mustSelectCanonical =
profile.reportedModelId !== requestedModel ||
status.models?.currentModelId !== requestedModel;
if (mustSelectCanonical) {
if (status.models?.currentModelId !== providerModel) {
if (!control.setModel) {
throw acpxModelVerificationError(
"ACPX_MODEL_SELECTION_UNAVAILABLE",
"ACPX agent cannot verify its canonical model through ACP config options",
"ACPX agent cannot verify its qualified model through ACP config options",
);
}
await control.setModel(requestedModel);
// The caller-facing model is already pinned by resolveQualifiedAcpxProfile.
// Select the immutable ACP-facing identifier from that same profile: some
// providers expose a stable selector (for example Claude's `sonnet`) while
// Paperclip publishes the canonical model name after verification.
await control.setModel(providerModel);
status = await control.getStatus();
}
if (status.models?.currentModelId !== profile.reportedModelId) {
if (status.models?.currentModelId !== providerModel) {
throw acpxModelVerificationError(
"ACPX_EFFECTIVE_MODEL_MISMATCH",
`ACPX effective model mismatch: requested ${requestedModel}, expected ACP selector ${profile.reportedModelId}, received ${status.models?.currentModelId ?? "unverified"}`,
`ACPX effective model mismatch: requested ${requestedModel}, expected ACP selector ${providerModel}, received ${status.models?.currentModelId ?? "unverified"}`,
);
}
return normalizeQualifiedModelStatus(status, profile);