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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions crates/cli/src/agents/codex/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ pub(crate) fn install_codex_config(path: &Path, gateway_url: &str) -> Result<(),
}
doc["model_provider"] = value("nemo-relay-openai");
ensure_table(&mut doc, "features")["hooks"] = value(true);
set_multi_agent_v2_enabled(&mut doc, false);

let providers = ensure_table(&mut doc, "model_providers");
let mut provider = Table::new();
Expand Down Expand Up @@ -932,6 +933,7 @@ fn sanitize_codex_backup_doc(
restore_codex_client_proof_from_backup(&mut backup, &empty, challenge);
}
remove_table_item_if_bool(&mut backup, "features", "hooks", true);
remove_managed_multi_agent_v2_enabled(&mut backup);
remove_empty_table(&mut backup, "model_providers");
remove_empty_table(&mut backup, "features");
backup
Expand Down Expand Up @@ -1192,6 +1194,7 @@ fn restore_codex_config_from_backup(
if !preserve_hooks || feature_hooks_enabled(doc) != Some(true) {
restore_table_item_if_bool(doc, backup_doc, "features", "hooks", true);
}
restore_multi_agent_v2_enabled(doc, backup_doc);
}

fn remove_codex_config_without_backup(
Expand All @@ -1211,6 +1214,7 @@ fn remove_codex_config_without_backup(
if !preserve_hooks {
remove_table_item_if_bool(doc, "features", "hooks", true);
}
remove_managed_multi_agent_v2_enabled(doc);
}

pub(crate) fn remove_legacy_codex_hooks(path: &Path) -> Result<(), String> {
Expand Down Expand Up @@ -1379,6 +1383,7 @@ pub(crate) fn codex_config_doc_has_managed_install(doc: &DocumentMut, gateway_ur
== Some("nemo-relay-openai")
&& codex_provider_item_is_managed(doc, gateway_url)
&& feature_hooks_enabled(doc) == Some(true)
&& feature_multi_agent_v2_enabled(doc) == Some(false)
}

#[cfg(test)]
Expand Down Expand Up @@ -1504,6 +1509,74 @@ pub(crate) fn feature_hooks_enabled(doc: &DocumentMut) -> Option<bool> {
.and_then(|value| value.as_bool())
}

fn feature_multi_agent_v2_enabled(doc: &DocumentMut) -> Option<bool> {
let item = doc
.get("features")
.and_then(Item::as_table)
.and_then(|features| features.get("multi_agent_v2"))?;
if let Some(table) = item.as_table() {
return table
.get("enabled")
.and_then(Item::as_value)
.and_then(TomlValue::as_bool);
}
item.as_inline_table()
.and_then(|table| table.get("enabled"))
.and_then(TomlValue::as_bool)
}

fn set_multi_agent_v2_enabled(doc: &mut DocumentMut, enabled: bool) {
let features = ensure_table(doc, "features");
if let Some(item) = features.get_mut("multi_agent_v2") {
if let Some(table) = item.as_table_mut() {
table["enabled"] = value(enabled);
return;
}
if let Some(table) = item.as_inline_table_mut() {
table.insert("enabled", TomlValue::from(enabled));
return;
}
}
let mut table = Table::new();
table["enabled"] = value(enabled);
features["multi_agent_v2"] = Item::Table(table);
}

fn restore_multi_agent_v2_enabled(doc: &mut DocumentMut, backup: &DocumentMut) {
if feature_multi_agent_v2_enabled(doc) != Some(false) {
return;
}
match feature_multi_agent_v2_enabled(backup) {
Some(enabled) => set_multi_agent_v2_enabled(doc, enabled),
None => remove_managed_multi_agent_v2_enabled(doc),
}
}
Comment thread
willkill07 marked this conversation as resolved.

fn remove_managed_multi_agent_v2_enabled(doc: &mut DocumentMut) {
if feature_multi_agent_v2_enabled(doc) != Some(false) {
return;
}
let Some(features) = doc.get_mut("features").and_then(Item::as_table_mut) else {
return;
};
let remove_feature = if let Some(item) = features.get_mut("multi_agent_v2") {
if let Some(table) = item.as_table_mut() {
table.remove("enabled");
table.is_empty()
} else if let Some(table) = item.as_inline_table_mut() {
table.remove("enabled");
table.is_empty()
} else {
false
}
} else {
false
};
if remove_feature {
features.remove("multi_agent_v2");
}
}

pub(crate) fn remove_empty_table(doc: &mut DocumentMut, key: &str) {
let is_empty = doc
.get(key)
Expand Down
2 changes: 2 additions & 0 deletions crates/cli/src/agents/codex/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub(crate) fn prepare(launch: &mut PreparedAgentLaunch, gateway_url: &str) -> Re
"--config".to_string(),
"features.hooks=true".to_string(),
"--config".to_string(),
"features.multi_agent_v2.enabled=false".to_string(),
"--config".to_string(),
"model_provider=\"nemo-relay-openai\"".to_string(),
"--config".to_string(),
gateway_provider_config(gateway_url),
Expand Down
5 changes: 5 additions & 0 deletions crates/cli/tests/coverage/agents/launcher_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ fn prepares_codex_config_overrides() {

assert!(!prepared.argv.iter().any(|arg| arg == "--profile"));
assert!(prepared.argv.contains(&"features.hooks=true".into()));
assert!(
prepared
.argv
.contains(&"features.multi_agent_v2.enabled=false".into())
);
assert!(
prepared
.argv
Expand Down
77 changes: 77 additions & 0 deletions crates/cli/tests/coverage/agents/plugin_host_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,10 @@ fn repeated_codex_install_does_not_overwrite_original_backup() {
.unwrap()
.parse::<DocumentMut>()
.unwrap();
assert_eq!(
doc["features"]["multi_agent_v2"]["enabled"].as_bool(),
Some(false)
);
let token = codex_provider_client_token(&doc).unwrap();
assert!(
BootstrapChallengeKey::load()
Expand All @@ -1169,6 +1173,75 @@ fn repeated_codex_install_does_not_overwrite_original_backup() {
);
}

#[test]
fn codex_uninstall_restores_multi_agent_v2_setting() {
let dir = tempdir().unwrap();
let _home = HomeScope::enter(dir.path());
let path = dir.path().join(".codex").join("config.toml");
fs::create_dir_all(path.parent().unwrap()).unwrap();
let original = r#"model_provider = "openai"

[features.multi_agent_v2]
enabled = true
tool_namespace = "agents"
"#;
fs::write(&path, original).unwrap();

install_codex_config(&path, DEFAULT_URL).unwrap();
let installed = fs::read_to_string(&path)
.unwrap()
.parse::<DocumentMut>()
.unwrap();
assert_eq!(
installed["features"]["multi_agent_v2"]["enabled"].as_bool(),
Some(false)
);
assert_eq!(
installed["features"]["multi_agent_v2"]["tool_namespace"].as_str(),
Some("agents")
);

uninstall_codex_config(&path, DEFAULT_URL, false).unwrap();

let restored = fs::read_to_string(&path)
.unwrap()
.parse::<DocumentMut>()
.unwrap();
assert_eq!(restored["model_provider"].as_str(), Some("openai"));
assert_eq!(
restored["features"]["multi_agent_v2"]["enabled"].as_bool(),
Some(true)
);
assert_eq!(
restored["features"]["multi_agent_v2"]["tool_namespace"].as_str(),
Some("agents")
);
assert!(restored.get("model_providers").is_none());
assert!(!backup_path(&path).exists());
}

#[test]
fn codex_uninstall_removes_multi_agent_v2_absent_from_backup() {
let dir = tempdir().unwrap();
let _home = HomeScope::enter(dir.path());
let path = dir.path().join(".codex").join("config.toml");
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(&path, "model_provider = \"openai\"\n").unwrap();

install_codex_config(&path, DEFAULT_URL).unwrap();
assert!(backup_path(&path).exists());

uninstall_codex_config(&path, DEFAULT_URL, false).unwrap();

let restored = fs::read_to_string(&path)
.unwrap()
.parse::<DocumentMut>()
.unwrap();
assert_eq!(restored["model_provider"].as_str(), Some("openai"));
assert!(restored.get("features").is_none());
assert!(!backup_path(&path).exists());
}

#[test]
fn codex_client_token_supports_regular_header_tables() {
let document = r#"
Expand Down Expand Up @@ -1992,6 +2065,9 @@ model_provider = "nemo-relay-openai"
[features]
hooks = true

[features.multi_agent_v2]
enabled = false

[model_providers.nemo-relay-openai]
name = "NeMo Relay"
base_url = "http://127.0.0.1:47632"
Expand All @@ -2008,6 +2084,7 @@ supports_websockets = false
assert!(!updated.contains("model_provider"));
assert!(!updated.contains("nemo-relay-openai"));
assert!(!updated.contains("hooks = true"));
assert!(!updated.contains("multi_agent_v2"));
}

#[test]
Expand Down
Loading