diff --git a/src/supervisor/agents/antigravity/antigravity.test.ts b/src/supervisor/agents/antigravity/antigravity.test.ts index 4461261e..92e05009 100644 --- a/src/supervisor/agents/antigravity/antigravity.test.ts +++ b/src/supervisor/agents/antigravity/antigravity.test.ts @@ -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"; @@ -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", () => { @@ -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); }); @@ -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({ @@ -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 { @@ -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, @@ -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, }); diff --git a/src/supervisor/agents/antigravity/argv.ts b/src/supervisor/agents/antigravity/argv.ts index 370207b1..4c5af3d5 100644 --- a/src/supervisor/agents/antigravity/argv.ts +++ b/src/supervisor/agents/antigravity/argv.ts @@ -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"); } diff --git a/src/supervisor/agents/antigravity/detection.ts b/src/supervisor/agents/antigravity/detection.ts index 4c271fd4..e87a0ed2 100644 --- a/src/supervisor/agents/antigravity/detection.ts +++ b/src/supervisor/agents/antigravity/detection.ts @@ -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: [], @@ -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" }, diff --git a/src/supervisor/agents/antigravity/index.ts b/src/supervisor/agents/antigravity/index.ts index ffb8bbf0..11b8c483 100644 --- a/src/supervisor/agents/antigravity/index.ts +++ b/src/supervisor/agents/antigravity/index.ts @@ -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, @@ -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, };