refactor!: remove google-antigravity provider support

This commit is contained in:
Peter Steinberger
2026-02-23 05:20:14 +01:00
parent 558a0137bb
commit 382fe8009a
41 changed files with 43 additions and 2373 deletions

View File

@@ -62,7 +62,7 @@ const AUTH_CHOICE_GROUP_DEFS: {
value: "google",
label: "Google",
hint: "Gemini API key + OAuth",
choices: ["gemini-api-key", "google-antigravity", "google-gemini-cli"],
choices: ["gemini-api-key", "google-gemini-cli"],
},
{
value: "xai",
@@ -254,11 +254,6 @@ const BASE_AUTH_CHOICE_OPTIONS: ReadonlyArray<AuthChoiceOption> = [
hint: "Uses GitHub device flow",
},
{ value: "gemini-api-key", label: "Google Gemini API key" },
{
value: "google-antigravity",
label: "Google Antigravity OAuth",
hint: "Uses the bundled Antigravity auth plugin",
},
{
value: "google-gemini-cli",
label: "Google Gemini CLI OAuth",

View File

@@ -1,14 +0,0 @@
import type { ApplyAuthChoiceParams, ApplyAuthChoiceResult } from "./auth-choice.apply.js";
import { applyAuthChoicePluginProvider } from "./auth-choice.apply.plugin-provider.js";
export async function applyAuthChoiceGoogleAntigravity(
params: ApplyAuthChoiceParams,
): Promise<ApplyAuthChoiceResult | null> {
return await applyAuthChoicePluginProvider(params, {
authChoice: "google-antigravity",
pluginId: "google-antigravity-auth",
providerId: "google-antigravity",
methodId: "oauth",
label: "Google Antigravity",
});
}

View File

@@ -6,7 +6,6 @@ import { applyAuthChoiceApiProviders } from "./auth-choice.apply.api-providers.j
import { applyAuthChoiceBytePlus } from "./auth-choice.apply.byteplus.js";
import { applyAuthChoiceCopilotProxy } from "./auth-choice.apply.copilot-proxy.js";
import { applyAuthChoiceGitHubCopilot } from "./auth-choice.apply.github-copilot.js";
import { applyAuthChoiceGoogleAntigravity } from "./auth-choice.apply.google-antigravity.js";
import { applyAuthChoiceGoogleGeminiCli } from "./auth-choice.apply.google-gemini-cli.js";
import { applyAuthChoiceMiniMax } from "./auth-choice.apply.minimax.js";
import { applyAuthChoiceOAuth } from "./auth-choice.apply.oauth.js";
@@ -44,7 +43,6 @@ export async function applyAuthChoice(
applyAuthChoiceApiProviders,
applyAuthChoiceMiniMax,
applyAuthChoiceGitHubCopilot,
applyAuthChoiceGoogleAntigravity,
applyAuthChoiceGoogleGeminiCli,
applyAuthChoiceCopilotProxy,
applyAuthChoiceQwenPortal,

View File

@@ -18,7 +18,6 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
"moonshot-api-key-cn": "moonshot",
"kimi-code-api-key": "kimi-coding",
"gemini-api-key": "google",
"google-antigravity": "google-antigravity",
"google-gemini-cli": "google-gemini-cli",
"mistral-api-key": "mistral",
"zai-api-key": "zai",

View File

@@ -13,24 +13,24 @@ function makeProvider(params: { id: string; label?: string; aliases?: string[] }
describe("resolveRequestedLoginProviderOrThrow", () => {
it("returns null when no provider was requested", () => {
const providers = [makeProvider({ id: "google-antigravity" })];
const providers = [makeProvider({ id: "google-gemini-cli" })];
const result = resolveRequestedLoginProviderOrThrow(providers, undefined);
expect(result).toBeNull();
});
it("resolves requested provider by id", () => {
const providers = [
makeProvider({ id: "google-antigravity" }),
makeProvider({ id: "google-gemini-cli" }),
makeProvider({ id: "qwen-portal" }),
];
const result = resolveRequestedLoginProviderOrThrow(providers, "google-antigravity");
expect(result?.id).toBe("google-antigravity");
const result = resolveRequestedLoginProviderOrThrow(providers, "google-gemini-cli");
expect(result?.id).toBe("google-gemini-cli");
});
it("resolves requested provider by alias", () => {
const providers = [makeProvider({ id: "google-antigravity", aliases: ["antigravity"] })];
const result = resolveRequestedLoginProviderOrThrow(providers, "antigravity");
expect(result?.id).toBe("google-antigravity");
const providers = [makeProvider({ id: "google-gemini-cli", aliases: ["gemini-cli"] })];
const result = resolveRequestedLoginProviderOrThrow(providers, "gemini-cli");
expect(result?.id).toBe("google-gemini-cli");
});
it("throws when requested provider is not loaded", () => {

View File

@@ -200,30 +200,6 @@ describe("models list/status", () => {
return JSON.parse(String(runtime.log.mock.calls[0]?.[0]));
}
async function runAvailabilityFallbackCase(params: {
setup?: () => void;
expectedErrorDetail: string;
}) {
configureGoogleAntigravityModel("claude-opus-4-6-thinking");
enableGoogleAntigravityAuthProfile();
const runtime = makeRuntime();
modelRegistryState.models = [
makeGoogleAntigravityTemplate("claude-opus-4-5-thinking", "Claude Opus 4.5 Thinking"),
];
modelRegistryState.available = [];
params.setup?.();
await modelsListCommand({ json: true }, runtime);
expect(runtime.error).toHaveBeenCalledTimes(1);
expect(runtime.error.mock.calls[0]?.[0]).toContain("falling back to auth heuristics");
expect(runtime.error.mock.calls[0]?.[0]).toContain(params.expectedErrorDetail);
const payload = parseJsonLog(runtime);
expect(payload.models[0]?.key).toBe("google-antigravity/claude-opus-4-6-thinking");
expect(payload.models[0]?.missing).toBe(false);
expect(payload.models[0]?.available).toBe(true);
}
async function expectZaiProviderFilter(provider: string) {
setDefaultZaiRegistry();
const runtime = makeRuntime();
@@ -242,66 +218,6 @@ describe("models list/status", () => {
modelRegistryState.available = available ? [ZAI_MODEL, OPENAI_MODEL] : [];
}
function setupGoogleAntigravityTemplateCase(params: {
configuredModelId: string;
templateId: string;
templateName: string;
available?: boolean;
}) {
configureGoogleAntigravityModel(params.configuredModelId);
const template = makeGoogleAntigravityTemplate(params.templateId, params.templateName);
modelRegistryState.models = [template];
modelRegistryState.available = params.available ? [template] : [];
return template;
}
async function runGoogleAntigravityListCase(params: {
configuredModelId: string;
templateId: string;
templateName: string;
available?: boolean;
withAuthProfile?: boolean;
}) {
setupGoogleAntigravityTemplateCase(params);
if (params.withAuthProfile) {
enableGoogleAntigravityAuthProfile();
}
const runtime = makeRuntime();
await modelsListCommand({ json: true }, runtime);
return parseJsonLog(runtime);
}
const GOOGLE_ANTIGRAVITY_OPUS_46_CASES = [
{
name: "thinking",
configuredModelId: "claude-opus-4-6-thinking",
templateId: "claude-opus-4-5-thinking",
templateName: "Claude Opus 4.5 Thinking",
expectedKey: "google-antigravity/claude-opus-4-6-thinking",
},
{
name: "non-thinking",
configuredModelId: "claude-opus-4-6",
templateId: "claude-opus-4-5",
templateName: "Claude Opus 4.5",
expectedKey: "google-antigravity/claude-opus-4-6",
},
] as const;
function expectAntigravityModel(
payload: Record<string, unknown>,
params: { key: string; available: boolean; includesTags?: boolean },
) {
const model = (payload.models as Array<Record<string, unknown>>)[0] ?? {};
expect(model.key).toBe(params.key);
expect(model.missing).toBe(false);
expect(model.available).toBe(params.available);
if (params.includesTags) {
expect(model.tags).toContain("default");
expect(model.tags).toContain("configured");
}
}
beforeAll(async () => {
({ modelsListCommand } = await import("./models/list.list-command.js"));
({ loadModelRegistry, toModelRow } = await import("./models/list.registry.js"));
@@ -357,177 +273,6 @@ describe("models list/status", () => {
expect(payload.models[0]?.available).toBe(false);
});
it.each(GOOGLE_ANTIGRAVITY_OPUS_46_CASES)(
"models list resolves antigravity opus 4.6 $name from 4.5 template",
async ({ configuredModelId, templateId, templateName, expectedKey }) => {
const payload = await runGoogleAntigravityListCase({
configuredModelId,
templateId,
templateName,
});
expectAntigravityModel(payload, {
key: expectedKey,
available: false,
includesTags: true,
});
},
);
it.each(GOOGLE_ANTIGRAVITY_OPUS_46_CASES)(
"models list marks synthesized antigravity opus 4.6 $name as available when template is available",
async ({ configuredModelId, templateId, templateName, expectedKey }) => {
const payload = await runGoogleAntigravityListCase({
configuredModelId,
templateId,
templateName,
available: true,
});
expectAntigravityModel(payload, {
key: expectedKey,
available: true,
});
},
);
it.each([
{
name: "high",
configuredModelId: "gemini-3-1-pro-high",
templateId: "gemini-3-pro-high",
templateName: "Gemini 3 Pro High",
expectedKey: "google-antigravity/gemini-3-1-pro-high",
},
{
name: "low",
configuredModelId: "gemini-3-1-pro-low",
templateId: "gemini-3-pro-low",
templateName: "Gemini 3 Pro Low",
expectedKey: "google-antigravity/gemini-3-1-pro-low",
},
] as const)(
"models list resolves antigravity gemini 3.1 $name from gemini 3 template",
async ({ configuredModelId, templateId, templateName, expectedKey }) => {
const payload = await runGoogleAntigravityListCase({
configuredModelId,
templateId,
templateName,
});
expectAntigravityModel(payload, {
key: expectedKey,
available: false,
includesTags: true,
});
},
);
it.each([
{
name: "high",
configuredModelId: "gemini-3-1-pro-high",
templateId: "gemini-3-pro-high",
templateName: "Gemini 3 Pro High",
expectedKey: "google-antigravity/gemini-3-1-pro-high",
},
{
name: "low",
configuredModelId: "gemini-3-1-pro-low",
templateId: "gemini-3-pro-low",
templateName: "Gemini 3 Pro Low",
expectedKey: "google-antigravity/gemini-3-1-pro-low",
},
] as const)(
"models list marks synthesized antigravity gemini 3.1 $name as available when template is available",
async ({ configuredModelId, templateId, templateName, expectedKey }) => {
const payload = await runGoogleAntigravityListCase({
configuredModelId,
templateId,
templateName,
available: true,
});
expectAntigravityModel(payload, {
key: expectedKey,
available: true,
});
},
);
it.each([
{
name: "high",
configuredModelId: "gemini-3.1-pro-high",
templateId: "gemini-3-pro-high",
templateName: "Gemini 3 Pro High",
expectedKey: "google-antigravity/gemini-3.1-pro-high",
},
{
name: "low",
configuredModelId: "gemini-3.1-pro-low",
templateId: "gemini-3-pro-low",
templateName: "Gemini 3 Pro Low",
expectedKey: "google-antigravity/gemini-3.1-pro-low",
},
] as const)(
"models list marks dot-notation antigravity gemini 3.1 $name as available when template is available",
async ({ configuredModelId, templateId, templateName, expectedKey }) => {
const payload = await runGoogleAntigravityListCase({
configuredModelId,
templateId,
templateName,
available: true,
});
expectAntigravityModel(payload, {
key: expectedKey,
available: true,
});
},
);
it("models list prefers registry availability over provider auth heuristics", async () => {
const payload = await runGoogleAntigravityListCase({
configuredModelId: "claude-opus-4-6-thinking",
templateId: "claude-opus-4-5-thinking",
templateName: "Claude Opus 4.5 Thinking",
withAuthProfile: true,
});
expectAntigravityModel(payload, {
key: "google-antigravity/claude-opus-4-6-thinking",
available: false,
});
listProfilesForProvider.mockReturnValue([]);
});
it("models list falls back to auth heuristics when registry availability is unavailable", async () => {
await runAvailabilityFallbackCase({
setup: () => {
modelRegistryState.getAvailableError = Object.assign(
new Error("availability unsupported: getAvailable failed"),
{ code: "MODEL_AVAILABILITY_UNAVAILABLE" },
);
},
expectedErrorDetail: "getAvailable failed",
});
});
it("models list falls back to auth heuristics when getAvailable returns invalid shape", async () => {
await runAvailabilityFallbackCase({
setup: () => {
modelRegistryState.available = { bad: true } as unknown as Array<Record<string, unknown>>;
},
expectedErrorDetail: "non-array value",
});
});
it("models list falls back to auth heuristics when getAvailable throws", async () => {
await runAvailabilityFallbackCase({
setup: () => {
modelRegistryState.getAvailableError = new Error(
"availability unsupported: getAvailable failed",
);
},
expectedErrorDetail: "availability unsupported: getAvailable failed",
});
});
it("models list does not treat availability-unavailable code as discovery fallback", async () => {
configureGoogleAntigravityModel("claude-opus-4-6-thinking");
modelRegistryState.getAllError = Object.assign(new Error("model discovery failed"), {

View File

@@ -7,11 +7,6 @@ import {
resolveAwsSdkEnvVarName,
resolveEnvApiKey,
} from "../../agents/model-auth.js";
import {
ANTIGRAVITY_GEMINI_31_FORWARD_COMPAT_CANDIDATES,
ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES,
resolveForwardCompatModel,
} from "../../agents/model-forward-compat.js";
import { ensureOpenClawModelsJson } from "../../agents/models-config.js";
import { ensurePiAuthJsonFromAuthProfiles } from "../../agents/pi-auth-json.js";
import type { ModelRegistry } from "../../agents/pi-model-discovery.js";
@@ -106,23 +101,13 @@ export async function loadModelRegistry(cfg: OpenClawConfig) {
await ensurePiAuthJsonFromAuthProfiles(agentDir);
const authStorage = discoverAuthStorage(agentDir);
const registry = discoverModels(authStorage, agentDir);
const appended = appendAntigravityForwardCompatModels(registry.getAll(), registry);
const models = appended.models;
const synthesizedForwardCompat = appended.synthesizedForwardCompat;
const models = registry.getAll();
let availableKeys: Set<string> | undefined;
let availabilityErrorMessage: string | undefined;
try {
const availableModels = loadAvailableModels(registry);
availableKeys = new Set(availableModels.map((model) => modelKey(model.provider, model.id)));
for (const synthesized of synthesizedForwardCompat) {
if (hasAvailableTemplate(availableKeys, synthesized.templatePrefixes)) {
availableKeys.add(synthesized.key);
for (const aliasKey of synthesized.availabilityAliasKeys) {
availableKeys.add(aliasKey);
}
}
}
} catch (err) {
if (!shouldFallbackToAuthHeuristics(err)) {
throw err;
@@ -138,60 +123,6 @@ export async function loadModelRegistry(cfg: OpenClawConfig) {
return { registry, models, availableKeys, availabilityErrorMessage };
}
type SynthesizedForwardCompat = {
key: string;
templatePrefixes: readonly string[];
availabilityAliasKeys: readonly string[];
};
function appendAntigravityForwardCompatModels(
models: Model<Api>[],
modelRegistry: ModelRegistry,
): { models: Model<Api>[]; synthesizedForwardCompat: SynthesizedForwardCompat[] } {
const nextModels = [...models];
const synthesizedForwardCompat: SynthesizedForwardCompat[] = [];
const candidates = [
...ANTIGRAVITY_OPUS_46_FORWARD_COMPAT_CANDIDATES,
...ANTIGRAVITY_GEMINI_31_FORWARD_COMPAT_CANDIDATES,
];
for (const candidate of candidates) {
const key = modelKey("google-antigravity", candidate.id);
const hasForwardCompat = nextModels.some((model) => modelKey(model.provider, model.id) === key);
if (hasForwardCompat) {
continue;
}
const fallback = resolveForwardCompatModel("google-antigravity", candidate.id, modelRegistry);
if (!fallback) {
continue;
}
nextModels.push(fallback);
synthesizedForwardCompat.push({
key,
templatePrefixes: candidate.templatePrefixes,
availabilityAliasKeys: candidate.availabilityAliasIds.map((id) =>
modelKey("google-antigravity", id),
),
});
}
return { models: nextModels, synthesizedForwardCompat };
}
function hasAvailableTemplate(
availableKeys: Set<string>,
templatePrefixes: readonly string[],
): boolean {
for (const key of availableKeys) {
if (templatePrefixes.some((prefix) => key.startsWith(prefix))) {
return true;
}
}
return false;
}
export function toModelRow(params: {
model?: Model<Api>;
key: string;

View File

@@ -26,7 +26,6 @@ export type AuthChoice =
| "codex-cli"
| "apiKey"
| "gemini-api-key"
| "google-antigravity"
| "google-gemini-cli"
| "zai-api-key"
| "zai-coding-global"