1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
//! Per-agent facts, kept as data.
use crate::agent::AuthKind;
/// Everything discovery and the adapters need to know about one agent.
pub(crate) struct AgentProfile {
pub id: &'static str,
pub name: &'static str,
/// Binary the user installs (`claude`, `codex`, `hermes`).
pub cli: &'static str,
/// Env var that overrides executable resolution (tests, custom installs).
pub executable_env: &'static str,
/// Env var that relocates the agent's config directory.
pub config_home_env: Option<&'static str>,
/// Which adapter drives it and how to put the CLI in protocol mode.
pub connection: Connection,
/// API-key env vars the agent documents; each is an `EnvVar` login
/// method when it reports logged out.
pub api_key_env: &'static [&'static str],
/// `Some(kind)` when a successful session open proves the agent is logged
/// in with this kind (probed: the agent refuses to open logged out).
/// `None` when an open proves nothing (opencode serves free models
/// logged out) or the adapter reads auth from its own wire.
pub open_auth_kind: Option<AuthKind>,
/// Fingerprints of the agent's own logged-out error, matched against a
/// failed open's message and stderr (probed per agent; empty = the agent
/// uses the ACP auth-required code, which needs no hint).
pub auth_error_hints: &'static [&'static str],
/// Args after the executable that start the agent's own login flow;
/// empty when the agent only logs in interactively.
pub login_args: &'static [&'static str],
/// Shown in `MissingAgent` when the agent is not found.
pub install_hint: &'static str,
/// Extra install locations searched last. `~`-relative unless absolute.
pub extra_paths: &'static [&'static str],
/// A richer, separately installed runtime for the same agent, preferred
/// when found. Without it the base CLI is opened and the installation
/// names the upgrade as missing.
pub upgrade: Option<Upgrade>,
}
/// A second executable that drives the same agent over ACP with more
/// capabilities than its CLI (Antigravity's ACP server).
pub(crate) struct Upgrade {
pub name: &'static str,
pub cli: &'static str,
pub acp_args: &'static [&'static str],
pub extra_paths: &'static [&'static str],
pub install_hint: &'static str,
/// ACP auth method to call when `session/new` says the server has no
/// login selected; it adopts the base CLI's login without a browser.
pub acp_auth_method: Option<&'static str>,
}
pub(crate) enum Connection {
Acp { args: &'static [&'static str] },
Native(NativeKind),
}
pub(crate) enum NativeKind {
// Current native supported agents.
Claude,
Codex,
/// Antigravity's `agy` CLI: its own stream-json event dialect
/// (`--input-format=stream-json`, probed 1.1.24 on 2026-09-02).
Antigravity,
/// The pi RPC wire (`--mode rpc`).
Pi,
/// opencode's HTTP + SSE server (`opencode serve`), legacy engine.
Opencode,
}
/// The supported agents. A profile ships only with launch flags verified
/// against a real install; guessed wire flags do not ship.
pub(crate) static PROFILES: &[AgentProfile] = &[
AgentProfile {
id: "claude",
name: "Claude Code",
cli: "claude",
executable_env: "ANYAGENT_CLAUDE_BIN",
config_home_env: Some("CLAUDE_CONFIG_DIR"),
connection: Connection::Native(NativeKind::Claude),
api_key_env: &["ANTHROPIC_API_KEY"],
open_auth_kind: None,
auth_error_hints: &[],
login_args: &["auth", "login"],
install_hint: "npm install -g @anthropic-ai/claude-code",
extra_paths: &[".claude/local", ".local/bin"],
upgrade: None,
},
AgentProfile {
id: "codex",
name: "Codex",
cli: "codex",
executable_env: "ANYAGENT_CODEX_BIN",
config_home_env: Some("CODEX_HOME"),
connection: Connection::Native(NativeKind::Codex),
// app-server 0.147.0 ignores `OPENAI_API_KEY` (probed 2026-08-27);
// auth comes only from `codex login`.
api_key_env: &[],
open_auth_kind: None,
auth_error_hints: &[],
login_args: &["login"],
install_hint: "npm install -g @openai/codex",
extra_paths: &[],
upgrade: None,
},
// Gemini CLI is deprecated upstream (personal OAuth sunset, users moved
// to Antigravity); its profile was replaced 2026-08-23.
AgentProfile {
id: "antigravity",
name: "Antigravity",
cli: "agy",
executable_env: "ANYAGENT_ANTIGRAVITY_BIN",
config_home_env: None,
connection: Connection::Native(NativeKind::Antigravity),
api_key_env: &[],
// Logged out, `agy` exits before its `init` frame (probed 1.1.24).
open_auth_kind: Some(AuthKind::Subscription),
auth_error_hints: &["authentication required", "authentication failed"],
// Login is the TUI itself: the bare executable is the command.
login_args: &[],
install_hint: "install Antigravity from https://antigravity.google, then run `agy install`",
extra_paths: &[".local/bin"],
// Google's ACP server (registry id `antigravity-acp`): permissions,
// questions, in-process cancel, and live mode and model switches
// that the CLI's headless wire lacks. No steer: a second prompt
// mid-turn breaks its agent connection (probed 2026-09-07), so
// those queue. Gemini models only.
upgrade: Some(Upgrade {
name: "Antigravity ACP server",
cli: "agy_acp_server.par",
acp_args: &[],
extra_paths: &[".local/agy-acp-server", ".local/bin"],
install_hint: "download the `antigravity-acp` archive for your platform from \
https://github.com/agentclientprotocol/registry/blob/main/antigravity-acp/agent.json \
and unpack it into ~/.local/agy-acp-server",
// The server keeps its own auth choice (`auth.type` in
// ~/.gemini/antigravity-acp/settings.json). Without one, a
// single in-protocol `authenticate` adopts the `agy` login in
// ~2 s with no browser (probed 2026-09-07).
acp_auth_method: Some("oauth-personal"),
}),
},
AgentProfile {
id: "cursor",
name: "Cursor CLI",
cli: "cursor-agent",
executable_env: "ANYAGENT_CURSOR_BIN",
config_home_env: None,
connection: Connection::Acp { args: &["acp"] },
// Cursor's documented headless alternative to `cursor-agent login`.
api_key_env: &["CURSOR_API_KEY"],
open_auth_kind: Some(AuthKind::Subscription),
// Every session call logged out fails with the auth code and this
// text (read from the 2026.09.02 bundle).
auth_error_hints: &["Authentication required"],
login_args: &["login"],
install_hint: "curl https://cursor.com/install -fsS | bash",
extra_paths: &[".local/bin"],
upgrade: None,
},
AgentProfile {
id: "grok",
name: "Grok",
cli: "grok",
executable_env: "ANYAGENT_GROK_BIN",
config_home_env: None,
// Verified against grok 1.0.4: top-level `--no-auto-update` skips a
// multi-second update check; `--no-leader` starts a fresh agent
// instead of attaching to a leader process that may be wedged.
connection: Connection::Acp {
args: &["--no-auto-update", "agent", "--no-leader", "stdio"],
},
// The API-key alternative to the OAuth login.
api_key_env: &["XAI_API_KEY"],
open_auth_kind: Some(AuthKind::Subscription),
// Grok's -32000 carries no runnable method (probed 1.0.x: "no auth
// method id provided"); login is its own TUI, so the fallback
// command is the bare executable.
auth_error_hints: &["Authentication required"],
login_args: &[],
install_hint: "npm install -g @xai-official/grok \
(or `curl -fsSL https://x.ai/cli/install.sh | bash`)",
extra_paths: &[".local/bin", ".grok/bin", ".npm-global/bin"],
upgrade: None,
},
AgentProfile {
id: "hermes",
name: "Hermes Agent",
cli: "hermes",
executable_env: "ANYAGENT_HERMES_BIN",
config_home_env: None,
connection: Connection::Acp { args: &["acp"] },
api_key_env: &[],
open_auth_kind: Some(AuthKind::ApiKey),
auth_error_hints: &["No LLM provider configured"],
login_args: &["login"],
install_hint: "see https://github.com/NousResearch/hermes-agent",
extra_paths: &[".local/bin"],
upgrade: None,
},
AgentProfile {
id: "opencode",
name: "opencode",
cli: "opencode",
executable_env: "ANYAGENT_OPENCODE_BIN",
// Data home; `opencode auth login` writes auth.json here.
// `XDG_DATA_HOME` relocates opencode's db/state but not its login
// (auth stays in the shared keychain/auth.json), so it does not
// isolate accounts — left unset, an isolation request fails typed.
config_home_env: None,
connection: Connection::Native(NativeKind::Opencode),
api_key_env: &[],
open_auth_kind: None,
auth_error_hints: &[],
login_args: &["auth", "login"],
install_hint: "brew install sst/tap/opencode",
extra_paths: &[],
upgrade: None,
},
AgentProfile {
id: "kiro",
name: "Kiro CLI",
cli: "kiro-cli",
executable_env: "ANYAGENT_KIRO_BIN",
config_home_env: None,
connection: Connection::Acp { args: &["acp"] },
api_key_env: &[],
open_auth_kind: Some(AuthKind::Subscription),
auth_error_hints: &["not logged in"],
login_args: &["login"],
install_hint: "install Kiro CLI from https://kiro.dev",
extra_paths: &[".local/bin"],
upgrade: None,
},
AgentProfile {
id: "pi",
name: "pi",
cli: "pi",
executable_env: "ANYAGENT_PI_BIN",
// The agent dir itself, which is what `PI_CODING_AGENT_DIR` replaces
// (documented in `pi --help`, probed 2026-08-30).
config_home_env: Some("PI_CODING_AGENT_DIR"),
connection: Connection::Native(NativeKind::Pi),
// The adapter answers auth for real from `pi auth check`; these are
// the API-key alternatives pi documents.
api_key_env: &["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "OPENROUTER_API_KEY"],
// A session opens fine with no provider configured, so an open proves
// nothing (probed 2026-08-30).
open_auth_kind: None,
auth_error_hints: &[],
// Login is the TUI's own `/login <provider>`; there is no CLI verb.
login_args: &[],
install_hint: "npm install -g @earendil-works/pi-coding-agent",
extra_paths: &[".bun/bin", ".local/bin"],
upgrade: None,
},
// oh-my-pi (`omp`) speaks the same pi RPC wire and would be one more
// profile on `NativeKind::Pi`; deferred to its own ticket.
AgentProfile {
id: "qwen",
name: "Qwen Code",
cli: "qwen",
executable_env: "ANYAGENT_QWEN_BIN",
config_home_env: None,
connection: Connection::Acp {
args: &["--experimental-acp"],
},
// Advertised by qwen's own auth method (probed 0.22.0).
api_key_env: &["OPENAI_API_KEY"],
open_auth_kind: Some(AuthKind::Subscription),
auth_error_hints: &[],
login_args: &[],
install_hint: "npm install -g @qwen-code/qwen-code",
extra_paths: &[],
upgrade: None,
},
];
/// The environment variable that relocates an agent's config home, if it has
/// one. `None` for an agent outside the catalog (an ad-hoc ACP install) or one
/// with no such variable.
pub(crate) fn config_home_env(id: &str) -> Option<&'static str> {
PROFILES
.iter()
.find(|p| p.id == id)
.and_then(|p| p.config_home_env)
}