diff --git a/codex-rs/app-server/tests/suite/v2/guardian_v2.rs b/codex-rs/app-server/tests/suite/v2/guardian_v2.rs index e3897c54a0..bc66fda917 100644 --- a/codex-rs/app-server/tests/suite/v2/guardian_v2.rs +++ b/codex-rs/app-server/tests/suite/v2/guardian_v2.rs @@ -536,10 +536,11 @@ async fn guardian_v2_routes_scoped_tool_approvals( ("approvals_reviewer = \"user\"", ApprovalsReviewer::User) } }; - let guardian_scope_config = if matches!(scope, GuardianToolScope::ComputerUseOnly { .. }) { - "\n\n[features.guardianv2]\nenabled = true\n\n[features.guardianv2.review_scope]\ncomputer_use_only = true" - } else { - "" + let guardian_scope_config = match scope { + GuardianToolScope::AllTools => { + "\n\n[features.guardianv2]\nenabled = true\n\n[features.guardianv2.review_scope]\ncomputer_use_only = false" + } + GuardianToolScope::ComputerUseOnly { .. } => "\n\n[features.guardianv2]\nenabled = true", }; let mut mock_config = MockResponsesConfig::new(&responses_url) .with_model(MODEL) @@ -550,9 +551,6 @@ async fn guardian_v2_routes_scoped_tool_approvals( "[mcp_servers.{server_name}]\nurl = \"{mcp_server_url}/mcp\"\ndefault_tools_approval_mode = \"prompt\"\n\n[analytics]\nenabled = true\n\n[otel]\nmetrics_exporter = {{ otlp-http = {{ endpoint = \"{responses_url}/metrics\", protocol = \"json\" }} }}{guardian_scope_config}" )) .enable_feature(Feature::GuardianApproval); - if matches!(scope, GuardianToolScope::AllTools) { - mock_config = mock_config.enable_feature(Feature::GuardianV2); - } if lifecycle.has_user_input() || lifecycle.has_root_user_input() { mock_config = mock_config.enable_feature(Feature::DefaultModeRequestUserInput); } diff --git a/codex-rs/ext/guardian-v2/src/async_scorer/config.rs b/codex-rs/ext/guardian-v2/src/async_scorer/config.rs index 66278a7fe6..e635d2187f 100644 --- a/codex-rs/ext/guardian-v2/src/async_scorer/config.rs +++ b/codex-rs/ext/guardian-v2/src/async_scorer/config.rs @@ -245,7 +245,7 @@ impl GuardianV2Config { .review_scope .as_ref() .and_then(|review_scope| review_scope.computer_use_only) - .unwrap_or(false) + .unwrap_or(true) { GuardianV2ReviewScope::ComputerUseOnly } else { @@ -265,7 +265,7 @@ impl GuardianV2Config { }), include_images: transcript_config .and_then(|transcript| transcript.include_images) - .unwrap_or(false), + .unwrap_or(true), max_message_entry_tokens, max_tool_entry_tokens, max_message_transcript_tokens, diff --git a/codex-rs/ext/guardian-v2/src/async_scorer/config_tests.rs b/codex-rs/ext/guardian-v2/src/async_scorer/config_tests.rs index f3c63c86fe..84c6a81cdb 100644 --- a/codex-rs/ext/guardian-v2/src/async_scorer/config_tests.rs +++ b/codex-rs/ext/guardian-v2/src/async_scorer/config_tests.rs @@ -13,22 +13,17 @@ use super::GuardianV2ReviewScope; use crate::async_scorer::transcript::truncate_entry; #[test] -fn sandboxed_exec_commands_are_excluded_by_default() { +fn review_scope_is_computer_use_only_by_default() { let config = GuardianV2Config::from_overrides(GuardianV2ConfigToml::default()).unwrap(); - assert_eq!( - config.review_scope, - GuardianV2ReviewScope::Standard { - sandboxed_exec_commands: false, - } - ); + assert_eq!(config.review_scope, GuardianV2ReviewScope::ComputerUseOnly); } #[test] fn sandboxed_exec_commands_can_be_included() { let config = GuardianV2Config::from_overrides(GuardianV2ConfigToml { review_scope: Some(GuardianV2ReviewScopeConfigToml { - computer_use_only: None, + computer_use_only: Some(false), sandboxed_exec_commands: Some(true), }), ..Default::default() diff --git a/codex-rs/ext/guardian-v2/src/async_scorer/extension_tests.rs b/codex-rs/ext/guardian-v2/src/async_scorer/extension_tests.rs index a531225b47..61f455b5b4 100644 --- a/codex-rs/ext/guardian-v2/src/async_scorer/extension_tests.rs +++ b/codex-rs/ext/guardian-v2/src/async_scorer/extension_tests.rs @@ -142,7 +142,7 @@ async fn installed_extension_reconnects_after_auth_refresh() -> Result<()> { .get::() .expect("Guardian v2 should initialize"); let turn_store = ExtensionData::new("turn-1"); - let tool_name = ToolName::plain("read_file"); + let tool_name = ToolName::namespaced("mcp__node_repl__", "js"); let payload = ToolPayload::Function { arguments: r#"{"path":"README.md"}"#.to_owned(), }; @@ -175,7 +175,7 @@ async fn installed_extension_reconnects_after_auth_refresh() -> Result<()> { .fast_approval_decision( &session_store, thread_store, - "review action", + r#"{"tool":"mcp_tool_call","server":"node_repl"}"#, /*extension_metrics*/ None, ) .await, @@ -675,7 +675,9 @@ async fn sample_configured_conversation_history( ) -> Result<(serde_json::Value, TestCodex, ExtensionRegistry)> { let thread_server = responses::start_mock_server().await; let guardian_policy = guardian_policy.map(str::to_owned); - let guardian_config = guardian_config.to_owned(); + let guardian_config = format!( + "{guardian_config}\n[features.guardianv2.review_scope]\ncomputer_use_only = false\n" + ); let has_model_defaults = model_defaults.is_some(); let builder = test_codex() .with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing()) @@ -692,10 +694,8 @@ async fn sample_configured_conversation_history( .with_model("gpt-5.5") .with_config(move |config| config.guardian_policy_config = guardian_policy) .with_pre_build_hook(move |home| { - if !guardian_config.is_empty() { - std::fs::write(home.join("config.toml"), guardian_config) - .expect("Guardian v2 configuration should be written"); - } + std::fs::write(home.join("config.toml"), guardian_config) + .expect("Guardian v2 configuration should be written"); }); let mut builder = if let Some(model_defaults) = model_defaults { builder.with_model_info_override("gpt-5.5", move |model| { @@ -1387,7 +1387,7 @@ max_recent_non_user_entries = 8 } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn contributor_includes_configured_transcript_images() -> Result<()> { +async fn contributor_includes_transcript_images_by_default() -> Result<()> { skip_if_no_network!(Ok(())); let history = vec![ @@ -1426,9 +1426,6 @@ async fn contributor_includes_configured_transcript_images() -> Result<()> { let configuration = r#" [features.guardianv2] enabled = true - -[features.guardianv2.transcript] -include_images = true "#; let (request, _test, _registry) = sample_configured_conversation_history( history, @@ -1869,7 +1866,7 @@ async fn contributor_skips_models_requiring_managed_guardian_review() -> Result< .fast_approval_decision( &session_store, thread_store, - "review action", + r#"{"tool":"mcp_tool_call","server":"node_repl"}"#, /*extension_metrics*/ None, ) .await, @@ -1877,7 +1874,7 @@ async fn contributor_skips_models_requiring_managed_guardian_review() -> Result< ); let turn_store = ExtensionData::new("turn-1"); - let tool_name = ToolName::plain("read_file"); + let tool_name = ToolName::namespaced("mcp__node_repl__", "js"); let payload = ToolPayload::Function { arguments: json!({ "path": "protected.md" }).to_string(), }; @@ -2262,7 +2259,7 @@ async fn contributor_reuses_the_latest_compatible_parent_compaction() -> Result< .with_pre_build_hook(|home| { std::fs::write( home.join("config.toml"), - "[features.guardianv2]\nenabled = true\nmax_parent_compaction_tokens = 256\n", + "[features.guardianv2]\nenabled = true\nmax_parent_compaction_tokens = 256\n\n[features.guardianv2.review_scope]\ncomputer_use_only = false\n", ) .expect("Guardian v2 parent compaction configuration should be written"); }) diff --git a/codex-rs/ext/guardian-v2/src/async_scorer/transcript.rs b/codex-rs/ext/guardian-v2/src/async_scorer/transcript.rs index 248490838f..ba59fae7c3 100644 --- a/codex-rs/ext/guardian-v2/src/async_scorer/transcript.rs +++ b/codex-rs/ext/guardian-v2/src/async_scorer/transcript.rs @@ -69,7 +69,7 @@ impl Default for TranscriptConfig { fn default() -> Self { Self { sources: vec![TranscriptSource::ToolCalls, TranscriptSource::ToolOutputs], - include_images: false, + include_images: true, max_message_entry_tokens: MAX_MESSAGE_ENTRY_TOKENS, max_tool_entry_tokens: MAX_TOOL_ENTRY_TOKENS, max_message_transcript_tokens: MAX_MESSAGE_TRANSCRIPT_TOKENS,