Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions code-rs/core/src/agent_defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,16 @@ const AGENT_MODEL_SPECS: &[AgentModelSpec] = &[
read_only_args: ANTIGRAVITY_READ_ONLY,
write_args: ANTIGRAVITY_WRITE,
model_args: &[],
description: "Google Antigravity CLI agent; use as the Google-agent path after consumer Gemini CLI retirement.",
description: "Google/Gemini-family agent via Antigravity CLI; use for Google perspective after consumer Gemini CLI retirement. AGY uses its configured model, not per-run Gemini Pro/Flash flags.",
enabled_by_default: true,
aliases: &["agy", "google-antigravity"],
aliases: &[
"agy",
"google",
"gemini",
"gemini-agent",
"gemini-perspective",
"google-antigravity",
],
gating_env: None,
is_frontline: true,
pro_only: false,
Expand Down Expand Up @@ -497,7 +504,9 @@ fn model_guide_intro(active_agents: &[String]) -> String {
}
let frontline_str = present_frontline.join(", ");

format!("Preferred agent models: use {frontline_str} for challenging coding/agentic work.")
format!(
"Preferred agent models: use {frontline_str} for challenging coding/agentic work. For explicit multi-agent or dissent requests, prefer diverse model families when useful and budget allows; include `antigravity` for Google/Gemini-family perspective."
)
}

