diff --git a/open-sse/config/agyModels.ts b/open-sse/config/agyModels.ts index d43fcc2531..28c1d69f8e 100644 --- a/open-sse/config/agyModels.ts +++ b/open-sse/config/agyModels.ts @@ -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"]); diff --git a/open-sse/config/antigravityModelAliases.ts b/open-sse/config/antigravityModelAliases.ts index 3cf9094cbb..30bdd01c47 100644 --- a/open-sse/config/antigravityModelAliases.ts +++ b/open-sse/config/antigravityModelAliases.ts @@ -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 diff --git a/open-sse/config/antigravitySharedModels.ts b/open-sse/config/antigravitySharedModels.ts new file mode 100644 index 0000000000..185472ca8b --- /dev/null +++ b/open-sse/config/antigravitySharedModels.ts @@ -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( + 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]); +} diff --git a/tests/unit/agy-antigravity-shared-catalog-12724.test.ts b/tests/unit/agy-antigravity-shared-catalog-12724.test.ts new file mode 100644 index 0000000000..8402be0834 --- /dev/null +++ b/tests/unit/agy-antigravity-shared-catalog-12724.test.ts @@ -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)); +});