fix(antigravity): normalize Gemini 3.5 Flash tier IDs (#3603)

Split-out PR A from #3584. Normalizes the Antigravity/agy Gemini 3.5 Flash tier IDs to clean public names (gemini-3.5-flash-low/medium/high), maps them to the live upstream IDs at the executor boundary, and removes Antigravity from the global model resolver so the executor owns wire normalization. Maintainer follow-up: kept gemini-3.5-flash-preview as a hidden backward-compat alias routing to the High tier (so saved combos/configs keep working). Live-validated the tier set via the agy CLI catalog. Integrated into release/v3.8.21. Thanks @dhaern!
This commit is contained in:
Raxxoor
2026-06-11 04:06:15 +01:00
committed by GitHub
parent e4675924cb
commit c6d92058f3
11 changed files with 129 additions and 116 deletions

View File

@@ -1,10 +1,10 @@
// Antigravity CLI (`agy`) model catalog.
//
// These IDs were pinned directly from the live `:fetchAvailableModels` endpoint
// These models are pinned from the live `:fetchAvailableModels` endpoint
// (https://daily-cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels) using a
// real `agy` consumer-OAuth token on 2026-05-29. They are the exact upstream model IDs
// the Antigravity backend accepts — no alias remapping is required (unlike the
// `antigravity` provider, whose catalog used display IDs that needed remapping).
// real `agy` consumer-OAuth token. The public catalog exposes the same clean Gemini
// 3.5 Flash tier names as the Antigravity IDE provider; the shared Antigravity executor
// maps those names to the legacy upstream IDs immediately before dispatch.
//
// The `agy` provider reuses the `antigravity` executor/translator (identical backend),
// but ships its OWN catalog so it can expose models the `antigravity` provider's static
@@ -62,24 +62,6 @@ export const AGY_PUBLIC_MODELS = Object.freeze([
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3-flash-agent",
name: "Gemini 3.5 Flash (Agent)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3-flash",
name: "Gemini 3 Flash",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.5-flash-low",
name: "Gemini 3.5 Flash (Low)",
@@ -89,10 +71,20 @@ export const AGY_PUBLIC_MODELS = Object.freeze([
toolCalling: true,
},
{
id: "gemini-3.5-flash-extra-low",
name: "Gemini 3.5 Flash (Extra Low)",
id: "gemini-3.5-flash-medium",
name: "Gemini 3.5 Flash (Medium)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.5-flash-high",
name: "Gemini 3.5 Flash (High)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},

View File

@@ -22,10 +22,16 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([
supportsVision: true,
toolCalling: true,
},
// Gemini 3.5 Flash — flagship model in Antigravity 2.0 (May 2026)
// Gemini 3.5 Flash tiers exposed by Antigravity 2.0.4's model selector.
// The user-facing names are Low / Medium / High, but fetchAvailableModels reports
// legacy upstream IDs for two of them:
// Low -> gemini-3.5-flash-extra-low (displayName: Gemini 3.5 Flash (Low))
// Medium -> gemini-3.5-flash-low (displayName: Gemini 3.5 Flash (Medium))
// High -> gemini-3-flash-agent (displayName: Gemini 3.5 Flash (High))
// Keep the clean public IDs here and map them below for routing/quota.
{
id: "gemini-3.5-flash-preview",
name: "Gemini 3.5 Flash",
id: "gemini-3.5-flash-low",
name: "Gemini 3.5 Flash (Low)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
@@ -33,8 +39,17 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([
toolCalling: true,
},
{
id: "gemini-3-flash-agent",
name: "Gemini 3.5 Flash Agent",
id: "gemini-3.5-flash-medium",
name: "Gemini 3.5 Flash (Medium)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.5-flash-high",
name: "Gemini 3.5 Flash (High)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
@@ -71,33 +86,6 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3-flash-preview",
name: "Gemini 3 Flash",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
// Gemini 3.5 Flash budget tiers — agy ships these as exact upstream ids; #3184 verified
// they work via the antigravity OAuth provider (no alias remapping required).
{
id: "gemini-3.5-flash-low",
name: "Gemini 3.5 Flash (Low)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.5-flash-extra-low",
name: "Gemini 3.5 Flash (Extra Low)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.1-flash-lite",
name: "Gemini 3.1 Flash Lite",
@@ -160,17 +148,20 @@ export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([
},
]);
// The Antigravity upstream API uses plain model IDs (no -high/-low suffix).
// The -high/-low suffix convention was speculative and caused 404 for all
// gemini-3.x models. Only plain IDs like "gemini-2.5-flash" are proven working.
export const ANTIGRAVITY_MODEL_ALIASES = Object.freeze({
"gemini-3.5-flash-low": "gemini-3.5-flash-extra-low",
"gemini-3.5-flash-medium": "gemini-3.5-flash-low",
"gemini-3.5-flash-high": "gemini-3-flash-agent",
// Backward-compat: the retired flagship public id `gemini-3.5-flash-preview`
// (Antigravity 2.0's "Gemini 3.5 Flash") is kept as a HIDDEN alias so saved
// combos/configs keep routing — it maps to the reasoning-capable High tier
// (upstream `gemini-3-flash-agent`). It is NOT re-added to the public catalog.
"gemini-3.5-flash-preview": "gemini-3-flash-agent",
"gemini-3-pro-preview": "gemini-3.1-pro",
// agy catalog exposes -high/-low budget tiers, but the upstream rejects the suffix
// for gemini-3.x (#3229) — map them to the plain proven id.
"gemini-3.1-pro-high": "gemini-3.1-pro",
"gemini-3.1-pro-low": "gemini-3.1-pro",
"gemini-3.5-flash-preview": "gemini-3.5-flash",
"gemini-3-flash-preview": "gemini-3-flash",
"gemini-3-pro-image-preview": "gemini-3-pro-image",
"gemini-2.5-computer-use-preview-10-2025": "rev19-uic3-1p",
// Legacy Claude display ids → current upstream ids. NOTE: an earlier comment here
@@ -186,10 +177,9 @@ export const ANTIGRAVITY_MODEL_ALIASES = Object.freeze({
type AntigravityModelAliasMap = Record<string, string>;
export const ANTIGRAVITY_REVERSE_MODEL_ALIASES: AntigravityModelAliasMap = Object.freeze({
"gemini-3.5-flash-extra-low": "gemini-3.5-flash-low",
"gemini-3-flash-agent": "gemini-3.5-flash-high",
"gemini-3.1-pro": "gemini-3-pro-preview",
"gemini-3.5-flash": "gemini-3.5-flash-preview",
"gemini-3-flash-agent": "gemini-3.5-flash-preview",
"gemini-3-flash": "gemini-3-flash-preview",
"gemini-3-pro-image": "gemini-3-pro-image-preview",
"rev19-uic3-1p": "gemini-2.5-computer-use-preview-10-2025",
});

View File

@@ -194,7 +194,7 @@ function addAntigravityTextualToolCall(
type AntigravityRequestEnvelope = Record<string, unknown> & {
project: string;
model: string;
model?: string;
userAgent: "antigravity" | "jetski";
requestType: "agent" | "image_gen";
requestId: string;
@@ -336,6 +336,15 @@ function processAntigravitySSEPayload(
if (!payload || payload === "[DONE]") return;
try {
const parsed = JSON.parse(payload);
const markdown =
typeof parsed?.markdown === "string"
? parsed.markdown
: typeof parsed?.response?.markdown === "string"
? parsed.response.markdown
: null;
if (markdown) {
collected.textContent += markdown;
}
const candidate = parsed?.response?.candidates?.[0];
if (candidate?.content?.parts) {
for (const part of candidate.content.parts) {
@@ -661,9 +670,14 @@ export class AntigravityExecutor extends BaseExecutor {
if (typeof p.text === "string" && p.text === "") return false;
if (p.functionCall && !p.functionCall.name) return false;
// Only strip if it's NOT our bypass sentinel.
// Only strip if it's NOT our bypass sentinel.
// Antigravity models (like Gemini) need this sentinel to bypass 400 errors.
return !p.thought && (hasFunctionCall || !p.thoughtSignature || p.thoughtSignature === "skip_thought_signature_validator");
return (
!p.thought &&
(hasFunctionCall ||
!p.thoughtSignature ||
p.thoughtSignature === "skip_thought_signature_validator")
);
}) || [];
return { ...c, role, parts };
}) || [];

View File

@@ -1,5 +1,4 @@
import { PROVIDER_ID_TO_ALIAS, PROVIDER_MODELS } from "../config/providerModels.ts";
import { ANTIGRAVITY_MODEL_ALIASES } from "../config/antigravityModelAliases.ts";
import { resolveWildcardAlias } from "./wildcardRouter.ts";
type ProviderModelAliasMap = Record<string, Record<string, string>>;
@@ -76,7 +75,12 @@ const PROVIDER_MODEL_ALIASES: ProviderModelAliasMap = {
"gpt-oss-20b": "openai/gpt-oss-20b",
"nvidia/gpt-oss-20b": "openai/gpt-oss-20b",
},
antigravity: { ...ANTIGRAVITY_MODEL_ALIASES },
// Antigravity model aliases must be applied by the Antigravity executor, not by
// the global model resolver. Applying them here rewrites the client-visible model
// before credential/account routing and before UI/logging, causing clean IDs like
// gemini-3.5-flash-high to be exposed and retried as upstream-only legacy ids such
// as gemini-3-flash-agent. The executor owns provider-wire normalization.
antigravity: {},
kiro: {
"claude-opus-4-7": "claude-opus-4.7",
"claude-opus-4-6": "claude-opus-4.6",

View File

@@ -108,7 +108,7 @@ test("checkFallbackError locks Antigravity quota-reached 429 for the full reset
429,
message,
0,
"gemini-3-flash-agent",
"gemini-3.5-flash-high",
"antigravity",
null,
makeProfile()
@@ -124,7 +124,7 @@ test("checkFallbackError locks Antigravity quota-reached 429 for the full reset
test("recordModelLockoutFailure honors a multi-day exactCooldownMs (under 30-day cap)", () => {
const provider = "antigravity";
const connectionId = "conn-quota-window";
const model = "gemini-3-flash-agent";
const model = "gemini-3.5-flash-high";
const exactCooldownMs = (164 * 3600 + 27 * 60 + 24) * 1000;
clearModelLock(provider, connectionId, model);

View File

@@ -4,7 +4,10 @@ import assert from "node:assert/strict";
import { AI_PROVIDERS, USAGE_SUPPORTED_PROVIDERS } from "../../src/shared/constants/providers.ts";
import { REGISTRY } from "../../open-sse/config/providerRegistry.ts";
import { PROVIDERS as LEGACY_PROVIDERS } from "../../open-sse/config/constants.ts";
import { PROVIDERS as OAUTH_PROVIDER_IDS, AGY_CONFIG } from "../../src/lib/oauth/constants/oauth.ts";
import {
PROVIDERS as OAUTH_PROVIDER_IDS,
AGY_CONFIG,
} from "../../src/lib/oauth/constants/oauth.ts";
import { supportsTokenRefresh, REFRESH_LEAD_MS } from "../../open-sse/services/tokenRefresh.ts";
import {
AGY_PUBLIC_MODELS,
@@ -47,6 +50,12 @@ test("agy ships its own catalog including the Claude models antigravity omits",
const ids = REGISTRY.agy.models.map((m) => m.id);
assert.ok(ids.includes("claude-opus-4-6-thinking"), "must expose Claude Opus 4.6 Thinking");
assert.ok(ids.includes("claude-sonnet-4-6"), "must expose Claude Sonnet 4.6");
assert.ok(ids.includes("gemini-3.5-flash-low"), "must expose clean Flash Low tier");
assert.ok(ids.includes("gemini-3.5-flash-medium"), "must expose clean Flash Medium tier");
assert.ok(ids.includes("gemini-3.5-flash-high"), "must expose clean Flash High tier");
assert.ok(!ids.includes("gemini-3-flash-agent"));
assert.ok(!ids.includes("gemini-3-flash"));
assert.ok(!ids.includes("gemini-3.5-flash-extra-low"));
// Tab-completion models are not chat-callable and must be excluded.
assert.ok(!ids.includes("tab_flash_lite_preview"));
assert.ok(!ids.includes("tab_jump_flash_lite_preview"));

View File

@@ -17,13 +17,16 @@ function getPublicModel(id: string) {
test("resolveAntigravityModelId maps the documented Antigravity aliases to upstream IDs", () => {
assert.equal(resolveAntigravityModelId("gemini-3-pro-preview"), "gemini-3.1-pro");
assert.equal(resolveAntigravityModelId("gemini-3.5-flash-preview"), "gemini-3.5-flash");
assert.equal(resolveAntigravityModelId("gemini-3-flash-preview"), "gemini-3-flash");
assert.equal(resolveAntigravityModelId("gemini-3-pro-image-preview"), "gemini-3-pro-image");
assert.equal(
resolveAntigravityModelId("gemini-2.5-computer-use-preview-10-2025"),
"rev19-uic3-1p"
);
assert.equal(resolveAntigravityModelId("gemini-3.5-flash-low"), "gemini-3.5-flash-extra-low");
assert.equal(resolveAntigravityModelId("gemini-3.5-flash-medium"), "gemini-3.5-flash-low");
assert.equal(resolveAntigravityModelId("gemini-3.5-flash-high"), "gemini-3-flash-agent");
// Backward-compat: retired flagship public id routes to the High tier upstream.
assert.equal(resolveAntigravityModelId("gemini-3.5-flash-preview"), "gemini-3-flash-agent");
assert.equal(resolveAntigravityModelId("gemini-claude-sonnet-4-5"), "claude-sonnet-4-6");
assert.equal(resolveAntigravityModelId("gemini-claude-sonnet-4-5-thinking"), "claude-sonnet-4-6");
assert.equal(
@@ -35,8 +38,8 @@ test("resolveAntigravityModelId maps the documented Antigravity aliases to upstr
test("toClientAntigravityModelId exposes client-visible aliases for known upstream IDs", () => {
assert.equal(toClientAntigravityModelId("gemini-3.1-pro"), "gemini-3-pro-preview");
assert.equal(toClientAntigravityModelId("gemini-3-flash-agent"), "gemini-3.5-flash-preview");
assert.equal(toClientAntigravityModelId("gemini-3-flash"), "gemini-3-flash-preview");
assert.equal(toClientAntigravityModelId("gemini-3.5-flash-extra-low"), "gemini-3.5-flash-low");
assert.equal(toClientAntigravityModelId("gemini-3-flash-agent"), "gemini-3.5-flash-high");
assert.equal(toClientAntigravityModelId("gpt-oss-120b-medium"), "gpt-oss-120b-medium");
assert.equal(toClientAntigravityModelId("claude-sonnet-4-6"), "claude-sonnet-4-6");
assert.equal(toClientAntigravityModelId("claude-opus-4-6-thinking"), "claude-opus-4-6-thinking");
@@ -45,6 +48,8 @@ test("toClientAntigravityModelId exposes client-visible aliases for known upstre
test("isUserCallableAntigravityModelId only allows public chat-capable model IDs", () => {
assert.equal(isUserCallableAntigravityModelId("gemini-3-pro-preview"), true);
assert.equal(isUserCallableAntigravityModelId("gemini-3.1-pro"), true);
// Retired flagship id stays callable as a hidden backward-compat alias (routes to High),
// even though it is no longer exposed in the public catalog.
assert.equal(isUserCallableAntigravityModelId("gemini-3.5-flash-preview"), true);
assert.equal(isUserCallableAntigravityModelId("gemini-3-flash-agent"), true);
assert.equal(isUserCallableAntigravityModelId("gemini-3.1-flash-lite"), true);
@@ -58,10 +63,12 @@ test("isUserCallableAntigravityModelId only allows public chat-capable model IDs
// 2.0 was wrong.
assert.equal(isUserCallableAntigravityModelId("claude-opus-4-6-thinking"), true);
assert.equal(isUserCallableAntigravityModelId("claude-sonnet-4-6"), true);
// #3184: Gemini budget tiers now exposed (agy parity).
// Antigravity 2.0.4 exposes Gemini 3.5 Flash as separate UI tiers.
assert.equal(isUserCallableAntigravityModelId("gemini-3.1-pro-high"), true);
assert.equal(isUserCallableAntigravityModelId("gemini-3.1-pro-low"), true);
assert.equal(isUserCallableAntigravityModelId("gemini-3.5-flash-low"), true);
assert.equal(isUserCallableAntigravityModelId("gemini-3.5-flash-medium"), true);
assert.equal(isUserCallableAntigravityModelId("gemini-3.5-flash-high"), true);
assert.equal(isUserCallableAntigravityModelId("gemini-3.5-flash-extra-low"), true);
assert.equal(isUserCallableAntigravityModelId("tab_flash_lite_preview"), false);
assert.equal(isUserCallableAntigravityModelId("unknown-model"), false);
@@ -79,9 +86,9 @@ test("ANTIGRAVITY_PUBLIC_MODELS exposes captured Antigravity 2.0.1 names and cap
toolCalling: true,
});
assert.equal(getPublicModel("claude-sonnet-4-6").name, "Claude Sonnet 4.6 (Thinking)");
assert.deepEqual(getPublicModel("gemini-3.5-flash-preview"), {
id: "gemini-3.5-flash-preview",
name: "Gemini 3.5 Flash",
assert.deepEqual(getPublicModel("gemini-3.5-flash-high"), {
id: "gemini-3.5-flash-high",
name: "Gemini 3.5 Flash (High)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
@@ -89,8 +96,8 @@ test("ANTIGRAVITY_PUBLIC_MODELS exposes captured Antigravity 2.0.1 names and cap
toolCalling: true,
});
assert.equal(
getClientVisibleAntigravityModelName("gemini-3.5-flash-preview"),
"Gemini 3.5 Flash"
getClientVisibleAntigravityModelName("gemini-3.5-flash-medium"),
"Gemini 3.5 Flash (Medium)"
);
assert.equal(getClientVisibleAntigravityModelName("gemini-2.5-flash"), "Gemini 2.5 Flash");
assert.equal(
@@ -127,15 +134,6 @@ test("ANTIGRAVITY_PUBLIC_MODELS has no duplicate model IDs", () => {
assert.deepEqual(duplicates, [], `duplicate model IDs found: ${duplicates.join(", ")}`);
});
test("gemini-3-flash-agent keeps its Agent display name (not the Flash High duplicate)", () => {
// A duplicate entry previously overwrote this name with "Gemini 3.5 Flash (High)"
// because the id-keyed name map kept the last occurrence.
assert.equal(
getClientVisibleAntigravityModelName("gemini-3-flash-agent"),
"Gemini 3.5 Flash Agent"
);
});
test("AntigravityExecutor.transformRequest resolves alias models before dispatching upstream", async () => {
const executor = new AntigravityExecutor();
const result = await executor.transformRequest(
@@ -153,10 +151,10 @@ test("AntigravityExecutor.transformRequest resolves alias models before dispatch
assert.equal(result.model, "gemini-3.1-pro");
});
test("AntigravityExecutor.transformRequest resolves Gemini 3.5 Flash alias upstream", async () => {
test("AntigravityExecutor.transformRequest maps Gemini 3.5 Flash tiers to live upstream IDs", async () => {
const executor = new AntigravityExecutor();
const result = await executor.transformRequest(
"antigravity/gemini-3.5-flash-preview",
"antigravity/gemini-3.5-flash-high",
{
request: {
contents: [{ role: "user", parts: [{ text: "Hello" }] }],
@@ -167,7 +165,11 @@ test("AntigravityExecutor.transformRequest resolves Gemini 3.5 Flash alias upstr
);
if (result instanceof Response) throw new Error("Unexpected Response from transformRequest");
assert.equal(result.model, "gemini-3.5-flash");
assert.equal(result.model, "gemini-3-flash-agent");
assert.equal(result.modelConfigId, undefined);
assert.equal(result.model_config_id, undefined);
assert.equal(result.request.modelConfigId, undefined);
assert.equal(result.request.model_config_id, undefined);
});
test("AntigravityExecutor.transformRequest sends Claude through Gemini-compatible Cloud Code schema", async () => {

View File

@@ -10,12 +10,15 @@ test("getExecutor('agy') returns AntigravityExecutor (not DefaultExecutor)", ()
test("getExecutor('antigravity') returns AntigravityExecutor", () => {
const executor = getExecutor("antigravity");
assert.ok(executor instanceof AntigravityExecutor, "antigravity provider should use AntigravityExecutor");
assert.ok(
executor instanceof AntigravityExecutor,
"antigravity provider should use AntigravityExecutor"
);
});
test("getExecutor('agy') builds valid streaming URL", () => {
const executor = getExecutor("agy");
const url = executor.buildUrl("gemini-3-flash", true);
const url = executor.buildUrl("gemini-3.5-flash-high", true);
assert.ok(
url.includes("streamGenerateContent?alt=sse"),
`expected streaming endpoint URL, got: ${url}`
@@ -24,7 +27,7 @@ test("getExecutor('agy') builds valid streaming URL", () => {
test("getExecutor('agy') builds valid non-streaming URL", () => {
const executor = getExecutor("agy");
const url = executor.buildUrl("gemini-3-flash", false);
const url = executor.buildUrl("gemini-3.5-flash-high", false);
// Antigravity executor always uses streaming endpoint (buildUrl ignores stream flag)
assert.ok(
url.includes("streamGenerateContent?alt=sse"),

View File

@@ -91,7 +91,7 @@ test("pruning stale connection available models during import", async () => {
db.prepare(
"INSERT INTO provider_connections (id, provider, auth_type, name, is_active, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)"
).run("conn-active", "openrouter", "apikey", "Active Connection", 1, "2026-05-29", "2026-05-29");
db.prepare(
"INSERT INTO provider_connections (id, provider, auth_type, name, is_active, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)"
).run("conn-stale", "openrouter", "apikey", "Stale Connection", 0, "2026-05-29", "2026-05-29");
@@ -114,7 +114,7 @@ test("pruning stale connection available models during import", async () => {
// Check models for "openrouter"
const allSyncedModels = await modelsDb.getSyncedAvailableModels("openrouter");
// Stale connection should be pruned. Active connection and the new syncing connection should be kept.
const ids = allSyncedModels.map((m) => m.id);
assert.ok(ids.includes("shared/model-active"));
@@ -149,11 +149,7 @@ test("antigravity sync dynamically builds and saves mitmAlias mappings", async (
assert.equal(mitmMappings["gemini-3.5-flash"], "antigravity/gemini-3.5-flash");
assert.equal(mitmMappings["custom-antigravity-model"], "antigravity/custom-antigravity-model");
// Should contain reverse alias mappings (gemini-3.5-flash-preview maps to gemini-3.5-flash)
assert.equal(mitmMappings["gemini-3.5-flash-preview"], "antigravity/gemini-3.5-flash");
assert.equal(mitmMappings["gemini-3-flash-agent"], "antigravity/gemini-3.5-flash");
// Should contain forward alias mappings (gemini-3.5-flash-preview maps to gemini-3.5-flash)
assert.equal(mitmMappings["gemini-3.5-flash-preview"], "antigravity/gemini-3.5-flash");
// Removed Antigravity 2.0 preview/agent aliases must not be reintroduced.
assert.equal(mitmMappings["gemini-3.5-flash-preview"], undefined);
assert.equal(mitmMappings["gemini-3-flash-agent"], undefined);
});

View File

@@ -690,11 +690,13 @@ test("v1 models catalog exposes Antigravity client-visible preview aliases inste
assert.equal(response.status, 200);
assert.ok(ids.has("antigravity/gemini-3-pro-preview"));
assert.ok(ids.has("antigravity/gemini-3-flash-preview"));
// #3184/#3303: the Gemini budget tiers (`-high`/`-low`) are user-callable
// client-visible aliases on the Antigravity OAuth backend (agy parity), so
// they ARE now exposed in the catalog. (They alias to the plain
// `gemini-3.1-pro` upstream id — see ANTIGRAVITY_MODEL_ALIASES.)
assert.ok(ids.has("antigravity/gemini-3.5-flash-low"));
assert.ok(ids.has("antigravity/gemini-3.5-flash-medium"));
assert.ok(ids.has("antigravity/gemini-3.5-flash-high"));
assert.equal(ids.has("antigravity/gemini-3-flash-preview"), false);
assert.equal(ids.has("antigravity/gemini-3-flash-agent"), false);
// Gemini 3.1 Pro budget tiers remain client-visible aliases for the plain
// `gemini-3.1-pro` upstream id — see ANTIGRAVITY_MODEL_ALIASES.
assert.ok(ids.has("antigravity/gemini-3.1-pro-high"));
// The legacy `gemini-claude-*` ids are alias KEYS (remapped to live upstream
// ids), not public catalog entries, so they stay unexposed.

View File

@@ -24,18 +24,19 @@ test("T28: gemini AI Studio catalog includes current preview models", () => {
assert.ok(geminiCliIds.includes("gemini-3-flash-preview"));
});
test("T28: antigravity static catalog exposes client-visible Gemini preview IDs", () => {
test("T28: antigravity static catalog exposes client-visible Gemini tier IDs", () => {
const staticIds = (getStaticModelsForProvider("antigravity") || []).map((m) => m.id);
assert.ok(staticIds.includes("gemini-3-pro-preview"));
assert.ok(staticIds.includes("gemini-3-flash-preview"));
// #3303 (agy parity, discussion #3184): the Gemini budget tiers ARE
// client-visible on the Antigravity OAuth backend — restoring tiers an
// earlier assumption had wrongly hidden.
assert.ok(staticIds.includes("gemini-3.5-flash-low"));
assert.ok(staticIds.includes("gemini-3.5-flash-medium"));
assert.ok(staticIds.includes("gemini-3.5-flash-high"));
assert.ok(staticIds.includes("gemini-3.1-pro-low"));
assert.ok(staticIds.includes("gemini-3.1-pro-high"));
// Legacy aliases that were never client-visible stay absent.
assert.ok(!staticIds.includes("gemini-3-pro-high"));
assert.ok(!staticIds.includes("gemini-3-flash-preview"));
assert.ok(!staticIds.includes("gemini-3-flash-agent"));
assert.ok(!staticIds.includes("gemini-claude-sonnet-4-5"));
assert.ok(!staticIds.includes("gemini-claude-sonnet-4-5-thinking"));
assert.ok(!staticIds.includes("gemini-claude-opus-4-5-thinking"));