fix(providers): de-duplicate agy and antigravity model catalogs (#13410)

De-duplicates the `agy` and `antigravity` model catalogs into `antigravitySharedModels.ts`, with an explicit per-surface add/remove delta (#12724). Verified behavior-neutral: every exported catalog constant of both modules serializes byte-identically before and after (201-line JSON dump).

Maintainer fix: `buildSurfaceCatalog` returned `readonly { id: string; [k: string]: unknown }[]`, which erased the element type. `name` became `unknown`, and the `RegistryEntry.models` assignments failed with 4× TS2322 under `check:open-sse-typecheck` (not covered by `typecheck:core`). It is now generic (`<T extends { id: string }>`), so the literal model shape flows through.

Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green.

Thanks @KooshaPari!
This commit is contained in:
Koosha Paridehpour
2026-09-14 19:21:56 -07:00
committed by GitHub
parent 3a186c1326
commit 38ce44992e
4 changed files with 203 additions and 203 deletions

View File

@@ -1,110 +1,22 @@
// Antigravity CLI (`agy`) model catalog.
//
// These models are pinned from the live `:fetchAvailableModels` endpoint
// (https://daily-cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels) using a
// real `agy` consumer-OAuth token. The public catalog exposes the upstream Gemini 3.7
// Flash ids verbatim; the shared Antigravity executor dispatches them unchanged.
// Models are derived from the shared Antigravity catalog (antigravitySharedModels.ts)
// which is the single source of truth for both agy and antigravity surfaces.
// The `agy` provider reuses the `antigravity` executor/translator (identical backend).
//
// The `agy` provider reuses the `antigravity` executor/translator (identical backend),
// but keeps its own catalog so the CLI and IDE model surfaces can evolve independently.
// Both currently expose the same callable Claude/Gemini/GPT set. Tab-completion models
// (`tab_flash_lite_preview`, `tab_jump_flash_lite_preview`) are intentionally excluded —
// they are not chat-callable.
// To add a model to both surfaces: edit antigravitySharedModels.ts.
// To add a model to CLI only: add an entry to `add` below.
// To hide a model from CLI only: add its id to `remove` below.
export const AGY_PUBLIC_MODELS = Object.freeze([
// Gemini 3.7 Flash tiers. The live endpoint selects High by default and advertises
// all three ids to both the IDE 2.5.5 and CLI 1.1.x clients.
{
id: "gemini-3.7-flash-high",
name: "Gemini 3.7 Flash (High)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-medium",
name: "Gemini 3.7 Flash (Medium)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-low",
name: "Gemini 3.7 Flash (Low)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-tiered",
name: "Gemini 3.7 Flash (Tiered)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
// Gemini 3.1 Pro
{
id: "gemini-pro-agent",
name: "Gemini 3.1 Pro (High)",
contextLength: 1048576,
maxOutputTokens: 65535,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.1-pro-low",
name: "Gemini 3.1 Pro (Low)",
contextLength: 1048576,
maxOutputTokens: 65535,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.1-flash-lite",
name: "Gemini 3.1 Flash Lite",
contextLength: 1048576,
maxOutputTokens: 65535,
toolCalling: true,
},
// Claude (Antigravity backend).
{
id: "claude-opus-4-6-thinking",
name: "Claude Opus 4.6 (Thinking)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "claude-sonnet-4-6",
name: "Claude Sonnet 4.6 (Thinking)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
// GPT-OSS
{
id: "gpt-oss-120b-medium",
name: "GPT-OSS 120B (Medium)",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: true,
toolCalling: true,
},
]);
import {
ANTIGRAVITY_SHARED_MODELS,
buildSurfaceCatalog,
} from "./antigravitySharedModels.ts";
export const AGY_PUBLIC_MODELS = buildSurfaceCatalog(ANTIGRAVITY_SHARED_MODELS, {
add: [], // CLI-only models (currently none)
remove: [], // Models hidden from CLI (currently none)
});
const AGY_PUBLIC_MODEL_IDS = new Set(AGY_PUBLIC_MODELS.map((model) => model.id));
const AGY_NON_CHAT_MODEL_IDS = new Set(["tab_flash_lite_preview", "tab_jump_flash_lite_preview"]);

View File

@@ -1,103 +1,12 @@
export const ANTIGRAVITY_PUBLIC_MODELS = Object.freeze([
// Gemini 3.7 Flash tiers listed by the current official Antigravity model catalog.
// Keep the upstream model ids unchanged so discovery and execution address the same
// models selected by the native client.
{
id: "gemini-3.7-flash-high",
name: "Gemini 3.7 Flash (High)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-medium",
name: "Gemini 3.7 Flash (Medium)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-low",
name: "Gemini 3.7 Flash (Low)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-tiered",
name: "Gemini 3.7 Flash (Tiered)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
// Gemini 3.1 Pro budget tiers. Live streamGenerateContent validation uses
// `gemini-pro-agent` for High; the separately advertised `gemini-3.1-pro-high`
// discovery slot currently returns HTTP 400 and is intentionally not public.
{
id: "gemini-pro-agent",
name: "Gemini 3.1 Pro (High)",
contextLength: 1048576,
maxOutputTokens: 65535,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.1-pro-low",
name: "Gemini 3.1 Pro (Low)",
contextLength: 1048576,
maxOutputTokens: 65535,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.1-flash-lite",
name: "Gemini 3.1 Flash Lite",
contextLength: 1048576,
maxOutputTokens: 65535,
toolCalling: true,
},
// Claude (Antigravity backend). The `agy` provider already ships these from the live
// :fetchAvailableModels probe (see agyModels.ts) and discussion #3184 confirmed they
// are user-callable through the `antigravity` OAuth provider too — same backend.
// `antigravity/claude-opus-4-6-thinking` and `antigravity/claude-sonnet-4-6` both work.
// They are upstream IDs, so no alias remapping is required.
{
id: "claude-opus-4-6-thinking",
name: "Claude Opus 4.6 (Thinking)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "claude-sonnet-4-6",
name: "Claude Sonnet 4.6 (Thinking)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gpt-oss-120b-medium",
name: "GPT-OSS 120B (Medium)",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: true,
toolCalling: true,
},
]);
import {
ANTIGRAVITY_SHARED_MODELS,
buildSurfaceCatalog,
} from "./antigravitySharedModels.ts";
export const ANTIGRAVITY_PUBLIC_MODELS = buildSurfaceCatalog(ANTIGRAVITY_SHARED_MODELS, {
add: [], // IDE-only models (currently none)
remove: [], // Models hidden from IDE (currently none)
});
export const ANTIGRAVITY_MODEL_ALIASES = Object.freeze({
// Gemini 3.7 Flash tiers map to the upstream tiered endpoint model; the thinking

View File

@@ -0,0 +1,118 @@
// Shared Antigravity model catalog — single source of truth for both surfaces.
//
// `agy` (CLI) and `antigravity` (IDE) share the same backend. Adding a model
// to both surfaces = one edit here. Each surface can still diverge via an
// explicit add/remove delta in its own file (currently both deltas are empty).
export const ANTIGRAVITY_SHARED_MODELS = Object.freeze([
// Gemini 3.7 Flash tiers. The live endpoint selects High by default and advertises
// all three ids to both the IDE 2.5.5 and CLI 1.1.x clients.
{
id: "gemini-3.7-flash-high",
name: "Gemini 3.7 Flash (High)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-medium",
name: "Gemini 3.7 Flash (Medium)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-low",
name: "Gemini 3.7 Flash (Low)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.7-flash-tiered",
name: "Gemini 3.7 Flash (Tiered)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
// Gemini 3.1 Pro budget tiers. Live streamGenerateContent validation uses
// `gemini-pro-agent` for High; the separately advertised `gemini-3.1-pro-high`
// discovery slot currently returns HTTP 400 and is intentionally not public.
{
id: "gemini-pro-agent",
name: "Gemini 3.1 Pro (High)",
contextLength: 1048576,
maxOutputTokens: 65535,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.1-pro-low",
name: "Gemini 3.1 Pro (Low)",
contextLength: 1048576,
maxOutputTokens: 65535,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "gemini-3.1-flash-lite",
name: "Gemini 3.1 Flash Lite",
contextLength: 1048576,
maxOutputTokens: 65535,
toolCalling: true,
},
// Claude (Antigravity backend). Discussion #3184 confirmed these are
// user-callable through both the agy and antigravity OAuth providers.
{
id: "claude-opus-4-6-thinking",
name: "Claude Opus 4.6 (Thinking)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
{
id: "claude-sonnet-4-6",
name: "Claude Sonnet 4.6 (Thinking)",
contextLength: 1048576,
maxOutputTokens: 65536,
supportsReasoning: true,
supportsVision: true,
toolCalling: true,
},
// GPT-OSS
{
id: "gpt-oss-120b-medium",
name: "GPT-OSS 120B (Medium)",
contextLength: 131072,
maxOutputTokens: 32768,
supportsReasoning: true,
toolCalling: true,
},
]);
/**
* Build a surface-specific catalog from the shared base plus an explicit delta.
* This preserves the ability for agy/antigravity to diverge in the future while
* keeping today's single-edit maintenance path.
*/
export function buildSurfaceCatalog<T extends { id: string }>(
base: readonly T[],
delta: { add?: readonly T[]; remove?: readonly string[] } = {}
): readonly T[] {
const removed = new Set(delta.remove ?? []);
const added = delta.add ?? [];
const filtered = base.filter((m) => !removed.has(m.id));
return Object.freeze([...filtered, ...added]);
}

View File

@@ -0,0 +1,61 @@
// #12724 — agy and antigravity catalogs must be derived from the shared base
// and stay identical until a per-surface delta is introduced.
import test from "node:test";
import assert from "node:assert/strict";
import {
ANTIGRAVITY_SHARED_MODELS,
buildSurfaceCatalog,
} from "../../open-sse/config/antigravitySharedModels.ts";
import { AGY_PUBLIC_MODELS } from "../../open-sse/config/agyModels.ts";
import { ANTIGRAVITY_PUBLIC_MODELS } from "../../open-sse/config/antigravityModelAliases.ts";
const serial = { concurrency: false };
test("#12724 — shared base has exactly 10 models", serial, () => {
assert.equal(ANTIGRAVITY_SHARED_MODELS.length, 10);
});
test("#12724 — agy catalog equals shared base (empty deltas)", serial, () => {
assert.deepEqual([...AGY_PUBLIC_MODELS], [...ANTIGRAVITY_SHARED_MODELS]);
});
test("#12724 — antigravity catalog equals shared base (empty deltas)", serial, () => {
assert.deepEqual([...ANTIGRAVITY_PUBLIC_MODELS], [...ANTIGRAVITY_SHARED_MODELS]);
});
test("#12724 — agy and antigravity catalogs are byte-identical", serial, () => {
assert.deepEqual([...AGY_PUBLIC_MODELS], [...ANTIGRAVITY_PUBLIC_MODELS]);
});
test("#12724 — buildSurfaceCatalog remove delta works", serial, () => {
const subset = buildSurfaceCatalog(ANTIGRAVITY_SHARED_MODELS, {
remove: ["gemini-3.7-flash-high"],
});
assert.equal(subset.length, 9);
assert.ok(!subset.some((m) => m.id === "gemini-3.7-flash-high"));
});
test("#12724 — buildSurfaceCatalog add delta works", serial, () => {
const extended = buildSurfaceCatalog(ANTIGRAVITY_SHARED_MODELS, {
add: [{ id: "custom-model-v1", name: "Custom Model" }],
});
assert.equal(extended.length, 11);
assert.ok(extended.some((m) => m.id === "custom-model-v1"));
});
test("#12724 — buildSurfaceCatalog with both add and remove", serial, () => {
const mixed = buildSurfaceCatalog(ANTIGRAVITY_SHARED_MODELS, {
add: [{ id: "custom-model-v1", name: "Custom Model" }],
remove: ["gemini-3.7-flash-high", "gemini-3.7-flash-medium"],
});
assert.equal(mixed.length, 9); // 10 - 2 + 1
assert.ok(!mixed.some((m) => m.id === "gemini-3.7-flash-high"));
assert.ok(mixed.some((m) => m.id === "custom-model-v1"));
});
test("#12724 — buildSurfaceCatalog returns frozen array", serial, () => {
const result = buildSurfaceCatalog(ANTIGRAVITY_SHARED_MODELS);
assert.ok(Object.isFrozen(result));
});