Agent chat: Antigravity replaces the retired Gemini CLI (#1790) (#1795)

Thanks @Rec0iL.
This commit is contained in:
Rec0iL authored and GitHub committed 2026-10-16 15:26:14 -04:00
1 parent 920ea42fe1
commit 04b4381b54
5 files changed
+101 -14

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
//! The agents the chat can use: the installed ones it finds by itself (Claude Code, Codex,
//! Gemini CLI, OpenCode, …) and the ones the user adds (another ACP command, or an
//! Antigravity, OpenCode, …) and the ones the user adds (another ACP command, or an
//! OpenAI-compatible endpoint such as Ollama, LM Studio or OpenAI). Also the permission modes.
//! Preferences only: no design data, no keys (those are in the system keychain).
+92 -6
View File
@@ -24,6 +24,11 @@ pub struct Known {
pub config_dir_var: Option<(&'static str, &'static str)>,
/// Variables holding a key it uses instead of a login.
pub key_vars: &'static [&'static str],
/// Retired by its maker: still used when it is installed, never offered for install.
pub retired: bool,
/// Whether `--version` prints its version. Antigravity's server prints its build date and takes
/// seconds to start, so it is not asked.
pub reports_version: bool,
}
/// The agents shown in the picker. The first one found is the default.
@@ -37,6 +42,8 @@ pub const KNOWN: &[Known] = &[
credentials: Some(".claude/.credentials.json"),
config_dir_var: Some(("CLAUDE_CONFIG_DIR", ".credentials.json")),
key_vars: &["ANTHROPIC_API_KEY"],
retired: false,
reports_version: true,
},
Known {
id: "codex",
@@ -47,7 +54,25 @@ pub const KNOWN: &[Known] = &[
credentials: Some(".codex/auth.json"),
config_dir_var: Some(("CODEX_HOME", "auth.json")),
key_vars: &["OPENAI_API_KEY", "CODEX_API_KEY"],
retired: false,
reports_version: true,
},
// Google's `agy` command line has no ACP mode; its ACP server is a separate download that the
// ACP registry lists (`agy_acp_server.par`, `agy_acp_server.exe` on Windows).
Known {
id: "antigravity",
name: "Google Antigravity",
programs: &["agy_acp_server.par", "agy_acp_server"],
args: &[],
install: "download the Antigravity ACP server for your system (see agentclientprotocol.com/registry), unzip it and put agy_acp_server.par on your PATH",
credentials: Some(".gemini/antigravity-acp/acp_token.json"),
config_dir_var: None,
key_vars: &["GEMINI_API_KEY"],
retired: false,
reports_version: false,
},
// Google retired it on 18 June 2026 in favour of Antigravity (#1790). Enterprise licences may
// still work, so an installed one is found, but nobody is sent to install it.
Known {
id: "gemini",
name: "Gemini CLI",
@@ -57,6 +82,8 @@ pub const KNOWN: &[Known] = &[
credentials: Some(".gemini/oauth_creds.json"),
config_dir_var: None,
key_vars: &["GEMINI_API_KEY", "GOOGLE_API_KEY"],
retired: true,
reports_version: true,
},
Known {
id: "opencode",
@@ -67,6 +94,8 @@ pub const KNOWN: &[Known] = &[
credentials: Some(".local/share/opencode/auth.json"),
config_dir_var: None,
key_vars: &[],
retired: false,
reports_version: true,
},
Known {
id: "grok",
@@ -77,6 +106,8 @@ pub const KNOWN: &[Known] = &[
credentials: Some(".grok/auth.json"),
config_dir_var: None,
key_vars: &["XAI_API_KEY"],
retired: false,
reports_version: true,
},
Known {
id: "copilot",
@@ -87,6 +118,8 @@ pub const KNOWN: &[Known] = &[
credentials: None,
config_dir_var: None,
key_vars: &["GH_TOKEN", "GITHUB_TOKEN"],
retired: false,
reports_version: true,
},
Known {
id: "qwen",
@@ -97,6 +130,8 @@ pub const KNOWN: &[Known] = &[
credentials: Some(".qwen/oauth_creds.json"),
config_dir_var: None,
key_vars: &["DASHSCOPE_API_KEY"],
retired: false,
reports_version: true,
},
Known {
id: "goose",
@@ -107,6 +142,8 @@ pub const KNOWN: &[Known] = &[
credentials: None,
config_dir_var: None,
key_vars: &[],
retired: false,
reports_version: true,
},
];
@@ -292,19 +329,24 @@ fn login_of(k: &Known, home: Option<&Path>) -> Login {
/// Look for every known agent. Blocking (each probe has a time limit): run it off the UI thread.
pub fn detect() -> Detection {
detect_in(&search_dirs(), home().as_deref())
}
fn detect_in(dirs: &[PathBuf], home: Option<&Path>) -> Detection {
let t0 = Instant::now();
let dirs = search_dirs();
let home = home();
let mut d = Detection::default();
for k in KNOWN {
let Some(path) = k.programs.iter().find_map(|p| find_in(p, &dirs)) else {
d.missing.push(Missing { id: k.id, name: k.name, install: k.install });
let Some(path) = k.programs.iter().find_map(|p| find_in(p, dirs)) else {
if !k.retired {
d.missing.push(Missing { id: k.id, name: k.name, install: k.install });
}
continue;
};
let login = login_of(k, home.as_deref());
let login = login_of(k, home);
let mut argv = vec![path.to_string_lossy().into_owned()];
argv.extend(k.args.iter().map(|a| a.to_string()));
d.found.push(Found { id: k.id, name: k.name, version: version(&path, Duration::from_secs(3)), argv, login });
let version = if k.reports_version { version(&path, Duration::from_secs(3)) } else { None };
d.found.push(Found { id: k.id, name: k.name, version, argv, login });
}
d.took_ms = u64::try_from(t0.elapsed().as_millis()).unwrap_or(u64::MAX);
d
@@ -376,6 +418,14 @@ mod tests {
if std::env::var_os("XAI_API_KEY").is_none() {
assert_eq!(login_of(grok, Some(&home)), Login::NotLoggedIn);
}
// Antigravity's server keeps its own login, apart from the retired Gemini CLI's.
let agy = KNOWN.iter().find(|k| k.id == "antigravity").unwrap();
if std::env::var_os("GEMINI_API_KEY").is_none() {
assert_eq!(login_of(agy, Some(&home)), Login::NotLoggedIn, "the Gemini CLI's file is not its login");
}
std::fs::create_dir_all(home.join(".gemini/antigravity-acp")).unwrap();
std::fs::write(home.join(".gemini/antigravity-acp/acp_token.json"), "not credential data").unwrap();
assert_eq!(login_of(agy, Some(&home)), Login::LooksLoggedIn);
std::fs::create_dir_all(home.join(".grok")).unwrap();
// Discovery checks only for the file; it never needs to parse the user's tokens.
std::fs::write(home.join(".grok/auth.json"), "not credential data").unwrap();
@@ -383,6 +433,42 @@ mod tests {
let _ = std::fs::remove_dir_all(home);
}
/// Google retired the Gemini CLI (#1790): one that is installed is still found, but nobody is
/// told to install it. Antigravity's ACP server is offered, and found under its file name.
#[test]
#[cfg(unix)]
fn a_retired_agent_is_found_but_not_offered_and_antigravity_is() {
use std::os::unix::fs::PermissionsExt;
let root = std::env::temp_dir().join(format!("sc-agent-retired-{}", std::process::id()));
std::fs::create_dir_all(&root).unwrap();
let run = |name: &str, body: &str| {
let path = root.join(name);
std::fs::write(&path, format!("#!/bin/sh\n{body}\n")).unwrap();
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o755)).unwrap();
};
let ids = |d: &Detection| (d.found.iter().map(|f| f.id).collect::<Vec<_>>(), d.missing.iter().map(|m| m.id).collect::<Vec<_>>());
let none = detect_in(std::slice::from_ref(&root), None);
let (found, missing) = ids(&none);
assert!(found.is_empty());
assert!(missing.contains(&"antigravity") && !missing.contains(&"gemini"), "{missing:?}");
let hint = none.missing.iter().find(|m| m.id == "antigravity").map(|m| m.install).unwrap_or_default();
assert!(hint.contains("agy_acp_server.par") && !hint.contains("npm"), "{hint}");
run("gemini", "echo gemini 0.64.0");
// A server that would take seconds to answer `--version`, which Antigravity is never asked.
run("agy_acp_server.par", "sleep 30");
let both = detect_in(std::slice::from_ref(&root), None);
let (found, missing) = ids(&both);
assert_eq!(found, ["antigravity", "gemini"]);
assert!(!missing.contains(&"antigravity") && !missing.contains(&"gemini"), "{missing:?}");
let agy = both.found.iter().find(|f| f.id == "antigravity");
assert_eq!(agy.map(|f| f.version.clone()), Some(None), "its --version is a build date, not asked");
assert_eq!(agy.and_then(|f| f.argv.get(1)), None, "the registry starts it without arguments");
assert!(both.took_ms < 5000, "Antigravity's server was started to read a version");
let _ = std::fs::remove_dir_all(root);
}
#[test]
fn every_known_agent_has_a_program_and_an_install_hint() {
for k in KNOWN {
+1 -1
View File
@@ -1,5 +1,5 @@
//! Agent chat: a side panel that talks to a coding agent the user already has (Claude Code,
//! Codex, Gemini CLI, OpenCode, …) over the Agent Client Protocol, and gives that agent
//! Codex, Antigravity, OpenCode, …) over the Agent Client Protocol, and gives that agent
//! SolidCraft's MCP server, bridged to this window, as its way to read and change the open design
//! (docs/agent-chat.md, docs/design/in-app-agent-chat.md).
//!
+2 -2
View File
@@ -1,7 +1,7 @@
# Agent chat
Agent chat is a side panel where you talk to a coding agent you already use (Claude Code,
Codex, Gemini CLI, OpenCode, Grok Build and others), or to a model behind an OpenAI-compatible endpoint you
Codex, Google Antigravity, OpenCode, Grok Build and others), or to a model behind an OpenAI-compatible endpoint you
choose (Ollama or LM Studio on your computer, OpenAI, any other URL), and have it work on the
design that's open. A coding agent runs on your computer under **your own account** with that
agent: SolidCraft doesn't sell or proxy a model and needs no key for it. An endpoint's key, if
@@ -88,7 +88,7 @@ the keychain, so SolidCraft doesn't guess there.
|---|---|
| Claude Code | `npm install -g @agentclientprotocol/claude-agent-acp` (the ACP adapter for Claude Code) |
| Codex | `npm install -g @agentclientprotocol/codex-acp` |
| Gemini CLI | `npm install -g @google/gemini-cli` (it speaks ACP itself: `gemini --acp`) |
| Google Antigravity | Google's ACP server, a separate download from Google's `agy` command (which has no ACP mode): take the zip for your system from the [ACP registry](https://agentclientprotocol.com/registry), unzip it and put `agy_acp_server.par` (`agy_acp_server.exe` on Windows) on your `PATH`. The zip also holds `localharness_external`: leave the two together. It signs in itself (browser). |
| OpenCode | `npm install -g opencode-ai` (`opencode acp`) |
| XAI Grok | [Grok Build installer](https://docs.x.ai/build/overview), then `grok login` (`grok agent stdio` speaks ACP) |
| GitHub Copilot CLI, Qwen Code, goose | listed with their install command when missing |
+5 -4
View File
@@ -8,7 +8,7 @@ Protocol and the agents that speak it, how the chat is built, and what's still o
## Goal
A chat panel inside SolidCraft that drives a coding agent **the user already has and is already
signed in to** (Claude Code, Codex, Gemini CLI, …). SolidCraft holds no API keys and no
signed in to** (Claude Code, Codex, Antigravity, …). SolidCraft holds no API keys and no
accounts: the agent runs as a child process under the user's own login, and SolidCraft gives
it one thing, its MCP server bridged to the open window, so the agent models on the design the
user is looking at.
@@ -48,7 +48,8 @@ with SolidCraft, the user installs the agent.
|---|---|---|---|---|
| Claude Code | `claude-agent-acp` adapter (renamed from `@zed-industries/claude-code-acp`, which is now deprecated) | `@agentclientprotocol/claude-agent-acp` 0.90.0, Apache-2.0. It depends on `@anthropic-ai/claude-agent-sdk`, which is under Anthropic's own terms ("SEE LICENSE IN README.md"): fine for the user to install, not something we would vendor. | `--version` prints `0.90.0` | Terminal method. Offered only when the client declares terminal login. On a desktop: "Claude Subscription" = `claude-agent-acp --cli auth login --claudeai`, "Anthropic Console" = `… --cli auth login --console`. Over SSH (no browser) it offers `claude-login` = `… --cli` (the TUI `/login`). The login is Claude Code's own: `~/.claude/.credentials.json` (Linux, Windows) or the macOS keychain. |
| Codex | `codex-acp` adapter (`@zed-industries/codex-acp` 0.16 moved to `@agentclientprotocol/codex-acp`) | `@agentclientprotocol/codex-acp` 2.2.2, Apache-2.0 | `--version` (0.16 rejected the flag; 2.x prints it) | Agent method `chat-gpt` (the adapter opens the browser for the ChatGPT login when the client calls `authenticate`), and `api-key` for a key the user configures in Codex. 0.16 also listed `env_var` methods for `CODEX_API_KEY` / `OPENAI_API_KEY`. The login is Codex's own: `~/.codex/auth.json`. |
| Gemini CLI | built in: `gemini --acp` (the ACP registry's current flag; older releases used `--experimental-acp`) | `@google/gemini-cli` 0.64, Apache-2.0 | `--version` | Its own Google login (`~/.gemini/oauth_creds.json`) or a key in the environment. |
| Google Antigravity | a separate server, `agy_acp_server.par` (`.exe` on Windows), from the ACP registry's zip (`agy-acp-server-1.4.0-…`, with `localharness_external` beside it, ~330 MB). The `agy` command (1.4.0) rejects `--acp`; its `--input-format stream-json` print mode is not ACP. Started without arguments (the registry's Linux entry passes an empty `--uid=`, not needed here). | proprietary (Google's terms) | none: `--version` prints a build date and takes about two seconds, so it is not asked | Agent methods `oauth-personal`, `oauth-business`, `gemini-api-key` (`GEMINI_API_KEY`), `agent-platform`; `session/new` answers "Authentication required" until one is chosen. Settings in `~/.gemini/antigravity-acp/settings.json`. |
| Gemini CLI (retired) | `gemini --acp` | `@google/gemini-cli` 0.64, Apache-2.0 | `--version` | Google ended it for consumer accounts on 2026-06-18 in favour of Antigravity. Still found when installed (enterprise licences may keep it working) but no longer offered for install. Its own login `~/.gemini/oauth_creds.json` or a key in the environment. |
| OpenCode | built in: `opencode acp` | `opencode-ai` 1.19, MIT | `--version` | Its own providers' logins (`~/.local/share/opencode/auth.json`) or keys in the environment. |
| XAI Grok | built in: `grok agent stdio` | [Grok Build](https://github.com/xai-org/grok-build), Apache-2.0; installed separately | `grok --version` prints `grok 1.0.58 (…) [alpha]` (checked 2026-10-15; in-app connection also checked with 1.0.60) | Its own `~/.grok/auth.json` or `XAI_API_KEY`. `grok login` signs in in the browser; ACP advertises `cached_token` and `grok.com` authentication methods. |
| Others in the ACP registry | `copilot --acp`, `qwen --acp`, `opencode acp`, `goose acp`, `cline --acp`, `kimi acp`, … | mixed; several are proprietary, one is GPL and one AGPL (we only start them as programs, never link them) | `--version` | each its own |
@@ -61,7 +62,7 @@ the picker instead of the hard-coded table.
- **SolidCraft never sees a credential.** It doesn't pass API keys, doesn't store tokens and
doesn't open login pages by itself. Each agent keeps its own login where its CLI always does
(Claude Code: `~/.claude` or the keychain; Codex: `~/.codex`; Gemini: `~/.gemini`). A user
(Claude Code: `~/.claude` or the keychain; Codex: `~/.codex`; Gemini and Antigravity: `~/.gemini`). A user
who already uses the agent in a terminal is already signed in, and the chat simply works.
- **Not signed in:** the agent answers "Authentication required". The panel switches to
*Sign-in needed* and lists the agent's own `authMethods`:
@@ -79,7 +80,7 @@ the picker instead of the hard-coded table.
## How it works
```
SolidCraft window ──(child process, own process group, ACP over stdio)──> agent (claude-agent-acp, codex-acp, gemini --acp, opencode acp, …)
SolidCraft window ──(child process, own process group, ACP over stdio)──> agent (claude-agent-acp, codex-acp, agy_acp_server, opencode acp, …)
▲ │ starts, per session/new
│ the chat's own endpoint (loopback, fresh token, gate) ▼
└──────────────────────────────────────────────────── solidcraft-cli mcp --connect 127.0.0.1:PORT