Skip to content

feat(enhancement): add antigravity-cli provider#1234

Open
zhh210 wants to merge 9 commits into
getpaseo:mainfrom
zhh210:feature/antigravity-provider
Open

feat(enhancement): add antigravity-cli provider#1234
zhh210 wants to merge 9 commits into
getpaseo:mainfrom
zhh210:feature/antigravity-provider

Conversation

@zhh210
Copy link
Copy Markdown

@zhh210 zhh210 commented May 30, 2026

Linked issue

Closes #1131

Type of change

  • Bug fix
  • New feature (with prior issue + design alignment)
  • Refactor / code improvement
  • Docs

What does this PR do

Added antigravity-cli as a provider to paseo. Since agy --acp does not exists, key changes are: passing agy --print "prompt", which runs non-interactively and writes the response to stdout. And add antigravity as a registered provider.

How did you verify it

Tested locally (Linux x86_64) that antigravity works:
image

Checklist

  • One focused change. Unrelated cleanups split out.
  • npm run typecheck passes
  • npm run lint passes
  • npm run format ran (Biome)
  • UI changes include screenshots or video for every affected platform
  • Tests added or updated where it made sense

zhh210 and others added 7 commits May 29, 2026 22:23
Integrates google-antigravity/antigravity-cli (binary: agy) as an
ACP-based provider following the recommended integration pattern.

- packages/protocol: add 'antigravity' to AGENT_PROVIDER_DEFINITIONS
- packages/server: add AntigravityACPAgentClient (ACP, defaultCommand: agy --acp)
  with getDiagnostic() support; register factory in PROVIDER_CLIENT_FACTORIES
- packages/app: add AntigravityIcon SVG component; register in BUILTIN_PROVIDER_ICONS
  and BuiltinProviderIconName
- packages/server/daemon-e2e: add antigravity to agentConfigs, isProviderAvailable,
  and allProviders

The provider delegates auth to the agy CLI (Google Sign-In), matching
the pattern used by Copilot. Modes are discovered dynamically via ACP
at runtime; no static mode IDs are assumed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
agy does not support --acp. Replace the ACP stub with a direct provider
that uses agy's --print flag for non-interactive single-turn execution.

Key implementation details:
- AntigravityAgentClient implements AgentClient directly (no ACP)
- AntigravityAgentSession spawns agy with stdio:['ignore','pipe','pipe']
  (ignore stdin prevents agy from hanging waiting for EOF)
- Session continuity: new conversation UUID discovered from new .pb file
  at ~/.gemini/antigravity-cli/conversations/ after first turn
- Follow-up turns use --conversation <uuid> to resume the session
- Two modes: 'default' (ask) and 'bypass' (--dangerously-skip-permissions)
- --add-dir cwd only passed in bypass mode to avoid permission hangs
  in default mode when large directories need indexing
- provider-manifest.ts: add real modes (default/bypass) with defaultModeId
- agent-configs.ts: add modes.full='bypass', modes.ask='default'

Verified: isAvailable(), createSession(), run(), session continuity,
describePersistence(), and follow-up turns all work end-to-end.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adopted two techniques from openab/agy-acp:

1. Delta extraction (fixes bug: follow-up turns previously emitted the
   full conversation history, not just the new response)

   agy --conversation <uuid> --print replays all prior turn output
   before the new response. Track _prevOutput per session and strip it
   as a prefix on every turn, mirroring the extract_delta() approach
   in openab/agy-acp/src/main.rs. If agy's output is not append-only
   (prefix strip fails), log a warning and reset the baseline.

2. Multi-file guard on conversation discovery

   If two agy sessions run concurrently, multiple .pb files may appear
   in the snapshot diff. Refuse to bind the conversation ID in that case
   (same guard as openab) rather than picking an arbitrary file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace Array.toReversed() with .slice().reverse() in agent-manager.ts
  and agent-timeline-store.ts (toReversed requires Node.js 20.x+)
- Add antigravity.real.e2e.test.ts: 4 e2e tests using in-process daemon
  harness (provider discovery, basic prompt, session continuity, identity)
- All 4 tests pass against live agy binary

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…arget)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…structured logging

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 30, 2026

Greptile Summary

