Remove retired model entries while preserving migration prompts (#44250)

## Why

Saved model selections can outlive their bundled catalog entries and still need migration guidance.

## What changed

- Remove `gpt-5.2` and `gpt-5.4-mini` from the bundled model catalog.
- Preserve migration from saved `gpt-5.4-mini` selections to `gpt-5.6-luna` when the old model is absent, while respecting catalog-provided migration metadata.
- Update tests and snapshots to use current models or explicit test fixtures, and remove the obsolete prompt fixture.

## Testing

Extend migration tests to cover catalog metadata precedence, saved selections, replacement model and reasoning-effort persistence, acknowledged prompts, and missing or hidden migration targets.

GitOrigin-RevId: 82beea504cc4a15699677c28a61ec0939af3069c
This commit is contained in:
rhan-oai authored and copyberry committed 2026-09-09 17:56:33 +00:00
1 parent 6eecd04fc1
commit eb7bd64ef9
58 files changed
+670 -1192

No files matched your search

+17 -77
View File
@@ -9,7 +9,6 @@ use codex_core::config::Constrained;
use codex_core::config::ThreadStoreConfig;
use codex_core::sandboxing::SandboxPermissions;
use codex_features::Feature;
use codex_models_manager::bundled_models_response;
use codex_protocol::approvals::NetworkApprovalProtocol;
use codex_protocol::approvals::NetworkPolicyAmendment;
use codex_protocol::approvals::NetworkPolicyRuleAction;
@@ -644,7 +643,6 @@ struct ScenarioSpec {
action: ActionKind,
sandbox_permissions: SandboxPermissions,
features: Vec<Feature>,
model_override: Option<&'static str>,
outcome: Outcome,
expectation: Expectation,
}
@@ -927,7 +925,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::FileCreated {
target: TargetPath::OutsideWorkspace("dfa_on_request.txt"),
@@ -944,7 +941,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::Auto,
expectation: Expectation::FileCreated {
target: TargetPath::OutsideWorkspace("dfa_on_request_5_1.txt"),
@@ -961,7 +957,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::NetworkSuccess {
body_contains: "danger-network-ok",
@@ -977,7 +972,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::Auto,
expectation: Expectation::NetworkSuccessNoExitCode {
body_contains: "danger-network-ok",
@@ -992,7 +986,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("blocked in untrusted project"),
expected_reason: None,
@@ -1010,7 +1003,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("blocked in untrusted project"),
expected_reason: None,
@@ -1028,7 +1020,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("blocked by distinctive approval policy"),
expected_reason: None,
@@ -1046,7 +1037,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1064,7 +1054,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApprovalWithAmendment {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1092,7 +1081,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApprovalWithAmendment {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1120,7 +1108,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::CommandFailure {
output_contains: "you cannot ask for escalated permissions",
@@ -1136,7 +1123,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApprovalWithAmendment {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1158,7 +1144,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1179,7 +1164,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1200,7 +1184,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApprovalWithAmendment {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1220,7 +1203,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1240,7 +1222,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1260,7 +1241,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::FileCreated {
target: TargetPath::OutsideWorkspace("dfa_never.txt"),
@@ -1277,7 +1257,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::Auto,
expectation: Expectation::FileCreatedNoExitCode {
target: TargetPath::OutsideWorkspace("dfa_never_5_1.txt"),
@@ -1294,7 +1273,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1314,7 +1292,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1333,7 +1310,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::CommandSuccess {
stdout_contains: "trusted-read-only",
@@ -1348,7 +1324,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::Auto,
expectation: Expectation::CommandSuccessNoExitCode {
stdout_contains: "trusted-read-only",
@@ -1364,7 +1339,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: None,
outcome: Outcome::Auto,
expectation: Expectation::NetworkFailure { expect_tag: "ERR:" },
},
@@ -1378,7 +1352,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: None,
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1398,7 +1371,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1417,7 +1389,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1436,7 +1407,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: None,
outcome: Outcome::PatchApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1456,7 +1426,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::Auto,
expectation: Expectation::PatchApplied {
target: TargetPath::Workspace("apply_patch_freeform.txt"),
@@ -1473,7 +1442,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::Auto,
expectation: Expectation::PatchApplied {
target: TargetPath::OutsideWorkspace("apply_patch_freeform_danger.txt"),
@@ -1490,7 +1458,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::PatchApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1510,7 +1477,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::PatchApproval {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1530,7 +1496,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: None,
outcome: Outcome::PatchApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1550,7 +1515,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::PatchApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1570,7 +1534,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.4"),
outcome: Outcome::Auto,
expectation: Expectation::FileNotCreated {
target: TargetPath::OutsideWorkspace("apply_patch_freeform_never.txt"),
@@ -1589,7 +1552,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: None,
outcome: Outcome::Auto,
expectation: Expectation::FileNotCreated {
target: TargetPath::Workspace("ro_never.txt"),
@@ -1612,7 +1574,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::CommandSuccess {
stdout_contains: "trusted-never",
@@ -1628,7 +1589,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::FileCreated {
target: TargetPath::Workspace("ww_on_request.txt"),
@@ -1645,7 +1605,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: None,
outcome: Outcome::Auto,
expectation: Expectation::NetworkFailure { expect_tag: "ERR:" },
},
@@ -1659,7 +1618,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::Approved,
expected_reason: None,
@@ -1679,7 +1637,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::NetworkSuccess {
body_contains: "workspace-network-ok",
@@ -1695,7 +1652,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: None,
outcome: Outcome::Auto,
expectation: Expectation::FileNotCreated {
target: TargetPath::OutsideWorkspace("ww_never.txt"),
@@ -1719,7 +1675,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![Feature::UnifiedExec],
model_override: Some("gpt-5.2"),
outcome: Outcome::Auto,
expectation: Expectation::CommandSuccess {
stdout_contains: "hello unified exec",
@@ -1737,7 +1692,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![Feature::UnifiedExec],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::Approved,
expected_reason: Some(DEFAULT_UNIFIED_EXEC_JUSTIFICATION),
@@ -1756,7 +1710,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![Feature::UnifiedExec],
model_override: None,
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1775,7 +1728,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![Feature::UnifiedExec],
model_override: None,
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1794,7 +1746,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::RequireEscalated,
features: vec![Feature::UnifiedExec],
model_override: None,
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected by user"),
expected_reason: None,
@@ -1814,7 +1765,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected dynamic shell word"),
expected_reason: None,
@@ -1832,7 +1782,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected dynamic shell word"),
expected_reason: None,
@@ -1851,7 +1800,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected dynamic shell word"),
expected_reason: None,
@@ -1870,7 +1818,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected dynamic shell word"),
expected_reason: None,
@@ -1888,7 +1835,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected dynamic shell word"),
expected_reason: None,
@@ -1906,7 +1852,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected dynamic shell word"),
expected_reason: None,
@@ -1924,7 +1869,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected dynamic shell word"),
expected_reason: None,
@@ -1943,7 +1887,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("rejected dynamic shell word"),
expected_reason: None,
@@ -1961,7 +1904,6 @@ fn scenarios() -> Vec<ScenarioSpec> {
},
sandbox_permissions: SandboxPermissions::UseDefault,
features: vec![],
model_override: Some("gpt-5.2"),
outcome: Outcome::ExecApproval {
decision: ReviewDecision::denied("blocked in untrusted project"),
expected_reason: None,
@@ -2027,29 +1969,27 @@ async fn run_scenario(scenario: &ScenarioSpec) -> Result<()> {
let approval_policy = scenario.approval_policy;
let sandbox_policy = scenario.sandbox_policy.clone();
let features = scenario.features.clone();
let model_override = scenario.model_override;
let model = model_override.unwrap_or("gpt-5.4");
let policy_src = scenario.action.policy_src();
let thread_store_id = format!("approval-scenario-{}", scenario.name);
let model_catalog = bundled_models_response()?;
let mut builder = test_codex().with_model(model).with_config(move |config| {
config.model_catalog = Some(model_catalog);
// These scenarios assert tool behavior, not rollout persistence.
config.experimental_thread_store = ThreadStoreConfig::InMemory {
id: thread_store_id,
};
config.permissions.approval_policy = Constrained::allow_any(approval_policy);
config
.set_legacy_sandbox_policy(sandbox_policy.clone())
.expect("set sandbox policy");
for feature in features {
let mut builder = test_codex()
.with_model("gpt-5.5")
.with_config(move |config| {
// These scenarios assert tool behavior, not rollout persistence.
config.experimental_thread_store = ThreadStoreConfig::InMemory {
id: thread_store_id,
};
config.permissions.approval_policy = Constrained::allow_any(approval_policy);
config
.features
.enable(feature)
.expect("test config should allow feature update");
}
});
.set_legacy_sandbox_policy(sandbox_policy.clone())
.expect("set sandbox policy");
for feature in features {
config
.features
.enable(feature)
.expect("test config should allow feature update");
}
});
if let Some(policy_src) = policy_src {
builder = builder.with_pre_build_hook(move |home| {
let rules_dir = home.join("rules");
+12 -12
View File
@@ -2309,7 +2309,7 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_info
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.4").build(&server).await?;
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.5").build(&server).await?;
codex
.start_or_steer_turn(TurnInputRequest::user_input(vec![UserInput::Text {
@@ -2463,12 +2463,12 @@ async fn model_without_summary_parameter_support_omits_configured_summary() -> a
let model = model_catalog
.models
.iter_mut()
.find(|model| model.slug == "gpt-5.4")
.expect("gpt-5.4 exists in bundled models.json");
.find(|model| model.slug == "gpt-5.5")
.expect("gpt-5.5 exists in bundled models.json");
model.supports_reasoning_summary_parameter = false;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.with_config(move |config| {
config.model_catalog = Some(model_catalog);
config.model_reasoning_effort = Some(ReasoningEffort::High);
@@ -2597,8 +2597,8 @@ async fn user_turn_explicit_reasoning_summary_overrides_model_catalog_default()
let model = model_catalog
.models
.iter_mut()
.find(|model| model.slug == "gpt-5.4")
.expect("gpt-5.4 exists in bundled models.json");
.find(|model| model.slug == "gpt-5.5")
.expect("gpt-5.5 exists in bundled models.json");
model.default_reasoning_summary = ReasoningSummary::Detailed;
let TestCodex {
@@ -2607,7 +2607,7 @@ async fn user_turn_explicit_reasoning_summary_overrides_model_catalog_default()
session_configured,
..
} = test_codex()
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.with_config(move |config| {
config.model_catalog = Some(model_catalog);
})
@@ -2710,12 +2710,12 @@ async fn reasoning_summary_none_overrides_model_catalog_default() -> anyhow::Res
let model = model_catalog
.models
.iter_mut()
.find(|model| model.slug == "gpt-5.4")
.expect("gpt-5.4 exists in bundled models.json");
.find(|model| model.slug == "gpt-5.5")
.expect("gpt-5.5 exists in bundled models.json");
model.default_reasoning_summary = ReasoningSummary::Detailed;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.with_config(move |config| {
config.model_reasoning_summary = Some(ReasoningSummary::None);
config.model_catalog = Some(model_catalog);
@@ -2754,7 +2754,7 @@ async fn includes_default_verbosity_in_request() -> anyhow::Result<()> {
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.4").build(&server).await?;
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.5").build(&server).await?;
codex
.start_or_steer_turn(TurnInputRequest::user_input(vec![UserInput::Text {
@@ -2832,7 +2832,7 @@ async fn configured_verbosity_is_sent() -> anyhow::Result<()> {
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.with_config(|config| {
config.model_verbosity = Some(Verbosity::High);
})
+23 -16
View File
@@ -1384,10 +1384,10 @@ if (!tool) {
let model = model_catalog
.models
.iter_mut()
.find(|model| model.slug == "gpt-5.4")
.expect("gpt-5.4 exists in bundled models.json");
.find(|model| model.slug == "gpt-5.5")
.expect("gpt-5.5 exists in bundled models.json");
config.chatgpt_base_url = apps_base_url;
config.model = Some("gpt-5.4".to_string());
config.model = Some("gpt-5.5".to_string());
model.supports_search_tool = true;
config.model_catalog = Some(model_catalog);
});
@@ -1879,7 +1879,7 @@ text(JSON.stringify([results[0].output.includes("code-alpha-ready"), results[1].
// This model uses token-based tool-output truncation, giving the downstream
// history assertions a stable `…N tokens truncated…` marker.
const TOKEN_POLICY_TEST_MODEL: &str = "gpt-5.4";
const TOKEN_POLICY_TEST_MODEL: &str = "gpt-5.5";
// A nested `exec_command` limit applies to `result.output` inside JavaScript.
// The outer code-mode and history budgets apply after the script calls `text`.
@@ -4079,7 +4079,7 @@ async fn code_mode_resizes_explicit_original_image() -> Result<()> {
&server,
"use exec to return a large original-detail image",
&code,
"gpt-5.4",
"gpt-5.5",
|_| {},
)
.await?;
@@ -4138,7 +4138,7 @@ image({{
&server,
"emit images with legacy detail arguments and MCP metadata",
&code,
"gpt-5.4",
"gpt-5.5",
|config| {
let _ = config.features.enable(Feature::UnifiedImageBudget);
},
@@ -4183,14 +4183,21 @@ async fn code_mode_unified_image_budget_preserves_legacy_contract_for_unsupporte
skip_if_no_network!(Ok(()));
let server = responses::start_mock_server().await;
let (_test, second_mock) = run_code_mode_turn_with_model_and_config(
&server,
"emit an image on a legacy model",
r#"image("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==");"#,
"gpt-5.2",
|config| {
let builder = test_codex()
.with_model_info_override("image-budget-unsupported-model", |model| {
model.tool_mode = Some(ToolMode::CodeMode);
model.supports_image_detail_original = false;
})
.with_config(|config| {
let _ = config.features.enable(Feature::CodeMode);
let _ = config.features.enable(Feature::ExecutedToolCallMetadata);
let _ = config.features.enable(Feature::UnifiedImageBudget);
},
});
let (_test, second_mock) = run_code_mode_turn_with_builder(
&server,
"emit an image on a model without original-detail support",
r#"image("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==");"#,
builder,
)
.await?;
@@ -4219,7 +4226,7 @@ async fn code_mode_view_image_rejects_invalid_file_without_exposing_contents() -
let server = responses::start_mock_server().await;
let builder = test_codex()
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.with_config(|config| {
let _ = config.features.enable(Feature::CodeMode);
})
@@ -4268,7 +4275,7 @@ async fn code_mode_can_use_view_image_result_with_image_helper(
let server = responses::start_mock_server().await;
let mut builder = test_codex()
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.with_config(move |config| {
let _ = config.features.enable(Feature::CodeMode);
if unified_image_budget {
@@ -4404,7 +4411,7 @@ image(imageItem);
&server,
"use exec to call the rmcp image scenario tool and emit its image output",
code,
"gpt-5.4",
"gpt-5.5",
)
.await?;
+4 -1
View File
@@ -407,9 +407,12 @@ fn model_info_with_context_window(slug: &str, context_window: i64) -> ModelInfo
let mut model_info = models_response
.models
.into_iter()
.find(|model| model.slug == slug)
.find(|model| model.slug == "gpt-5.5")
.expect("model missing from models.json");
model_info.slug = slug.to_string();
model_info.context_window = Some(context_window);
model_info.max_context_window = Some(context_window);
model_info.comp_hash = None;
model_info
}
+51 -35
View File
@@ -166,6 +166,15 @@ fn test_model_info(
}
}
fn configure_model_switching_fixture(model: &mut ModelInfo) {
// Exercise model selection and history without changing the request transport,
// starting Code Mode, or compacting the mocked history on a model switch.
model.tool_mode = None;
model.multi_agent_version = None;
model.use_responses_lite = false;
model.comp_hash = None;
}
#[test_case(None; "model only")]
#[test_case(Some(Personality::Pragmatic); "model and personality")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -177,14 +186,16 @@ async fn first_turn_model_change_appends_model_instructions_developer_message(
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let mut builder = test_codex().with_model("gpt-5.2").with_config(|config| {
config
.features
.enable(Feature::Personality)
.expect("test config should allow feature update");
});
let mut builder = test_codex()
.with_model_info_override("gpt-5.6-terra", configure_model_switching_fixture)
.with_config(|config| {
config
.features
.enable(Feature::Personality)
.expect("test config should allow feature update");
});
let test = builder.build_with_auto_env(&server).await?;
let next_model = "gpt-5.4";
let next_model = "gpt-5.5";
submit_model_turn(
&test.codex,
@@ -220,10 +231,10 @@ async fn first_turn_model_change_appends_model_instructions_developer_message(
Ok(())
}
#[test_case(None, "gpt-5.2"; "model-generated base instructions and original model")]
#[test_case(None, "gpt-5.4"; "model-generated base instructions and fork model")]
#[test_case(Some("inherited custom base instructions"), "gpt-5.2"; "custom base instructions and original model")]
#[test_case(Some("inherited custom base instructions"), "gpt-5.4"; "custom base instructions and fork model")]
#[test_case(None, "gpt-5.6-terra"; "model-generated base instructions and original model")]
#[test_case(None, "gpt-5.5"; "model-generated base instructions and fork model")]
#[test_case(Some("inherited custom base instructions"), "gpt-5.6-terra"; "custom base instructions and original model")]
#[test_case(Some("inherited custom base instructions"), "gpt-5.5"; "custom base instructions and fork model")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn first_turn_after_empty_prefix_fork_preserves_inherited_base_instructions(
custom_base_instructions: Option<&'static str>,
@@ -234,9 +245,9 @@ async fn first_turn_after_empty_prefix_fork_preserves_inherited_base_instruction
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-fork")).await;
let initial_model = "gpt-5.2";
let initial_model = "gpt-5.6-terra";
let mut builder = test_codex()
.with_model(initial_model)
.with_model_info_override(initial_model, configure_model_switching_fixture)
.with_config(move |config| {
config.base_instructions = custom_base_instructions.map(str::to_string);
});
@@ -260,7 +271,7 @@ async fn first_turn_after_empty_prefix_fork_preserves_inherited_base_instruction
);
let mut fork_config = test.config.clone();
fork_config.model = Some("gpt-5.4".to_string());
fork_config.model = Some("gpt-5.5".to_string());
fork_config.base_instructions = None;
let fork = test
.thread_manager
@@ -337,13 +348,13 @@ async fn rollback_first_turn_model_change_removes_its_instructions(
)
.await;
let initial_model = "gpt-5.2";
let switched_model = "gpt-5.4";
let initial_model = "gpt-5.6-terra";
let switched_model = "gpt-5.5";
let rollback_ready = Arc::new(RollbackReady::default());
let mut extensions = ExtensionRegistryBuilder::new();
extensions.thread_lifecycle_contributor(rollback_ready.clone());
let mut builder = test_codex()
.with_model(initial_model)
.with_model_info_override(initial_model, configure_model_switching_fixture)
.with_extensions(Arc::new(extensions.build()));
let test = builder.build_with_auto_env(&server).await?;
@@ -368,7 +379,9 @@ async fn rollback_first_turn_model_change_removes_its_instructions(
let test = match followup {
RollbackFollowup::ColdResume => {
let mut resume_builder = test_codex().with_model(switched_model);
let mut resume_builder = test_codex()
.with_model_info_override(initial_model, configure_model_switching_fixture)
.with_model(switched_model);
resume_builder.restart(&server, &test).await?
}
RollbackFollowup::StartupModel | RollbackFollowup::SwitchedModel => test,
@@ -425,9 +438,10 @@ async fn model_change_appends_model_instructions_developer_message() -> Result<(
)
.await;
let mut builder = test_codex().with_model("gpt-5.2");
let mut builder =
test_codex().with_model_info_override("gpt-5.6-terra", configure_model_switching_fixture);
let test = builder.build(&server).await?;
let next_model = "gpt-5.4";
let next_model = "gpt-5.5";
test.codex
.start_or_steer_turn(read_only_user_turn(
@@ -516,7 +530,7 @@ async fn model_and_personality_change_only_appends_model_instructions() -> Resul
)
.await;
let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| {
let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| {
config
.features
.enable(Feature::Personality)
@@ -624,16 +638,18 @@ async fn settings_update_during_active_turn_applies_to_next_turn_only() -> Resul
],
)
.await;
let mut builder = test_codex().with_model("gpt-5.2").with_config(|config| {
config
.features
.enable(Feature::DefaultModeRequestUserInput)
.expect("test config should allow feature update");
config.model_reasoning_effort = Some(ReasoningEffort::Low);
config.model_reasoning_summary = Some(ReasoningSummary::Concise);
config.permissions.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
config.approvals_reviewer = ApprovalsReviewer::User;
});
let mut builder = test_codex()
.with_model_info_override("gpt-5.6-terra", configure_model_switching_fixture)
.with_config(|config| {
config
.features
.enable(Feature::DefaultModeRequestUserInput)
.expect("test config should allow feature update");
config.model_reasoning_effort = Some(ReasoningEffort::Low);
config.model_reasoning_summary = Some(ReasoningSummary::Concise);
config.permissions.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
config.approvals_reviewer = ApprovalsReviewer::User;
});
let test = builder.build_with_auto_env(&server).await?;
test.codex
@@ -651,7 +667,7 @@ async fn settings_update_during_active_turn_applies_to_next_turn_only() -> Resul
core_test_support::submit_thread_settings(
&test.codex,
ThreadSettingsOverrides {
model: Some("gpt-5.4".to_string()),
model: Some("gpt-5.5".to_string()),
effort: Some(Some(ReasoningEffort::High)),
summary: Some(ReasoningSummary::Detailed),
service_tier: Some(Some(ServiceTier::Fast.request_value().to_string())),
@@ -701,19 +717,19 @@ async fn settings_update_during_active_turn_applies_to_next_turn_only() -> Resul
request_settings,
vec![
json!({
"model": "gpt-5.2",
"model": "gpt-5.6-terra",
"reasoning": { "effort": "low", "summary": "concise" },
"service_tier": null,
"approval_policy_never": false,
}),
json!({
"model": "gpt-5.2",
"model": "gpt-5.6-terra",
"reasoning": { "effort": "low", "summary": "concise" },
"service_tier": null,
"approval_policy_never": false,
}),
json!({
"model": "gpt-5.4",
"model": "gpt-5.5",
"reasoning": { "effort": "high", "summary": "detailed" },
"service_tier": "priority",
"approval_policy_never": true,
@@ -514,7 +514,7 @@ async fn snapshot_model_visible_layout_resume_with_personality_change() -> Resul
.with_extensions(skills_extensions())
.with_config(|config| {
config.update_plan_enabled = true;
config.model = Some("gpt-5.2".to_string());
config.model = Some("gpt-5.5".to_string());
});
let initial = initial_builder.build(&server).await?;
let codex = Arc::clone(&initial.codex);
@@ -26,7 +26,7 @@ async fn auth_and_provider_switches_do_not_reuse_chatgpt_catalog() -> Result<()>
let mut model = bundled_models_response()?
.models
.into_iter()
.find(|model| model.slug == "gpt-5.4")
.find(|model| model.slug == "gpt-5.5")
.unwrap();
model.visibility = ModelVisibility::List;
model.default_service_tier = Some("priority".into());
@@ -40,7 +40,7 @@ async fn auth_and_provider_switches_do_not_reuse_chatgpt_catalog() -> Result<()>
let chatgpt = test_codex()
.with_home(home.clone())
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.build_with_auto_env(&server)
.await?;
assert!(home.path().join("models_cache.json").exists());
@@ -77,7 +77,7 @@ async fn auth_and_provider_switches_do_not_reuse_chatgpt_catalog() -> Result<()>
let api = test_codex()
.with_home(home.clone())
.with_auth(CodexAuth::from_api_key("api-key"))
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.build_with_auto_env(&server)
.await?;
let ordinary = responses::mount_sse_once(
@@ -124,7 +124,7 @@ async fn auth_and_provider_switches_do_not_reuse_chatgpt_catalog() -> Result<()>
let other = test_codex()
.with_home(home)
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.with_config(|config| {
config.model_provider_id = "second".into();
config.model_provider.name = "Second".into();
+12 -11
View File
@@ -44,6 +44,7 @@ use wiremock::MockServer;
const LOCAL_FRIENDLY_TEMPLATE: &str =
"You optimize for team morale and being a supportive teammate as much as code quality.";
const BUNDLED_FRIENDLY_TEMPLATE: &str = "You have a vivid inner life as Codex:";
const LOCAL_PRAGMATIC_TEMPLATE: &str = "You are a deeply pragmatic, effective software engineer.";
fn read_only_text_turn(
@@ -93,7 +94,7 @@ async fn user_turn_personality_none_does_not_add_update_message() -> anyhow::Res
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| {
let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| {
config
.features
.enable(Feature::Personality)
@@ -130,7 +131,7 @@ async fn config_personality_some_sets_instructions_template() -> anyhow::Result<
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| {
let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| {
config
.features
.enable(Feature::Personality)
@@ -154,7 +155,7 @@ async fn config_personality_some_sets_instructions_template() -> anyhow::Result<
let instructions_text = request.instructions_text();
assert!(
instructions_text.contains(LOCAL_FRIENDLY_TEMPLATE),
instructions_text.contains(BUNDLED_FRIENDLY_TEMPLATE),
"expected personality update to include the local friendly template, got: {instructions_text:?}"
);
@@ -175,7 +176,7 @@ async fn config_personality_none_sends_no_personality() -> anyhow::Result<()> {
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| {
let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| {
config
.features
.enable(Feature::Personality)
@@ -198,7 +199,7 @@ async fn config_personality_none_sends_no_personality() -> anyhow::Result<()> {
let request = resp_mock.single_request();
let instructions_text = request.instructions_text();
assert!(
!instructions_text.contains(LOCAL_FRIENDLY_TEMPLATE),
!instructions_text.contains(BUNDLED_FRIENDLY_TEMPLATE),
"expected no friendly personality template, got: {instructions_text:?}"
);
assert!(
@@ -228,7 +229,7 @@ async fn config_personality_none_strips_baked_personality_section() -> anyhow::R
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let mut builder = test_codex()
.with_model_info_override("gpt-5.4", |model_info| {
.with_model_info_override("gpt-5.5", |model_info| {
if let Some(model_messages) = model_info.model_messages.as_mut() {
model_messages.instructions_template = Some("Base instructions\n# Personality\nBaked personality\n## Writing Style\nNested writing style\n# General\nGeneral instructions".to_string());
model_messages.instructions_variables = None;
@@ -270,7 +271,7 @@ async fn config_personality_none_preserves_explicit_base_instructions() -> anyho
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| {
let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| {
config
.features
.enable(Feature::Personality)
@@ -305,7 +306,7 @@ async fn default_personality_is_pragmatic_without_config_toml() -> anyhow::Resul
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let mut builder = test_codex().with_model("gpt-5.4").with_config(|config| {
let mut builder = test_codex().with_model("gpt-5.5").with_config(|config| {
config
.features
.enable(Feature::Personality)
@@ -490,8 +491,8 @@ async fn disabled_personality_sends_remote_default_instructions() -> anyhow::Res
let mut remote_model = bundled_models_response()?
.models
.into_iter()
.find(|model| model.slug == "gpt-5.4")
.expect("bundled gpt-5.4 model");
.find(|model| model.slug == "gpt-5.5")
.expect("bundled gpt-5.5 model");
remote_model.slug = remote_slug.to_string();
if let Some(model_messages) = remote_model.model_messages.as_mut() {
model_messages.instructions_template = Some("remote base\n{{ personality }}".to_string());
@@ -837,7 +838,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
.features
.enable(Feature::Personality)
.expect("test config should allow feature update");
config.model = Some("gpt-5.4".to_string());
config.model = Some("gpt-5.5".to_string());
});
let test = builder.build(&server).await?;
+26 -2
View File
@@ -2,17 +2,21 @@
use std::fs;
use std::path::Path;
use std::sync::Arc;
use codex_core::TurnInputRequest;
use codex_core::shell::default_user_shell;
use codex_features::Feature;
use codex_models_manager::bundled_models_response;
use codex_models_manager::collaboration_mode_presets::builtin_collaboration_mode_presets;
use codex_models_manager::manager::StaticModelsManager;
use codex_protocol::config_types::CollaborationMode;
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::Settings;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::models::PermissionProfile;
use codex_protocol::openai_models::ModelsResponse;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
@@ -135,6 +139,25 @@ async fn prompt_tools_are_consistent_across_requests(
const CUSTOM_BASE_INSTRUCTIONS: &str =
"Custom base.\r\n## Plan tool\r\nPreserve this custom workflow.\r\n";
const MODEL_BASE_INSTRUCTIONS: &str = "Model base.\n\n## Planning\nYou have access to an `update_plan` tool which tracks steps.\n\n## Work\nKeep working.\n\n## `update_plan`\nUpdate the plan.\n";
let mut model = bundled_models_response()?
.models
.into_iter()
.find(|model| model.slug == "gpt-5.5")
.expect("bundled gpt-5.5 model");
model
.model_messages
.as_mut()
.expect("bundled model messages")
.instructions_template = Some(MODEL_BASE_INSTRUCTIONS.to_string());
// Supply model instructions without making them an explicit config catalog override.
let models_manager = Arc::new(StaticModelsManager::new(
/*auth_manager*/ None,
ModelsResponse {
models: vec![model],
},
));
let server = start_mock_server().await;
let req1 = mount_sse_once(
@@ -154,9 +177,10 @@ async fn prompt_tools_are_consistent_across_requests(
thread_manager,
..
} = test_codex()
.with_models_manager(models_manager)
.with_pre_build_hook(write_global_instructions)
.with_config(move |config| {
config.model = Some("gpt-5.2".to_string());
config.model = Some("gpt-5.5".to_string());
if let Some(update_plan_enabled) = update_plan_enabled {
config.update_plan_enabled = update_plan_enabled;
}
@@ -220,7 +244,7 @@ async fn prompt_tools_are_consistent_across_requests(
collaboration_mode: Some(CollaborationMode {
mode: ModeKind::Plan,
settings: Settings {
model: "gpt-5.2".to_string(),
model: "gpt-5.5".to_string(),
reasoning_effort: None,
developer_instructions: Some(mode_instructions.clone()),
},
+4 -4
View File
@@ -76,8 +76,8 @@ async fn disabled_update_plan_preserves_custom_catalog_instructions() -> Result<
let model = catalog
.models
.iter_mut()
.find(|model| model.slug == "gpt-5.2")
.expect("bundled gpt-5.2 model");
.find(|model| model.slug == "gpt-5.5")
.expect("bundled gpt-5.5 model");
let messages = model
.model_messages
.as_mut()
@@ -85,7 +85,7 @@ async fn disabled_update_plan_preserves_custom_catalog_instructions() -> Result<
messages.instructions_template = Some(INSTRUCTIONS.to_string());
messages.instructions_variables = None;
let test = test_codex()
.with_model("gpt-5.2")
.with_model("gpt-5.5")
.with_config(move |config| {
config.update_plan_enabled = false;
config.model_catalog = Some(catalog);
@@ -544,7 +544,7 @@ async fn namespaced_model_slug_uses_catalog_metadata_without_fallback_warning()
skip_if_sandbox!(Ok(()));
let server = MockServer::start().await;
let requested_model = "custom/gpt-5.2-codex";
let requested_model = "custom/gpt-5.5-codex";
let response_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
@@ -137,10 +137,10 @@ fn configure_apps_without_search_tool(config: &mut Config, apps_base_url: &str)
let model = model_catalog
.models
.iter_mut()
.find(|model| model.slug == "gpt-5.4")
.expect("gpt-5.4 exists in bundled models.json");
.find(|model| model.slug == "gpt-5.5")
.expect("gpt-5.5 exists in bundled models.json");
config.chatgpt_base_url = apps_base_url.to_string();
config.model = Some("gpt-5.4".to_string());
config.model = Some("gpt-5.5".to_string());
config.tool_suggest.discoverables = vec![ToolSuggestDiscoverable {
kind: ToolSuggestDiscoverableType::Connector,
id: DISCOVERABLE_GMAIL_ID.to_string(),
@@ -1059,7 +1059,7 @@ async fn run_remote_plugin_install_metadata_case() -> Result<()> {
"source": "endpoint_recommendation",
"thread_id": thread_id,
"turn_id": turn_id,
"model_slug": "gpt-5.4",
"model_slug": "gpt-5.5",
"product_client_id": codex_login::default_client::originator().value,
}
})
+2 -2
View File
@@ -1749,7 +1749,7 @@ async fn stdio_mcp_tool_call_includes_sandbox_state_meta(
let model = models
.models
.iter_mut()
.find(|model| model.slug == "gpt-5.4")
.find(|model| model.slug == "gpt-5.5")
.expect("bundled model should exist");
model.node_repl_auto_review_required = node_repl_auto_review_required;
model.node_repl_disabled = node_repl_disabled;
@@ -1803,7 +1803,7 @@ async fn stdio_mcp_tool_call_includes_sandbox_state_meta(
let rmcp_test_server_bin = remote_aware_stdio_server_bin()?;
let fixture = test_codex()
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
.with_model("gpt-5.4")
.with_model("gpt-5.5")
.with_config(move |config| {
insert_mcp_server(
config,
@@ -310,7 +310,7 @@ async fn model_verification_emits_structured_event_without_reroute_or_warning()
]));
let _mock = mount_response_once(&server, response).await;
let mut builder = test_codex().with_model(SERVER_MODEL);
let mut builder = test_codex().with_model("gpt-5.5");
let test = builder.build(&server).await?;
test.codex
+1 -1
View File
@@ -702,7 +702,7 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -
apps_tool_call
.pointer("/params/_meta/x-codex-turn-metadata/model")
.and_then(Value::as_str),
Some("gpt-5.4")
Some("gpt-5.5")
);
let first_request_reasoning_effort = first_request_body
.pointer("/reasoning/effort")
+2 -2
View File
@@ -121,8 +121,8 @@ fn step_settings_models() -> Vec<ModelInfo> {
.expect("bundled models should parse")
.models
.into_iter()
.find(|model| model.slug == "gpt-5.4")
.expect("bundled gpt-5.4 model");
.find(|model| model.slug == "gpt-5.5")
.expect("bundled gpt-5.5 model");
[MODEL_A, MODEL_B, MODEL_C]
.into_iter()
.map(|slug| {
@@ -66,7 +66,7 @@ async fn ordinary_settings_updates_keep_the_running_turn_snapshot() -> Result<()
.unwrap()
.models
.into_iter()
.find(|model| model.slug == "gpt-5.4")
.find(|model| model.slug == "gpt-5.5")
.unwrap();
config.model_catalog = Some(ModelsResponse {
models: ["snapshot-model-a", "snapshot-model-b"]
@@ -79,15 +79,15 @@ const TURN_0_FORK_PROMPT: &str = "seed fork context";
const TURN_1_PROMPT: &str = "spawn a child and continue";
const TURN_2_NO_WAIT_PROMPT: &str = "follow up without wait";
const CHILD_PROMPT: &str = "child: do work";
const INHERITED_MODEL: &str = "gpt-5.2";
const INHERITED_MODEL: &str = "gpt-5.5";
const INHERITED_REASONING_EFFORT: ReasoningEffort = ReasoningEffort::XHigh;
const REQUESTED_MODEL: &str = "gpt-5.4";
const REQUESTED_MODEL: &str = "gpt-5.6-luna";
const REQUESTED_REASONING_EFFORT: ReasoningEffort = ReasoningEffort::Low;
const V2_DEFAULT_MODEL: &str = "gpt-5.6-terra";
const V2_DEFAULT_REASONING_EFFORT: ReasoningEffort = ReasoningEffort::High;
const V2_REQUESTED_MODEL: &str = "gpt-5.6-sol";
const V2_REQUESTED_REASONING_EFFORT: ReasoningEffort = ReasoningEffort::Low;
const ROLE_MODEL: &str = "gpt-5.4";
const ROLE_MODEL: &str = "gpt-5.5";
const ROLE_REASONING_EFFORT: ReasoningEffort = ReasoningEffort::High;
const SUBAGENT_START_CONTEXT: &str = "subagent start context reaches child";
const SUBAGENT_STOP_CONTINUATION: &str = "continue only the child";
@@ -1024,6 +1024,7 @@ async fn spawned_child_receives_forked_parent_context(
.await;
let mut builder = test_codex()
.with_model_info_override(INHERITED_MODEL, |model| model.comp_hash = None)
.with_history_mode(history_mode)
.with_config(|config| {
config
@@ -1522,6 +1523,9 @@ async fn spawned_full_history_v2_child_uses_model_precedence_without_dropping_co
.iter_mut()
.find(|model_info| model_info.slug == model)
.unwrap_or_else(|| panic!("{model} should exist in bundled models.json"));
if model == INHERITED_MODEL {
model_info.comp_hash = None;
}
let multi_agent = model_info
.model_messages
.as_mut()
@@ -2010,9 +2014,9 @@ async fn spawned_agent_uses_summary_support_for_final_model(
};
assert_eq!(child_body["model"], json!(REQUESTED_MODEL));
let expected_reasoning = if child_supports_summary {
json!({"effort": "medium", "summary": "detailed"})
json!({"effort": "medium", "summary": "detailed", "context": "all_turns"})
} else {
json!({"effort": "medium"})
json!({"effort": "medium", "context": "all_turns"})
};
assert_eq!(child_body["reasoning"], expected_reasoning);
assert_eq!(
@@ -3178,7 +3182,7 @@ async fn spawn_agent_rejects_reasoning_effort_unsupported_by_role_model() -> Res
assert_eq!(
output.as_deref(),
Some(
"Reasoning effort `ultra` is not supported for model `gpt-5.4`. Supported reasoning efforts: low, medium, high, xhigh"
"Reasoning effort `ultra` is not supported for model `gpt-5.5`. Supported reasoning efforts: low, medium, high, xhigh"
)
);
Ok(())
@@ -3251,7 +3255,7 @@ async fn spawn_agent_tool_description_mentions_role_locked_settings() -> Result<
role_block(&agent_type_description, "custom").expect("custom role description");
assert_eq!(
custom_role_description,
"custom: {\nCustom role\n- This role's model is set to `gpt-5.4` and its reasoning effort is set to `high`. These settings cannot be changed.\n}"
"custom: {\nCustom role\n- This role's model is set to `gpt-5.5` and its reasoning effort is set to `high`. These settings cannot be changed.\n}"
);
Ok(())