diff --git a/docs/providers.md b/docs/providers.md index 2cad144..a50b52c 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -76,18 +76,21 @@ Runs Claude-style coding sessions against OpenAI's Codex using your ChatGPT/Code **Authentication:** Sign in with `coven-code codex login` (ChatGPT/Codex OAuth). Tokens are stored per profile under `~/.coven-code/accounts/codex//`. See [auth.md](auth.md) for the multi-account flow. -**Default model:** `gpt-5.5` +**Default model:** `gpt-5.6-sol` **Bundled Codex model list:** | Model ID | Notes | |---|---| -| `gpt-5.5` | Default; recommended for most Codex tasks | +| `gpt-5.6-sol` | Default; flagship GPT-5.6 model for complex coding, computer use, research, and cybersecurity | +| `gpt-5.6-terra` | Balanced GPT-5.6 model for everyday work at lower cost than Sol | +| `gpt-5.6-luna` | Fast, lower-cost GPT-5.6 model for clear, repeatable tasks | +| `gpt-5.5` | Previous-generation frontier model for complex Codex tasks | | `gpt-5.4` | Frontier Codex model | | `gpt-5.4-mini` | Fast/lower-cost model for lighter coding tasks and subagents | | `gpt-5.3-codex-spark` | Research preview for ChatGPT Pro users | | `gpt-5.3-codex` | Deprecated for ChatGPT sign-in; retained for explicit selection | -| `gpt-5.2-codex` | Deprecated for ChatGPT sign-in; retained for explicit selection | +| `gpt-5.2-codex` | Legacy Codex/API-key compatibility model | | `gpt-5.1-codex` | Legacy Codex model | | `gpt-5.1-codex-mini` | Legacy mini model | | `gpt-5.1-codex-max` | Legacy max model | diff --git a/src-rust/crates/api/src/lib.rs b/src-rust/crates/api/src/lib.rs index 0fea2b1..f2c6f73 100644 --- a/src-rust/crates/api/src/lib.rs +++ b/src-rust/crates/api/src/lib.rs @@ -674,7 +674,7 @@ pub mod client { .header("anthropic-version", &self.config.api_version) .header("content-type", "application/json"); if self.config.use_bearer_auth { - req = req.header("Authorization", format!("Bearer {}", &self.config.api_key)); + req = req.header("Authorization", format!("Bearer {}", self.config.api_key)); } else { req = req.header("x-api-key", &self.config.api_key); } @@ -727,7 +727,7 @@ pub mod client { .header("accept", "text/event-stream"); if use_oauth { - req = req.header("Authorization", format!("Bearer {}", &self.config.api_key)); + req = req.header("Authorization", format!("Bearer {}", self.config.api_key)); } else { // Compute CCH billing hash and attach on the API-key path // only — this is the codepath the official client uses diff --git a/src-rust/crates/api/src/model_registry.rs b/src-rust/crates/api/src/model_registry.rs index 7277f99..c4e1c24 100644 --- a/src-rust/crates/api/src/model_registry.rs +++ b/src-rust/crates/api/src/model_registry.rs @@ -970,6 +970,11 @@ fn flagship_patterns_for(provider_id: &str) -> &'static [&'static str] { "claude-sonnet-3", ], "openai" => &[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.6", + "gpt-5.5", "gpt-5.2-pro", "gpt-5.2", "gpt-5.1", @@ -1001,7 +1006,16 @@ fn flagship_patterns_for(provider_id: &str) -> &'static [&'static str] { ], "zai" => &["glm-5.1", "glm-5", "glm-4.7"], "minimax" => &["minimax-m2"], - "codex" | "openai-codex" => &["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark"], + "codex" | "openai-codex" => &[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.6", + "gpt-5.5", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.3-codex-spark", + ], "ollama" | "lmstudio" | "lm-studio" | "llamacpp" | "llama-cpp" => &[ "qwen3-coder", "qwen2.5-coder", @@ -1034,11 +1048,17 @@ fn codex_models_fallback(provider_id: &str, small: bool) -> Option { return None; } if small { - // Prefer the documented "mini" entry; fall through to the default - // model id if the constant is ever pruned. + // Prefer the latest documented cost-efficient Codex entry; fall + // through to older mini models or the default if the constant is ever + // pruned. claurst_core::codex_oauth::CODEX_MODELS .iter() - .find(|(id, _)| id.contains("mini")) + .find(|(id, _)| *id == "gpt-5.6-luna") + .or_else(|| { + claurst_core::codex_oauth::CODEX_MODELS + .iter() + .find(|(id, _)| id.contains("mini")) + }) .map(|(id, _)| (*id).to_string()) .or_else(|| Some(claurst_core::codex_oauth::DEFAULT_CODEX_MODEL.to_string())) } else { @@ -1049,7 +1069,7 @@ fn codex_models_fallback(provider_id: &str, small: bool) -> Option { fn small_patterns_for(provider_id: &str) -> &'static [&'static str] { match provider_id { "anthropic" => &["claude-haiku-4", "claude-haiku-3-5", "claude-haiku"], - "codex" | "openai" | "openai-codex" => &["gpt-5.4-mini", "gpt-5-mini"], + "codex" | "openai" | "openai-codex" => &["gpt-5.6-luna", "gpt-5.4-mini", "gpt-5-mini"], _ => &["mini", "haiku", "flash", "lite", "small", "nano"], } } @@ -1188,6 +1208,7 @@ mod tests { let best = reg .best_model_for_provider("codex") .expect("codex best model must fall back to CODEX_MODELS default"); + assert_eq!(best, claurst_core::codex_oauth::DEFAULT_CODEX_MODEL); assert!( claurst_core::codex_oauth::CODEX_MODELS .iter() @@ -1197,6 +1218,7 @@ mod tests { let small = reg .best_small_model_for_provider("codex") .expect("codex small model must fall back to a mini/default id"); + assert_eq!(small, "gpt-5.6-luna"); assert!( claurst_core::codex_oauth::CODEX_MODELS .iter() diff --git a/src-rust/crates/api/src/providers/codex.rs b/src-rust/crates/api/src/providers/codex.rs index 596b3b1..d1beaaa 100644 --- a/src-rust/crates/api/src/providers/codex.rs +++ b/src-rust/crates/api/src/providers/codex.rs @@ -918,7 +918,9 @@ impl LlmProvider for CodexProvider { provider_id: self.id.clone(), name: name.to_string(), context_window: match *id { - "gpt-5.5" | "gpt-5.4" => 1_050_000, + "gpt-5.6-sol" | "gpt-5.6-terra" | "gpt-5.6-luna" | "gpt-5.5" | "gpt-5.4" => { + 1_050_000 + } "gpt-5.4-mini" | "gpt-5.3-codex" | "gpt-5.2-codex" | "gpt-5.1-codex" | "gpt-5.1-codex-mini" | "gpt-5.1-codex-max" | "gpt-5.2" | "gpt-5-codex" | "gpt-5-mini" => 400_000, @@ -928,9 +930,10 @@ impl LlmProvider for CodexProvider { _ => 128_000, }, max_output_tokens: match *id { - "gpt-5.5" | "gpt-5.4" | "gpt-5.4-mini" | "gpt-5.3-codex" | "gpt-5.2-codex" - | "gpt-5.1-codex" | "gpt-5.1-codex-mini" | "gpt-5.1-codex-max" | "gpt-5.2" - | "gpt-5-codex" | "gpt-5-mini" => 128_000, + "gpt-5.6-sol" | "gpt-5.6-terra" | "gpt-5.6-luna" | "gpt-5.5" | "gpt-5.4" + | "gpt-5.4-mini" | "gpt-5.3-codex" | "gpt-5.2-codex" | "gpt-5.1-codex" + | "gpt-5.1-codex-mini" | "gpt-5.1-codex-max" | "gpt-5.2" | "gpt-5-codex" + | "gpt-5-mini" => 128_000, _ => 32_768, }, }) diff --git a/src-rust/crates/commands/src/lib.rs b/src-rust/crates/commands/src/lib.rs index 137d6ac..39f5ff9 100644 --- a/src-rust/crates/commands/src/lib.rs +++ b/src-rust/crates/commands/src/lib.rs @@ -773,7 +773,7 @@ impl SlashCommand for ModelCommand { /model — open the model picker\n\ /model claude-sonnet-4-6 — switch to Claude Sonnet 4.6\n\ /model claude-haiku-4-5 — switch to Claude Haiku 4.5\n\ - /model codex/gpt-5.5 — switch to Codex (requires codex login)" + /model codex/gpt-5.6-sol — switch to Codex (requires codex login)" } async fn execute(&self, args: &str, ctx: &mut CommandContext) -> CommandResult { diff --git a/src-rust/crates/core/src/codex_oauth.rs b/src-rust/crates/core/src/codex_oauth.rs index c9bd9d5..0a613d3 100644 --- a/src-rust/crates/core/src/codex_oauth.rs +++ b/src-rust/crates/core/src/codex_oauth.rs @@ -27,7 +27,10 @@ pub const CODEX_SCOPES: &str = "openid profile email offline_access"; /// Available Codex models pub const CODEX_MODELS: &[(&str, &str)] = &[ - ("gpt-5.5", "GPT-5.5 (default)"), + ("gpt-5.6-sol", "GPT-5.6 Sol (default)"), + ("gpt-5.6-terra", "GPT-5.6 Terra"), + ("gpt-5.6-luna", "GPT-5.6 Luna"), + ("gpt-5.5", "GPT-5.5"), ("gpt-5.4", "GPT-5.4"), ("gpt-5.4-mini", "GPT-5.4 Mini"), ("gpt-5.3-codex-spark", "GPT-5.3 Codex Spark (Pro preview)"), @@ -46,7 +49,7 @@ pub const CODEX_MODELS: &[(&str, &str)] = &[ ]; /// Default Codex model to use -pub const DEFAULT_CODEX_MODEL: &str = "gpt-5.5"; +pub const DEFAULT_CODEX_MODEL: &str = "gpt-5.6-sol"; #[cfg(test)] mod tests { diff --git a/src-rust/crates/core/src/lib.rs b/src-rust/crates/core/src/lib.rs index 8cdc88e..a1671fd 100644 --- a/src-rust/crates/core/src/lib.rs +++ b/src-rust/crates/core/src/lib.rs @@ -832,17 +832,17 @@ pub mod config { ManagedAgentPreset { name: "codex-tiered", label: "Codex Tiered", - description: "gpt-5.5 manages, gpt-5.4-mini executes", - manager_model: "codex/gpt-5.5", - executor_model: "codex/gpt-5.4-mini", + description: "gpt-5.6-sol manages, gpt-5.6-luna executes", + manager_model: "codex/gpt-5.6-sol", + executor_model: "codex/gpt-5.6-luna", executor_max_turns: 10, max_concurrent_executors: 4, }, ManagedAgentPreset { name: "cross-codex-anthropic", label: "Cross: Codex + Claude", - description: "gpt-5.5 manages, Sonnet 4.6 executes", - manager_model: "codex/gpt-5.5", + description: "gpt-5.6-sol manages, Sonnet 4.6 executes", + manager_model: "codex/gpt-5.6-sol", executor_model: "anthropic/claude-sonnet-4-6", executor_max_turns: 10, max_concurrent_executors: 4, diff --git a/src-rust/crates/tui/src/app.rs b/src-rust/crates/tui/src/app.rs index 9136efc..3a47437 100644 --- a/src-rust/crates/tui/src/app.rs +++ b/src-rust/crates/tui/src/app.rs @@ -275,6 +275,7 @@ pub(crate) mod test_env { old_anthropic_config_dir: Option, old_anthropic_api_key: Option, old_oauth_client_id: Option, + old_claude_bin: Option, old_user: Option, old_username: Option, _lock: MutexGuard<'static, ()>, @@ -293,6 +294,8 @@ pub(crate) mod test_env { old_anthropic_config_dir: std::env::var("ANTHROPIC_CONFIG_DIR").ok(), old_anthropic_api_key: std::env::var("ANTHROPIC_API_KEY").ok(), old_oauth_client_id: std::env::var(claurst_core::oauth::CLIENT_ID_ENV).ok(), + old_claude_bin: std::env::var(claurst_api::providers::claude_cli::CLAUDE_BIN_ENV) + .ok(), old_user: std::env::var("USER").ok(), old_username: std::env::var("USERNAME").ok(), _lock: lock, @@ -302,6 +305,7 @@ pub(crate) mod test_env { std::env::remove_var("ANTHROPIC_CONFIG_DIR"); std::env::remove_var("ANTHROPIC_API_KEY"); std::env::remove_var(claurst_core::oauth::CLIENT_ID_ENV); + std::env::remove_var(claurst_api::providers::claude_cli::CLAUDE_BIN_ENV); match user { Some(value) => std::env::set_var("USER", value), None => std::env::remove_var("USER"), @@ -335,6 +339,12 @@ pub(crate) mod test_env { Some(value) => std::env::set_var(claurst_core::oauth::CLIENT_ID_ENV, value), None => std::env::remove_var(claurst_core::oauth::CLIENT_ID_ENV), } + match &self.old_claude_bin { + Some(value) => { + std::env::set_var(claurst_api::providers::claude_cli::CLAUDE_BIN_ENV, value) + } + None => std::env::remove_var(claurst_api::providers::claude_cli::CLAUDE_BIN_ENV), + } match &self.old_user { Some(value) => std::env::set_var("USER", value), None => std::env::remove_var("USER"), @@ -7941,18 +7951,23 @@ role = "Research" } #[test] - fn provider_picker_prioritizes_import_when_claude_credentials_exist() { + fn provider_picker_marks_claude_cli_local_when_binary_exists() { let temp = tempfile::tempdir().expect("tempdir"); let home = temp.path().join("home"); let coven_home = temp.path().join("coven"); - std::fs::create_dir_all(home.join(".claude")).expect("claude dir"); + let claude_bin = temp.path().join(if cfg!(windows) { + "claude.cmd" + } else { + "claude" + }); + std::fs::create_dir_all(&home).expect("home"); std::fs::create_dir_all(&coven_home).expect("coven home"); - std::fs::write( - home.join(".claude").join(".credentials.json"), - r#"{"claudeAiOauth":{"accessToken":"sk-ant-oat01-test"}}"#, - ) - .expect("credentials"); + std::fs::write(&claude_bin, "").expect("claude binary"); let _guard = EnvGuard::set(&home, &coven_home); + std::env::set_var( + claurst_api::providers::claude_cli::CLAUDE_BIN_ENV, + &claude_bin, + ); let items = provider_picker_items(); diff --git a/src-rust/crates/tui/src/dialog_select.rs b/src-rust/crates/tui/src/dialog_select.rs index c4e640f..28e10ba 100644 --- a/src-rust/crates/tui/src/dialog_select.rs +++ b/src-rust/crates/tui/src/dialog_select.rs @@ -445,7 +445,7 @@ mod tests { SelectItem { id: "codex".into(), title: "Codex".into(), - description: "gpt-5.5 via ChatGPT".into(), + description: "gpt-5.6-sol via ChatGPT".into(), category: "Recommended".into(), badge: None, }, diff --git a/src-rust/crates/tui/src/model_picker.rs b/src-rust/crates/tui/src/model_picker.rs index 7b88b40..bf66dc4 100644 --- a/src-rust/crates/tui/src/model_picker.rs +++ b/src-rust/crates/tui/src/model_picker.rs @@ -361,7 +361,9 @@ fn codex_provider_models() -> Vec { .iter() .map(|(id, name)| { let ctx = match *id { - "gpt-5.5" | "gpt-5.4" => "1M ctx", + "gpt-5.6-sol" | "gpt-5.6-terra" | "gpt-5.6-luna" | "gpt-5.5" | "gpt-5.4" => { + "1M ctx" + } "gpt-5.4-mini" | "gpt-5.3-codex" | "gpt-5.2" | "gpt-5.2-codex" | "gpt-5.1-codex" | "gpt-5.1-codex-mini" | "gpt-5.1-codex-max" => "400K ctx", "gpt-5.3-codex-spark" => "128K ctx", diff --git a/src-rust/crates/tui/src/onboarding_dialog.rs b/src-rust/crates/tui/src/onboarding_dialog.rs index 7eaf065..9488d83 100644 --- a/src-rust/crates/tui/src/onboarding_dialog.rs +++ b/src-rust/crates/tui/src/onboarding_dialog.rs @@ -165,7 +165,7 @@ const PROVIDER_ENTRIES: &[ProviderEntry] = &[ }, ProviderEntry { name: "Codex", - tagline: " gpt-5.5 via Codex CLI login", + tagline: " gpt-5.6-sol via Codex CLI login", setup: "Codex CLI", setup_suffix: "", },