This PR adds Antigravity CLI (agy) as a new agent provider by spawning it non-interactively via agy --print "<prompt>" and extracting the new response text using a prefix-diff approach against accumulated prior output. The integration covers the full provider stack: session management, mode definitions (default / bypass), model listing, icon, and real e2e tests.

  • antigravity-agent.ts: Handles conversation ID detection via filesystem snapshot diffs, drains stderr to prevent pipe-buffer deadlocks (addressing a prior review thread), and emits turn_failed on prefix-mismatch (addressing another prior thread). The shared settings.json write in updateSettingsModel is not concurrency-safe when two sessions with different models start simultaneously.
  • agent-configs.ts: A stray /** line was left immediately before the existing JSDoc block for forEachProvider, producing a garbled comment.
  • provider-manifest.ts: The Antigravity provider description text still advertises features that don't match the declared capability flags (flagged in a prior review thread).

Confidence Score: 4/5

Safe to merge with one issue worth addressing: concurrent sessions selecting different models can silently run with the wrong model due to the shared settings.json write race.

The core turn-execution logic is solid and the previously-reported stderr and prefix-mismatch issues have been fixed. However, updateSettingsModel performs a read-modify-write on a single shared file without any locking, so two sessions that start turns at the same time with different model selections will race — the last write wins and one session's agy process launches with the model the other session chose.

packages/server/src/server/agent/providers/antigravity-agent.ts — specifically the updateSettingsModel function and how it interacts with concurrent sessions.

Important Files Changed

Filename Overview
packages/server/src/server/agent/providers/antigravity-agent.ts Core provider implementation — spawns agy --print, strips replayed history via prefix-diff, detects new conversation IDs by diffing the conversations directory; previously-flagged stderr drain and mismatch handling are now addressed, but updateSettingsModel has an unguarded read-modify-write race on the shared settings.json when concurrent sessions use different models.
packages/server/src/server/daemon-e2e/agent-configs.ts Adds antigravity to the shared test config map; contains a stray /** line immediately before the existing JSDoc block for forEachProvider, producing a garbled comment.
packages/protocol/src/provider-manifest.ts Adds Antigravity provider definition and its two modes (default / bypass) to AGENT_PROVIDER_DEFINITIONS; description text has already been flagged in a prior review thread for advertising MCP support while capabilities declare supportsMcpServers: false.
packages/server/src/server/agent/provider-registry.ts Registers AntigravityAgentClient in the provider factory map — straightforward wiring with no issues.
packages/server/src/server/daemon-e2e/antigravity.real.e2e.test.ts Real e2e tests covering availability, basic prompt, session continuity, and model identity; uses return to skip when agy is unavailable (tests appear "passed" rather than "skipped" in CI, but this is a pre-existing Vitest pattern used elsewhere in the project).
packages/app/src/components/icons/antigravity-icon.tsx New SVG icon component for Antigravity using react-native-svg; straightforward and matches the pattern of other provider icons in the codebase.
packages/app/src/components/provider-icon-name.ts Adds "antigravity" to BuiltinProviderIconName union type and BUILTIN_PROVIDER_IDS set — correct and consistent with the existing pattern.
packages/app/src/components/provider-icons.ts Maps the "antigravity" id to AntigravityIcon in BUILTIN_PROVIDER_ICONS; uses the same as unknown as ProviderIconComponent cast pattern as other icons.

Sequence Diagram

sequenceDiagram
    participant UI as App UI
    participant Reg as ProviderRegistry
    participant Sess as AntigravityAgentSession
    participant FS as settings.json
    participant AGY as agy (child process)

    UI->>Reg: createSession(config)
    Reg->>Sess: "new AntigravityAgentSession(conversationId=null)"

    UI->>Sess: startTurn(prompt)
    Sess->>FS: updateSettingsModel(model) [sync read-modify-write]
    Sess->>Sess: snapshot preConversations
    Sess->>AGY: spawn("agy", ["--conversation", id?, "--print", prompt])
    Sess-->>UI: emit turn_started

    loop stdout lines
        AGY-->>Sess: line
        Sess->>Sess: prefix-diff against _prevOutput
        alt new content
            Sess-->>UI: emit timeline (assistant_message)
        else mismatch
            Sess->>Sess: "mismatchDetected = true"
        end
    end

    AGY-->>Sess: close(code)
    alt first turn (no conversationId)
        Sess->>Sess: diff postConversations → bind new ID
    end
    alt mismatchDetected
        Sess-->>UI: emit turn_failed
    else "code === 0"
        Sess->>Sess: "_prevOutput = fullOutput"
        Sess-->>UI: emit turn_completed
    else "code !== 0"
        Sess-->>UI: emit turn_failed
    end
Loading

Reviews (3): Last reviewed commit: "Merge branch 'upstream/main' into featur..." | Re-trigger Greptile

Comment thread packages/server/src/server/agent/providers/antigravity-agent.ts
Comment thread packages/protocol/src/provider-manifest.ts
Comment thread packages/server/src/server/agent/providers/antigravity-agent.ts Outdated
Comment thread packages/server/src/server/agent/providers/antigravity-agent.ts Outdated
zhh210 and others added 2 commits May 30, 2026 01:41
- Drain proc.stderr to prevent pipe buffer deadlock (64KB OS limit)
- Suppress streaming emission on delta mismatch; emit turn_failed at
  close instead of broadcasting replayed conversation history
- Remove 'MCP support' from provider description (supportsMcpServers: false)
- Fix install diagnostic: use official curl script, not a made-up npm package

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines +42 to +57
function updateSettingsModel(model: string, logger: Logger) {
const settingsPath = join(homedir(), ".gemini", "antigravity-cli", "settings.json");
try {
let settings: AntigravitySettings = {};
if (existsSync(settingsPath)) {
settings = JSON.parse(readFileSync(settingsPath, "utf8")) as AntigravitySettings;
}
if (settings.model !== model) {
settings.model = model;
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf8");
logger.info({ model }, "antigravity: updated model in settings.json");
}
} catch (err) {
logger.error({ err }, "antigravity: failed to update settings.json model");
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 updateSettingsModel is not concurrency-safe across sessions with different models

updateSettingsModel follows a read → compare → write sequence using synchronous Node.js file I/O with no locking. If two AntigravityAgentSession instances start turns concurrently — e.g., user switches to Antigravity in two windows, one with "Gemini 3.5 Flash (High)" and one with "Gemini 3.1 Pro (Low)" — both processes can race through the read and land on different write calls. The last writer wins and overwrites the other session's model. The agy process for the first session then picks up the wrong model from settings.json at startup, silently running an entirely different model than the user selected.

Consider moving the model into a per-invocation CLI flag (if agy exposes one) rather than mutating the shared global settings.json, or at minimum serialising access through a file lock or a module-level async queue.

@liyuankui
Copy link
Copy Markdown

agy is quite generous in quota... Looking forward to this!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Antigravity CLI support

2 participants