OpenHands / OpenHands/software-agent-sdk
[Feature]: Add Antigravity CLI as a new, additive ACP provider (alongside gemini-cli)
@simonrosenberg is already working on this.
Since Aug 25, 2026.
- 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. Noteagyitself is not npm-distributed either — the only published package is the Apache-2.0google-antigravitySDK 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_serverappears 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:
- the archive shrinks between registry bumps — one
HEADrequest ← the gate- an npm or otherwise thin client appears
agy --acpships (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_datasection 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; pointdefault_commandat 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 thedl.google.combinary above, nothing else. An unrelated npm package is also calledantigravity-acp. It is not Google's, is not what this issue is about, and must not be used. The only correct artifact is theagy_acp_serverarchive at thedl.google.comURL above, referenced by registry entryantigravity-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 pinnedGEMINI_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): theACPServerKindliteral andresolve_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): thenpm install -gblock and/usr/local/binwrappers. - Downstream:
typescript-client(generated mirror), Agent Canvas inOpenHands/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'sAGENT_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 aftergemini-cliinACP_PROVIDERSsodetect_acp_provider_*ordering and thedefault_acp_file_secrets()union stay stable.agent_name_patterns=("antigravity",). Not"agy"—detect_acp_provider_by_agent_namedoes a plain substring match in insertion order, first match wins, so a three-character fragment can misattribute an unrelated agent name. The runtime name isantigravity-acp, so"antigravity"matches safely. (detect_acp_provider_by_commandtolerates short tokens because it anchors withstartswithon the basename; the name-based one does not.)default_commandis not annpxline. Launch is a checksummed fetch fromdl.google.complus 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_methodgainsgemini-api-keyandagent-platformarms. Both are headless, so the deployable paths have direct equivalents;oauth-personal/oauth-businessstay interactive and are ignored for deployment.- Add
"antigravity"toACPServerKind.
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:
initializeadvertisesoauth-personal,oauth-business,gemini-api-key,agent-platform.gemini-api-keyauthenticates headless from theGEMINI_API_KEYenv var, writes~/.gemini/antigravity-acp/settings.json={"auth":{"type":"gemini-api-key"}}, and does not persist the key.- Pre-seeding that
settings.jsonplus the env var skipsauthenticateentirely — the same materialise-a-file + inject-an-env-var pattern the SDK already implements viaACPFileSecretSpec. agent-platformacceptsGOOGLE_API_KEY, orGOOGLE_CLOUD_PROJECT+GOOGLE_CLOUD_LOCATION(ADC), orgcp.project/gcp.locationinsettings.json— near 1:1 with the existingvertex-aispec.- In sandboxes without IPv6 loopback (
::1) the serverSIGABRTs unless launched with--enforce_kernel_ipv6_support=false. Our runtime pods have no::1and noCAP_NET_ADMIN, so this flag is required.
Decisions to settle before implementation
- 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.
- Stability expectations. Google does not publicly document this server as ACP — the manifest's own
websitefield points at a docs page that never mentions it, and this is anRC01build 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
-
antigravityis registered inACP_PROVIDERSaftergemini-cli, withagent_name_patterns=("antigravity",), and added to theACPServerKindliteral. -
gemini-cliremains fully functional and selectable; no persistedacp_server="gemini-cli"value fails validation, and existing conversations still resolve their provider. -
default_acp_file_secrets()returns a stable, non-colliding set — theGOOGLE_APPLICATION_CREDENTIALS_JSONsecret must not be materialised to two different subdirs by two providers. -
_select_auth_methodselectsgemini-api-keyandagent-platformfor 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/promptturn 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 hardcodeskey === "gemini-cli"for the Vertex-safe default), and a brand mark. -
OpenHands/enterpriserenders anacp-antigravitychip kind and groups Antigravity sessions correctly in admin usage analytics (bothusage-dashboard-utils.tsand_agent_label_expression). - If evaluation gains an Antigravity agent type, the
AgentNamevalue lands inopenhands-index-resultsfirst, andacp-geministays mapped toGemini CLIso 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 --acpmode. Still open; if it lands,default_commandsimplifies 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_commandis not annpxline (see Distribution above), and there is nonpx
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 sacp_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.pywith 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, andfile_secretsif 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.
- The two
- Version constant alongside
CLAUDE_AGENT_ACP_VERSION/CODEX_ACP_VERSION/GEMINI_CLI_VERSION. - Pin-parity test — add the key to
_REGISTRY_ACP_PACKAGESintests/cross/test_agent_server_build_metadata.py. This gate fails silently: the test does.get(provider_key)thenif 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 mirror —
clients/typescript/src/models/acp-providers.json.check-acp-drift.pycompares field-for-field intypescript-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
npxfallback 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 fromdl.google.com), so that fallback does not apply — it is preinstalled or unavailable. That makes the size question sharper, not softer: adding it toINSTALL_ACP_PROVIDERSgrows every published image for every user by a large margin, against the active shrinking effort in #4643. If preinstalling,.github/workflows/server.ymlhardcodes the list and a test asserts it verbatim. - Tests —
tests/sdk/settings/test_acp_providers.py(its exact key-set assertion forces an update), plustests/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_nameanddata_dir_env_varare part of the record and easy to overlook.binary_nameis what_prefer_pinned_binary()looks for to use an installed binary instead ofnpx; omit it and the provider always pays thenpxpath.data_dir_env_varis the env var pointed at a per-conversation directory so credentials, sessions, plugins and caches don't leak between runs.data_dir_env_varis not always a dedicated variable.gemini-cliuses plainHOME, because the CLI has no config-dir override — which is why isolation for it means relocatingHOMErather than setting a scoped var. If this harness has a dedicated config-dir variable, prefer it; if it only respectsHOME, say so explicitly on the issue.- All three set both
supports_*flags toTrue. That makesFalsethe value that needs justifying: it should mean this server genuinely cannot do it, verified against the running server, not not implemented yet. A silentFalseremoves the model picker or mid-conversation switching for users of this harness. session_meta_keyis the exception, not the rule — onlyclaude-codesets one ("claudeCode"), becauseclaude-agent-acpignores session-_metamodel selection and needs the model pushed via a protocol call. Default toNoneunless the server actually reads a_metakey.default_commandmay carry arguments, not just a package:gemini-cliappends--acpto 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.