diff --git a/apps/server/src/provider/acp/AntigravityAcpSupport.test.ts b/apps/server/src/provider/acp/AntigravityAcpSupport.test.ts index dc755d1e7a5a4..4145a8bdec0cf 100644 --- a/apps/server/src/provider/acp/AntigravityAcpSupport.test.ts +++ b/apps/server/src/provider/acp/AntigravityAcpSupport.test.ts @@ -50,6 +50,7 @@ function makeModelRuntime( selections.push( `${configId}=${String(value)}${options?.allowUnlistedValue === true ? " (unlisted)" : ""}`, ); + if (failure) return yield* failure; return { configOptions }; }); return { @@ -155,24 +156,27 @@ describe("applyAntigravityAcpModelSelection", () => { }), ); - it.effect.each(["gemini-removed", "Gemini saved", "gemini-saved[reasoning=high]"])( - "rejects unavailable or non-native model ID %s without selecting a fallback", - (model) => - Effect.gen(function* () { - const { runtime, selections } = makeModelRuntime(); - const error = yield* applyAntigravityAcpModelSelection({ - runtime, - model, - mapError: (cause) => cause, - }).pipe(Effect.flip); - - expect(error).toMatchObject({ - _tag: "AcpRequestError", - code: -32602, - errorMessage: expect.stringContaining(`'${model}' is unavailable`), - }); - expect(selections).toEqual([]); - }), + it.effect("sends an explicit model absent from cached choices to the provider", () => + Effect.gen(function* () { + const { runtime, selections } = makeModelRuntime(); + const model = yield* applyAntigravityAcpModelSelection({ + runtime, + model: "gemini-new", + mapError: (cause) => cause, + }); + expect(model).toBe("gemini-new"); + expect(selections).toEqual(["model=gemini-new (unlisted)"]); + + const failure = EffectAcpErrors.AcpRequestError.invalidParams("Provider rejected it."); + const rejecting = makeModelRuntime([modelConfig], failure); + const error = yield* applyAntigravityAcpModelSelection({ + runtime: rejecting.runtime, + model: "gemini-removed", + mapError: (cause) => ({ cause }), + }).pipe(Effect.flip); + expect(error).toEqual({ cause: failure }); + expect(rejecting.selections).toEqual(["model=gemini-removed (unlisted)"]); + }), ); it.effect("accepts exact model IDs from grouped native options", () => diff --git a/apps/server/src/provider/acp/AntigravityAcpSupport.ts b/apps/server/src/provider/acp/AntigravityAcpSupport.ts index 7fd64e2534fa7..7d893a5816815 100644 --- a/apps/server/src/provider/acp/AntigravityAcpSupport.ts +++ b/apps/server/src/provider/acp/AntigravityAcpSupport.ts @@ -273,17 +273,18 @@ export const applyAntigravityAcpModelSelection = Effect.fn("applyAntigravityAcpM }); const explicit = Boolean(input.model) && input.model !== ANTIGRAVITY_DEFAULT_MODEL; if (resolved === undefined || (!explicit && resolved === current)) return current; - const options = antigravityModelOptions(configOptions); - const legacyFamily = options.some((option) => option.value === resolved) - ? undefined - : legacyAntigravityFamily(configOptions, resolved); - if (legacyFamily !== undefined) { + const listed = antigravityModelOptions(configOptions).some( + (option) => option.value === resolved, + ); + const legacyFamily = listed ? undefined : legacyAntigravityFamily(configOptions, resolved); + // A live session's choices go stale when the account gains models; let the agent decide. + if (legacyFamily !== undefined || (explicit && !listed)) { yield* input.runtime .setConfigOption("model", resolved, { allowUnlistedValue: true }) .pipe(Effect.mapError(input.mapError)); - return legacyFamily; + return legacyFamily ?? resolved; } - if (!options.some((option) => option.value === resolved)) { + if (!listed) { return yield* Effect.fail( input.mapError( EffectAcpErrors.AcpRequestError.invalidParams(