Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions packages/antigravity-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ uses a stub for its Node-only callback surface. Explicit
browser authorization uses a Node HTTP callback listener on localhost port 36742.
There are no Core imports, SDK transport wrappers, or proxy servers.

- Provider/model transforms expose the shipped Gemini 3.8 variants and older
subscription variants without overriding the user's default model. Fallback
requests without a model ID resolve to `gemini-3.8-flash-high`.
- Provider/model transforms expose the shipped Gemini 4 Argon, Gemini 3.8 Flash,
and older subscription variants without overriding the user's default model.
Fallback requests without a model ID resolve to `gemini-3.8-flash-high`.
- Access-only imports activate using shipped models without discovery requests.
Normal OAuth connections may discover model enums, with shipped defaults if
discovery is unavailable.
Normal OAuth connections may discover model enums and suppress shipped variants
explicitly denied by catalog entries (such as `gemini-4-argon-disabled` or a
disabled shipped model ID), with shipped defaults if discovery is unavailable.
- Native Gemini requests resolve the active credential afresh, then use the
Cloud Code envelope and `metadata.projectId`. Only responses belonging to
rewritten requests are unwrapped. API-key connections are unchanged.
Expand Down
8 changes: 6 additions & 2 deletions packages/antigravity-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export const GoogleAntigravityPlugin = Plugin.define({
const mapped = new WeakSet<Request>()
let subscription = false
let modelEnums = new Map(GoogleAntigravityWire.modelEnumDefaults)
let disabledModels: ReadonlySet<string> = new Set<string>()
const load = Effect.fn("GoogleAntigravityPlugin.load")(function* () {
const connection = yield* ctx.integration.connection.active(GoogleAntigravityOAuth.integrationID)
// Keep login and reload subscriptions alive when stored auth expires. Inference
Expand All @@ -92,6 +93,7 @@ export const GoogleAntigravityPlugin = Plugin.define({
: undefined
subscription = GoogleAntigravityOAuth.isSubscription(resolved)
modelEnums = new Map(GoogleAntigravityWire.modelEnumDefaults)
disabledModels = new Set<string>()
// Imported access-only credentials must activate without discovery network I/O.
if (!subscription || resolved?.type !== "oauth" || resolved.metadata?.shuvcodeAuthImport === "access-only") return
const projectId = GoogleAntigravityOAuth.projectId(resolved.metadata)
Expand All @@ -100,7 +102,9 @@ export const GoogleAntigravityPlugin = Plugin.define({
try: (signal) => GoogleAntigravityOAuth.fetchAvailableModels(resolved.access, projectId, undefined, signal),
catch: (cause) => cause,
}).pipe(Effect.orElseSucceed(() => undefined))
if (models) modelEnums = GoogleAntigravityWire.modelEnumsFrom(GoogleAntigravityWire.filterGoogleModels(models))
if (!models) return
disabledModels = GoogleAntigravityWire.disabledModelsFrom(models)
modelEnums = GoogleAntigravityWire.modelEnumsFrom(models)
})
yield* ctx.integration.transform((draft) => {
draft.update(GoogleAntigravityOAuth.integrationID, (integration) => {
Expand All @@ -110,7 +114,7 @@ export const GoogleAntigravityPlugin = Plugin.define({
})
yield* load()
yield* ctx.provider.transform((evt) => GoogleAntigravityWire.applyProvider(evt, subscription))
yield* ctx.model.transform((evt) => GoogleAntigravityWire.applyModels(evt, subscription))
yield* ctx.model.transform((evt) => GoogleAntigravityWire.applyModels(evt, subscription, disabledModels))
yield* ctx.session.hook(
"http.request",
(evt) =>
Expand Down
2 changes: 2 additions & 0 deletions packages/antigravity-plugin/src/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export type CatalogModel = {
modelEnum?: string
provider?: string
internal?: boolean
disabled?: boolean
recommended?: boolean
}

Expand Down Expand Up @@ -284,6 +285,7 @@ export function parseCatalogModels(payload: unknown): CatalogModel[] {
modelEnum: stringField(model.model) ?? stringField(model.model_enum) ?? stringField(model.modelEnum),
provider: stringField(model.modelProvider) ?? stringField(model.model_provider),
internal: model.isInternal === true || model.is_internal === true,
disabled: model.disabled === true,
recommended: model.recommended === true,
},
]
Expand Down
54 changes: 45 additions & 9 deletions packages/antigravity-plugin/src/wire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ export type ShippedModel = {
}

export const shippedModels: readonly ShippedModel[] = [
{ id: "gemini-3.8-flash-high", name: "Gemini 3.8 Flash (High)" },
{ id: "gemini-3.8-flash-medium", name: "Gemini 3.8 Flash (Medium)" },
{ id: "gemini-3.8-flash-low", name: "Gemini 3.8 Flash (Low)" },
{ id: "gemini-4-argon-high", name: "Gemini 4 Argon (High)", modelEnum: "MODEL_PLACEHOLDER_M417" },
{ id: "gemini-4-argon-medium", name: "Gemini 4 Argon (Medium)", modelEnum: "MODEL_PLACEHOLDER_M418" },
{ id: "gemini-4-argon-low", name: "Gemini 4 Argon (Low)", modelEnum: "MODEL_PLACEHOLDER_M419" },
{ id: "gemini-3.8-flash-high", name: "Gemini 3.8 Flash (High)", modelEnum: "MODEL_PLACEHOLDER_M318" },
{ id: "gemini-3.8-flash-medium", name: "Gemini 3.8 Flash (Medium)", modelEnum: "MODEL_PLACEHOLDER_M319" },
{ id: "gemini-3.8-flash-low", name: "Gemini 3.8 Flash (Low)", modelEnum: "MODEL_PLACEHOLDER_M320" },
{ id: "gemini-3.7-flash-high", name: "Gemini 3.7 Flash (High)", modelEnum: "MODEL_PLACEHOLDER_M298" },
{ id: "gemini-3.7-flash-medium", name: "Gemini 3.7 Flash (Medium)", modelEnum: "MODEL_PLACEHOLDER_M299" },
{ id: "gemini-3.7-flash-low", name: "Gemini 3.7 Flash (Low)", modelEnum: "MODEL_PLACEHOLDER_M300" },
Expand Down Expand Up @@ -59,12 +62,12 @@ export const isBlockedModelID = (id: string) => {
const lower = id.toLowerCase()
if (lower.startsWith("claude") || lower.startsWith("gpt") || lower.includes("gpt-oss")) return true
if (lower.startsWith("tab_") || lower.startsWith("chat_")) return true
if (lower.includes("image")) return true
if (lower.includes("image") || lower.endsWith("-disabled")) return true
return false
}

export const isGoogleCatalogModel = (model: GoogleAntigravityOAuth.CatalogModel) => {
if (model.internal) return false
if (model.internal || model.disabled) return false
if (isBlockedModelID(model.id)) return false
if (model.provider && model.provider !== "MODEL_PROVIDER_GOOGLE") return false
return true
Expand All @@ -73,11 +76,43 @@ export const isGoogleCatalogModel = (model: GoogleAntigravityOAuth.CatalogModel)
export const filterGoogleModels = (models: readonly GoogleAntigravityOAuth.CatalogModel[]) =>
models.filter(isGoogleCatalogModel)

export function disabledModelsFrom(models: readonly GoogleAntigravityOAuth.CatalogModel[]): ReadonlySet<string> {
const disabled = new Set<string>()
for (const model of models) {
if (model.internal) continue
if (model.provider && model.provider !== "MODEL_PROVIDER_GOOGLE") continue
const suffix = model.id.toLowerCase().endsWith("-disabled")
if (!model.disabled && !suffix) continue
const base = suffix ? model.id.slice(0, -"-disabled".length) : model.id
if (!base) continue
disabled.add(model.id)
disabled.add(base)
for (const shipped of shippedModels) {
const api = catalogID(shipped.id)
if (
shipped.id === model.id ||
shipped.id === base ||
shipped.id.startsWith(`${base}-`) ||
api === model.id ||
api === base ||
api.startsWith(`${base}-`)
) {
disabled.add(shipped.id)
}
}
}
return disabled
}

export function modelEnumsFrom(models: readonly GoogleAntigravityOAuth.CatalogModel[]) {
const disabled = disabledModelsFrom(models)
const next = new Map(modelEnumDefaults)
for (const model of models) {
for (const model of filterGoogleModels(models)) {
if (model.modelEnum) next.set(model.id, model.modelEnum)
}
for (const id of disabled) {
next.delete(id)
}
return next
}

Expand Down Expand Up @@ -423,18 +458,19 @@ export function applyProvider(evt: ProviderEditor, active: boolean) {
})
}

export function applyModels(evt: ModelEditor, active: boolean) {
export function applyModels(evt: ModelEditor, active: boolean, disabled?: ReadonlySet<string>) {
if (!active) return
// Antigravity model ids are absent from the upstream catalog, so synthesized
// entries receive the generic fallback limits (200k/32k), not real Gemini
// limits. Only a real catalog entry's limit wins over the Flash numbers.
const cataloged = new Set(evt.provider.get(googleProviderID)?.models.keys() ?? [])
for (const model of shippedModels) {
const denied = disabled?.has(model.id) === true || disabled?.has(catalogID(model.id)) === true
evt.update(googleProviderID, model.id, (draft) => {
draft.modelID = Model.ID.make(model.apiID ?? model.id)
draft.name = model.name
draft.cost = []
draft.enabled = true
draft.enabled = !denied
draft.status = "active"
if (!cataloged.has(model.id)) draft.limit = { ...FLASH_LIMIT }
draft.capabilities = { tools: true, input: ["text", "image"], output: ["text"] }
Expand All @@ -444,7 +480,7 @@ export function applyModels(evt: ModelEditor, active: boolean) {
const item = evt.provider.get(googleProviderID)
if (item) {
for (const id of item.models.keys()) {
if (shippedIDs.has(id)) continue
if (shippedIDs.has(id) && !disabled?.has(id) && !disabled?.has(catalogID(id))) continue
evt.update(googleProviderID, id, (draft) => {
draft.enabled = false
})
Expand Down
60 changes: 36 additions & 24 deletions packages/antigravity-plugin/test/error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,29 +73,32 @@ describe("Cloud Code 429 bodies", () => {
})
})

test("surfaces remainingFraction and resetTime and marks an empty 5h bucket as quota", () => {
const explained = GoogleAntigravityWire.explainCloudCodeError({
error: {
status: "RESOURCE_EXHAUSTED",
message: "Resource has been exhausted (e.g. check quota).",
reason: "RATE_LIMIT_EXCEEDED",
},
summary,
httpStatus: 429,
modelID: "gemini-3.8-flash-high",
})
expect(explained?.quota).toBe(true)
expect(explained?.message).toContain("RESOURCE_EXHAUSTED: Resource has been exhausted (e.g. check quota).")
expect(explained?.message).toContain("gemini:5h remainingFraction=0 resetTime=2026-08-14T18:00:00Z")
expect(explained?.message).toContain("gemini:weekly remainingFraction=0.4 resetTime=2026-08-20T00:00:00Z")
expect(JSON.parse(explained?.body ?? "{}")).toMatchObject({
error: {
code: 429,
status: "RESOURCE_EXHAUSTED",
details: [{ reason: "QUOTA_EXHAUSTED" }],
},
})
})
test.each(["gemini-4-argon-high", "gemini-4-argon-medium", "gemini-4-argon-low", "gemini-3.8-flash-high"])(
"surfaces remainingFraction and resetTime and marks an empty 5h bucket as quota for %s",
(modelID) => {
const explained = GoogleAntigravityWire.explainCloudCodeError({
error: {
status: "RESOURCE_EXHAUSTED",
message: "Resource has been exhausted (e.g. check quota).",
reason: "RATE_LIMIT_EXCEEDED",
},
summary,
httpStatus: 429,
modelID,
})
expect(explained?.quota).toBe(true)
expect(explained?.message).toContain("RESOURCE_EXHAUSTED: Resource has been exhausted (e.g. check quota).")
expect(explained?.message).toContain("gemini:5h remainingFraction=0 resetTime=2026-08-14T18:00:00Z")
expect(explained?.message).toContain("gemini:weekly remainingFraction=0.4 resetTime=2026-08-20T00:00:00Z")
expect(JSON.parse(explained?.body ?? "{}")).toMatchObject({
error: {
code: 429,
status: "RESOURCE_EXHAUSTED",
details: [{ reason: "QUOTA_EXHAUSTED" }],
},
})
},
)

test("keeps a short throttle retryable when the 5h bucket still has quota", () => {
const explained = GoogleAntigravityWire.explainCloudCodeError({
Expand All @@ -113,7 +116,7 @@ describe("Cloud Code 429 bodies", () => {
],
},
httpStatus: 429,
modelID: "gemini-3.8-flash-high",
modelID: "gemini-4-argon-high",
})
expect(explained?.quota).toBe(false)
expect(explained?.message).toContain("gemini:5h remainingFraction=0.5 resetTime=2026-08-14T18:00:00Z")
Expand All @@ -123,16 +126,25 @@ describe("Cloud Code 429 bodies", () => {
})

test.each([
{
modelID: "gemini-4-argon-high",
groups: [{ displayName: "Claude + GPT", buckets: [{ window: "5h", remainingFraction: 0 }] }],
},
{
modelID: "gemini-3.8-flash-high",
groups: [{ displayName: "Claude + GPT", buckets: [{ window: "5h", remainingFraction: 0 }] }],
},
{ modelID: "claude-sonnet-4-6", groups: summary.groups },
{ modelID: "gpt-oss-120b", groups: summary.groups },
{
modelID: "gemini-4-argon-high",
groups: [{ displayName: "Unknown models", buckets: [{ window: "5h", remainingFraction: 0 }] }],
},
{
modelID: "gemini-3.8-flash-high",
groups: [{ displayName: "Unknown models", buckets: [{ window: "5h", remainingFraction: 0 }] }],
},
{ modelID: "gemini-4-argon-high", groups: [] },
{ modelID: "gemini-3.8-flash-high", groups: [] },
{ modelID: "unknown-model", groups: summary.groups },
{ modelID: undefined, groups: summary.groups },
Expand Down
Loading
Loading