Files
Mohamed Boudra 7b7685678d feat(antigravity): add Antigravity provider plugin (#5714)
* test(antigravity): record agy stream-json fixtures

* feat(antigravity): add Antigravity provider plugin

* fix(antigravity): address Phase 1 review findings

* test(antigravity): record parity discovery blockers

* feat(antigravity): support image prompts and daemon e2e

* docs(antigravity): document provider setup and limitations

* test(antigravity): respect Windows interrupt semantics

* docs(plugins): avoid duplicate provider override guidance

* fix(antigravity): report fresh denials and correct mode metadata

Native denial results accumulate within a driver process. Track reported occurrences until respawn so later text-only turns do not repeat notices, while new denials of the same action remain visible.

* fix(antigravity): use the official Antigravity mark
2026-09-30 14:09:07 +01:00

97 lines
3.3 KiB
TypeScript

import type {
ProviderCatalog,
ProviderLaunch,
ProviderModel,
ProviderMode,
ProviderStatus,
} from "@getpaseo/plugin/server/provider";
import { probe } from "./process.js";
import { AntigravityError } from "./wire.js";
const modes: readonly ProviderMode[] = [
{
id: "default",
icon: "Shield",
colorTier: "moderate",
label: "Default",
description: "Workspace reads and edits are allowed; shell commands are denied.",
},
{
id: "accept-edits",
icon: "ShieldPlus",
colorTier: "moderate",
label: "Accept edits",
description:
"Native accept-edits mode. Workspace edits are allowed; shell commands are denied.",
},
{
id: "plan",
icon: "ShieldEllipsis",
colorTier: "planning",
label: "Plan",
description:
"Native plan mode permits workspace edits. Shell commands are denied; this is not read-only.",
},
{
id: "full-access",
icon: "ShieldOff",
colorTier: "dangerous",
label: "Full access",
description: "Skip native permission checks, including for shell commands.",
isUnattended: true,
},
];
export async function getCatalog(launch: ProviderLaunch, cwd?: string): Promise<ProviderCatalog> {
const output = await probe({ launch, args: ["models"], cwd });
const models: ProviderModel[] = [];
for (const line of output.trim().split(/\r?\n/)) {
const [id, label, ...extra] = line.split("\t");
if (!id || !label || extra.length > 0)
throw new AntigravityError("Invalid `agy models` output", "INVALID_CATALOG");
if (models.some((model) => model.id === id))
throw new AntigravityError(`Duplicate Antigravity model: ${id}`, "INVALID_CATALOG");
models.push({ id, label });
}
return { models, modes, thinkingOptions: [], defaultMode: "default" };
}
export async function getStatus(launch: ProviderLaunch): Promise<ProviderStatus> {
try {
const version = (await probe({ launch, args: ["--version"] })).trim();
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
if (!match)
return {
available: false,
diagnostic: "Could not read the Antigravity version. Update `agy` to 1.1.15 or later.",
};
const [, major, minor, patch] = match.map(Number);
const supported = major > 1 || (major === 1 && (minor > 1 || (minor === 1 && patch >= 15)));
if (!supported)
return {
available: false,
diagnostic: `Antigravity ${version} is unsupported. Update \`agy\` to 1.1.15 or later.`,
};
await getCatalog(launch);
return { available: true };
} catch (error) {
if (!(error instanceof AntigravityError)) throw error;
return { available: false, diagnostic: error.message };
}
}
export function validateSelection(
config: { model?: string; mode?: string; thinkingOption?: string },
catalog: ProviderCatalog,
): void {
if (config.model && !catalog.models.some((model) => model.id === config.model))
throw new AntigravityError(`Unknown Antigravity model: ${config.model}`, "INVALID_MODEL");
if (config.mode && !catalog.modes.some((mode) => mode.id === config.mode))
throw new AntigravityError(`Unknown Antigravity mode: ${config.mode}`, "INVALID_MODE");
if (config.thinkingOption)
throw new AntigravityError(
"Antigravity effort is selected through the model ID",
"UNSUPPORTED_THINKING",
);
}