fn model_guide_line(spec: &AgentModelSpec) -> String {
Expand Down Expand Up @@ -698,6 +707,28 @@ mod tests {
assert_eq!(legacy.slug, "claude-opus-4.8");
}

#[test]
fn google_intent_aliases_resolve_to_antigravity() {
for alias in ["google", "gemini", "gemini-agent", "gemini-perspective"] {
let spec = agent_model_spec(alias).expect("google-family alias should resolve");
assert_eq!(spec.slug, "antigravity");
assert!(spec.model_args.is_empty());
}
}

#[test]
fn model_guide_describes_antigravity_as_google_family_lane() {
let guide = build_model_guide_description(&[
"code-gpt-5.5".to_string(),
"claude-sonnet-4.6".to_string(),
"antigravity".to_string(),
]);

assert!(guide.contains("include `antigravity` for Google/Gemini-family perspective"));
assert!(guide.contains("AGY uses its configured model"));
assert!(guide.contains("not per-run Gemini Pro/Flash flags"));
}

#[test]
fn retired_codex_models_are_not_default_agent_specs() {
let pro_specs = enabled_agent_model_specs_for_auth(Some(AuthMode::Chatgpt), true);
Expand Down
131 changes: 128 additions & 3 deletions code-rs/core/src/agent_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2004,8 +2004,7 @@ async fn execute_model_with_permissions(
remove_review_output_json(review_output_json_path);

let spec_opt = agent_model_spec(model)
.or_else(|| config.as_ref().and_then(|cfg| agent_model_spec(&cfg.name)))
.or_else(|| config.as_ref().and_then(|cfg| agent_model_spec(&cfg.command)));
.or_else(|| config.as_ref().and_then(|cfg| agent_model_spec(&cfg.name)));

if let Some(spec) = spec_opt {
if !spec.is_enabled() {
Expand Down Expand Up @@ -2873,7 +2872,7 @@ pub fn create_agent_tool(allowed_models: &[String]) -> OpenAiTool {
},
}),
description: Some(
"Optional array of agent/model selector slugs (e.g., ['code-gpt-5.5','claude-sonnet-4.6','antigravity']; external CLI selectors use that tool's configured model)".to_string(),
"Optional array of agent/model selector slugs. For explicit multi-agent/dissent requests, prefer diverse families when useful and budget allows (for example ['code-gpt-5.5','claude-sonnet-4.6','antigravity']). Use `antigravity` for Google/Gemini-family perspective; AGY uses its configured model rather than per-run Gemini Pro/Flash selection. If you skip an obvious family, briefly explain why.".to_string(),
),
},
);
Expand Down Expand Up @@ -3269,6 +3268,7 @@ mod tests {
use super::AgentRetryMetadata;
use super::AgentStatus;
use super::AGENT_PROVIDER_RETRY_MAX_DELAY;
use super::create_agent_tool;
use super::MAX_AGENT_PROGRESS_ENTRIES;
use super::MAX_AGENT_RESULT_BYTES;
use super::MAX_TRACKED_TERMINAL_AGENTS;
Expand All @@ -3285,6 +3285,7 @@ mod tests {
use super::agent_retry_delay;
use super::AGENT_MANAGER;
use crate::config_types::AgentConfig;
use crate::openai_tools::{JsonSchema, OpenAiTool};
use code_protocol::config_types::ReasoningEffort;
use serial_test::serial;
use std::collections::HashMap;
Expand Down Expand Up @@ -4102,6 +4103,95 @@ mod tests {
);
}

#[tokio::test]
async fn gemini_selector_uses_antigravity_cli() {
let _lock = env_lock().lock().expect("env lock");
let _reset_path = EnvReset::capture("PATH");

let dir = tempdir().expect("tempdir");
let agy = script_path(dir.path(), "agy");
write_argv_script(&agy);
let workspace = dir.path().join("workspace");
std::fs::create_dir_all(&workspace).expect("create workspace");

unsafe {
std::env::set_var("PATH", prepend_path(dir.path()));
}

let output = execute_model_with_permissions(
"agent-test",
"gemini",
"hello from google lane",
true,
Some(workspace.clone()),
None,
ReasoningEffort::Low,
None,
None,
None,
)
.await
.expect("execute gemini selector through antigravity");

let args: Vec<&str> = output.trim().split('|').collect();
assert_eq!(
args,
vec![
"--add-dir",
workspace.to_str().expect("workspace path utf-8"),
"-p",
"hello from google lane",
]
);
}

#[tokio::test]
async fn explicit_gemini_command_keeps_gemini_cli_args() {
let _lock = env_lock().lock().expect("env lock");
let _reset_path = EnvReset::capture("PATH");

let dir = tempdir().expect("tempdir");
let gemini = script_path(dir.path(), "gemini");
write_argv_script(&gemini);
let workspace = dir.path().join("workspace");
std::fs::create_dir_all(&workspace).expect("create workspace");

unsafe {
std::env::set_var("PATH", prepend_path(dir.path()));
}

let cfg = AgentConfig {
name: "corp-gemini".to_string(),
command: "gemini".to_string(),
args: Vec::new(),
read_only: true,
enabled: true,
description: None,
env: None,
args_read_only: None,
args_write: None,
instructions: None,
};

let output = execute_model_with_permissions(
"agent-test",
"corp-gemini",
"hello from gemini cli",
true,
Some(workspace),
Some(cfg),
ReasoningEffort::Low,
None,
None,
None,
)
.await
.expect("execute explicit gemini CLI config");

let args: Vec<&str> = output.trim().split('|').collect();
assert_eq!(args, vec!["-p", "hello from gemini cli"]);
}

fn env_lock() -> &'static Mutex<()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(()))
Expand Down Expand Up @@ -4461,6 +4551,41 @@ exit 0
assert_eq!(agent_retry_delay(8), AGENT_PROVIDER_RETRY_MAX_DELAY);
}

#[test]
fn agent_tool_models_description_guides_google_family_delegation() {
let tool = create_agent_tool(&[
"code-gpt-5.5".to_string(),
"claude-sonnet-4.6".to_string(),
"antigravity".to_string(),
]);

let function = match tool {
OpenAiTool::Function(function) => function,
_ => panic!("agent tool should be a function"),
};
let JsonSchema::Object { properties, .. } = function.parameters else {
panic!("agent tool should have object parameters");
};
let create_schema = properties.get("create").expect("create schema");
let JsonSchema::Object { properties: create_properties, .. } = create_schema else {
panic!("create schema should be an object");
};
let models_schema = create_properties.get("models").expect("models schema");
let JsonSchema::Array { items, description } = models_schema else {
panic!("models schema should be an array");
};
let description = description.as_deref().expect("models description");
assert!(description.contains("diverse families"));
assert!(description.contains("Use `antigravity` for Google/Gemini-family perspective"));
assert!(description.contains("AGY uses its configured model"));

let JsonSchema::String { allowed_values, .. } = items.as_ref() else {
panic!("models items should be strings");
};
let values = allowed_values.as_ref().expect("allowed models");
assert!(values.contains(&"antigravity".to_string()));
}

#[tokio::test]
#[serial]
async fn agent_provider_retry_wrapper_recovers_from_transient_failure() {
Expand Down
13 changes: 12 additions & 1 deletion code-rs/core/tests/antigravity_agent_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,18 @@ fn antigravity_replaces_builtin_gemini_agent_path() {
.slug,
"antigravity"
);
assert!(agent_model_spec("gemini").is_none());
assert_eq!(
agent_model_spec("gemini")
.expect("gemini intent alias present")
.slug,
"antigravity"
);
assert_eq!(
agent_model_spec("google")
.expect("google intent alias present")
.slug,
"antigravity"
);
assert!(agent_model_spec("gemini-3-flash-preview").is_none());
assert!(agent_model_spec("gemini-3.1-pro-preview").is_none());
}
4 changes: 3 additions & 1 deletion docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Field recap: `name` (slug/alias), `command` (absolute paths ok), `args*` (RO/RW
### Built-in defaults
If no `[[agents]]` are configured, Every Code advertises built-in agent/model selectors (gated by env `CODE_ENABLE_CLOUD_AGENT_MODEL` for cloud variants): `code-gpt-5.5`, `code-gpt-5.4`, `code-gpt-5.4-mini`, `claude-opus-4.8`, `antigravity`, `claude-sonnet-4.6`, `claude-haiku-4.5`, `qwen3-coder-plus`, `cloud-gpt-5.1-codex-max`. Built-ins strip any user `--model/-m` flags to avoid conflicts and inject their own when the target CLI supports model flags.

Tip: `antigravity` uses Google's Antigravity CLI (`agy`) as the Google-agent path. Consumer Gemini CLI is no longer a built-in default; configure it manually only when you intentionally rely on enterprise/API-key Gemini CLI access.
Tip: `antigravity` uses Google's Antigravity CLI (`agy`) as the Google/Gemini-family agent path. Gemini/Google intent can resolve to `antigravity`, but AGY uses its configured model rather than a per-run Gemini Pro/Flash flag. Consumer Gemini CLI is no longer a built-in default; configure it manually only when you intentionally rely on enterprise/API-key Gemini CLI access.

## Subagents (`[[subagents.commands]]`)
```toml
Expand All @@ -40,6 +40,8 @@ agent_instructions = "Preamble added to each spawned agent"

The orchestrator fans out agents, waits for results, and merges reasoning according to your `hide_agent_reasoning` / `show_raw_agent_reasoning` settings.

When you ask the Every Code agent to "ask agents" or gather dissent, it should prefer a small, diverse batch when the task benefits from multiple viewpoints and budget allows. A typical diverse batch includes GPT, Claude, and `antigravity` for the Google/Gemini-family perspective. Narrow mechanical work can use fewer agents; if an obvious family is skipped, the agent should briefly say why.

## TUI controls
- `/agents` opens the settings overlay to the Agents section: toggle enabled/read-only, view defaults, and open editors.
- Agent editor: create or edit a single agent (enable/disable, read-only, instructions). Args/env come from `config.toml`.
Expand Down
4 changes: 2 additions & 2 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ approval_policy = "never"

Use `[[agents]]` blocks to register additional CLI programs that Code can launch as peers. Each block maps a short `name` (referenced elsewhere in the config) to the command to execute, optional default flags, and environment variables.

> **Note:** Built-in agent/model selectors (for example `code-gpt-5.4`, `claude-sonnet-4.6`, or `antigravity`) map to a CLI plus optional model flags. Code strips any user `--model`/`-m` flags from `args`, `args_read_only`, or `args_write` before launching a built-in to avoid conflicts. `antigravity` launches `agy` and uses Antigravity's configured model rather than injecting a Gemini model flag.
> **Note:** Built-in agent/model selectors (for example `code-gpt-5.4`, `claude-sonnet-4.6`, or `antigravity`) map to a CLI plus optional model flags. Code strips any user `--model`/`-m` flags from `args`, `args_read_only`, or `args_write` before launching a built-in to avoid conflicts. `antigravity` launches `agy` as the Google/Gemini-family path and uses Antigravity's configured model rather than injecting a Gemini model flag.

```toml
[[agents]]
Expand Down Expand Up @@ -473,7 +473,7 @@ tool_timeout_sec = 30

Sub-agents are orchestrated helper workflows you can trigger with slash commands (for example `/plan`, `/solve`, `/code`). Each entry under `[[subagents.commands]]` defines the slash command name, whether spawned agents run in read-only mode, which `agents` to launch, and extra guidance for both the orchestrator (Code) and the individual agents.

By default (when no `[[agents]]` are configured) Code advertises these agent/model selectors for multi-agent runs: `code-gpt-5.5`, `code-gpt-5.4`, `code-gpt-5.4-mini`, `claude-opus-4.8`, `antigravity`, `claude-sonnet-4.6`, `claude-haiku-4.5`, and `qwen3-coder-plus`. The cloud counterpart, `cloud-gpt-5.1-codex-max`, only appears when `CODE_ENABLE_CLOUD_AGENT_MODEL=1` is set. You can override the list by defining `[[agents]]` entries or by specifying `agents = [ … ]` on a given `[[subagents.commands]]` entry. Consumer Gemini CLI is not a built-in default; add a custom `[[agents]]` block only when you intentionally rely on enterprise/API-key Gemini CLI access.
By default (when no `[[agents]]` are configured) Code advertises these agent/model selectors for multi-agent runs: `code-gpt-5.5`, `code-gpt-5.4`, `code-gpt-5.4-mini`, `claude-opus-4.8`, `antigravity`, `claude-sonnet-4.6`, `claude-haiku-4.5`, and `qwen3-coder-plus`. The cloud counterpart, `cloud-gpt-5.1-codex-max`, only appears when `CODE_ENABLE_CLOUD_AGENT_MODEL=1` is set. You can override the list by defining `[[agents]]` entries or by specifying `agents = [ … ]` on a given `[[subagents.commands]]` entry. Consumer Gemini CLI is not a built-in default; add a custom `[[agents]]` block only when you intentionally rely on enterprise/API-key Gemini CLI access. Legacy Gemini-style agent selectors are treated as Google-family intent and resolve to `antigravity`, which still uses AGY's configured model.

```toml
[[subagents.commands]]
Expand Down