OpenHands / OpenHands/software-agent-sdk

[Feature]: Add Antigravity CLI as a new, additive ACP provider (alongside gemini-cli)

Open
#4,624 4 comments 0 reactions 1 assignee View on GitHub

@simonrosenberg is already working on this.

Since Aug 25, 2026.

acp enhancement ready-for-dev release-note-required
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

[!IMPORTANT]
Watch item — not scheduled. (Added 2026-09-04.)

Gate: a distribution we can consume — an npm or otherwise thin install path, or an artifact small enough to fetch on demand.

Today it is a 650.4 MiB download unpacking to ~1.9 GiB, shipped as a platform binary rather than npm. That needs both a non-npm install mechanism (#4823) and a dedicated image variant, since it is far too large to fetch inside the 90 s acp_startup_timeout. Neither is justified without demand. Note agy itself is not npm-distributed either — the only published package is the Apache-2.0 google-antigravity SDK on PyPI (verified: 0.1.16, "SDK for building AI agents", not the ACP server) — so a non-npm install mechanism is needed for gates 1 and 3 below, though not for gate 2: if an npm or thin client appears, the existing npm path works and no new mechanism is needed. #4823 is closed as a watch item for the same reason as this issue — no scheduled consumer — with its reduced design recorded there (a preinstall-only recipe needing zero SDK runtime changes, ~a week) so nothing is re-derived on reopen.

Secondary risk, not the gate: agy_acp_server appears in no Google documentation and bug reports about it are unanswered (antigravity-cli#921, #915), so adopting it means owning its failures. Provenance itself is fine — Google's CDN, authors: ["Google LLC"], registry entry maintained by Google staff.

Watch — any one is worth reopening this:

  1. the archive shrinks between registry bumps — one HEAD request ← the gate
  2. an npm or otherwise thin client appears
  3. agy --acp ships (antigravity-cli#31)

Figures corrected (2026-09-04). The registry now points at v1.1.1, not agy_acp_server_20260818_01_RC01, so the "RC build" caveat below is stale. The download grew from 518.2 MiB to 650.4 MiB (+25% in two weeks). Inside: one unstripped ELF whose .par_data section is 1200 MiB of embedded Python, plus 283 MiB of debug symbols and symbol tables. Nothing is prunable from our side — it is one file, all or nothing.

Where it could plausibly land: local canvas on a workstation, where a one-time large fetch is tolerable. Cloud and evaluation are per-pod and deadline-bound, and #4841 keeps newly registered harnesses out of Cloud and Replicated by default regardless.

[!NOTE]
Description rewritten 2026-08-26 to reflect verified findings, and kept as the single source of truth for this issue. The original framing (Gemini CLI sunset; point default_command at a third-party adapter) was corrected by @jpshackelford here; every claim below was then re-verified against primary sources. The original file inventory was accurate and is preserved below. Scope changed from replace to add alongside.

Problem Statement

Google ships a first-party Agent Client Protocol server for Antigravity, and OpenHands has no provider for it. We should add one.

Two corrections to the framing this issue was originally filed under, both verified against primary sources:

Gemini CLI was not sunset — one auth tier was. Google's 2026-06-18 cutoff stopped serving consumer tiers only (Google AI Pro/Ultra and free). Gemini Code Assist Standard/Enterprise licences, API keys, and Vertex AI all continue. @google/gemini-cli remains Apache-2.0 and actively published, and --acp is still supported. No deployed OpenHands path was affected: cloud and enterprise authenticate via vertex-ai service accounts, benchmark and evaluation runs via GEMINI_API_KEY. The genuine Gemini-CLI bugs (a retired auth method outranking a working API key, and a stale version pin) are tracked separately in #4629.

agy still has no ACP flag, but a separate Google ACP server exists. agy --acp does not exist — google-antigravity/antigravity-cli#31 remains open. What does exist is a standalone, Google-published binary, agy_acp_server, listed in the ACP registry:

Field Value
Registry entry agentclientprotocol/registry/antigravity-acp
name / authors Google Antigravity / ["Google LLC"]
Build agy_acp_server_20260818_01_RC01
Distribution dl.google.com/agy-extensions/releases/... (darwin-arm64, linux x86_64 + arm64, windows x86_64 + arm64)
Linux launch ./agy_acp_server.par --uid=
Artifact size 543,411,011 bytes (518.2 MiB), last-modified 2026-08-19
Runtime agentInfo.name antigravity-acp
Provenance registry PR #542 by cynthialong0-0 (GitHub profile company: @google), merged 2026-08-20

This resolves the supply-chain objection that closed the issue previously: the server is Google's own binary, published on Google's own CDN.

[!WARNING]
Name collision — use the dl.google.com binary above, nothing else. An unrelated npm package is also called antigravity-acp. It is not Google's, is not what this issue is about, and must not be used. The only correct artifact is the agy_acp_server archive at the dl.google.com URL above, referenced by registry entry antigravity-acp. Verify by URL, not by name.

Current State in OpenHands / software-agent-sdk

The original inventory, still accurate. gemini-cli is referenced in:

  • Provider registry (openhands-sdk/openhands/sdk/settings/acp_providers.py): ACP_PROVIDERS, the pinned GEMINI_CLI_VERSION, binary_name="gemini", agent_name_patterns=("gemini-cli",), _GEMINI_FILE_SECRETS, data_dir_env_var="HOME".
  • Settings types (openhands-sdk/openhands/sdk/settings/model.py): the ACPServerKind literal and resolve_acp_command / _prefer_pinned_binary.
  • Auth (openhands-sdk/openhands/sdk/agent/acp_agent.py): _select_auth_method, _AUTH_METHOD_ENV_MAP, _auth_selection_failure_reason, _materialise_file_secrets.
  • Agent-server image (openhands-agent-server/openhands/agent_server/docker/Dockerfile): the npm install -g block and /usr/local/bin wrappers.
  • Downstream: typescript-client (generated mirror), Agent Canvas in OpenHands/OpenHands (src/constants/acp-providers.ts, src/api/acp-service/acp-service.api.ts, src/constants/acp-brand-marks.ts), OpenHands/enterprise (acp-provider-secrets.ts, agent-display-label.ts, agent-chip-icon.tsx, usage-dashboard-utils.ts, org_conversation_service.py, app_conversation_models.py), benchmarks (benchmarks/utils/acp.py), and evaluation's AGENT_NAME_BY_TYPE.
Desired Behavior

Add antigravity as a new, additive ACP provider. gemini-cli is never removed or renamed — it is a validated Literal in persisted settings, a value in the enterprise acpserver tag column, and a published leaderboard label; removing it fails validation on existing rows and orphans history.

Verified specification:

  • key="antigravity", display_name="Antigravity CLI", inserted after gemini-cli in ACP_PROVIDERS so detect_acp_provider_* ordering and the default_acp_file_secrets() union stay stable.
  • agent_name_patterns=("antigravity",). Not "agy"detect_acp_provider_by_agent_name does a plain substring match in insertion order, first match wins, so a three-character fragment can misattribute an unrelated agent name. The runtime name is antigravity-acp, so "antigravity" matches safely. (detect_acp_provider_by_command tolerates short tokens because it anchors with startswith on the basename; the name-based one does not.)
  • default_command is not an npx line. Launch is a checksummed fetch from dl.google.com plus a wrapper invoking ./agy_acp_server.par --uid=. Pin the URL and our own SHA-256 rather than resolving through the registry at runtime — the registry entry is a community-submitted pointer, not our source of truth.
  • _select_auth_method gains gemini-api-key and agent-platform arms. Both are headless, so the deployable paths have direct equivalents; oauth-personal / oauth-business stay interactive and are ignored for deployment.
  • Add "antigravity" to ACPServerKind.

Verification provenance. Everything in the table above was confirmed independently from primary sources: the registry manifest, a HEAD request against the dl.google.com archive (HTTP 200, 543,411,011 bytes), the registry PR and its author's GitHub profile, and antigravity-cli releases and issues. The auth details immediately below come from @jpshackelford's sandbox run and were not independently reproduced — doing so requires downloading and executing the 518 MiB binary. Treat them as well-evidenced but unconfirmed, and re-check them when implementing:

  • initialize advertises oauth-personal, oauth-business, gemini-api-key, agent-platform.
  • gemini-api-key authenticates headless from the GEMINI_API_KEY env var, writes ~/.gemini/antigravity-acp/settings.json = {"auth":{"type":"gemini-api-key"}}, and does not persist the key.
  • Pre-seeding that settings.json plus the env var skips authenticate entirely — the same materialise-a-file + inject-an-env-var pattern the SDK already implements via ACPFileSecretSpec.
  • agent-platform accepts GOOGLE_API_KEY, or GOOGLE_CLOUD_PROJECT + GOOGLE_CLOUD_LOCATION (ADC), or gcp.project / gcp.location in settings.json — near 1:1 with the existing vertex-ai spec.
  • In sandboxes without IPv6 loopback (::1) the server SIGABRTs unless launched with --enforce_kernel_ipv6_support=false. Our runtime pods have no ::1 and no CAP_NET_ADMIN, so this flag is required.
Decisions to settle before implementation
  1. Delivery mechanism. A 518 MiB artifact is a large image cost for a provider most conversations will not use. Fetching at first use rather than baking a layer is what the registry model already assumes, and keeps the image lean for the majority of conversations that never select this provider. Decide before writing the Dockerfile change. See #4643 for the broader image reorganisation this feeds into.
  2. Stability expectations. Google does not publicly document this server as ACP — the manifest's own website field points at a docs page that never mentions it, and this is an RC01 build indexed only through a community-submitted registry entry. Provenance is solid; a public support commitment does not exist. Pin hard and fail soft.
Acceptance Criteria
  • antigravity is registered in ACP_PROVIDERS after gemini-cli, with agent_name_patterns=("antigravity",), and added to the ACPServerKind literal.
  • gemini-cli remains fully functional and selectable; no persisted acp_server="gemini-cli" value fails validation, and existing conversations still resolve their provider.
  • default_acp_file_secrets() returns a stable, non-colliding set — the GOOGLE_APPLICATION_CREDENTIALS_JSON secret must not be materialised to two different subdirs by two providers.
  • _select_auth_method selects gemini-api-key and agent-platform for Antigravity sessions, headless, with no browser step; rebased onto the precedence fix in #4629 rather than reintroducing the old ordering.
  • The server binary is obtained by pinned URL + verified SHA-256, launched with --uid= and --enforce_kernel_ipv6_support=false, and starts successfully in a runtime pod with no IPv6 loopback.
  • A live ACP session against the Antigravity server completes a real session/prompt turn on a Vertex/ADC credential.
  • The delivery-mechanism decision is recorded on this issue before any image change merges, and is consistent with #4643.
  • Agent Canvas (OpenHands/OpenHands) surfaces the provider only once branding and secrets exist — ACP_PROVIDER_UI, getAcpPreferredDefaultModel (which currently hardcodes key === "gemini-cli" for the Vertex-safe default), and a brand mark.
  • OpenHands/enterprise renders an acp-antigravity chip kind and groups Antigravity sessions correctly in admin usage analytics (both usage-dashboard-utils.ts and _agent_label_expression).
  • If evaluation gains an Antigravity agent type, the AgentName value lands in openhands-index-results first, and acp-gemini stays mapped to Gemini CLI so historical leaderboard entries keep their attribution.
Sequencing

Adding a provider key touches seven repositories in a strict order, and two are easy to miss:

software-agent-sdk  →  typescript-client  →  OpenHands (Agent Canvas)
                                          →  enterprise
                    →  benchmarks         →  evaluation  →  openhands-index-results

typescript-client is a generated mirror — Agent Canvas and enterprise read the registry only through it, so it must be regenerated and republished or a new key is invisible downstream. openhands-index-results owns the AgentName enum that gates leaderboard pushes. benchmarks bypasses the provider registry entirely (_ACP_COMMANDS is hardcoded and acp_command is passed explicitly), so it needs its own change.

Useful safety property: Agent Canvas builds its provider list by iterating its own ACP_PROVIDER_UI map rather than the SDK registry, so a new SDK key stays invisible in the UI until that map opts in — a free feature gate for staged rollout.

Related
  • #4629 — Gemini CLI maintenance track (auth precedence + stale pin). Independent of this issue; both touch _select_auth_method.
  • OpenHands/enterprise#235 — enterprise branding, secrets, and analytics for the new provider. Follows this issue.
  • google-antigravity/antigravity-cli#31 — upstream request for a native agy --acp mode. Still open; if it lands, default_command simplifies considerably.

Product parity contract (added for #4820)

[!WARNING]
This contract was written for the npm-based providers; two items do not transfer to Antigravity.
default_command is not an npx line (see Distribution above), and there is no npx
fallback, so the "registry-only is the default" guidance below does not hold. Everything else —
the registry fields, the TypeScript mirror, the tests, live verification — applies unchanged.
Deferred, not blocked. This needs a non-npm install mechanism, specced as #4823 and now
closed as a watch item alongside this issue — with Antigravity unscheduled and Hermes
eval-only, it had no consumer. Its reduced design is recorded there (preinstall-only, zero SDK
runtime changes, ~a week). A lazy fetch was never viable regardless: 650.4 MiB cannot fit the
90 s acp_startup_timeout, so this would always have been preinstall plus a dedicated image variant.

Parent epic: #4820 — build-in ACP harness parity.

Antigravity is done as a product provider when it does everything claude-code, codex and gemini-cli do:

  • Registry record in openhands-sdk/openhands/sdk/settings/acp_providers.py with every field populated from Antigravity's real server behaviour: key, display_name, default_command, binary_name, api_key_env_var, base_url_env_var, data_dir_env_var, default_session_mode, agent_name_patterns, supports_set_session_model, supports_runtime_model_switch, session_meta_key, available_models + default_model, and file_secrets if it authenticates from a file rather than an env var. See the reference table below for what each of the three existing providers sets.
    • The two supports_* flags decide user-visible behaviour (initial model selection, mid-conversation model switching). Determine them from Antigravity's actual server, don't copy a neighbour's values.
  • Version constant alongside CLAUDE_AGENT_ACP_VERSION / CODEX_ACP_VERSION / GEMINI_CLI_VERSION.
  • Pin-parity test — add the key to _REGISTRY_ACP_PACKAGES in tests/cross/test_agent_server_build_metadata.py. This gate fails silently: the test does .get(provider_key) then if registry_package is None: continue, so a Dockerfile arm with no dict entry is skipped rather than flagged, and the pin drifts with CI green.
  • TypeScript mirrorclients/typescript/src/models/acp-providers.json. check-acp-drift.py compares field-for-field in typescript-client-ci.yml, so skipping it fails CI.
  • Preinstall decision, recorded — and note the usual escape hatch does not exist here. For the npm-based providers, #4805's npx fallback means an absent provider still works, so registry-only is the safe default. Antigravity is not npm-distributed (see Distribution above: a checksummed ~518 MiB fetch from dl.google.com), so that fallback does not apply — it is preinstalled or unavailable. That makes the size question sharper, not softer: adding it to INSTALL_ACP_PROVIDERS grows every published image for every user by a large margin, against the active shrinking effort in #4643. If preinstalling, .github/workflows/server.yml hardcodes the list and a test asserts it verbatim.
  • Teststests/sdk/settings/test_acp_providers.py (its exact key-set assertion forces an update), plus tests/sdk/agent/test_acp_agent.py, tests/agent_server/test_credential_binding.py, test_settings_router.py.
  • Live verification — a real ACP turn against the actual Antigravity server: session created, tool call executed, reply returned. The registry record is a claim about server behaviour; only a live run substantiates it.

What the existing providers actually set

The contract above is abstract; this is the concrete shape, read from the registry on main. Use it to decide what each field should be for this harness — and to see which values are genuinely provider-specific versus conventional.

Field claude-code codex gemini-cli
display_name Claude Code Codex Gemini CLI
default_command npx -y --prefer-offline @agentclientprotocol/claude-agent-acp@0.63.0 npx -y --prefer-offline @agentclientprotocol/codex-acp@1.1.7 npx -y --prefer-offline @google/gemini-cli@0.46.0 --acp
binary_name claude-agent-acp codex-acp gemini
api_key_env_var ANTHROPIC_API_KEY OPENAI_API_KEY GEMINI_API_KEY
base_url_env_var ANTHROPIC_BASE_URL OPENAI_BASE_URL GEMINI_BASE_URL
data_dir_env_var CLAUDE_CONFIG_DIR CODEX_HOME HOME
default_session_mode bypassPermissions agent-full-access default
agent_name_patterns ["claude-agent"] ["codex-acp"] ["gemini-cli"]
supports_set_session_model True True True
supports_runtime_model_switch True True True
session_meta_key "claudeCode" None None
default_model opus[1m] gpt-5.5 auto
available_models 5 7 7
file_secrets none CODEX_AUTH_JSON GOOGLE_APPLICATION_CREDENTIALS_JSON
What to take from it
  • binary_name and data_dir_env_var are part of the record and easy to overlook. binary_name is what _prefer_pinned_binary() looks for to use an installed binary instead of npx; omit it and the provider always pays the npx path. data_dir_env_var is the env var pointed at a per-conversation directory so credentials, sessions, plugins and caches don't leak between runs.
  • data_dir_env_var is not always a dedicated variable. gemini-cli uses plain HOME, because the CLI has no config-dir override — which is why isolation for it means relocating HOME rather than setting a scoped var. If this harness has a dedicated config-dir variable, prefer it; if it only respects HOME, say so explicitly on the issue.
  • All three set both supports_* flags to True. That makes False the value that needs justifying: it should mean this server genuinely cannot do it, verified against the running server, not not implemented yet. A silent False removes the model picker or mid-conversation switching for users of this harness.
  • session_meta_key is the exception, not the rule — only claude-code sets one ("claudeCode"), because claude-agent-acp ignores session-_meta model selection and needs the model pushed via a protocol call. Default to None unless the server actually reads a _meta key.
  • default_command may carry arguments, not just a package: gemini-cli appends --acp to put the CLI into ACP mode.
file_secrets, if the CLI authenticates from a file

Only needed when the credential is a file on disk rather than an env var. The two live specs:

// codex — env var points at the DIRECTORY containing auth.json
{ "secret_name": "CODEX_AUTH_JSON", "filename": "auth.json",
  "env_var": "CODEX_HOME", "subdir": "codex",
  "env_points_to": "dir", "warn_if_unset": [] }

// gemini-cli — env var points at the FILE itself
{ "secret_name": "GOOGLE_APPLICATION_CREDENTIALS_JSON", "filename": "gcloud-credentials.json",
  "env_var": "GOOGLE_APPLICATION_CREDENTIALS", "subdir": "gemini-cli",
  "env_points_to": "file", "warn_if_unset": ["GOOGLE_CLOUD_PROJECT", "GOOGLE_CLOUD_LOCATION"] }

env_points_to distinguishes the two shapes and is the field most likely to be set wrong — Codex wants a directory, Gemini wants the file. warn_if_unset names companion variables that are useless alone (Gemini's Vertex project and location). The SDK owns the mechanism — materialising the file under the conversation's durable root, seed-if-absent, setting the env var; the spec is only policy.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.