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
57 changes: 54 additions & 3 deletions src/supervisor/agents/antigravity/antigravity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import type { McpServer, ProjectLocation, ThreadConfig } from "@/shared/contracts";
import { EXTRACTION_PROMPT } from "@/supervisor/contextExtractor";
import { createAntigravityAdapter } from ".";
import { buildAntigravityArgs } from "./argv";
import { ANTIGRAVITY_DEFAULT_MODEL_ID, antigravityDetectionSpec } from "./detection";
Expand Down Expand Up @@ -67,6 +68,21 @@ describe("buildAntigravityArgs", () => {
"--sandbox",
]);
});

it("maps the latest agy execution modes", () => {
expect(buildAntigravityArgs({ ...config, mode: "plan" }, "")).toEqual([
"--model",
"Gemini 3.5 Flash (Medium)",
"--mode",
"plan",
]);
expect(buildAntigravityArgs({ ...config, approvalPolicy: "accept-edits" }, "")).toEqual([
"--model",
"Gemini 3.5 Flash (Medium)",
"--mode",
"accept-edits",
]);
});
});

describe("createAntigravityAdapter", () => {
Expand Down Expand Up @@ -103,8 +119,11 @@ describe("createAntigravityAdapter", () => {
});
expect(adapter.capabilities.approvalPolicies.map((policy) => policy.id)).toEqual([
"default",
"accept-edits",
"yolo",
]);
expect(adapter.capabilities.modes).toEqual(["agent", "plan"]);
expect(adapter.capabilities.defaultApprovalPolicy).toBe("default");
expect(adapter.defaultOneShotModel).toBe(ANTIGRAVITY_DEFAULT_MODEL_ID);
});

Expand Down Expand Up @@ -168,7 +187,7 @@ describe("createAntigravityAdapter", () => {
// Isolate the cwd so the one-shot's last_conversations.json[cwd] write
// can't be mistaken for the real interactive session (see index.ts).
isolateCwd: true,
// agy print mode emits its answer only when attached to a terminal.
// Retain PTY compatibility with older installed agy versions.
pty: true,
});
expect(adapter.buildOneShotCommand?.("Gemini 3.5 Flash", "Low", "summarize")).toEqual({
Expand All @@ -180,6 +199,32 @@ describe("createAntigravityAdapter", () => {
});
});

it("builds direct context extraction for a resumed conversation", () => {
const adapter = createAntigravityAdapter();

expect(
adapter.buildContextExtractionCommand?.(
{
providerSessionId: "conversation-id",
discoveredAt: "2026-05-20T00:00:00.000Z",
},
project,
"Gemini 3.1 Pro (High)",
),
).toEqual({
command: "agy",
args: [
"--conversation",
"conversation-id",
"--model",
"Gemini 3.1 Pro (High)",
"-p",
EXTRACTION_PROMPT,
],
stdin: "",
});
});

it("does not project custom MCP servers into workspace config", () => {
const projectDir = mkdtempSync(join(tmpdir(), "antigravity-mcp-"));
try {
Expand Down Expand Up @@ -213,7 +258,7 @@ describe("createAntigravityAdapter", () => {
}
});

it("builds a subagent one-shot command that runs in the project cwd (no isolateCwd)", () => {
it("builds a subagent one-shot command with the headless permission bypass", () => {
const adapter = createAntigravityAdapter();
const cmd = adapter.buildSubagentOneShotCommand?.({
model: ANTIGRAVITY_DEFAULT_MODEL_ID,
Expand All @@ -223,7 +268,13 @@ describe("createAntigravityAdapter", () => {
});
expect(cmd).toEqual({
command: "agy",
args: ["--model", "Gemini 3.5 Flash (High)", "-p", "implement it"],
args: [
"--model",
"Gemini 3.5 Flash (High)",
"--dangerously-skip-permissions",
"-p",
"implement it",
],
stdin: "",
pty: true,
});
Expand Down
5 changes: 5 additions & 0 deletions src/supervisor/agents/antigravity/argv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export function buildAntigravityArgs(
args.push("--conversation", resumeConversationId);
}
args.push("--model", resolveAntigravityModel(config.model, config.effort));
if (config.mode === "plan") {
args.push("--mode", "plan");
} else if (config.approvalPolicy === "accept-edits") {
args.push("--mode", "accept-edits");
}
if (config.approvalPolicy === "never" || config.approvalPolicy === "yolo") {
args.push("--dangerously-skip-permissions");
}
Expand Down
7 changes: 4 additions & 3 deletions src/supervisor/agents/antigravity/detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ export const defaultAntigravityCapabilities: AgentCapability = {
efforts: defaultModelCapabilities.efforts,
defaultEffort: defaultModelCapabilities.defaultEffort,
modelEfforts: defaultModelCapabilities.modelEfforts,
modes: [],
modes: ["agent", "plan"],
approvalPolicies: [
{ id: "default", label: "Default" },
{ id: "default", label: "Request Review" },
{ id: "accept-edits", label: "Accept Edits" },
{ id: "yolo", label: "Bypass Permissions" },
],
sandboxModes: [],
Expand All @@ -30,7 +31,7 @@ export const defaultAntigravityCapabilities: AgentCapability = {
liveInputMode: "terminal",
presentationMode: "terminal",
presentationModes: ["terminal"],
defaultApprovalPolicy: "yolo",
defaultApprovalPolicy: "default",
bypassPermissions: { approvalPolicy: "yolo" },
// No dedicated-server hosting path in any presentation.
mcpScope: { terminal: "none", gui: "none" },
Expand Down
29 changes: 26 additions & 3 deletions src/supervisor/agents/antigravity/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AgentCapability, PromptSegment, ProjectLocation } from "@/shared/contracts";
import { inlinePromptSegmentText } from "@/shared/promptContent";
import { EXTRACTION_PROMPT } from "@/supervisor/contextExtractor";
import {
createKnownSessionRef,
detectAgentInstall,
Expand Down Expand Up @@ -220,15 +221,37 @@ export function createAntigravityAdapter(): AgentAdapter {
};
},

buildContextExtractionCommand(sessionRef, _location, model) {
return {
command: "agy",
args: [
"--conversation",
sessionRef.providerSessionId,
"--model",
resolveAntigravityModel(model),
"-p",
EXTRACTION_PROMPT,
],
stdin: "",
};
},

// Antigravity has no structured (GUI) runtime, so it joins the subagent
// roster via the one-shot child lane. Unlike title/commit generation this
// does NOT isolate the cwd — a child runs in the parent's project directory
// so it can actually read/edit the repo. `agy -p` print mode is fully
// non-interactive (no approval prompts), so there is no extra bypass flag.
// so it can actually read/edit the repo. Since agy 1.1.3, headless mode
// soft-denies tools requiring confirmation, so subagents need the explicit
// bypass to perform their assigned project work.
buildSubagentOneShotCommand({ model, effort, prompt }) {
return {
command: "agy",
args: ["--model", resolveAntigravityModel(model, effort), "-p", prompt],
args: [
"--model",
resolveAntigravityModel(model, effort),
"--dangerously-skip-permissions",
"-p",
prompt,
],
stdin: "",
pty: true,
};
Expand Down