feat(models): update default agent model routing

This commit is contained in:
Auto Review
2026-05-31 16:22:59 +10:00
parent 76937a5ba3
commit f1d069bf38
17 changed files with 210 additions and 97 deletions
+2 -2
View File
@@ -19,8 +19,8 @@
- Stress tests now cover heavy agent churn plus concurrent Auto Review + Esc/typing responsiveness.
- **New/updated models and agents**
- Auto Drive CLI model support includes `gpt-5.3-codex` (planning/problem-solving) and `gpt-5.3-codex-spark` (fast coding/fix loops), with `medium | high | xhigh` reasoning controls.
- Frontline and alias-aware agent model handling now includes `code-gpt-5.3-codex` and `code-gpt-5.3-codex-spark`, with compatibility alias upgrades for `gpt-5.1-codex`, `gpt-5.1-codex-mini`, `gpt-5.2-codex`, etc.
- Auto Drive CLI model support defaults to `gpt-5.5` (planning/problem-solving) and `gpt-5.4-mini` (fast coding/fix loops), with `medium | high | xhigh` reasoning controls.
- Frontline and alias-aware agent model handling now includes `code-gpt-5.5`, `code-gpt-5.4`, `claude-opus-4.8`, and `claude-sonnet-4.6`, with compatibility aliases for older model names where supported.
- Auto Drive decision schema and coordinator payloads now enforce bounded history while preserving goal and recent context.
See commit `60727b068` and related Auto Drive hardening commits in git history for details.
@@ -66,8 +66,8 @@ const MAX_QUEUED_CONVERSATION_UPDATES: usize = 24;
const DEBUG_JSON_MAX_CHARS: usize = 1200;
const CLI_PROMPT_MIN_CHARS: usize = 4;
const CLI_PROMPT_MAX_CHARS: usize = 600;
const AUTO_DRIVE_CLI_MODEL_PRIMARY: &str = "gpt-5.3-codex";
const AUTO_DRIVE_CLI_MODEL_SPARK: &str = "gpt-5.3-codex-spark";
const AUTO_DRIVE_CLI_MODEL_PRIMARY: &str = "gpt-5.5";
const AUTO_DRIVE_CLI_MODEL_FAST: &str = "gpt-5.4-mini";
const AUTO_DRIVE_PRIMARY_ROUTING_DESCRIPTION: &str =
"Hard planning and complex problem solving";
const AUTO_DRIVE_SPARK_ROUTING_DESCRIPTION: &str =
@@ -160,7 +160,7 @@ fn default_auto_drive_cli_routing_entries() -> Vec<AutoDriveCliRoutingEntry> {
description: AUTO_DRIVE_PRIMARY_ROUTING_DESCRIPTION.to_string(),
},
AutoDriveCliRoutingEntry {
model: AUTO_DRIVE_CLI_MODEL_SPARK.to_string(),
model: AUTO_DRIVE_CLI_MODEL_FAST.to_string(),
reasoning_levels: vec![ReasoningEffort::High],
description: AUTO_DRIVE_SPARK_ROUTING_DESCRIPTION.to_string(),
},
@@ -168,24 +168,10 @@ fn default_auto_drive_cli_routing_entries() -> Vec<AutoDriveCliRoutingEntry> {
}
fn auto_drive_cli_routing_entries_for_auth(
auth_mode: Option<code_app_server_protocol::AuthMode>,
supports_pro_only_models: bool,
_auth_mode: Option<code_app_server_protocol::AuthMode>,
_supports_pro_only_models: bool,
) -> Vec<AutoDriveCliRoutingEntry> {
let mut entries = vec![AutoDriveCliRoutingEntry {
model: AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string(),
reasoning_levels: vec![ReasoningEffort::High, ReasoningEffort::XHigh],
description: AUTO_DRIVE_PRIMARY_ROUTING_DESCRIPTION.to_string(),
}];
if auth_mode.is_some_and(code_app_server_protocol::AuthMode::is_chatgpt)
&& supports_pro_only_models
{
entries.push(AutoDriveCliRoutingEntry {
model: AUTO_DRIVE_CLI_MODEL_SPARK.to_string(),
reasoning_levels: vec![ReasoningEffort::High],
description: AUTO_DRIVE_SPARK_ROUTING_DESCRIPTION.to_string(),
});
}
entries
default_auto_drive_cli_routing_entries()
}
#[cfg(test)]
@@ -268,9 +254,6 @@ fn resolve_auto_drive_cli_routing_entries(
available_models: &[String],
) -> Vec<AutoDriveCliRoutingEntry> {
let mut entries = normalize_auto_drive_cli_routing_entries(&settings.model_routing_entries);
if !auth_mode.is_some_and(|mode| mode.is_chatgpt()) || !supports_pro_only_models {
entries.retain(|entry| !entry.model.eq_ignore_ascii_case(AUTO_DRIVE_CLI_MODEL_SPARK));
}
entries.retain(|entry| {
available_models
.iter()
@@ -1183,7 +1166,7 @@ mod tests {
"status_title": "Fixing tests",
"status_sent_to_user": "Running clear failing-test loops.",
"cli_milestone_instruction": "Take the failing test from red to green and report the passing evidence.",
"cli_model": "gpt-5.3-codex-spark",
"cli_model": "gpt-5.4-mini",
"cli_reasoning_effort": "high"
}"#;
@@ -1197,18 +1180,18 @@ mod tests {
.expect("routing-enabled decision should parse");
let cli = decision.cli.expect("cli action expected");
assert_eq!(cli.model_override.as_deref(), Some("gpt-5.3-codex-spark"));
assert_eq!(cli.model_override.as_deref(), Some("gpt-5.4-mini"));
assert_eq!(cli.reasoning_effort_override, Some(ReasoningEffort::High));
}
#[test]
fn parse_decision_rejects_spark_when_not_allowed() {
fn parse_decision_rejects_fast_model_when_not_allowed() {
let raw = r#"{
"finish_status": "continue",
"status_title": "Fixing tests",
"status_sent_to_user": "Running clear failing-test loops.",
"cli_milestone_instruction": "Take the failing test from red to green and report the passing evidence.",
"cli_model": "gpt-5.3-codex-spark",
"cli_model": "gpt-5.4-mini",
"cli_reasoning_effort": "high"
}"#;
@@ -1223,7 +1206,7 @@ mod tests {
}],
},
)
.expect_err("non-pro routing should reject spark");
.expect_err("routing should reject unavailable fast model");
assert!(err.to_string().contains("unsupported cli_model"));
assert!(err.to_string().contains(AUTO_DRIVE_CLI_MODEL_PRIMARY));
@@ -1236,7 +1219,7 @@ mod tests {
"status_title": "Fixing tests",
"status_sent_to_user": "Running clear failing-test loops.",
"cli_milestone_instruction": "Take the failing test from red to green and report the passing evidence.",
"cli_model": "gpt-5.3-codex",
"cli_model": "gpt-5.5",
"cli_reasoning_effort": "xhigh"
}"#;
@@ -1259,25 +1242,25 @@ mod tests {
}
#[test]
fn auto_drive_cli_models_gates_spark_by_auth() {
fn auto_drive_cli_models_include_primary_and_fast_for_auth_modes() {
let pro_models = auto_drive_cli_models_for_auth(Some(AuthMode::Chatgpt), true);
assert!(pro_models.contains(&AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string()));
assert!(pro_models.contains(&AUTO_DRIVE_CLI_MODEL_SPARK.to_string()));
assert!(pro_models.contains(&AUTO_DRIVE_CLI_MODEL_FAST.to_string()));
let non_pro_models = auto_drive_cli_models_for_auth(Some(AuthMode::Chatgpt), false);
assert!(non_pro_models.contains(&AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string()));
assert!(!non_pro_models.contains(&AUTO_DRIVE_CLI_MODEL_SPARK.to_string()));
assert!(non_pro_models.contains(&AUTO_DRIVE_CLI_MODEL_FAST.to_string()));
let api_key_models = auto_drive_cli_models_for_auth(Some(AuthMode::ApiKey), false);
assert!(api_key_models.contains(&AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string()));
assert!(!api_key_models.contains(&AUTO_DRIVE_CLI_MODEL_SPARK.to_string()));
assert!(api_key_models.contains(&AUTO_DRIVE_CLI_MODEL_FAST.to_string()));
}
#[test]
fn resolve_cli_routing_entries_falls_back_when_enabled_entries_missing() {
let mut settings = AutoDriveSettings::default();
settings.model_routing_entries = vec![AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex".to_string(),
model: "gpt-5.5".to_string(),
enabled: false,
reasoning_levels: vec![ReasoningEffort::High],
description: "disabled".to_string(),
@@ -1285,7 +1268,7 @@ mod tests {
let available_models = vec![
AUTO_DRIVE_CLI_MODEL_PRIMARY.to_string(),
AUTO_DRIVE_CLI_MODEL_SPARK.to_string(),
AUTO_DRIVE_CLI_MODEL_FAST.to_string(),
];
let entries = resolve_auto_drive_cli_routing_entries(
@@ -1296,7 +1279,7 @@ mod tests {
);
assert!(entries.iter().any(|entry| entry.model == AUTO_DRIVE_CLI_MODEL_PRIMARY));
assert!(entries.iter().any(|entry| entry.model == AUTO_DRIVE_CLI_MODEL_SPARK));
assert!(entries.iter().any(|entry| entry.model == AUTO_DRIVE_CLI_MODEL_FAST));
}
#[test]
+6 -6
View File
@@ -75,7 +75,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
is_default: true,
upgrade: None,
pro_only: false,
show_in_picker: true,
@@ -226,14 +226,14 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
},
],
supported_text_verbosity: &[TextVerbosityConfig::Medium],
is_default: true,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.3-codex".to_string(),
reasoning_effort_mapping: None,
migration_config_key: HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG.to_string(),
}),
pro_only: false,
show_in_picker: true,
show_in_picker: false,
},
ModelPreset {
id: "gpt-5.2".to_string(),
@@ -577,7 +577,7 @@ pub fn model_preset_available_for_auth(
match auth_mode {
Some(AuthMode::ApiKey) => {
preset.id != "gpt-5.2-codex" && preset.id != "gpt-5.3-codex"
preset.id != "gpt-5.2-codex" && preset.id != "gpt-5.3-codex-spark"
}
_ => true,
}
@@ -668,13 +668,13 @@ mod tests {
}
#[test]
fn gpt_5_codex_hidden_for_api_key_auth() {
fn deprecated_and_chatgpt_only_codex_models_hidden_for_api_key_auth() {
let presets = builtin_model_presets(Some(AuthMode::ApiKey), false);
assert!(presets.iter().all(|preset| {
preset.id != "gpt-5.2-codex"
&& preset.id != "gpt-5.3-codex"
&& preset.id != "gpt-5.3-codex-spark"
}));
assert!(presets.iter().any(|preset| preset.id == "gpt-5.3-codex"));
}
#[test]
+1 -1
View File
@@ -60,7 +60,7 @@ agent {
"task": "Implement JWT middleware (RS256) with key rotation and unit/integration tests. Preserve existing OAuth flows. Provide README usage snippet.",
"context": "Service: services/api (Rust Axum). Secrets via env. CI: `cargo test --all`.",
"files": ["services/api", "services/api/src", "services/api/Cargo.toml"],
"models": ["code-gpt-5.4","claude-sonnet-4.5","gemini-3.5-flash"],
"models": ["code-gpt-5.5","claude-sonnet-4.6","gemini-3.5-flash"],
"output": "Middleware + passing tests + README snippet",
"write": true // Allow changes - will launch every agent in a separate worktree
}
+51 -14
View File
@@ -40,14 +40,13 @@ const MODELS_MANIFEST: &str = include_str!("../../../codex-rs/models-manager/mod
/// CLI-name lookups.
pub const DEFAULT_AGENT_NAMES: &[&str] = &[
// Frontline for moderate/challenging tasks
"code-gpt-5.5",
"code-gpt-5.4",
"code-gpt-5.4-mini",
"code-gpt-5.3-codex",
"code-gpt-5.3-codex-spark",
"claude-opus-4.8",
"gemini-3.1-pro",
// Straightforward / cost-aware
"claude-sonnet-4.5",
"claude-sonnet-4.6",
"gemini-3.5-flash",
"github-copilot",
// Mixed/general and alternates
@@ -148,7 +147,7 @@ const AGENT_MODEL_SPECS: &[AgentModelSpec] = &[
write_args: CODE_GPT5_CODEX_WRITE,
model_args: &["--model", "gpt-5.3-codex"],
description: "Primary coding agent for implementation and multi-file edits; strong speed and reliability.",
enabled_by_default: true,
enabled_by_default: false,
aliases: &[
"code-gpt-5.2-codex",
"code-gpt-5.1-codex-max",
@@ -159,9 +158,6 @@ const AGENT_MODEL_SPECS: &[AgentModelSpec] = &[
"gpt-5.1-codex-max",
"gpt-5.1-codex",
"gpt-5-codex",
"coder",
"code",
"codex",
],
gating_env: None,
is_frontline: true,
@@ -175,7 +171,7 @@ const AGENT_MODEL_SPECS: &[AgentModelSpec] = &[
write_args: CODE_GPT5_CODEX_WRITE,
model_args: &["--model", "gpt-5.3-codex-spark"],
description: "Fast codex variant tuned for responsive coding loops and smaller edits.",
enabled_by_default: true,
enabled_by_default: false,
aliases: &["gpt-5.3-codex-spark", "code-gpt-5.3-spark", "codex-spark"],
gating_env: None,
is_frontline: false,
@@ -202,7 +198,7 @@ const AGENT_MODEL_SPECS: &[AgentModelSpec] = &[
pro_only: false,
},
AgentModelSpec {
slug: "claude-sonnet-4.5",
slug: "claude-sonnet-4.6",
family: "claude",
cli: "claude",
read_only_args: CLAUDE_SONNET_READ_ONLY,
@@ -210,7 +206,7 @@ const AGENT_MODEL_SPECS: &[AgentModelSpec] = &[
model_args: &["--model", "sonnet"],
description: "Balanced Claude model for implementation and debugging; a solid default when you want Claude.",
enabled_by_default: true,
aliases: &["claude", "claude-sonnet"],
aliases: &["claude", "claude-sonnet", "claude-sonnet-4.5"],
gating_env: None,
is_frontline: false,
pro_only: false,
@@ -369,7 +365,7 @@ fn dynamic_code_agent_spec(model: ManifestModel) -> Option<AgentModelSpec> {
let model_slug = leak_str(model.slug);
let description = leak_str(model.description);
let _display_name = leak_str(model.display_name);
let aliases = leak_str_slice(vec![model_slug]);
let aliases = dynamic_code_agent_aliases(model_slug);
let model_args = leak_str_slice(vec!["--model", model_slug]);
let pro_only = matches!(track, CodeAgentTrack::CodexSpark);
let is_frontline = !matches!(track, CodeAgentTrack::Mini | CodeAgentTrack::CodexSpark);
@@ -390,6 +386,14 @@ fn dynamic_code_agent_spec(model: ManifestModel) -> Option<AgentModelSpec> {
})
}
fn dynamic_code_agent_aliases(model_slug: &'static str) -> &'static [&'static str] {
if model_slug.eq_ignore_ascii_case("gpt-5.5") {
return leak_str_slice(vec![model_slug, "coder", "code", "codex"]);
}
leak_str_slice(vec![model_slug])
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum CodeAgentTrack {
Base,
@@ -546,7 +550,7 @@ fn model_guide_intro(active_agents: &[String]) -> String {
.collect();
if present_frontline.is_empty() {
present_frontline.push("code-gpt-5.4".to_string());
present_frontline.push("code-gpt-5.5".to_string());
}
let frontline_str = present_frontline.join(", ");
@@ -720,6 +724,11 @@ mod tests {
#[test]
fn gpt_codex_aliases_resolve() {
for alias in ["coder", "code", "codex"] {
let spec = agent_model_spec(alias).expect("generic codex alias resolves");
assert_eq!(spec.slug, "code-gpt-5.5");
}
let codex = agent_model_spec("gpt-5.1-codex").expect("alias for codex present");
assert_eq!(codex.slug, "code-gpt-5.3-codex");
@@ -762,6 +771,11 @@ mod tests {
assert_eq!(opus.slug, "claude-opus-4.8");
assert_eq!(opus.model_args, &["--model", "opus"]);
let sonnet =
agent_model_spec("claude-sonnet-4.5").expect("legacy sonnet alias resolves");
assert_eq!(sonnet.slug, "claude-sonnet-4.6");
assert_eq!(sonnet.model_args, &["--model", "sonnet"]);
let gemini = agent_model_spec("gemini").expect("gemini alias resolves");
assert_eq!(gemini.slug, "gemini-3.5-flash");
assert_eq!(gemini.cli, "agy");
@@ -781,12 +795,30 @@ mod tests {
}
#[test]
fn spark_agent_model_requires_pro_chatgpt_auth() {
fn spark_agent_model_remains_explicit_pro_only() {
let spark = agent_model_spec("gpt-5.3-codex-spark").expect("spark spec present");
assert!(!spark.is_enabled());
assert!(agent_model_available_for_auth(
spark,
Some(AuthMode::Chatgpt),
true
));
assert!(!agent_model_available_for_auth(
spark,
Some(AuthMode::Chatgpt),
false
));
assert!(!agent_model_available_for_auth(
spark,
Some(AuthMode::ApiKey),
false
));
let pro_specs = enabled_agent_model_specs_for_auth(Some(AuthMode::Chatgpt), true);
assert!(
pro_specs
.iter()
.any(|spec| spec.slug == "code-gpt-5.3-codex-spark")
.all(|spec| spec.slug != "code-gpt-5.3-codex-spark")
);
let non_pro_specs = enabled_agent_model_specs_for_auth(Some(AuthMode::Chatgpt), false);
@@ -844,5 +876,10 @@ mod tests {
.iter()
.all(|spec| spec.slug != "code-gpt-5.2")
);
assert!(
enabled_agent_model_specs()
.iter()
.all(|spec| spec.slug != "code-gpt-5.3-codex")
);
}
}
+3 -3
View File
@@ -2439,7 +2439,7 @@ pub fn create_agent_tool(allowed_models: &[String]) -> OpenAiTool {
},
}),
description: Some(
"Optional array of model names (e.g., ['code-gpt-5.4','claude-sonnet-4.5','code-gpt-5.3-codex-spark','gemini-3.5-flash'])".to_string(),
"Optional array of model names (e.g., ['code-gpt-5.5','claude-sonnet-4.6','code-gpt-5.4-mini','gemini-3.5-flash'])".to_string(),
),
},
);
@@ -2997,7 +2997,7 @@ mod tests {
}
let cfg = AgentConfig {
name: "claude-sonnet-4.5".to_string(),
name: "claude-sonnet-4.6".to_string(),
command: "claude".to_string(),
args: Vec::new(),
read_only: true,
@@ -3011,7 +3011,7 @@ mod tests {
let output = execute_model_with_permissions(
"agent-test",
"claude-sonnet-4.5",
"claude-sonnet-4.6",
"ok",
true,
None,
+20 -10
View File
@@ -142,9 +142,9 @@ pub use crate::config_constraint::ConstraintResult;
pub(crate) use defaults::merge_with_default_agents;
pub(crate) use validation::upgrade_legacy_model_slugs;
pub(crate) const OPENAI_DEFAULT_MODEL: &str = "gpt-5.4";
const OPENAI_DEFAULT_REVIEW_MODEL: &str = "gpt-5.4";
pub const GPT_5_CODEX_MEDIUM_MODEL: &str = "gpt-5.4";
pub(crate) const OPENAI_DEFAULT_MODEL: &str = "gpt-5.5";
const OPENAI_DEFAULT_REVIEW_MODEL: &str = "gpt-5.5";
pub const GPT_5_CODEX_MEDIUM_MODEL: &str = "gpt-5.5";
pub(crate) const DEFAULT_SUBAGENT_MAX_DEPTH: i32 = 1;
/// Maximum number of bytes of the documentation that will be embedded. Larger
@@ -229,7 +229,7 @@ pub struct Config {
/// Whether planning should inherit the chat model instead of using a dedicated override.
pub planning_use_chat_model: bool,
/// Model used specifically for review sessions. Defaults to "gpt-5.4".
/// Model used specifically for review sessions. Defaults to "gpt-5.5".
pub review_model: String,
/// Reasoning effort used when running review sessions.
@@ -1398,7 +1398,7 @@ impl Config {
|| agent.name.eq_ignore_ascii_case("cloud")
{
tracing::warn!(
"legacy agent name '{}' detected; update config to use model slugs (e.g., code-gpt-5.3-codex)",
"legacy agent name '{}' detected; update config to use model slugs (e.g., code-gpt-5.5)",
agent.name
);
}
@@ -2820,7 +2820,7 @@ model_verbosity = "high"
assert_eq!(config.auto_drive.model_routing_entries.len(), 2);
assert_eq!(
config.auto_drive.model_routing_entries[0].model,
"gpt-5.3-codex"
"gpt-5.5"
);
assert_eq!(
config.auto_drive.model_routing_entries[0].reasoning_levels,
@@ -2828,7 +2828,7 @@ model_verbosity = "high"
);
assert_eq!(
config.auto_drive.model_routing_entries[1].model,
"gpt-5.3-codex-spark"
"gpt-5.4-mini"
);
assert_eq!(
config.auto_drive.model_routing_entries[1].reasoning_levels,
@@ -2892,13 +2892,13 @@ model_verbosity = "high"
auto_drive.model_routing_enabled = true;
auto_drive.model_routing_entries = vec![
AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex".to_string(),
model: "gpt-5.5".to_string(),
enabled: false,
reasoning_levels: vec![ReasoningEffort::High],
description: String::new(),
},
AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex-spark".to_string(),
model: "gpt-5.4-mini".to_string(),
enabled: false,
reasoning_levels: vec![ReasoningEffort::High],
description: String::new(),
@@ -3066,11 +3066,20 @@ model_verbosity = "high"
review_model: Some("gemini-3-flash".to_string()),
..Default::default()
};
cfg.profiles.insert(
"claude".to_string(),
ConfigProfile {
model: Some("claude-sonnet-4.5".to_string()),
..Default::default()
},
);
upgrade_legacy_model_slugs(&mut cfg);
assert_eq!(cfg.model.as_deref(), Some("claude-opus-4.8"));
assert_eq!(cfg.review_model.as_deref(), Some("gemini-3.5-flash"));
let profile = cfg.profiles.get("claude").expect("profile exists");
assert_eq!(profile.model.as_deref(), Some("claude-sonnet-4.6"));
}
#[test]
@@ -3157,9 +3166,10 @@ model_verbosity = "high"
.map(|agent| agent.name.to_ascii_lowercase())
.collect();
assert!(enabled_names.contains("code-gpt-5.5"));
assert!(enabled_names.contains("code-gpt-5.3-codex"));
assert!(enabled_names.contains("code-gpt-5.4"));
assert!(enabled_names.contains("claude-sonnet-4.5"));
assert!(enabled_names.contains("claude-sonnet-4.6"));
assert!(enabled_names.contains("gemini-3.1-pro"));
assert!(enabled_names.contains("gemini-3.5-flash"));
assert!(enabled_names.contains("qwen-3-coder"));
+4
View File
@@ -201,6 +201,10 @@ fn upgrade_legacy_model_slug(slug: &str) -> Option<String> {
return Some("claude-opus-4.8".to_string());
}
if slug.eq_ignore_ascii_case("claude-sonnet-4.5") {
return Some("claude-sonnet-4.6".to_string());
}
// Upgrade Gemini Pro aliases to the current Gemini CLI preset.
if matches!(
slug.to_ascii_lowercase().as_str(),
+2 -2
View File
@@ -956,13 +956,13 @@ fn default_auto_drive_model_routing_reasoning_levels() -> Vec<ReasoningEffort> {
pub fn default_auto_drive_model_routing_entries() -> Vec<AutoDriveModelRoutingEntry> {
vec![
AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex".to_string(),
model: "gpt-5.5".to_string(),
enabled: true,
reasoning_levels: vec![ReasoningEffort::High, ReasoningEffort::XHigh],
description: "Hard planning and complex problem solving".to_string(),
},
AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex-spark".to_string(),
model: "gpt-5.4-mini".to_string(),
enabled: true,
reasoning_levels: vec![ReasoningEffort::High],
description: "Fast implementation loops and failing-test iteration".to_string(),
+3 -3
View File
@@ -295,8 +295,8 @@ mod tests {
}
let defaults = get_default_models();
assert!(defaults.iter().any(|v| v == "code-gpt-5.5"));
assert!(defaults.iter().any(|v| v == "code-gpt-5.4"));
assert!(defaults.iter().any(|v| v == "code-gpt-5.3-codex"));
assert!(!defaults.iter().any(|v| v == "qwen-3-coder"));
assert!(!defaults.iter().any(|v| v == "gemini-3-flash"));
assert!(!defaults.iter().any(|v| v == "claude-sonnet-4.5"));
@@ -334,7 +334,7 @@ mod tests {
}
let defaults = get_default_models();
assert!(defaults.iter().any(|v| v == "claude-sonnet-4.5"));
assert!(defaults.iter().any(|v| v == "claude-sonnet-4.6"));
restore_var("PATH", orig_path);
restore_var("HOME", orig_home);
@@ -375,8 +375,8 @@ mod tests {
let plan_prompt = result.unwrap();
assert!(plan_prompt.contains("final, comprehensive plan"));
// Default agents list should include non-Codex providers when no [[agents]] configured
assert!(plan_prompt.contains("code-gpt-5.5"));
assert!(plan_prompt.contains("code-gpt-5.4"));
assert!(plan_prompt.contains("code-gpt-5.3-codex"));
assert!(!plan_prompt.contains("cloud-gpt-5.1-codex-max"));
// Test /solve command
@@ -236,7 +236,7 @@ impl AutoDriveSettingsView {
}
fn default_routing_model() -> String {
"gpt-5.3-codex".to_string()
"gpt-5.5".to_string()
}
fn normalize_routing_model(model: &str) -> Option<String> {
@@ -1278,7 +1278,7 @@ mod tests {
let (tx, _rx) = channel();
AutoDriveSettingsView::new(
AppEventSender::new(tx),
"gpt-5.3-codex".to_string(),
"gpt-5.5".to_string(),
ReasoningEffort::High,
false,
true,
@@ -1287,7 +1287,7 @@ mod tests {
true,
model_routing_enabled,
entries,
vec!["gpt-5.3-codex".to_string(), "gpt-5.3-codex-spark".to_string()],
vec!["gpt-5.5".to_string(), "gpt-5.4-mini".to_string()],
AutoContinueMode::Manual,
)
}
@@ -1297,7 +1297,7 @@ mod tests {
let mut view = build_view(
true,
vec![AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex".to_string(),
model: "gpt-5.5".to_string(),
enabled: true,
reasoning_levels: vec![ReasoningEffort::High],
description: String::new(),
@@ -1318,7 +1318,7 @@ mod tests {
let mut view = build_view(
true,
vec![AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex".to_string(),
model: "gpt-5.5".to_string(),
enabled: true,
reasoning_levels: vec![ReasoningEffort::High],
description: String::new(),
@@ -1349,7 +1349,7 @@ mod tests {
let mut view = build_view(
false,
vec![AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex".to_string(),
model: "gpt-5.5".to_string(),
enabled: false,
reasoning_levels: vec![ReasoningEffort::High],
description: String::new(),
@@ -1373,7 +1373,7 @@ mod tests {
let mut view = build_view(
true,
vec![AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex".to_string(),
model: "gpt-5.5".to_string(),
enabled: true,
reasoning_levels: vec![ReasoningEffort::High],
description: String::new(),
@@ -1409,7 +1409,7 @@ mod tests {
let mut view = build_view(
true,
vec![AutoDriveModelRoutingEntry {
model: "gpt-5.3-codex".to_string(),
model: "gpt-5.5".to_string(),
enabled: true,
reasoning_levels: vec![ReasoningEffort::High],
description: String::new(),
+2 -2
View File
@@ -35154,7 +35154,7 @@ use code_core::protocol::OrderMeta;
write: false,
write_requested: Some(false),
models: Some(vec![
"claude-sonnet-4.5".to_string(),
"claude-sonnet-4.6".to_string(),
"gemini-3.1-pro".to_string(),
]),
}];
@@ -35169,7 +35169,7 @@ use code_core::protocol::OrderMeta;
assert!(message.contains("Run diagnostics"));
assert!(message.contains("Please run agent.create"));
assert!(message.contains("write: false"));
assert!(message.contains("Models: [claude-sonnet-4.5, gemini-3.1-pro]"));
assert!(message.contains("Models: [claude-sonnet-4.6, gemini-3.1-pro]"));
assert!(message.contains("Draft alternative fix"));
assert!(message.contains("Focus on parser module"));
assert!(message.contains("agent.wait"));
+2 -3
View File
@@ -42,11 +42,10 @@ pub(crate) fn migration_copy_for_key(key: &str) -> ModelMigrationCopy {
can_opt_out: true,
},
code_common::model_presets::HIDE_GPT_5_2_CODEX_MIGRATION_PROMPT_CONFIG => ModelMigrationCopy {
heading: "Upgrade available: GPT-5.2 Codex",
heading: "Upgrade available: GPT-5.3 Codex",
content: &[
"OpenAI's latest frontier agentic coding model is here: gpt-5.2-codex.",
"GPT-5.2 Codex has been superseded by gpt-5.3-codex.",
"Switch now for better coding results; you can keep your current model if you prefer.",
"Learn more: https://openai.com/index/introducing-gpt-5-2-codex/",
],
can_opt_out: true,
},
+80
View File
@@ -45,6 +45,9 @@ pub(crate) fn merge_remote_models(
if remote_slugs.contains(&preset.model.to_ascii_lowercase()) {
continue;
}
if should_skip_chatgpt_account_catalog_fallback(&preset, auth_mode) {
continue;
}
preset.is_default = false;
remote_presets.push(preset);
}
@@ -60,6 +63,20 @@ pub(crate) fn merge_remote_models(
remote_presets
}
fn should_skip_chatgpt_account_catalog_fallback(
preset: &ModelPreset,
auth_mode: Option<AuthMode>,
) -> bool {
if !auth_mode.is_some_and(AuthMode::is_chatgpt) {
return false;
}
matches!(
preset.model.to_ascii_lowercase().as_str(),
"gpt-5.2" | "gpt-5.2-codex" | "gpt-5.3-codex" | "gpt-5.3-codex-spark"
)
}
fn model_info_to_preset(info: ModelInfo) -> ModelPreset {
let pro_only = info.slug.eq_ignore_ascii_case("gpt-5.3-codex-spark");
let show_in_picker = info.visibility == ModelVisibility::List
@@ -102,6 +119,69 @@ fn model_info_to_preset(info: ModelInfo) -> ModelPreset {
}
}
#[cfg(test)]
mod tests {
use super::*;
fn remote_model(slug: &str, priority: i32) -> ModelInfo {
serde_json::from_value(serde_json::json!({
"slug": slug,
"display_name": slug,
"description": format!("{slug} desc"),
"default_reasoning_level": "medium",
"supported_reasoning_levels": [
{"effort": "low", "description": "low"},
{"effort": "medium", "description": "medium"}
],
"shell_type": "shell_command",
"visibility": "list",
"supported_in_api": true,
"priority": priority,
"upgrade": null,
"base_instructions": "",
"supports_reasoning_summaries": false,
"support_verbosity": false,
"default_verbosity": null,
"apply_patch_tool_type": null,
"truncation_policy": {"mode": "bytes", "limit": 10000},
"supports_parallel_tool_calls": false,
"context_window": null,
"experimental_supported_tools": []
}))
.expect("valid model")
}
#[test]
fn chatgpt_remote_catalog_does_not_resurrect_sunset_local_models() {
let merged = merge_remote_models(
vec![remote_model("gpt-5.5", 0), remote_model("gpt-5.4", 1)],
code_common::model_presets::builtin_model_presets(Some(AuthMode::ChatGPT), true),
Some(AuthMode::ChatGPT),
true,
);
let ids: Vec<&str> = merged.iter().map(|preset| preset.id.as_str()).collect();
assert!(ids.contains(&"gpt-5.5"));
assert!(ids.contains(&"gpt-5.4"));
assert!(!ids.contains(&"gpt-5.2"));
assert!(!ids.contains(&"gpt-5.2-codex"));
assert!(!ids.contains(&"gpt-5.3-codex"));
assert!(!ids.contains(&"gpt-5.3-codex-spark"));
}
#[test]
fn api_key_presets_keep_gpt_5_3_codex_available() {
let merged = merge_remote_models(
vec![remote_model("gpt-5.5", 0)],
code_common::model_presets::builtin_model_presets(Some(AuthMode::ApiKey), false),
Some(AuthMode::ApiKey),
false,
);
assert!(merged.iter().any(|preset| preset.id == "gpt-5.3-codex"));
}
}
fn map_reasoning_effort(effort: RemoteReasoningEffort) -> ProtocolReasoningEffort {
match effort {
RemoteReasoningEffort::None => ProtocolReasoningEffort::Minimal,
+2 -2
View File
@@ -5,7 +5,7 @@ Every Code can launch external CLI “agents” and orchestrate them in multi-ag
## Agent configuration (`[[agents]]` in `config.toml`)
```toml
[[agents]]
name = "code-gpt-5.3-codex" # slug or alias shown in pickers
name = "code-gpt-5.5" # slug or alias shown in pickers
command = "coder" # executable; defaults to name
args = ["--foo", "bar"] # base argv
args_read_only = ["-s", "read-only", "-a", "never", "exec", "--skip-git-repo-check"]
@@ -19,7 +19,7 @@ instructions = "Preamble added to this agent’s prompt"
Field recap: `name` (slug/alias), `command` (absolute paths ok), `args*` (RO/RW lists override base), `env`, `read_only`, `enabled`, optional `description` and `instructions`.
### Built-in defaults
If no `[[agents]]` are configured, Code advertises built-ins (gated by env `CODE_ENABLE_CLOUD_AGENT_MODEL` for cloud variants): `code-gpt-5.4`, `code-gpt-5.4-mini`, `code-gpt-5.3-codex`, `code-gpt-5.3-codex-spark`, `claude-opus-4.8`, `gemini-3.1-pro`, `claude-sonnet-4.5`, `gemini-3.5-flash`, `claude-haiku-4.5`, `qwen-3-coder`, `cloud-gpt-5.1-codex-max`. Built-ins strip any user `--model/-m` flags to avoid conflicts and inject their own.
If no `[[agents]]` are configured, Code advertises built-ins (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`, `gemini-3.1-pro`, `claude-sonnet-4.6`, `gemini-3.5-flash`, `claude-haiku-4.5`, `qwen-3-coder`, `cloud-gpt-5.1-codex-max`. Built-ins strip any user `--model/-m` flags to avoid conflicts and inject their own.
Tip: `gemini` resolves to `gemini-3.5-flash` through Antigravity CLI. Use `gemini-3.1-pro` when you want the higher-capacity Gemini CLI option.
+2 -2
View File
@@ -198,7 +198,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 model slugs (for example `code-gpt-5.4`, `claude-sonnet-4.5`) automatically inject the correct `--model` or `-m` flag. To avoid conflicting arguments, Code strips any `--model`/`-m` flags you place in `args`, `args_read_only`, or `args_write` before launching the agent. If you need a new model variant, add a slug in `code-rs/core/src/agent_defaults.rs` (or set an environment variable consumed by the CLI) rather than pinning the flag here.
> **Note:** Built-in model slugs (for example `code-gpt-5.5`, `claude-sonnet-4.6`) automatically inject the correct `--model` or `-m` flag. To avoid conflicting arguments, Code strips any `--model`/`-m` flags you place in `args`, `args_read_only`, or `args_write` before launching the agent. If you need a new model variant, add a slug in `code-rs/core/src/agent_defaults.rs` (or set an environment variable consumed by the CLI) rather than pinning the flag here.
```toml
[[agents]]
@@ -449,7 +449,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 model slugs for multi-agent runs: `code-gpt-5.4`, `code-gpt-5.4-mini`, `code-gpt-5.3-codex`, `code-gpt-5.3-codex-spark`, `claude-opus-4.8`, `gemini-3.1-pro`, `claude-sonnet-4.5`, `gemini-3.5-flash`, `claude-haiku-4.5`, and `qwen-3-coder`. The cloud counterpart, `cloud-gpt-5.1-codex-max`, only appears when `CODE_ENABLE_CLOUD_AGENT_MODEL=1` is set. (`gemini` resolves to the Antigravity-backed `gemini-3.5-flash`.) You can override the list by defining `[[agents]]` entries or by specifying `agents = [ … ]` on a given `[[subagents.commands]]` entry.
By default (when no `[[agents]]` are configured) Code advertises these model slugs for multi-agent runs: `code-gpt-5.5`, `code-gpt-5.4`, `code-gpt-5.4-mini`, `claude-opus-4.8`, `gemini-3.1-pro`, `claude-sonnet-4.6`, `gemini-3.5-flash`, `claude-haiku-4.5`, and `qwen-3-coder`. The cloud counterpart, `cloud-gpt-5.1-codex-max`, only appears when `CODE_ENABLE_CLOUD_AGENT_MODEL=1` is set. (`gemini` resolves to the Antigravity-backed `gemini-3.5-flash`.) You can override the list by defining `[[agents]]` entries or by specifying `agents = [ … ]` on a given `[[subagents.commands]]` entry.
```toml
[[subagents.commands]]
+3 -3
View File
@@ -119,10 +119,10 @@ Implementation Notes
- Prompt formatting for `/plan`, `/solve`, and `/code` lives in
`code-rs/core/src/slash_commands.rs`.
When no `[[agents]]` are configured, the orchestrator advertises the
following model slugs to the LLM for multi-agent runs: `code-gpt-5.4`,
`code-gpt-5.4-mini`, `code-gpt-5.3-codex`, `code-gpt-5.3-codex-spark`,
following model slugs to the LLM for multi-agent runs: `code-gpt-5.5`,
`code-gpt-5.4`, `code-gpt-5.4-mini`,
`claude-opus-4.8`, `gemini-3.1-pro`,
`claude-sonnet-4.5`, `gemini-3.5-flash`,
`claude-sonnet-4.6`, `gemini-3.5-flash`,
`claude-haiku-4.5`, and `qwen-3-coder` (with
`cloud-gpt-5.1-codex-max` gated by `CODE_ENABLE_CLOUD_AGENT_MODEL`). (`gemini`
resolves to `gemini-3.5-flash`.) You can replace or pin this set via