mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
chore: clean up argument-comment lint and roll out all-target CI on macOS (#16054)
## Why `argument-comment-lint` was green in CI even though the repo still had many uncommented literal arguments. The main gap was target coverage: the repo wrapper did not force Cargo to inspect test-only call sites, so examples like the `latest_session_lookup_params(true, ...)` tests in `codex-rs/tui_app_server/src/lib.rs` never entered the blocking CI path. This change cleans up the existing backlog, makes the default repo lint path cover all Cargo targets, and starts rolling that stricter CI enforcement out on the platform where it is currently validated. ## What changed - mechanically fixed existing `argument-comment-lint` violations across the `codex-rs` workspace, including tests, examples, and benches - updated `tools/argument-comment-lint/run-prebuilt-linter.sh` and `tools/argument-comment-lint/run.sh` so non-`--fix` runs default to `--all-targets` unless the caller explicitly narrows the target set - fixed both wrappers so forwarded cargo arguments after `--` are preserved with a single separator - documented the new default behavior in `tools/argument-comment-lint/README.md` - updated `rust-ci` so the macOS lint lane keeps the plain wrapper invocation and therefore enforces `--all-targets`, while Linux and Windows temporarily pass `-- --lib --bins` That temporary CI split keeps the stricter all-targets check where it is already cleaned up, while leaving room to finish the remaining Linux- and Windows-specific target-gated cleanup before enabling `--all-targets` on those runners. The Linux and Windows failures on the intermediate revision were caused by the wrapper forwarding bug, not by additional lint findings in those lanes. ## Validation - `bash -n tools/argument-comment-lint/run.sh` - `bash -n tools/argument-comment-lint/run-prebuilt-linter.sh` - shell-level wrapper forwarding check for `-- --lib --bins` - shell-level wrapper forwarding check for `-- --tests` - `just argument-comment-lint` - `cargo test` in `tools/argument-comment-lint` - `cargo test -p codex-terminal-detection` ## Follow-up - Clean up remaining Linux-only target-gated callsites, then switch the Linux lint lane back to the plain wrapper invocation. - Clean up remaining Windows-only target-gated callsites, then switch the Windows lint lane back to the plain wrapper invocation.
This commit is contained in:
@@ -289,7 +289,7 @@ async fn spawn_agent_errors_when_manager_dropped() {
|
||||
let control = AgentControl::default();
|
||||
let (_home, config) = test_config().await;
|
||||
let err = control
|
||||
.spawn_agent(config, text_input("hello"), None)
|
||||
.spawn_agent(config, text_input("hello"), /*session_source*/ None)
|
||||
.await
|
||||
.expect_err("spawn_agent should fail without a manager");
|
||||
assert_eq!(
|
||||
@@ -423,7 +423,7 @@ async fn send_inter_agent_communication_without_turn_queues_message_without_trig
|
||||
AgentPath::try_from("/root/worker").expect("agent path"),
|
||||
Vec::new(),
|
||||
"hello from tests".to_string(),
|
||||
false,
|
||||
/*trigger_turn*/ false,
|
||||
);
|
||||
|
||||
let submission_id = harness
|
||||
@@ -535,7 +535,11 @@ async fn spawn_agent_creates_thread_and_sends_prompt() {
|
||||
let harness = AgentControlHarness::new().await;
|
||||
let thread_id = harness
|
||||
.control
|
||||
.spawn_agent(harness.config.clone(), text_input("spawned"), None)
|
||||
.spawn_agent(
|
||||
harness.config.clone(),
|
||||
text_input("spawned"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("spawn_agent should succeed");
|
||||
let _thread = harness
|
||||
@@ -830,12 +834,20 @@ async fn spawn_agent_respects_max_threads_limit() {
|
||||
.expect("start thread");
|
||||
|
||||
let first_agent_id = control
|
||||
.spawn_agent(config.clone(), text_input("hello"), None)
|
||||
.spawn_agent(
|
||||
config.clone(),
|
||||
text_input("hello"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("spawn_agent should succeed");
|
||||
|
||||
let err = control
|
||||
.spawn_agent(config, text_input("hello again"), None)
|
||||
.spawn_agent(
|
||||
config,
|
||||
text_input("hello again"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect_err("spawn_agent should respect max threads");
|
||||
let CodexErr::AgentLimitReached {
|
||||
@@ -871,7 +883,11 @@ async fn spawn_agent_releases_slot_after_shutdown() {
|
||||
let control = manager.agent_control();
|
||||
|
||||
let first_agent_id = control
|
||||
.spawn_agent(config.clone(), text_input("hello"), None)
|
||||
.spawn_agent(
|
||||
config.clone(),
|
||||
text_input("hello"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("spawn_agent should succeed");
|
||||
let _ = control
|
||||
@@ -880,7 +896,11 @@ async fn spawn_agent_releases_slot_after_shutdown() {
|
||||
.expect("shutdown agent");
|
||||
|
||||
let second_agent_id = control
|
||||
.spawn_agent(config.clone(), text_input("hello again"), None)
|
||||
.spawn_agent(
|
||||
config.clone(),
|
||||
text_input("hello again"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("spawn_agent should succeed after shutdown");
|
||||
let _ = control
|
||||
@@ -909,12 +929,20 @@ async fn spawn_agent_limit_shared_across_clones() {
|
||||
let cloned = control.clone();
|
||||
|
||||
let first_agent_id = cloned
|
||||
.spawn_agent(config.clone(), text_input("hello"), None)
|
||||
.spawn_agent(
|
||||
config.clone(),
|
||||
text_input("hello"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("spawn_agent should succeed");
|
||||
|
||||
let err = control
|
||||
.spawn_agent(config, text_input("hello again"), None)
|
||||
.spawn_agent(
|
||||
config,
|
||||
text_input("hello again"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect_err("spawn_agent should respect shared guard");
|
||||
let CodexErr::AgentLimitReached { max_threads } = err else {
|
||||
@@ -947,7 +975,11 @@ async fn resume_agent_respects_max_threads_limit() {
|
||||
let control = manager.agent_control();
|
||||
|
||||
let resumable_id = control
|
||||
.spawn_agent(config.clone(), text_input("hello"), None)
|
||||
.spawn_agent(
|
||||
config.clone(),
|
||||
text_input("hello"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("spawn_agent should succeed");
|
||||
let _ = control
|
||||
@@ -956,7 +988,11 @@ async fn resume_agent_respects_max_threads_limit() {
|
||||
.expect("shutdown resumable thread");
|
||||
|
||||
let active_id = control
|
||||
.spawn_agent(config.clone(), text_input("occupy"), None)
|
||||
.spawn_agent(
|
||||
config.clone(),
|
||||
text_input("occupy"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("spawn_agent should succeed for active slot");
|
||||
|
||||
@@ -1002,7 +1038,7 @@ async fn resume_agent_releases_slot_after_resume_failure() {
|
||||
.expect_err("resume should fail for missing rollout path");
|
||||
|
||||
let resumed_id = control
|
||||
.spawn_agent(config, text_input("hello"), None)
|
||||
.spawn_agent(config, text_input("hello"), /*session_source*/ None)
|
||||
.await
|
||||
.expect("spawn should succeed after failed resume");
|
||||
let _ = control
|
||||
@@ -1140,7 +1176,7 @@ async fn multi_agent_v2_completion_ignores_dead_direct_parent() {
|
||||
AgentPath::root(),
|
||||
Vec::new(),
|
||||
"done".to_string(),
|
||||
true,
|
||||
/*trigger_turn*/ true,
|
||||
)
|
||||
));
|
||||
assert!(!has_subagent_notification(&root_history_items));
|
||||
@@ -1203,7 +1239,7 @@ async fn multi_agent_v2_completion_queues_message_for_direct_parent() {
|
||||
worker_path.clone(),
|
||||
Vec::new(),
|
||||
expected_message.clone(),
|
||||
false,
|
||||
/*trigger_turn*/ false,
|
||||
),
|
||||
},
|
||||
);
|
||||
@@ -1238,7 +1274,7 @@ async fn multi_agent_v2_completion_queues_message_for_direct_parent() {
|
||||
AgentPath::root(),
|
||||
Vec::new(),
|
||||
expected_message,
|
||||
false,
|
||||
/*trigger_turn*/ false,
|
||||
)
|
||||
));
|
||||
}
|
||||
@@ -1259,7 +1295,7 @@ async fn completion_watcher_notifies_parent_when_child_is_missing() {
|
||||
agent_role: Some("explorer".to_string()),
|
||||
})),
|
||||
child_thread_id.to_string(),
|
||||
None,
|
||||
/*child_agent_path*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(wait_for_subagent_notification(&parent_thread).await, true);
|
||||
@@ -1520,7 +1556,11 @@ async fn resume_agent_from_rollout_reads_archived_rollout_path() {
|
||||
let harness = AgentControlHarness::new().await;
|
||||
let child_thread_id = harness
|
||||
.control
|
||||
.spawn_agent(harness.config.clone(), text_input("hello"), None)
|
||||
.spawn_agent(
|
||||
harness.config.clone(),
|
||||
text_input("hello"),
|
||||
/*session_source*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("child spawn should succeed");
|
||||
|
||||
|
||||
@@ -16,11 +16,26 @@ fn agent_metadata(thread_id: ThreadId) -> AgentMetadata {
|
||||
|
||||
#[test]
|
||||
fn format_agent_nickname_adds_ordinals_after_reset() {
|
||||
assert_eq!(format_agent_nickname("Plato", 0), "Plato");
|
||||
assert_eq!(format_agent_nickname("Plato", 1), "Plato the 2nd");
|
||||
assert_eq!(format_agent_nickname("Plato", 2), "Plato the 3rd");
|
||||
assert_eq!(format_agent_nickname("Plato", 10), "Plato the 11th");
|
||||
assert_eq!(format_agent_nickname("Plato", 20), "Plato the 21st");
|
||||
assert_eq!(
|
||||
format_agent_nickname("Plato", /*nickname_reset_count*/ 0),
|
||||
"Plato"
|
||||
);
|
||||
assert_eq!(
|
||||
format_agent_nickname("Plato", /*nickname_reset_count*/ 1),
|
||||
"Plato the 2nd"
|
||||
);
|
||||
assert_eq!(
|
||||
format_agent_nickname("Plato", /*nickname_reset_count*/ 2),
|
||||
"Plato the 3rd"
|
||||
);
|
||||
assert_eq!(
|
||||
format_agent_nickname("Plato", /*nickname_reset_count*/ 10),
|
||||
"Plato the 11th"
|
||||
);
|
||||
assert_eq!(
|
||||
format_agent_nickname("Plato", /*nickname_reset_count*/ 20),
|
||||
"Plato the 21st"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -39,7 +54,10 @@ fn thread_spawn_depth_increments_and_enforces_limit() {
|
||||
});
|
||||
let child_depth = next_thread_spawn_depth(&session_source);
|
||||
assert_eq!(child_depth, 2);
|
||||
assert!(exceeds_thread_spawn_depth_limit(child_depth, 1));
|
||||
assert!(exceeds_thread_spawn_depth_limit(
|
||||
child_depth,
|
||||
/*max_depth*/ 1
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -47,7 +65,9 @@ fn non_thread_spawn_subagents_default_to_depth_zero() {
|
||||
let session_source = SessionSource::SubAgent(SubAgentSource::Review);
|
||||
assert_eq!(session_depth(&session_source), 0);
|
||||
assert_eq!(next_thread_spawn_depth(&session_source), 1);
|
||||
assert!(!exceeds_thread_spawn_depth_limit(1, 1));
|
||||
assert!(!exceeds_thread_spawn_depth_limit(
|
||||
/*depth*/ 1, /*max_depth*/ 1
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -142,14 +162,18 @@ fn release_is_idempotent_for_registered_threads() {
|
||||
#[test]
|
||||
fn failed_spawn_keeps_nickname_marked_used() {
|
||||
let registry = Arc::new(AgentRegistry::default());
|
||||
let mut reservation = registry.reserve_spawn_slot(None).expect("reserve slot");
|
||||
let mut reservation = registry
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve slot");
|
||||
let agent_nickname = reservation
|
||||
.reserve_agent_nickname_with_preference(&["alpha"], /*preferred*/ None)
|
||||
.expect("reserve agent name");
|
||||
assert_eq!(agent_nickname, "alpha");
|
||||
drop(reservation);
|
||||
|
||||
let mut reservation = registry.reserve_spawn_slot(None).expect("reserve slot");
|
||||
let mut reservation = registry
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve slot");
|
||||
let agent_nickname = reservation
|
||||
.reserve_agent_nickname_with_preference(&["alpha", "beta"], /*preferred*/ None)
|
||||
.expect("unused name should still be preferred");
|
||||
@@ -160,7 +184,7 @@ fn failed_spawn_keeps_nickname_marked_used() {
|
||||
fn agent_nickname_resets_used_pool_when_exhausted() {
|
||||
let registry = Arc::new(AgentRegistry::default());
|
||||
let mut first = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve first slot");
|
||||
let first_name = first
|
||||
.reserve_agent_nickname_with_preference(&["alpha"], /*preferred*/ None)
|
||||
@@ -170,7 +194,7 @@ fn agent_nickname_resets_used_pool_when_exhausted() {
|
||||
assert_eq!(first_name, "alpha");
|
||||
|
||||
let mut second = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve second slot");
|
||||
let second_name = second
|
||||
.reserve_agent_nickname_with_preference(&["alpha"], /*preferred*/ None)
|
||||
@@ -188,7 +212,7 @@ fn released_nickname_stays_used_until_pool_reset() {
|
||||
let registry = Arc::new(AgentRegistry::default());
|
||||
|
||||
let mut first = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve first slot");
|
||||
let first_name = first
|
||||
.reserve_agent_nickname_with_preference(&["alpha"], /*preferred*/ None)
|
||||
@@ -200,7 +224,7 @@ fn released_nickname_stays_used_until_pool_reset() {
|
||||
registry.release_spawned_thread(first_id);
|
||||
|
||||
let mut second = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve second slot");
|
||||
let second_name = second
|
||||
.reserve_agent_nickname_with_preference(&["alpha", "beta"], /*preferred*/ None)
|
||||
@@ -211,7 +235,7 @@ fn released_nickname_stays_used_until_pool_reset() {
|
||||
registry.release_spawned_thread(second_id);
|
||||
|
||||
let mut third = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve third slot");
|
||||
let third_name = third
|
||||
.reserve_agent_nickname_with_preference(&["alpha", "beta"], /*preferred*/ None)
|
||||
@@ -230,7 +254,7 @@ fn repeated_resets_advance_the_ordinal_suffix() {
|
||||
let registry = Arc::new(AgentRegistry::default());
|
||||
|
||||
let mut first = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve first slot");
|
||||
let first_name = first
|
||||
.reserve_agent_nickname_with_preference(&["Plato"], /*preferred*/ None)
|
||||
@@ -241,7 +265,7 @@ fn repeated_resets_advance_the_ordinal_suffix() {
|
||||
registry.release_spawned_thread(first_id);
|
||||
|
||||
let mut second = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve second slot");
|
||||
let second_name = second
|
||||
.reserve_agent_nickname_with_preference(&["Plato"], /*preferred*/ None)
|
||||
@@ -252,7 +276,7 @@ fn repeated_resets_advance_the_ordinal_suffix() {
|
||||
registry.release_spawned_thread(second_id);
|
||||
|
||||
let mut third = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve third slot");
|
||||
let third_name = third
|
||||
.reserve_agent_nickname_with_preference(&["Plato"], /*preferred*/ None)
|
||||
@@ -282,7 +306,7 @@ fn register_root_thread_indexes_root_path() {
|
||||
fn reserved_agent_path_is_released_when_spawn_fails() {
|
||||
let registry = Arc::new(AgentRegistry::default());
|
||||
let mut first = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve first slot");
|
||||
first
|
||||
.reserve_agent_path(&agent_path("/root/researcher"))
|
||||
@@ -290,7 +314,7 @@ fn reserved_agent_path_is_released_when_spawn_fails() {
|
||||
drop(first);
|
||||
|
||||
let mut second = registry
|
||||
.reserve_spawn_slot(None)
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve second slot");
|
||||
second
|
||||
.reserve_agent_path(&agent_path("/root/researcher"))
|
||||
@@ -301,7 +325,9 @@ fn reserved_agent_path_is_released_when_spawn_fails() {
|
||||
fn committed_agent_path_is_indexed_until_release() {
|
||||
let registry = Arc::new(AgentRegistry::default());
|
||||
let thread_id = ThreadId::new();
|
||||
let mut reservation = registry.reserve_spawn_slot(None).expect("reserve slot");
|
||||
let mut reservation = registry
|
||||
.reserve_spawn_slot(/*max_threads*/ None)
|
||||
.expect("reserve slot");
|
||||
reservation
|
||||
.reserve_agent_path(&agent_path("/root/researcher"))
|
||||
.expect("reserve path");
|
||||
|
||||
@@ -40,7 +40,10 @@ async fn write_role_config(home: &TempDir, name: &str, contents: &str) -> PathBu
|
||||
fn session_flags_layer_count(config: &Config) -> usize {
|
||||
config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|layer| layer.name == ConfigLayerSource::SessionFlags)
|
||||
.count()
|
||||
@@ -51,7 +54,7 @@ async fn apply_role_defaults_to_default_and_leaves_config_unchanged() {
|
||||
let (_home, mut config) = test_config_with_cli_overrides(Vec::new()).await;
|
||||
let before = config.clone();
|
||||
|
||||
apply_role_to_config(&mut config, None)
|
||||
apply_role_to_config(&mut config, /*role_name*/ None)
|
||||
.await
|
||||
.expect("default role should apply");
|
||||
|
||||
@@ -529,7 +532,10 @@ writable_roots = ["./sandbox-root"]
|
||||
|
||||
let role_layer = config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.rfind(|layer| layer.name == ConfigLayerSource::SessionFlags)
|
||||
.expect("expected a session flags layer");
|
||||
@@ -630,7 +636,10 @@ enabled = false
|
||||
.expect("custom role should apply");
|
||||
|
||||
let plugins_manager = Arc::new(PluginsManager::new(home.path().to_path_buf()));
|
||||
let skills_manager = SkillsManager::new(home.path().to_path_buf(), true);
|
||||
let skills_manager = SkillsManager::new(
|
||||
home.path().to_path_buf(),
|
||||
/*bundled_skills_enabled*/ true,
|
||||
);
|
||||
let plugin_outcome = plugins_manager.plugins_for_config(&config);
|
||||
let effective_skill_roots = plugin_outcome.effective_skill_roots();
|
||||
let skills_input = skills_load_input_from_config(&config, effective_skill_roots);
|
||||
|
||||
@@ -76,7 +76,8 @@ mod tests {
|
||||
supports_websockets: false,
|
||||
};
|
||||
|
||||
let telemetry = collect_auth_env_telemetry(&provider, false);
|
||||
let telemetry =
|
||||
collect_auth_env_telemetry(&provider, /*codex_api_key_env_enabled*/ false);
|
||||
|
||||
assert_eq!(
|
||||
telemetry.provider_env_key_name,
|
||||
|
||||
@@ -52,7 +52,8 @@ fn serializes_text_schema_with_strict_format() {
|
||||
"required": ["answer"],
|
||||
});
|
||||
let text_controls =
|
||||
create_text_param_for_request(None, &Some(schema.clone())).expect("text controls");
|
||||
create_text_param_for_request(/*verbosity*/ None, &Some(schema.clone()))
|
||||
.expect("text controls");
|
||||
|
||||
let req = ResponsesApiRequest {
|
||||
model: "gpt-5.1".to_string(),
|
||||
|
||||
@@ -16,14 +16,14 @@ fn test_model_client(session_source: SessionSource) -> ModelClient {
|
||||
crate::model_provider_info::WireApi::Responses,
|
||||
);
|
||||
ModelClient::new(
|
||||
None,
|
||||
/*auth_manager*/ None,
|
||||
ThreadId::new(),
|
||||
provider,
|
||||
session_source,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
/*model_verbosity*/ None,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@ fn test_session_telemetry() -> SessionTelemetry {
|
||||
ThreadId::new(),
|
||||
"gpt-test",
|
||||
"gpt-test",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*account_id*/ None,
|
||||
/*account_email*/ None,
|
||||
/*auth_mode*/ None,
|
||||
"test-originator".to_string(),
|
||||
false,
|
||||
/*log_user_prompts*/ false,
|
||||
"test-terminal".to_string(),
|
||||
SessionSource::Cli,
|
||||
)
|
||||
@@ -91,7 +91,12 @@ async fn summarize_memories_returns_empty_for_empty_input() {
|
||||
let session_telemetry = test_session_telemetry();
|
||||
|
||||
let output = client
|
||||
.summarize_memories(Vec::new(), &model_info, None, &session_telemetry)
|
||||
.summarize_memories(
|
||||
Vec::new(),
|
||||
&model_info,
|
||||
/*effort*/ None,
|
||||
&session_telemetry,
|
||||
)
|
||||
.await
|
||||
.expect("empty summarize request should succeed");
|
||||
assert_eq!(output.len(), 0);
|
||||
|
||||
@@ -41,7 +41,7 @@ fn inter_agent_assistant_message(text: &str) -> ResponseItem {
|
||||
AgentPath::root().join("worker").unwrap(),
|
||||
Vec::new(),
|
||||
text.to_string(),
|
||||
true,
|
||||
/*trigger_turn*/ true,
|
||||
);
|
||||
ResponseItem::Message {
|
||||
id: None,
|
||||
|
||||
@@ -247,17 +247,17 @@ async fn interrupting_regular_turn_waiting_on_startup_prewarm_emits_turn_aborted
|
||||
|
||||
fn test_model_client_session() -> crate::client::ModelClientSession {
|
||||
crate::client::ModelClient::new(
|
||||
None,
|
||||
/*auth_manager*/ None,
|
||||
ThreadId::try_from("00000000-0000-4000-8000-000000000001")
|
||||
.expect("test thread id should be valid"),
|
||||
crate::model_provider_info::ModelProviderInfo::create_openai_provider(
|
||||
/* base_url */ None,
|
||||
/* base_url */ /*base_url*/ None,
|
||||
),
|
||||
codex_protocol::protocol::SessionSource::Exec,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
/*model_verbosity*/ None,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
)
|
||||
.new_session()
|
||||
}
|
||||
@@ -313,7 +313,7 @@ fn make_connector(id: &str, name: &str) -> AppInfo {
|
||||
|
||||
#[test]
|
||||
fn assistant_message_stream_parsers_can_be_seeded_from_output_item_added_text() {
|
||||
let mut parsers = AssistantMessageStreamParsers::new(false);
|
||||
let mut parsers = AssistantMessageStreamParsers::new(/*plan_mode*/ false);
|
||||
let item_id = "msg-1";
|
||||
|
||||
let seeded = parsers.seed_item_text(item_id, "hello <oai-mem-citation>doc");
|
||||
@@ -330,7 +330,7 @@ fn assistant_message_stream_parsers_can_be_seeded_from_output_item_added_text()
|
||||
|
||||
#[test]
|
||||
fn assistant_message_stream_parsers_seed_buffered_prefix_stays_out_of_finish_tail() {
|
||||
let mut parsers = AssistantMessageStreamParsers::new(false);
|
||||
let mut parsers = AssistantMessageStreamParsers::new(/*plan_mode*/ false);
|
||||
let item_id = "msg-1";
|
||||
|
||||
let seeded = parsers.seed_item_text(item_id, "hello <oai-mem-");
|
||||
@@ -347,7 +347,7 @@ fn assistant_message_stream_parsers_seed_buffered_prefix_stays_out_of_finish_tai
|
||||
|
||||
#[test]
|
||||
fn assistant_message_stream_parsers_seed_plan_parser_across_added_and_delta_boundaries() {
|
||||
let mut parsers = AssistantMessageStreamParsers::new(true);
|
||||
let mut parsers = AssistantMessageStreamParsers::new(/*plan_mode*/ true);
|
||||
let item_id = "msg-1";
|
||||
|
||||
let seeded = parsers.seed_item_text(item_id, "Intro\n<proposed");
|
||||
@@ -449,7 +449,7 @@ fn validated_network_policy_amendment_host_rejects_mismatch() {
|
||||
async fn start_managed_network_proxy_applies_execpolicy_network_rules() -> anyhow::Result<()> {
|
||||
let spec = crate::config::NetworkProxySpec::from_config_and_constraints(
|
||||
NetworkProxyConfig::default(),
|
||||
None,
|
||||
/*requirements*/ None,
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
)?;
|
||||
let mut exec_policy = Policy::empty();
|
||||
@@ -457,16 +457,16 @@ async fn start_managed_network_proxy_applies_execpolicy_network_rules() -> anyho
|
||||
"example.com",
|
||||
NetworkRuleProtocol::Https,
|
||||
Decision::Allow,
|
||||
None,
|
||||
/*justification*/ None,
|
||||
)?;
|
||||
|
||||
let (started_proxy, _) = Session::start_managed_network_proxy(
|
||||
&spec,
|
||||
&exec_policy,
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
/*network_policy_decider*/ None,
|
||||
/*blocked_request_observer*/ None,
|
||||
/*managed_network_requirements_enabled*/ false,
|
||||
crate::config::NetworkProxyAuditMetadata::default(),
|
||||
)
|
||||
.await?;
|
||||
@@ -501,16 +501,16 @@ async fn start_managed_network_proxy_ignores_invalid_execpolicy_network_rules()
|
||||
"example.com",
|
||||
NetworkRuleProtocol::Https,
|
||||
Decision::Allow,
|
||||
None,
|
||||
/*justification*/ None,
|
||||
)?;
|
||||
|
||||
let (started_proxy, _) = Session::start_managed_network_proxy(
|
||||
&spec,
|
||||
&exec_policy,
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
/*network_policy_decider*/ None,
|
||||
/*blocked_request_observer*/ None,
|
||||
/*managed_network_requirements_enabled*/ false,
|
||||
crate::config::NetworkProxyAuditMetadata::default(),
|
||||
)
|
||||
.await?;
|
||||
@@ -737,7 +737,9 @@ fn non_app_mcp_tools_remain_visible_without_search_selection() {
|
||||
),
|
||||
(
|
||||
"mcp__rmcp__echo".to_string(),
|
||||
make_mcp_tool("rmcp", "echo", None, None),
|
||||
make_mcp_tool(
|
||||
"rmcp", "echo", /*connector_id*/ None, /*connector_name*/ None,
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -785,7 +787,9 @@ fn search_tool_selection_keeps_codex_apps_tools_without_mentions() {
|
||||
),
|
||||
(
|
||||
"mcp__rmcp__echo".to_string(),
|
||||
make_mcp_tool("rmcp", "echo", None, None),
|
||||
make_mcp_tool(
|
||||
"rmcp", "echo", /*connector_id*/ None, /*connector_name*/ None,
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -835,7 +839,9 @@ fn apps_mentions_add_codex_apps_tools_to_search_selected_set() {
|
||||
),
|
||||
(
|
||||
"mcp__rmcp__echo".to_string(),
|
||||
make_mcp_tool("rmcp", "echo", None, None),
|
||||
make_mcp_tool(
|
||||
"rmcp", "echo", /*connector_id*/ None, /*connector_name*/ None,
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -1179,7 +1185,13 @@ async fn fork_startup_context_then_first_turn_diff_snapshot() -> anyhow::Result<
|
||||
codex_config::Constrained::allow_any(AskForApproval::UnlessTrusted);
|
||||
let forked = initial
|
||||
.thread_manager
|
||||
.fork_thread(usize::MAX, fork_config, rollout_path, false, None)
|
||||
.fork_thread(
|
||||
usize::MAX,
|
||||
fork_config,
|
||||
rollout_path,
|
||||
/*persist_extended_history*/ false,
|
||||
/*parent_trace*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let collaboration_mode = CollaborationMode {
|
||||
@@ -1351,7 +1363,7 @@ async fn thread_rollback_drops_last_turn_from_history() {
|
||||
state.set_reference_context_item(Some(tc.to_turn_context_item()));
|
||||
}
|
||||
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 1).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 1).await;
|
||||
|
||||
let rollback_event = wait_for_thread_rolled_back(&rx).await;
|
||||
assert_eq!(rollback_event.num_turns, 1);
|
||||
@@ -1398,7 +1410,7 @@ async fn thread_rollback_clears_history_when_num_turns_exceeds_existing_turns()
|
||||
.collect();
|
||||
sess.persist_rollout_items(&rollout_items).await;
|
||||
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 99).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 99).await;
|
||||
|
||||
let rollback_event = wait_for_thread_rolled_back(&rx).await;
|
||||
assert_eq!(rollback_event.num_turns, 99);
|
||||
@@ -1415,7 +1427,7 @@ async fn thread_rollback_fails_without_persisted_rollout_path() {
|
||||
sess.record_into_history(&initial_context, tc.as_ref())
|
||||
.await;
|
||||
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 1).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 1).await;
|
||||
|
||||
let error_event = wait_for_thread_rollback_failed(&rx).await;
|
||||
assert_eq!(
|
||||
@@ -1509,7 +1521,7 @@ async fn thread_rollback_recomputes_previous_turn_settings_and_reference_context
|
||||
}))
|
||||
.await;
|
||||
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 1).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 1).await;
|
||||
let rollback_event = wait_for_thread_rolled_back(&rx).await;
|
||||
assert_eq!(rollback_event.num_turns, 1);
|
||||
|
||||
@@ -1617,7 +1629,7 @@ async fn thread_rollback_restores_cleared_reference_context_item_after_compactio
|
||||
)
|
||||
.await;
|
||||
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 1).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 1).await;
|
||||
let rollback_event = wait_for_thread_rolled_back(&rx).await;
|
||||
assert_eq!(rollback_event.num_turns, 1);
|
||||
|
||||
@@ -1695,10 +1707,10 @@ async fn thread_rollback_persists_marker_and_replays_cumulatively() {
|
||||
])
|
||||
.await;
|
||||
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 1).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 1).await;
|
||||
let first_rollback = wait_for_thread_rolled_back(&rx).await;
|
||||
assert_eq!(first_rollback.num_turns, 1);
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 1).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 1).await;
|
||||
let second_rollback = wait_for_thread_rolled_back(&rx).await;
|
||||
assert_eq!(second_rollback.num_turns, 1);
|
||||
|
||||
@@ -1733,7 +1745,7 @@ async fn thread_rollback_fails_when_turn_in_progress() {
|
||||
.await;
|
||||
|
||||
*sess.active_turn.lock().await = Some(crate::state::ActiveTurn::default());
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 1).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 1).await;
|
||||
|
||||
let error_event = wait_for_thread_rollback_failed(&rx).await;
|
||||
assert_eq!(
|
||||
@@ -1753,7 +1765,7 @@ async fn thread_rollback_fails_when_num_turns_is_zero() {
|
||||
sess.record_into_history(&initial_context, tc.as_ref())
|
||||
.await;
|
||||
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), 0).await;
|
||||
handlers::thread_rollback(&sess, "sub-1".to_string(), /*num_turns*/ 0).await;
|
||||
|
||||
let error_event = wait_for_thread_rollback_failed(&rx).await;
|
||||
assert_eq!(error_event.message, "num_turns must be >= 1");
|
||||
@@ -2152,14 +2164,14 @@ async fn attach_rollout_recorder(session: &Arc<Session>) -> PathBuf {
|
||||
config.as_ref(),
|
||||
RolloutRecorderParams::new(
|
||||
ThreadId::default(),
|
||||
None,
|
||||
/*forked_from_id*/ None,
|
||||
SessionSource::Exec,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
/*state_db_ctx*/ None,
|
||||
/*state_builder*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("create rollout recorder");
|
||||
@@ -2198,11 +2210,11 @@ fn session_telemetry(
|
||||
conversation_id,
|
||||
ModelsManager::get_model_offline_for_tests(config.model.as_deref()).as_str(),
|
||||
model_info.slug.as_str(),
|
||||
None,
|
||||
/*account_id*/ None,
|
||||
Some("test@test.com".to_string()),
|
||||
Some(TelemetryAuthMode::Chatgpt),
|
||||
"test_originator".to_string(),
|
||||
false,
|
||||
/*log_user_prompts*/ false,
|
||||
"test".to_string(),
|
||||
session_source,
|
||||
)
|
||||
@@ -2325,7 +2337,7 @@ async fn new_default_turn_uses_config_aware_skills_for_role_overrides() {
|
||||
.skills_manager
|
||||
.skills_for_cwd(
|
||||
&crate::skills_load_input_from_config(&parent_config, Vec::new()),
|
||||
true,
|
||||
/*force_reload*/ true,
|
||||
)
|
||||
.await;
|
||||
let parent_skill = parent_outcome
|
||||
@@ -2473,7 +2485,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
config.codex_home.clone(),
|
||||
auth_manager.clone(),
|
||||
None,
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
let model = ModelsManager::get_model_offline_for_tests(config.model.as_deref());
|
||||
@@ -2522,7 +2534,10 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
||||
let (agent_status_tx, _agent_status_rx) = watch::channel(AgentStatus::PendingInit);
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone()));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone(), true));
|
||||
let skills_manager = Arc::new(SkillsManager::new(
|
||||
config.codex_home.clone(),
|
||||
/*bundled_skills_enabled*/ true,
|
||||
));
|
||||
let result = Session::new(
|
||||
session_configuration,
|
||||
Arc::clone(&config),
|
||||
@@ -2563,7 +2578,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
config.codex_home.clone(),
|
||||
auth_manager.clone(),
|
||||
None,
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
let agent_control = AgentControl::default();
|
||||
@@ -2626,7 +2641,10 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
let state = SessionState::new(session_configuration.clone());
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone()));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone(), true));
|
||||
let skills_manager = Arc::new(SkillsManager::new(
|
||||
config.codex_home.clone(),
|
||||
/*bundled_skills_enabled*/ true,
|
||||
));
|
||||
let network_approval = Arc::new(NetworkApprovalService::default());
|
||||
let environment = Arc::new(
|
||||
codex_exec_server::Environment::create(/*exec_server_url*/ None)
|
||||
@@ -2712,7 +2730,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
per_turn_config,
|
||||
model_info,
|
||||
&models_manager,
|
||||
None,
|
||||
/*network*/ None,
|
||||
environment,
|
||||
"turn_id".to_string(),
|
||||
Arc::clone(&js_repl),
|
||||
@@ -3400,7 +3418,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
config.codex_home.clone(),
|
||||
auth_manager.clone(),
|
||||
None,
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
let agent_control = AgentControl::default();
|
||||
@@ -3463,7 +3481,10 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
||||
let state = SessionState::new(session_configuration.clone());
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone()));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone(), true));
|
||||
let skills_manager = Arc::new(SkillsManager::new(
|
||||
config.codex_home.clone(),
|
||||
/*bundled_skills_enabled*/ true,
|
||||
));
|
||||
let network_approval = Arc::new(NetworkApprovalService::default());
|
||||
let environment = Arc::new(
|
||||
codex_exec_server::Environment::create(/*exec_server_url*/ None)
|
||||
@@ -3549,7 +3570,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
||||
per_turn_config,
|
||||
model_info,
|
||||
&models_manager,
|
||||
None,
|
||||
/*network*/ None,
|
||||
environment,
|
||||
"turn_id".to_string(),
|
||||
Arc::clone(&js_repl),
|
||||
@@ -3659,7 +3680,8 @@ async fn record_model_warning_appends_user_message() {
|
||||
#[tokio::test]
|
||||
async fn spawn_task_does_not_update_previous_turn_settings_for_non_run_turn_tasks() {
|
||||
let (sess, tc, _rx) = make_session_and_context_with_rx().await;
|
||||
sess.set_previous_turn_settings(None).await;
|
||||
sess.set_previous_turn_settings(/*previous_turn_settings*/ None)
|
||||
.await;
|
||||
let input = vec![UserInput::Text {
|
||||
text: "hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
@@ -3712,7 +3734,10 @@ async fn build_settings_update_items_emits_environment_item_for_network_changes(
|
||||
));
|
||||
let layers = config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
@@ -3909,7 +3934,7 @@ async fn build_initial_context_omits_default_image_save_location_with_image_hist
|
||||
revised_prompt: Some("a tiny blue square".to_string()),
|
||||
result: "Zm9v".to_string(),
|
||||
}],
|
||||
None,
|
||||
/*reference_context_item*/ None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -3963,7 +3988,7 @@ async fn handle_output_item_done_records_image_save_history_message() {
|
||||
tool_runtime: test_tool_runtime(Arc::clone(&session), Arc::clone(&turn_context)),
|
||||
cancellation_token: CancellationToken::new(),
|
||||
};
|
||||
handle_output_item_done(&mut ctx, item.clone(), None)
|
||||
handle_output_item_done(&mut ctx, item.clone(), /*previously_active_item*/ None)
|
||||
.await
|
||||
.expect("image generation item should succeed");
|
||||
|
||||
@@ -4020,7 +4045,7 @@ async fn handle_output_item_done_skips_image_save_message_when_save_fails() {
|
||||
tool_runtime: test_tool_runtime(Arc::clone(&session), Arc::clone(&turn_context)),
|
||||
cancellation_token: CancellationToken::new(),
|
||||
};
|
||||
handle_output_item_done(&mut ctx, item.clone(), None)
|
||||
handle_output_item_done(&mut ctx, item.clone(), /*previously_active_item*/ None)
|
||||
.await
|
||||
.expect("image generation item should still complete");
|
||||
|
||||
@@ -4112,10 +4137,13 @@ async fn record_context_updates_and_set_reference_context_item_reinjects_full_co
|
||||
.await;
|
||||
{
|
||||
let mut state = session.state.lock().await;
|
||||
state.set_reference_context_item(None);
|
||||
state.set_reference_context_item(/*item*/ None);
|
||||
}
|
||||
session
|
||||
.replace_history(vec![compacted_summary.clone()], None)
|
||||
.replace_history(
|
||||
vec![compacted_summary.clone()],
|
||||
/*reference_context_item*/ None,
|
||||
)
|
||||
.await;
|
||||
|
||||
session
|
||||
@@ -4150,14 +4178,14 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
config.as_ref(),
|
||||
RolloutRecorderParams::new(
|
||||
ThreadId::default(),
|
||||
None,
|
||||
/*forked_from_id*/ None,
|
||||
SessionSource::Exec,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
/*state_db_ctx*/ None,
|
||||
/*state_builder*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("create rollout recorder");
|
||||
@@ -4247,14 +4275,14 @@ async fn record_context_updates_and_set_reference_context_item_persists_full_rei
|
||||
config.as_ref(),
|
||||
RolloutRecorderParams::new(
|
||||
ThreadId::default(),
|
||||
None,
|
||||
/*forked_from_id*/ None,
|
||||
SessionSource::Exec,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
/*state_db_ctx*/ None,
|
||||
/*state_builder*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("create rollout recorder");
|
||||
@@ -4276,7 +4304,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_full_rei
|
||||
.await;
|
||||
{
|
||||
let mut state = session.state.lock().await;
|
||||
state.set_reference_context_item(None);
|
||||
state.set_reference_context_item(/*item*/ None);
|
||||
}
|
||||
|
||||
session
|
||||
@@ -4315,7 +4343,7 @@ async fn run_user_shell_command_does_not_set_reference_context_item() {
|
||||
let (session, _turn_context, rx) = make_session_and_context_with_rx().await;
|
||||
{
|
||||
let mut state = session.state.lock().await;
|
||||
state.set_reference_context_item(None);
|
||||
state.set_reference_context_item(/*item*/ None);
|
||||
}
|
||||
|
||||
handlers::run_user_shell_command(&session, "sub-id".to_string(), "echo shell".to_string())
|
||||
@@ -4468,7 +4496,8 @@ async fn task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input()
|
||||
.await
|
||||
.expect("inject pending input into active turn");
|
||||
|
||||
sess.on_task_finished(Arc::clone(&tc), None).await;
|
||||
sess.on_task_finished(Arc::clone(&tc), /*last_agent_message*/ None)
|
||||
.await;
|
||||
|
||||
let history = sess.clone_history().await;
|
||||
let expected = ResponseItem::Message {
|
||||
@@ -4560,7 +4589,7 @@ async fn steer_input_requires_active_turn() {
|
||||
}];
|
||||
|
||||
let err = sess
|
||||
.steer_input(input, None)
|
||||
.steer_input(input, /*expected_turn_id*/ None)
|
||||
.await
|
||||
.expect_err("steering without active turn should fail");
|
||||
|
||||
|
||||
@@ -425,11 +425,14 @@ async fn guardian_subagent_does_not_inherit_parent_exec_policy_rules() {
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
config.codex_home.clone(),
|
||||
auth_manager.clone(),
|
||||
None,
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone()));
|
||||
let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone(), true));
|
||||
let skills_manager = Arc::new(SkillsManager::new(
|
||||
config.codex_home.clone(),
|
||||
/*bundled_skills_enabled*/ true,
|
||||
));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
let skills_watcher = Arc::new(SkillsWatcher::noop());
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ fn blank_attribution_disables_trailer_prompt() {
|
||||
#[test]
|
||||
fn default_attribution_uses_codex_trailer() {
|
||||
assert_eq!(
|
||||
build_commit_message_trailer(None).as_deref(),
|
||||
build_commit_message_trailer(/*config_attribution*/ None).as_deref(),
|
||||
Some("Co-authored-by: Codex <noreply@openai.com>")
|
||||
);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ fn default_attribution_uses_codex_trailer() {
|
||||
#[test]
|
||||
fn resolve_value_handles_default_custom_and_blank() {
|
||||
assert_eq!(
|
||||
resolve_attribution_value(None),
|
||||
resolve_attribution_value(/*config_attribution*/ None),
|
||||
Some("Codex <noreply@openai.com>".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
@@ -216,8 +216,11 @@ async fn process_compacted_history_replaces_developer_messages() {
|
||||
phase: None,
|
||||
},
|
||||
];
|
||||
let (refreshed, mut expected) =
|
||||
process_compacted_history_with_test_session(compacted_history, None).await;
|
||||
let (refreshed, mut expected) = process_compacted_history_with_test_session(
|
||||
compacted_history,
|
||||
/*previous_turn_settings*/ None,
|
||||
)
|
||||
.await;
|
||||
expected.push(ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
@@ -241,8 +244,11 @@ async fn process_compacted_history_reinjects_full_initial_context() {
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
let (refreshed, mut expected) =
|
||||
process_compacted_history_with_test_session(compacted_history, None).await;
|
||||
let (refreshed, mut expected) = process_compacted_history_with_test_session(
|
||||
compacted_history,
|
||||
/*previous_turn_settings*/ None,
|
||||
)
|
||||
.await;
|
||||
expected.push(ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
@@ -317,8 +323,11 @@ keep me updated
|
||||
phase: None,
|
||||
},
|
||||
];
|
||||
let (refreshed, mut expected) =
|
||||
process_compacted_history_with_test_session(compacted_history, None).await;
|
||||
let (refreshed, mut expected) = process_compacted_history_with_test_session(
|
||||
compacted_history,
|
||||
/*previous_turn_settings*/ None,
|
||||
)
|
||||
.await;
|
||||
expected.push(ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
@@ -363,8 +372,11 @@ async fn process_compacted_history_inserts_context_before_last_real_user_message
|
||||
},
|
||||
];
|
||||
|
||||
let (refreshed, initial_context) =
|
||||
process_compacted_history_with_test_session(compacted_history, None).await;
|
||||
let (refreshed, initial_context) = process_compacted_history_with_test_session(
|
||||
compacted_history,
|
||||
/*previous_turn_settings*/ None,
|
||||
)
|
||||
.await;
|
||||
let mut expected = vec![
|
||||
ResponseItem::Message {
|
||||
id: None,
|
||||
|
||||
@@ -507,7 +507,7 @@ fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::io::Re
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(None),
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
@@ -709,7 +709,10 @@ fn permissions_profiles_allow_unknown_special_paths() -> std::io::Result<()> {
|
||||
config.permissions.file_system_sandbox_policy,
|
||||
FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::unknown(":future_special_path", None),
|
||||
value: FileSystemSpecialPath::unknown(
|
||||
":future_special_path",
|
||||
/*subpath*/ None
|
||||
),
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
}]),
|
||||
@@ -976,10 +979,10 @@ network_access = false # This should be ignored.
|
||||
let sandbox_mode_override = None;
|
||||
let resolution = sandbox_full_access_cfg.derive_sandbox_policy(
|
||||
sandbox_mode_override,
|
||||
None,
|
||||
/*profile_sandbox_mode*/ None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
None,
|
||||
/*sandbox_policy_constraint*/ None,
|
||||
);
|
||||
assert_eq!(resolution, SandboxPolicy::DangerFullAccess);
|
||||
|
||||
@@ -995,10 +998,10 @@ network_access = true # This should be ignored.
|
||||
let sandbox_mode_override = None;
|
||||
let resolution = sandbox_read_only_cfg.derive_sandbox_policy(
|
||||
sandbox_mode_override,
|
||||
None,
|
||||
/*profile_sandbox_mode*/ None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
None,
|
||||
/*sandbox_policy_constraint*/ None,
|
||||
);
|
||||
assert_eq!(resolution, SandboxPolicy::new_read_only_policy());
|
||||
|
||||
@@ -1022,10 +1025,10 @@ exclude_slash_tmp = true
|
||||
let sandbox_mode_override = None;
|
||||
let resolution = sandbox_workspace_write_cfg.derive_sandbox_policy(
|
||||
sandbox_mode_override,
|
||||
None,
|
||||
/*profile_sandbox_mode*/ None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
None,
|
||||
/*sandbox_policy_constraint*/ None,
|
||||
);
|
||||
if cfg!(target_os = "windows") {
|
||||
assert_eq!(resolution, SandboxPolicy::new_read_only_policy());
|
||||
@@ -1064,10 +1067,10 @@ trust_level = "trusted"
|
||||
let sandbox_mode_override = None;
|
||||
let resolution = sandbox_workspace_write_cfg.derive_sandbox_policy(
|
||||
sandbox_mode_override,
|
||||
None,
|
||||
/*profile_sandbox_mode*/ None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
None,
|
||||
/*sandbox_policy_constraint*/ None,
|
||||
);
|
||||
if cfg!(target_os = "windows") {
|
||||
assert_eq!(resolution, SandboxPolicy::new_read_only_policy());
|
||||
@@ -1245,7 +1248,7 @@ fn filter_mcp_servers_by_allowlist_allows_all_when_unset() {
|
||||
("server-b".to_string(), http_mcp("https://example.com/b")),
|
||||
]);
|
||||
|
||||
filter_mcp_servers_by_requirements(&mut servers, None);
|
||||
filter_mcp_servers_by_requirements(&mut servers, /*mcp_requirements*/ None);
|
||||
|
||||
assert_eq!(
|
||||
servers
|
||||
@@ -1874,7 +1877,7 @@ async fn replace_mcp_servers_round_trips_entries() -> anyhow::Result<()> {
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -1904,7 +1907,7 @@ async fn replace_mcp_servers_round_trips_entries() -> anyhow::Result<()> {
|
||||
let empty = BTreeMap::new();
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(empty.clone())],
|
||||
)?;
|
||||
let loaded = load_global_mcp_servers(codex_home.path()).await?;
|
||||
@@ -2101,7 +2104,7 @@ async fn replace_mcp_servers_serializes_env_sorted() -> anyhow::Result<()> {
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2174,7 +2177,7 @@ async fn replace_mcp_servers_serializes_env_vars() -> anyhow::Result<()> {
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2227,7 +2230,7 @@ async fn replace_mcp_servers_serializes_cwd() -> anyhow::Result<()> {
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2278,7 +2281,7 @@ async fn replace_mcp_servers_streamable_http_serializes_bearer_token() -> anyhow
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2344,7 +2347,7 @@ async fn replace_mcp_servers_streamable_http_serializes_custom_headers() -> anyh
|
||||
)]);
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2424,7 +2427,7 @@ async fn replace_mcp_servers_streamable_http_removes_optional_sections() -> anyh
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
let serialized_with_optional = std::fs::read_to_string(&config_path)?;
|
||||
@@ -2455,7 +2458,7 @@ async fn replace_mcp_servers_streamable_http_removes_optional_sections() -> anyh
|
||||
);
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2546,7 +2549,7 @@ async fn replace_mcp_servers_streamable_http_isolates_headers_between_servers()
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2630,7 +2633,7 @@ async fn replace_mcp_servers_serializes_disabled_flag() -> anyhow::Result<()> {
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2677,7 +2680,7 @@ async fn replace_mcp_servers_serializes_required_flag() -> anyhow::Result<()> {
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2724,7 +2727,7 @@ async fn replace_mcp_servers_serializes_tool_filters() -> anyhow::Result<()> {
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2775,7 +2778,7 @@ async fn replace_mcp_servers_streamable_http_serializes_oauth_resource() -> anyh
|
||||
|
||||
apply_blocking(
|
||||
codex_home.path(),
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)?;
|
||||
|
||||
@@ -2929,7 +2932,7 @@ async fn set_feature_enabled_updates_profile() -> anyhow::Result<()> {
|
||||
|
||||
ConfigEditsBuilder::new(codex_home.path())
|
||||
.with_profile(Some("dev"))
|
||||
.set_feature_enabled("guardian_approval", true)
|
||||
.set_feature_enabled("guardian_approval", /*enabled*/ true)
|
||||
.apply()
|
||||
.await?;
|
||||
|
||||
@@ -2965,13 +2968,13 @@ async fn set_feature_enabled_persists_default_false_feature_disable_in_profile()
|
||||
|
||||
ConfigEditsBuilder::new(codex_home.path())
|
||||
.with_profile(Some("dev"))
|
||||
.set_feature_enabled("guardian_approval", true)
|
||||
.set_feature_enabled("guardian_approval", /*enabled*/ true)
|
||||
.apply()
|
||||
.await?;
|
||||
|
||||
ConfigEditsBuilder::new(codex_home.path())
|
||||
.with_profile(Some("dev"))
|
||||
.set_feature_enabled("guardian_approval", false)
|
||||
.set_feature_enabled("guardian_approval", /*enabled*/ false)
|
||||
.apply()
|
||||
.await?;
|
||||
|
||||
@@ -3005,13 +3008,13 @@ async fn set_feature_enabled_profile_disable_overrides_root_enable() -> anyhow::
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
ConfigEditsBuilder::new(codex_home.path())
|
||||
.set_feature_enabled("guardian_approval", true)
|
||||
.set_feature_enabled("guardian_approval", /*enabled*/ true)
|
||||
.apply()
|
||||
.await?;
|
||||
|
||||
ConfigEditsBuilder::new(codex_home.path())
|
||||
.with_profile(Some("dev"))
|
||||
.set_feature_enabled("guardian_approval", false)
|
||||
.set_feature_enabled("guardian_approval", /*enabled*/ false)
|
||||
.apply()
|
||||
.await?;
|
||||
|
||||
@@ -4323,7 +4326,8 @@ model_verbosity = "high"
|
||||
supports_websockets: false,
|
||||
};
|
||||
let model_provider_map = {
|
||||
let mut model_provider_map = built_in_model_providers(/* openai_base_url */ None);
|
||||
let mut model_provider_map =
|
||||
built_in_model_providers(/* openai_base_url */ /*openai_base_url*/ None);
|
||||
model_provider_map.insert("openai-custom".to_string(), openai_custom_provider.clone());
|
||||
model_provider_map
|
||||
};
|
||||
@@ -4392,7 +4396,7 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
windows_sandbox_private_desktop: true,
|
||||
},
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
enforce_residency: Constrained::allow_any(None),
|
||||
enforce_residency: Constrained::allow_any(/*initial_value*/ None),
|
||||
user_instructions: None,
|
||||
notify: None,
|
||||
cwd: fixture.cwd(),
|
||||
@@ -4534,7 +4538,7 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
windows_sandbox_private_desktop: true,
|
||||
},
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
enforce_residency: Constrained::allow_any(None),
|
||||
enforce_residency: Constrained::allow_any(/*initial_value*/ None),
|
||||
user_instructions: None,
|
||||
notify: None,
|
||||
cwd: fixture.cwd(),
|
||||
@@ -4674,7 +4678,7 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
windows_sandbox_private_desktop: true,
|
||||
},
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
enforce_residency: Constrained::allow_any(None),
|
||||
enforce_residency: Constrained::allow_any(/*initial_value*/ None),
|
||||
user_instructions: None,
|
||||
notify: None,
|
||||
cwd: fixture.cwd(),
|
||||
@@ -4800,7 +4804,7 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
windows_sandbox_private_desktop: true,
|
||||
},
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
enforce_residency: Constrained::allow_any(None),
|
||||
enforce_residency: Constrained::allow_any(/*initial_value*/ None),
|
||||
user_instructions: None,
|
||||
notify: None,
|
||||
cwd: fixture.cwd(),
|
||||
@@ -5142,11 +5146,11 @@ trust_level = "untrusted"
|
||||
.expect("TOML deserialization should succeed");
|
||||
|
||||
let resolution = cfg.derive_sandbox_policy(
|
||||
None,
|
||||
None,
|
||||
/*sandbox_mode_override*/ None,
|
||||
/*profile_sandbox_mode*/ None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
None,
|
||||
/*sandbox_policy_constraint*/ None,
|
||||
);
|
||||
|
||||
// Verify that untrusted projects get WorkspaceWrite (or ReadOnly on Windows due to downgrade)
|
||||
@@ -5194,8 +5198,8 @@ fn derive_sandbox_policy_falls_back_to_constraint_value_for_implicit_defaults()
|
||||
})?;
|
||||
|
||||
let resolution = cfg.derive_sandbox_policy(
|
||||
None,
|
||||
None,
|
||||
/*sandbox_mode_override*/ None,
|
||||
/*profile_sandbox_mode*/ None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&project_path,
|
||||
Some(&constrained),
|
||||
@@ -5234,8 +5238,8 @@ fn derive_sandbox_policy_preserves_windows_downgrade_for_unsupported_fallback()
|
||||
})?;
|
||||
|
||||
let resolution = cfg.derive_sandbox_policy(
|
||||
None,
|
||||
None,
|
||||
/*sandbox_mode_override*/ None,
|
||||
/*profile_sandbox_mode*/ None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&project_path,
|
||||
Some(&constrained),
|
||||
@@ -5252,7 +5256,11 @@ fn derive_sandbox_policy_preserves_windows_downgrade_for_unsupported_fallback()
|
||||
#[test]
|
||||
fn test_resolve_oss_provider_explicit_override() {
|
||||
let config_toml = ConfigToml::default();
|
||||
let result = resolve_oss_provider(Some("custom-provider"), &config_toml, None);
|
||||
let result = resolve_oss_provider(
|
||||
Some("custom-provider"),
|
||||
&config_toml,
|
||||
/*config_profile*/ None,
|
||||
);
|
||||
assert_eq!(result, Some("custom-provider".to_string()));
|
||||
}
|
||||
|
||||
@@ -5269,7 +5277,11 @@ fn test_resolve_oss_provider_from_profile() {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let result = resolve_oss_provider(None, &config_toml, Some("test-profile".to_string()));
|
||||
let result = resolve_oss_provider(
|
||||
/*explicit_provider*/ None,
|
||||
&config_toml,
|
||||
Some("test-profile".to_string()),
|
||||
);
|
||||
assert_eq!(result, Some("profile-provider".to_string()));
|
||||
}
|
||||
|
||||
@@ -5280,7 +5292,11 @@ fn test_resolve_oss_provider_from_global_config() {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let result = resolve_oss_provider(None, &config_toml, None);
|
||||
let result = resolve_oss_provider(
|
||||
/*explicit_provider*/ None,
|
||||
&config_toml,
|
||||
/*config_profile*/ None,
|
||||
);
|
||||
assert_eq!(result, Some("global-provider".to_string()));
|
||||
}
|
||||
|
||||
@@ -5295,14 +5311,22 @@ fn test_resolve_oss_provider_profile_fallback_to_global() {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let result = resolve_oss_provider(None, &config_toml, Some("test-profile".to_string()));
|
||||
let result = resolve_oss_provider(
|
||||
/*explicit_provider*/ None,
|
||||
&config_toml,
|
||||
Some("test-profile".to_string()),
|
||||
);
|
||||
assert_eq!(result, Some("global-provider".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_oss_provider_none_when_not_configured() {
|
||||
let config_toml = ConfigToml::default();
|
||||
let result = resolve_oss_provider(None, &config_toml, None);
|
||||
let result = resolve_oss_provider(
|
||||
/*explicit_provider*/ None,
|
||||
&config_toml,
|
||||
/*config_profile*/ None,
|
||||
);
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ fn blocking_set_model_top_level() {
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetModel {
|
||||
model: Some("gpt-5.1-codex".to_string()),
|
||||
effort: Some(ReasoningEffort::High),
|
||||
@@ -150,7 +150,7 @@ profiles = { fast = { model = "gpt-4o", sandbox_mode = "strict" } }
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetModel {
|
||||
model: Some("o4-mini".to_string()),
|
||||
effort: None,
|
||||
@@ -195,7 +195,7 @@ fn blocking_set_model_writes_through_symlink_chain() {
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetModel {
|
||||
model: Some("gpt-5.1-codex".to_string()),
|
||||
effort: Some(ReasoningEffort::High),
|
||||
@@ -228,7 +228,7 @@ fn blocking_set_model_replaces_symlink_on_cycle() {
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetModel {
|
||||
model: Some("gpt-5.1-codex".to_string()),
|
||||
effort: None,
|
||||
@@ -267,7 +267,7 @@ network_access = false
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[
|
||||
ConfigEdit::SetPath {
|
||||
segments: vec![
|
||||
@@ -322,7 +322,7 @@ profiles = { fast = { model = "gpt-4o", sandbox_mode = "strict" } }
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetModel {
|
||||
model: None,
|
||||
effort: Some(ReasoningEffort::High),
|
||||
@@ -356,7 +356,7 @@ model_reasoning_effort = "low"
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetModel {
|
||||
model: Some("o5-preview".to_string()),
|
||||
effort: Some(ReasoningEffort::Minimal),
|
||||
@@ -420,7 +420,7 @@ existing = "value"
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetNoticeHideFullAccessWarning(true)],
|
||||
)
|
||||
.expect("persist");
|
||||
@@ -450,7 +450,7 @@ existing = "value"
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetNoticeHideRateLimitModelNudge(true)],
|
||||
)
|
||||
.expect("persist");
|
||||
@@ -476,7 +476,7 @@ existing = "value"
|
||||
.expect("seed");
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetNoticeHideModelMigrationPrompt(
|
||||
"hide_gpt5_1_migration_prompt".to_string(),
|
||||
true,
|
||||
@@ -505,7 +505,7 @@ existing = "value"
|
||||
.expect("seed");
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetNoticeHideModelMigrationPrompt(
|
||||
"hide_gpt-5.1-codex-max_migration_prompt".to_string(),
|
||||
true,
|
||||
@@ -534,7 +534,7 @@ existing = "value"
|
||||
.expect("seed");
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::RecordModelMigrationSeen {
|
||||
from: "gpt-5".to_string(),
|
||||
to: "gpt-5.1".to_string(),
|
||||
@@ -616,7 +616,7 @@ fn blocking_replace_mcp_servers_round_trips() {
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers.clone())],
|
||||
)
|
||||
.expect("persist");
|
||||
@@ -681,7 +681,12 @@ fn blocking_replace_mcp_servers_serializes_tool_approval_overrides() {
|
||||
},
|
||||
);
|
||||
|
||||
apply_blocking(codex_home, None, &[ConfigEdit::ReplaceMcpServers(servers)]).expect("persist");
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers)],
|
||||
)
|
||||
.expect("persist");
|
||||
|
||||
let raw = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = "\
|
||||
@@ -731,7 +736,12 @@ foo = { command = "cmd" }
|
||||
},
|
||||
);
|
||||
|
||||
apply_blocking(codex_home, None, &[ConfigEdit::ReplaceMcpServers(servers)]).expect("persist");
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers)],
|
||||
)
|
||||
.expect("persist");
|
||||
|
||||
let contents = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = r#"[mcp_servers]
|
||||
@@ -777,7 +787,12 @@ foo = { command = "cmd" } # keep me
|
||||
},
|
||||
);
|
||||
|
||||
apply_blocking(codex_home, None, &[ConfigEdit::ReplaceMcpServers(servers)]).expect("persist");
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers)],
|
||||
)
|
||||
.expect("persist");
|
||||
|
||||
let contents = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = r#"[mcp_servers]
|
||||
@@ -822,7 +837,12 @@ foo = { command = "cmd", args = ["--flag"] } # keep me
|
||||
},
|
||||
);
|
||||
|
||||
apply_blocking(codex_home, None, &[ConfigEdit::ReplaceMcpServers(servers)]).expect("persist");
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers)],
|
||||
)
|
||||
.expect("persist");
|
||||
|
||||
let contents = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = r#"[mcp_servers]
|
||||
@@ -868,7 +888,12 @@ foo = { command = "cmd" }
|
||||
},
|
||||
);
|
||||
|
||||
apply_blocking(codex_home, None, &[ConfigEdit::ReplaceMcpServers(servers)]).expect("persist");
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(servers)],
|
||||
)
|
||||
.expect("persist");
|
||||
|
||||
let contents = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = r#"[mcp_servers]
|
||||
@@ -885,7 +910,7 @@ fn blocking_clear_path_noop_when_missing() {
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ClearPath {
|
||||
segments: vec!["missing".to_string()],
|
||||
}],
|
||||
@@ -906,7 +931,7 @@ fn blocking_set_path_updates_notifications() {
|
||||
let item = value(false);
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::SetPath {
|
||||
segments: vec!["tui".to_string(), "notifications".to_string()],
|
||||
value: item,
|
||||
@@ -982,7 +1007,7 @@ async fn blocking_set_asynchronous_helpers_available() {
|
||||
let codex_home = tmp.path().to_path_buf();
|
||||
|
||||
ConfigEditsBuilder::new(&codex_home)
|
||||
.set_hide_full_access_warning(true)
|
||||
.set_hide_full_access_warning(/*acknowledged*/ true)
|
||||
.apply()
|
||||
.await
|
||||
.expect("persist");
|
||||
@@ -1024,7 +1049,7 @@ fn blocking_builder_set_realtime_audio_persists_and_clears() {
|
||||
);
|
||||
|
||||
ConfigEditsBuilder::new(codex_home)
|
||||
.set_realtime_microphone(None)
|
||||
.set_realtime_microphone(/*microphone*/ None)
|
||||
.apply_blocking()
|
||||
.expect("clear realtime microphone");
|
||||
|
||||
@@ -1053,7 +1078,7 @@ fn replace_mcp_servers_blocking_clears_table_when_empty() {
|
||||
|
||||
apply_blocking(
|
||||
codex_home,
|
||||
None,
|
||||
/*profile*/ None,
|
||||
&[ConfigEdit::ReplaceMcpServers(BTreeMap::new())],
|
||||
)
|
||||
.expect("persist");
|
||||
|
||||
@@ -96,7 +96,10 @@ impl ManagedFeatures {
|
||||
impl From<Features> for ManagedFeatures {
|
||||
fn from(features: Features) -> Self {
|
||||
Self {
|
||||
value: ConstrainedWithSource::new(Constrained::allow_any(features), None),
|
||||
value: ConstrainedWithSource::new(
|
||||
Constrained::allow_any(features),
|
||||
/*source*/ None,
|
||||
),
|
||||
pinned_features: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn normalize_absolute_path_for_platform_simplifies_windows_verbatim_paths() {
|
||||
let parsed =
|
||||
normalize_absolute_path_for_platform(r"\\?\D:\c\x\worktrees\2508\swift-base", true);
|
||||
let parsed = normalize_absolute_path_for_platform(
|
||||
r"\\?\D:\c\x\worktrees\2508\swift-base",
|
||||
/*is_windows*/ true,
|
||||
);
|
||||
assert_eq!(parsed, PathBuf::from(r"D:\c\x\worktrees\2508\swift-base"));
|
||||
}
|
||||
|
||||
|
||||
@@ -797,7 +797,7 @@ async fn load_config_layers_fails_when_cloud_requirements_loader_fails() -> anyh
|
||||
CloudRequirementsLoader::new(async {
|
||||
Err(CloudRequirementsLoadError::new(
|
||||
codex_config::CloudRequirementsLoadErrorCode::RequestFailed,
|
||||
None,
|
||||
/*status_code*/ None,
|
||||
"cloud requirements failed",
|
||||
))
|
||||
}),
|
||||
@@ -833,7 +833,13 @@ async fn project_layers_prefer_closest_cwd() -> std::io::Result<()> {
|
||||
|
||||
let codex_home = tmp.path().join("home");
|
||||
tokio::fs::create_dir_all(&codex_home).await?;
|
||||
make_config_for_test(&codex_home, &project_root, TrustLevel::Trusted, None).await?;
|
||||
make_config_for_test(
|
||||
&codex_home,
|
||||
&project_root,
|
||||
TrustLevel::Trusted,
|
||||
/*project_root_markers*/ None,
|
||||
)
|
||||
.await?;
|
||||
let cwd = AbsolutePathBuf::from_absolute_path(&nested)?;
|
||||
let layers = load_config_layers_state(
|
||||
&codex_home,
|
||||
@@ -899,7 +905,13 @@ model_instructions_file = "child.txt"
|
||||
|
||||
let codex_home = tmp.path().join("home");
|
||||
tokio::fs::create_dir_all(&codex_home).await?;
|
||||
make_config_for_test(&codex_home, &project_root, TrustLevel::Trusted, None).await?;
|
||||
make_config_for_test(
|
||||
&codex_home,
|
||||
&project_root,
|
||||
TrustLevel::Trusted,
|
||||
/*project_root_markers*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home)
|
||||
@@ -965,7 +977,13 @@ async fn project_layer_is_added_when_dot_codex_exists_without_config_toml() -> s
|
||||
|
||||
let codex_home = tmp.path().join("home");
|
||||
tokio::fs::create_dir_all(&codex_home).await?;
|
||||
make_config_for_test(&codex_home, &project_root, TrustLevel::Trusted, None).await?;
|
||||
make_config_for_test(
|
||||
&codex_home,
|
||||
&project_root,
|
||||
TrustLevel::Trusted,
|
||||
/*project_root_markers*/ None,
|
||||
)
|
||||
.await?;
|
||||
let cwd = AbsolutePathBuf::from_absolute_path(&nested)?;
|
||||
let layers = load_config_layers_state(
|
||||
&codex_home,
|
||||
@@ -1018,7 +1036,7 @@ async fn codex_home_is_not_loaded_as_project_layer_from_home_dir() -> std::io::R
|
||||
let project_layers: Vec<_> = layers
|
||||
.get_layers(
|
||||
super::ConfigLayerStackOrdering::HighestPrecedenceFirst,
|
||||
true,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. }))
|
||||
@@ -1046,7 +1064,13 @@ async fn codex_home_within_project_tree_is_not_double_loaded() -> std::io::Resul
|
||||
tokio::fs::write(nested_dot_codex.join(CONFIG_TOML_FILE), "foo = \"child\"\n").await?;
|
||||
|
||||
tokio::fs::create_dir_all(&project_dot_codex).await?;
|
||||
make_config_for_test(&project_dot_codex, &project_root, TrustLevel::Trusted, None).await?;
|
||||
make_config_for_test(
|
||||
&project_dot_codex,
|
||||
&project_root,
|
||||
TrustLevel::Trusted,
|
||||
/*project_root_markers*/ None,
|
||||
)
|
||||
.await?;
|
||||
let user_config_path = project_dot_codex.join(CONFIG_TOML_FILE);
|
||||
let user_config_contents = tokio::fs::read_to_string(&user_config_path).await?;
|
||||
tokio::fs::write(
|
||||
@@ -1068,7 +1092,7 @@ async fn codex_home_within_project_tree_is_not_double_loaded() -> std::io::Resul
|
||||
let project_layers: Vec<_> = layers
|
||||
.get_layers(
|
||||
super::ConfigLayerStackOrdering::HighestPrecedenceFirst,
|
||||
true,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. }))
|
||||
@@ -1115,7 +1139,7 @@ async fn project_layers_disabled_when_untrusted_or_unknown() -> std::io::Result<
|
||||
&codex_home_untrusted,
|
||||
&project_root,
|
||||
TrustLevel::Untrusted,
|
||||
None,
|
||||
/*project_root_markers*/ None,
|
||||
)
|
||||
.await?;
|
||||
let untrusted_config_path = codex_home_untrusted.join(CONFIG_TOML_FILE);
|
||||
@@ -1137,7 +1161,7 @@ async fn project_layers_disabled_when_untrusted_or_unknown() -> std::io::Result<
|
||||
let project_layers_untrusted: Vec<_> = layers_untrusted
|
||||
.get_layers(
|
||||
super::ConfigLayerStackOrdering::HighestPrecedenceFirst,
|
||||
true,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. }))
|
||||
@@ -1175,7 +1199,7 @@ async fn project_layers_disabled_when_untrusted_or_unknown() -> std::io::Result<
|
||||
let project_layers_unknown: Vec<_> = layers_unknown
|
||||
.get_layers(
|
||||
super::ConfigLayerStackOrdering::HighestPrecedenceFirst,
|
||||
true,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. }))
|
||||
@@ -1218,7 +1242,13 @@ enabled = false
|
||||
"#,
|
||||
)
|
||||
.await?;
|
||||
make_config_for_test(&codex_home, &project_root, TrustLevel::Trusted, None).await?;
|
||||
make_config_for_test(
|
||||
&codex_home,
|
||||
&project_root,
|
||||
TrustLevel::Trusted,
|
||||
/*project_root_markers*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home)
|
||||
@@ -1303,7 +1333,13 @@ async fn invalid_project_config_ignored_when_untrusted_or_unknown() -> std::io::
|
||||
let config_path = codex_home.join(CONFIG_TOML_FILE);
|
||||
|
||||
if let Some(trust_level) = trust_level {
|
||||
make_config_for_test(&codex_home, &project_root, trust_level, None).await?;
|
||||
make_config_for_test(
|
||||
&codex_home,
|
||||
&project_root,
|
||||
trust_level,
|
||||
/*project_root_markers*/ None,
|
||||
)
|
||||
.await?;
|
||||
let config_contents = tokio::fs::read_to_string(&config_path).await?;
|
||||
tokio::fs::write(&config_path, format!("foo = \"user\"\n{config_contents}")).await?;
|
||||
} else {
|
||||
@@ -1321,7 +1357,7 @@ async fn invalid_project_config_ignored_when_untrusted_or_unknown() -> std::io::
|
||||
let project_layers: Vec<_> = layers
|
||||
.get_layers(
|
||||
super::ConfigLayerStackOrdering::HighestPrecedenceFirst,
|
||||
true,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|layer| matches!(layer.name, super::ConfigLayerSource::Project { .. }))
|
||||
@@ -1358,7 +1394,13 @@ async fn cli_overrides_with_relative_paths_do_not_break_trust_check() -> std::io
|
||||
|
||||
let codex_home = tmp.path().join("home");
|
||||
tokio::fs::create_dir_all(&codex_home).await?;
|
||||
make_config_for_test(&codex_home, &project_root, TrustLevel::Trusted, None).await?;
|
||||
make_config_for_test(
|
||||
&codex_home,
|
||||
&project_root,
|
||||
TrustLevel::Trusted,
|
||||
/*project_root_markers*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let cwd = AbsolutePathBuf::from_absolute_path(&nested)?;
|
||||
let cli_overrides = vec![(
|
||||
|
||||
@@ -171,7 +171,7 @@ fn accessible_connectors_from_mcp_tools_carries_plugin_display_names() {
|
||||
codex_app_tool(
|
||||
"calendar_list_events",
|
||||
"calendar",
|
||||
None,
|
||||
/*connector_name*/ None,
|
||||
&["sample", "sample"],
|
||||
),
|
||||
),
|
||||
@@ -229,8 +229,8 @@ async fn refresh_accessible_connectors_cache_from_mcp_tools_writes_latest_instal
|
||||
.build()
|
||||
.await
|
||||
.expect("config should load");
|
||||
let _ = config.features.set_enabled(Feature::Apps, true);
|
||||
let cache_key = accessible_connectors_cache_key(&config, None);
|
||||
let _ = config.features.set_enabled(Feature::Apps, /*enabled*/ true);
|
||||
let cache_key = accessible_connectors_cache_key(&config, /*auth*/ None);
|
||||
let tools = HashMap::from([
|
||||
(
|
||||
"mcp__codex_apps__calendar_list_events".to_string(),
|
||||
@@ -253,7 +253,7 @@ async fn refresh_accessible_connectors_cache_from_mcp_tools_writes_latest_instal
|
||||
]);
|
||||
|
||||
let cached = with_accessible_connectors_cache_cleared(|| {
|
||||
refresh_accessible_connectors_cache_from_mcp_tools(&config, None, &tools);
|
||||
refresh_accessible_connectors_cache_from_mcp_tools(&config, /*auth*/ None, &tools);
|
||||
read_cached_accessible_connectors(&cache_key).expect("cache should be populated")
|
||||
});
|
||||
|
||||
@@ -367,8 +367,8 @@ fn app_tool_policy_uses_global_defaults_for_destructive_hints() {
|
||||
Some(&apps_config),
|
||||
Some("calendar"),
|
||||
"events/create",
|
||||
None,
|
||||
Some(&annotations(Some(true), None)),
|
||||
/*tool_title*/ None,
|
||||
Some(&annotations(Some(true), /*open_world_hint*/ None)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -392,7 +392,7 @@ fn app_is_enabled_uses_default_for_unconfigured_apps() {
|
||||
};
|
||||
|
||||
assert!(!app_is_enabled(&apps_config, Some("calendar")));
|
||||
assert!(!app_is_enabled(&apps_config, None));
|
||||
assert!(!app_is_enabled(&apps_config, /*connector_id*/ None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -518,7 +518,13 @@ enabled = true
|
||||
.await
|
||||
.expect("config should build");
|
||||
|
||||
let policy = app_tool_policy(&config, Some("connector_123123"), "events.list", None, None);
|
||||
let policy = app_tool_policy(
|
||||
&config,
|
||||
Some("connector_123123"),
|
||||
"events.list",
|
||||
/*tool_title*/ None,
|
||||
/*annotations*/ None,
|
||||
);
|
||||
assert_eq!(
|
||||
policy,
|
||||
AppToolPolicy {
|
||||
@@ -555,7 +561,13 @@ async fn cloud_requirements_disable_connector_applies_without_user_apps_table()
|
||||
.await
|
||||
.expect("config should build");
|
||||
|
||||
let policy = app_tool_policy(&config, Some("connector_123123"), "events.list", None, None);
|
||||
let policy = app_tool_policy(
|
||||
&config,
|
||||
Some("connector_123123"),
|
||||
"events.list",
|
||||
/*tool_title*/ None,
|
||||
/*annotations*/ None,
|
||||
);
|
||||
assert_eq!(
|
||||
policy,
|
||||
AppToolPolicy {
|
||||
@@ -602,7 +614,13 @@ enabled = true
|
||||
.expect("apps config"),
|
||||
);
|
||||
|
||||
let policy = app_tool_policy(&config, Some("connector_123123"), "events.list", None, None);
|
||||
let policy = app_tool_policy(
|
||||
&config,
|
||||
Some("connector_123123"),
|
||||
"events.list",
|
||||
/*tool_title*/ None,
|
||||
/*annotations*/ None,
|
||||
);
|
||||
assert_eq!(
|
||||
policy,
|
||||
AppToolPolicy {
|
||||
@@ -637,7 +655,13 @@ async fn local_requirements_disable_connector_applies_without_user_apps_table()
|
||||
ConfigLayerStack::new(Vec::new(), ConfigRequirements::default(), requirements)
|
||||
.expect("requirements stack");
|
||||
|
||||
let policy = app_tool_policy(&config, Some("connector_123123"), "events.list", None, None);
|
||||
let policy = app_tool_policy(
|
||||
&config,
|
||||
Some("connector_123123"),
|
||||
"events.list",
|
||||
/*tool_title*/ None,
|
||||
/*annotations*/ None,
|
||||
);
|
||||
assert_eq!(
|
||||
policy,
|
||||
AppToolPolicy {
|
||||
@@ -699,8 +723,10 @@ fn app_tool_policy_honors_default_app_enabled_false() {
|
||||
Some(&apps_config),
|
||||
Some("calendar"),
|
||||
"events/list",
|
||||
None,
|
||||
Some(&annotations(None, None)),
|
||||
/*tool_title*/ None,
|
||||
Some(&annotations(
|
||||
/*destructive_hint*/ None, /*open_world_hint*/ None,
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -737,8 +763,10 @@ fn app_tool_policy_allows_per_app_enable_when_default_is_disabled() {
|
||||
Some(&apps_config),
|
||||
Some("calendar"),
|
||||
"events/list",
|
||||
None,
|
||||
Some(&annotations(None, None)),
|
||||
/*tool_title*/ None,
|
||||
Some(&annotations(
|
||||
/*destructive_hint*/ None, /*open_world_hint*/ None,
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -779,7 +807,7 @@ fn app_tool_policy_per_tool_enabled_true_overrides_app_level_disable_flags() {
|
||||
Some(&apps_config),
|
||||
Some("calendar"),
|
||||
"events/create",
|
||||
None,
|
||||
/*tool_title*/ None,
|
||||
Some(&annotations(Some(true), Some(true))),
|
||||
);
|
||||
|
||||
@@ -813,7 +841,7 @@ fn app_tool_policy_default_tools_enabled_true_overrides_app_level_tool_hints() {
|
||||
Some(&apps_config),
|
||||
Some("calendar"),
|
||||
"events/create",
|
||||
None,
|
||||
/*tool_title*/ None,
|
||||
Some(&annotations(Some(true), Some(true))),
|
||||
);
|
||||
|
||||
@@ -847,8 +875,10 @@ fn app_tool_policy_default_tools_enabled_false_overrides_app_level_tool_hints()
|
||||
Some(&apps_config),
|
||||
Some("calendar"),
|
||||
"events/list",
|
||||
None,
|
||||
Some(&annotations(None, None)),
|
||||
/*tool_title*/ None,
|
||||
Some(&annotations(
|
||||
/*destructive_hint*/ None, /*open_world_hint*/ None,
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -883,8 +913,10 @@ fn app_tool_policy_uses_default_tools_approval_mode() {
|
||||
Some(&apps_config),
|
||||
Some("calendar"),
|
||||
"events/list",
|
||||
None,
|
||||
Some(&annotations(None, None)),
|
||||
/*tool_title*/ None,
|
||||
Some(&annotations(
|
||||
/*destructive_hint*/ None, /*open_world_hint*/ None,
|
||||
)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
||||
@@ -51,7 +51,7 @@ fn inter_agent_assistant_msg(text: &str) -> ResponseItem {
|
||||
AgentPath::root().join("worker").unwrap(),
|
||||
Vec::new(),
|
||||
text.to_string(),
|
||||
true,
|
||||
/*trigger_turn*/ true,
|
||||
);
|
||||
ResponseItem::Message {
|
||||
id: None,
|
||||
@@ -246,7 +246,8 @@ fn filters_non_api_messages() {
|
||||
|
||||
#[test]
|
||||
fn non_last_reasoning_tokens_return_zero_when_no_user_messages() {
|
||||
let history = create_history_with_items(vec![reasoning_with_encrypted_content(800)]);
|
||||
let history =
|
||||
create_history_with_items(vec![reasoning_with_encrypted_content(/*len*/ 800)]);
|
||||
|
||||
assert_eq!(history.get_non_last_reasoning_items_tokens(), 0);
|
||||
}
|
||||
@@ -254,11 +255,11 @@ fn non_last_reasoning_tokens_return_zero_when_no_user_messages() {
|
||||
#[test]
|
||||
fn non_last_reasoning_tokens_ignore_entries_after_last_user() {
|
||||
let history = create_history_with_items(vec![
|
||||
reasoning_with_encrypted_content(900),
|
||||
reasoning_with_encrypted_content(/*len*/ 900),
|
||||
user_msg("first"),
|
||||
reasoning_with_encrypted_content(1_000),
|
||||
reasoning_with_encrypted_content(/*len*/ 1_000),
|
||||
user_msg("second"),
|
||||
reasoning_with_encrypted_content(2_000),
|
||||
reasoning_with_encrypted_content(/*len*/ 2_000),
|
||||
]);
|
||||
// first: (900 * 0.75 - 650) / 4 = 6.25 tokens
|
||||
// second: (1000 * 0.75 - 650) / 4 = 25 tokens
|
||||
@@ -330,7 +331,7 @@ fn drop_last_n_user_turns_treats_inter_agent_assistant_messages_as_instruction_t
|
||||
inter_agent_reply,
|
||||
]);
|
||||
|
||||
history.drop_last_n_user_turns(1);
|
||||
history.drop_last_n_user_turns(/*num_turns*/ 1);
|
||||
|
||||
assert_eq!(history.raw_items(), &vec![first_turn, first_reply]);
|
||||
}
|
||||
@@ -352,7 +353,7 @@ fn total_token_usage_includes_all_items_after_last_model_generated_item() {
|
||||
total_tokens: 100,
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
/*model_context_window*/ None,
|
||||
);
|
||||
let added_user = user_msg("new user message");
|
||||
let added_tool_output = custom_tool_call_output("tool-tail", "new tool output");
|
||||
@@ -362,7 +363,7 @@ fn total_token_usage_includes_all_items_after_last_model_generated_item() {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
history.get_total_token_usage(true),
|
||||
history.get_total_token_usage(/*server_reasoning_included*/ true),
|
||||
100 + estimate_item_token_count(&added_user)
|
||||
+ estimate_item_token_count(&added_tool_output)
|
||||
);
|
||||
@@ -606,7 +607,12 @@ fn for_prompt_clears_image_generation_result_when_images_are_unsupported() {
|
||||
#[test]
|
||||
fn get_history_for_prompt_drops_ghost_commits() {
|
||||
let items = vec![ResponseItem::GhostSnapshot {
|
||||
ghost_commit: GhostCommit::new("ghost-1".to_string(), None, Vec::new(), Vec::new()),
|
||||
ghost_commit: GhostCommit::new(
|
||||
"ghost-1".to_string(),
|
||||
/*parent*/ None,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
),
|
||||
}];
|
||||
let history = create_history_with_items(items);
|
||||
let modalities = default_input_modalities();
|
||||
@@ -792,7 +798,7 @@ fn drop_last_n_user_turns_preserves_prefix() {
|
||||
|
||||
let modalities = default_input_modalities();
|
||||
let mut history = create_history_with_items(items);
|
||||
history.drop_last_n_user_turns(1);
|
||||
history.drop_last_n_user_turns(/*num_turns*/ 1);
|
||||
assert_eq!(
|
||||
history.for_prompt(&modalities),
|
||||
vec![
|
||||
@@ -809,7 +815,7 @@ fn drop_last_n_user_turns_preserves_prefix() {
|
||||
user_msg("u2"),
|
||||
assistant_msg("a2"),
|
||||
]);
|
||||
history.drop_last_n_user_turns(99);
|
||||
history.drop_last_n_user_turns(/*num_turns*/ 99);
|
||||
assert_eq!(
|
||||
history.for_prompt(&modalities),
|
||||
vec![assistant_msg("session prefix item")]
|
||||
@@ -838,7 +844,7 @@ fn drop_last_n_user_turns_ignores_session_prefix_user_messages() {
|
||||
|
||||
let modalities = default_input_modalities();
|
||||
let mut history = create_history_with_items(items);
|
||||
history.drop_last_n_user_turns(1);
|
||||
history.drop_last_n_user_turns(/*num_turns*/ 1);
|
||||
|
||||
let expected_prefix_and_first_turn = vec![
|
||||
user_input_text_msg("<environment_context>ctx</environment_context>"),
|
||||
@@ -892,7 +898,7 @@ fn drop_last_n_user_turns_ignores_session_prefix_user_messages() {
|
||||
user_input_text_msg("turn 2 user"),
|
||||
assistant_msg("turn 2 assistant"),
|
||||
]);
|
||||
history.drop_last_n_user_turns(2);
|
||||
history.drop_last_n_user_turns(/*num_turns*/ 2);
|
||||
assert_eq!(history.for_prompt(&modalities), expected_prefix_only);
|
||||
|
||||
let mut history = create_history_with_items(vec![
|
||||
@@ -912,7 +918,7 @@ fn drop_last_n_user_turns_ignores_session_prefix_user_messages() {
|
||||
user_input_text_msg("turn 2 user"),
|
||||
assistant_msg("turn 2 assistant"),
|
||||
]);
|
||||
history.drop_last_n_user_turns(3);
|
||||
history.drop_last_n_user_turns(/*num_turns*/ 3);
|
||||
assert_eq!(history.for_prompt(&modalities), expected_prefix_only);
|
||||
}
|
||||
|
||||
@@ -935,7 +941,7 @@ fn drop_last_n_user_turns_trims_context_updates_above_rolled_back_turn() {
|
||||
let mut history = create_history_with_items(items);
|
||||
let reference_context_item = reference_context_item();
|
||||
history.set_reference_context_item(Some(reference_context_item.clone()));
|
||||
history.drop_last_n_user_turns(1);
|
||||
history.drop_last_n_user_turns(/*num_turns*/ 1);
|
||||
|
||||
assert_eq!(
|
||||
history.clone().for_prompt(&modalities),
|
||||
@@ -973,7 +979,7 @@ fn drop_last_n_user_turns_clears_reference_context_for_mixed_developer_context_b
|
||||
let modalities = default_input_modalities();
|
||||
let mut history = create_history_with_items(items);
|
||||
history.set_reference_context_item(Some(reference_context_item()));
|
||||
history.drop_last_n_user_turns(1);
|
||||
history.drop_last_n_user_turns(/*num_turns*/ 1);
|
||||
|
||||
assert_eq!(
|
||||
history.clone().for_prompt(&modalities),
|
||||
@@ -1165,7 +1171,7 @@ fn format_exec_output_truncates_large_error() {
|
||||
|
||||
let truncated = truncate_exec_output(&large_error);
|
||||
|
||||
assert_truncated_message_matches(&truncated, line, 36250);
|
||||
assert_truncated_message_matches(&truncated, line, /*expected_removed*/ 36250);
|
||||
assert_ne!(truncated, large_error);
|
||||
}
|
||||
|
||||
@@ -1174,7 +1180,7 @@ fn format_exec_output_marks_byte_truncation_without_omitted_lines() {
|
||||
let long_line = "a".repeat(EXEC_FORMAT_MAX_BYTES + 10000);
|
||||
let truncated = truncate_exec_output(&long_line);
|
||||
assert_ne!(truncated, long_line);
|
||||
assert_truncated_message_matches(&truncated, "a", 2500);
|
||||
assert_truncated_message_matches(&truncated, "a", /*expected_removed*/ 2500);
|
||||
assert!(
|
||||
!truncated.contains("omitted"),
|
||||
"line omission marker should not appear when no lines were dropped: {truncated}"
|
||||
@@ -1196,7 +1202,7 @@ fn format_exec_output_reports_omitted_lines_and_keeps_head_and_tail() {
|
||||
.collect();
|
||||
|
||||
let truncated = truncate_exec_output(&content);
|
||||
assert_truncated_message_matches(&truncated, "line-0-", 34_723);
|
||||
assert_truncated_message_matches(&truncated, "line-0-", /*expected_removed*/ 34_723);
|
||||
assert!(
|
||||
truncated.contains("line-0-"),
|
||||
"expected head line to remain: {truncated}"
|
||||
@@ -1219,7 +1225,7 @@ fn format_exec_output_prefers_line_marker_when_both_limits_exceeded() {
|
||||
|
||||
let truncated = truncate_exec_output(&content);
|
||||
|
||||
assert_truncated_message_matches(&truncated, "line-0-", 17_423);
|
||||
assert_truncated_message_matches(&truncated, "line-0-", /*expected_removed*/ 17_423);
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
|
||||
@@ -86,8 +86,8 @@ fn detects_hook_prompt_fragment_and_roundtrips_escaping() {
|
||||
let ContentItem::InputText { text } = content_item else {
|
||||
panic!("expected input text content item");
|
||||
};
|
||||
let parsed =
|
||||
parse_visible_hook_prompt_message(None, content.as_slice()).expect("visible hook prompt");
|
||||
let parsed = parse_visible_hook_prompt_message(/*id*/ None, content.as_slice())
|
||||
.expect("visible hook prompt");
|
||||
assert_eq!(
|
||||
parsed.fragments,
|
||||
vec![HookPromptFragment {
|
||||
|
||||
@@ -20,8 +20,8 @@ fn serialize_workspace_write_environment_context() {
|
||||
fake_shell(),
|
||||
Some("2026-02-26".to_string()),
|
||||
Some("America/Los_Angeles".to_string()),
|
||||
None,
|
||||
None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
let expected = format!(
|
||||
@@ -49,7 +49,7 @@ fn serialize_environment_context_with_network() {
|
||||
Some("2026-02-26".to_string()),
|
||||
Some("America/Los_Angeles".to_string()),
|
||||
Some(network),
|
||||
None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
let expected = format!(
|
||||
@@ -73,12 +73,12 @@ fn serialize_environment_context_with_network() {
|
||||
#[test]
|
||||
fn serialize_read_only_environment_context() {
|
||||
let context = EnvironmentContext::new(
|
||||
None,
|
||||
/*cwd*/ None,
|
||||
fake_shell(),
|
||||
Some("2026-02-26".to_string()),
|
||||
Some("America/Los_Angeles".to_string()),
|
||||
None,
|
||||
None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
let expected = r#"<environment_context>
|
||||
@@ -93,12 +93,12 @@ fn serialize_read_only_environment_context() {
|
||||
#[test]
|
||||
fn serialize_external_sandbox_environment_context() {
|
||||
let context = EnvironmentContext::new(
|
||||
None,
|
||||
/*cwd*/ None,
|
||||
fake_shell(),
|
||||
Some("2026-02-26".to_string()),
|
||||
Some("America/Los_Angeles".to_string()),
|
||||
None,
|
||||
None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
let expected = r#"<environment_context>
|
||||
@@ -113,12 +113,12 @@ fn serialize_external_sandbox_environment_context() {
|
||||
#[test]
|
||||
fn serialize_external_sandbox_with_restricted_network_environment_context() {
|
||||
let context = EnvironmentContext::new(
|
||||
None,
|
||||
/*cwd*/ None,
|
||||
fake_shell(),
|
||||
Some("2026-02-26".to_string()),
|
||||
Some("America/Los_Angeles".to_string()),
|
||||
None,
|
||||
None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
let expected = r#"<environment_context>
|
||||
@@ -133,12 +133,12 @@ fn serialize_external_sandbox_with_restricted_network_environment_context() {
|
||||
#[test]
|
||||
fn serialize_full_access_environment_context() {
|
||||
let context = EnvironmentContext::new(
|
||||
None,
|
||||
/*cwd*/ None,
|
||||
fake_shell(),
|
||||
Some("2026-02-26".to_string()),
|
||||
Some("America/Los_Angeles".to_string()),
|
||||
None,
|
||||
None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
let expected = r#"<environment_context>
|
||||
@@ -155,18 +155,18 @@ fn equals_except_shell_compares_cwd() {
|
||||
let context1 = EnvironmentContext::new(
|
||||
Some(PathBuf::from("/repo")),
|
||||
fake_shell(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*current_date*/ None,
|
||||
/*timezone*/ None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
let context2 = EnvironmentContext::new(
|
||||
Some(PathBuf::from("/repo")),
|
||||
fake_shell(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*current_date*/ None,
|
||||
/*timezone*/ None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
assert!(context1.equals_except_shell(&context2));
|
||||
}
|
||||
@@ -176,18 +176,18 @@ fn equals_except_shell_ignores_sandbox_policy() {
|
||||
let context1 = EnvironmentContext::new(
|
||||
Some(PathBuf::from("/repo")),
|
||||
fake_shell(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*current_date*/ None,
|
||||
/*timezone*/ None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
let context2 = EnvironmentContext::new(
|
||||
Some(PathBuf::from("/repo")),
|
||||
fake_shell(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*current_date*/ None,
|
||||
/*timezone*/ None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
assert!(context1.equals_except_shell(&context2));
|
||||
@@ -198,18 +198,18 @@ fn equals_except_shell_compares_cwd_differences() {
|
||||
let context1 = EnvironmentContext::new(
|
||||
Some(PathBuf::from("/repo1")),
|
||||
fake_shell(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*current_date*/ None,
|
||||
/*timezone*/ None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
let context2 = EnvironmentContext::new(
|
||||
Some(PathBuf::from("/repo2")),
|
||||
fake_shell(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*current_date*/ None,
|
||||
/*timezone*/ None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
assert!(!context1.equals_except_shell(&context2));
|
||||
@@ -224,10 +224,10 @@ fn equals_except_shell_ignores_shell() {
|
||||
shell_path: "/bin/bash".into(),
|
||||
shell_snapshot: crate::shell::empty_shell_snapshot_receiver(),
|
||||
},
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*current_date*/ None,
|
||||
/*timezone*/ None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
let context2 = EnvironmentContext::new(
|
||||
Some(PathBuf::from("/repo")),
|
||||
@@ -236,10 +236,10 @@ fn equals_except_shell_ignores_shell() {
|
||||
shell_path: "/bin/zsh".into(),
|
||||
shell_snapshot: crate::shell::empty_shell_snapshot_receiver(),
|
||||
},
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*current_date*/ None,
|
||||
/*timezone*/ None,
|
||||
/*network*/ None,
|
||||
/*subagents*/ None,
|
||||
);
|
||||
|
||||
assert!(context1.equals_except_shell(&context2));
|
||||
@@ -252,7 +252,7 @@ fn serialize_environment_context_with_subagents() {
|
||||
fake_shell(),
|
||||
Some("2026-02-26".to_string()),
|
||||
Some("America/Los_Angeles".to_string()),
|
||||
None,
|
||||
/*network*/ None,
|
||||
Some("- agent-1: atlas\n- agent-2".to_string()),
|
||||
);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ fn parses_user_message_with_text_and_two_images() {
|
||||
#[test]
|
||||
fn skips_local_image_label_text() {
|
||||
let image_url = "data:image/png;base64,abc".to_string();
|
||||
let label = codex_protocol::models::local_image_open_tag_text(1);
|
||||
let label = codex_protocol::models::local_image_open_tag_text(/*label_number*/ 1);
|
||||
let user_text = "Please review this image.".to_string();
|
||||
|
||||
let item = ResponseItem::Message {
|
||||
|
||||
@@ -121,7 +121,7 @@ fn populate_env_inserts_thread_id() {
|
||||
fn populate_env_omits_thread_id_when_missing() {
|
||||
let vars = make_vars(&[("PATH", "/usr/bin")]);
|
||||
let policy = ShellEnvironmentPolicy::default();
|
||||
let result = populate_env(vars, &policy, None);
|
||||
let result = populate_env(vars, &policy, /*thread_id*/ None);
|
||||
|
||||
let expected: HashMap<String, String> = hashmap! {
|
||||
"PATH".to_string() => "/usr/bin".to_string(),
|
||||
|
||||
@@ -115,7 +115,10 @@ async fn child_uses_parent_exec_policy_when_non_exec_policy_layers_differ() {
|
||||
let mut child_config = parent_config.clone();
|
||||
let mut layers: Vec<_> = child_config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
@@ -156,7 +159,10 @@ async fn child_does_not_use_parent_exec_policy_when_requirements_exec_policy_dif
|
||||
child_config.config_layer_stack = ConfigLayerStack::new(
|
||||
child_config
|
||||
.config_layer_stack
|
||||
.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true)
|
||||
.get_layers(
|
||||
ConfigLayerStackOrdering::LowestPrecedenceFirst,
|
||||
/*include_disabled*/ true,
|
||||
)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
@@ -291,7 +297,7 @@ async fn merges_requirements_exec_policy_network_rules() -> anyhow::Result<()> {
|
||||
"blocked.example.com",
|
||||
codex_execpolicy::NetworkRuleProtocol::Https,
|
||||
Decision::Forbidden,
|
||||
None,
|
||||
/*justification*/ None,
|
||||
)?;
|
||||
|
||||
let requirements = ConfigRequirements {
|
||||
@@ -338,7 +344,7 @@ host_executable(name = "git", paths = ["{git_path_literal}"])
|
||||
"blocked.example.com",
|
||||
codex_execpolicy::NetworkRuleProtocol::Https,
|
||||
Decision::Forbidden,
|
||||
None,
|
||||
/*justification*/ None,
|
||||
)?;
|
||||
|
||||
let requirements = ConfigRequirements {
|
||||
@@ -805,7 +811,7 @@ fn unmatched_granular_policy_still_prompts_for_restricted_sandbox_escalation() {
|
||||
&read_only_file_system_sandbox_policy(),
|
||||
&command,
|
||||
SandboxPermissions::RequireEscalated,
|
||||
false,
|
||||
/*used_complex_parsing*/ false,
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -823,7 +829,7 @@ fn unmatched_on_request_uses_split_filesystem_policy_for_escalation_prompts() {
|
||||
&restricted_file_system_policy,
|
||||
&command,
|
||||
SandboxPermissions::RequireEscalated,
|
||||
false,
|
||||
/*used_complex_parsing*/ false,
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1348,7 +1354,7 @@ fn derive_requested_execpolicy_amendment_for_test(
|
||||
fn derive_requested_execpolicy_amendment_returns_none_for_missing_prefix_rule() {
|
||||
assert_eq!(
|
||||
None,
|
||||
derive_requested_execpolicy_amendment_for_test(None, &[])
|
||||
derive_requested_execpolicy_amendment_for_test(/*prefix_rule*/ None, &[])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ fn make_exec_output(
|
||||
|
||||
#[test]
|
||||
fn sandbox_detection_requires_keywords() {
|
||||
let output = make_exec_output(1, "", "", "");
|
||||
let output = make_exec_output(/*exit_code*/ 1, "", "", "");
|
||||
assert!(!is_likely_sandbox_denied(
|
||||
SandboxType::LinuxSeccomp,
|
||||
&output
|
||||
@@ -33,13 +33,13 @@ fn sandbox_detection_requires_keywords() {
|
||||
|
||||
#[test]
|
||||
fn sandbox_detection_identifies_keyword_in_stderr() {
|
||||
let output = make_exec_output(1, "", "Operation not permitted", "");
|
||||
let output = make_exec_output(/*exit_code*/ 1, "", "Operation not permitted", "");
|
||||
assert!(is_likely_sandbox_denied(SandboxType::LinuxSeccomp, &output));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sandbox_detection_respects_quick_reject_exit_codes() {
|
||||
let output = make_exec_output(127, "", "command not found", "");
|
||||
let output = make_exec_output(/*exit_code*/ 127, "", "command not found", "");
|
||||
assert!(!is_likely_sandbox_denied(
|
||||
SandboxType::LinuxSeccomp,
|
||||
&output
|
||||
@@ -48,14 +48,14 @@ fn sandbox_detection_respects_quick_reject_exit_codes() {
|
||||
|
||||
#[test]
|
||||
fn sandbox_detection_ignores_non_sandbox_mode() {
|
||||
let output = make_exec_output(1, "", "Operation not permitted", "");
|
||||
let output = make_exec_output(/*exit_code*/ 1, "", "Operation not permitted", "");
|
||||
assert!(!is_likely_sandbox_denied(SandboxType::None, &output));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sandbox_detection_ignores_network_policy_text_in_non_sandbox_mode() {
|
||||
let output = make_exec_output(
|
||||
0,
|
||||
/*exit_code*/ 0,
|
||||
"",
|
||||
"",
|
||||
r#"CODEX_NETWORK_POLICY_DECISION {"decision":"ask","reason":"not_allowed","source":"decider","protocol":"http","host":"google.com","port":80}"#,
|
||||
@@ -66,7 +66,7 @@ fn sandbox_detection_ignores_network_policy_text_in_non_sandbox_mode() {
|
||||
#[test]
|
||||
fn sandbox_detection_uses_aggregated_output() {
|
||||
let output = make_exec_output(
|
||||
101,
|
||||
/*exit_code*/ 101,
|
||||
"",
|
||||
"",
|
||||
"cargo failed: Read-only file system when writing target",
|
||||
@@ -80,7 +80,7 @@ fn sandbox_detection_uses_aggregated_output() {
|
||||
#[test]
|
||||
fn sandbox_detection_ignores_network_policy_text_with_zero_exit_code() {
|
||||
let output = make_exec_output(
|
||||
0,
|
||||
/*exit_code*/ 0,
|
||||
"",
|
||||
"",
|
||||
r#"CODEX_NETWORK_POLICY_DECISION {"decision":"ask","source":"decider","protocol":"http","host":"google.com","port":80}"#,
|
||||
@@ -100,9 +100,14 @@ async fn read_output_limits_retained_bytes_for_shell_capture() {
|
||||
writer.write_all(&bytes).await.expect("write");
|
||||
});
|
||||
|
||||
let out = read_output(reader, None, false, Some(EXEC_OUTPUT_MAX_BYTES))
|
||||
.await
|
||||
.expect("read");
|
||||
let out = read_output(
|
||||
reader,
|
||||
/*stream*/ None,
|
||||
/*is_stderr*/ false,
|
||||
Some(EXEC_OUTPUT_MAX_BYTES),
|
||||
)
|
||||
.await
|
||||
.expect("read");
|
||||
assert_eq!(out.text.len(), EXEC_OUTPUT_MAX_BYTES);
|
||||
}
|
||||
|
||||
@@ -196,7 +201,11 @@ async fn read_output_retains_all_bytes_for_full_buffer_capture() {
|
||||
writer.write_all(&bytes).await.expect("write");
|
||||
});
|
||||
|
||||
let out = read_output(reader, None, false, None).await.expect("read");
|
||||
let out = read_output(
|
||||
reader, /*stream*/ None, /*is_stderr*/ false, /*max_bytes*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("read");
|
||||
assert_eq!(out.text.len(), expected_len);
|
||||
}
|
||||
|
||||
@@ -211,7 +220,7 @@ fn aggregate_output_keeps_all_bytes_when_uncapped() {
|
||||
truncated_after_lines: None,
|
||||
};
|
||||
|
||||
let aggregated = aggregate_output(&stdout, &stderr, None);
|
||||
let aggregated = aggregate_output(&stdout, &stderr, /*max_bytes*/ None);
|
||||
|
||||
assert_eq!(aggregated.text.len(), EXEC_OUTPUT_MAX_BYTES * 2);
|
||||
assert_eq!(
|
||||
@@ -362,8 +371,8 @@ async fn process_exec_tool_call_preserves_full_buffer_capture_policy() -> Result
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
cwd.as_path(),
|
||||
&None,
|
||||
false,
|
||||
None,
|
||||
/*use_legacy_landlock*/ false,
|
||||
/*stdout_stream*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -675,14 +684,15 @@ fn windows_elevated_rejects_split_write_read_carveouts() {
|
||||
|
||||
#[test]
|
||||
fn process_exec_tool_call_uses_platform_sandbox_for_network_only_restrictions() {
|
||||
let expected = codex_sandboxing::get_platform_sandbox(false).unwrap_or(SandboxType::None);
|
||||
let expected = codex_sandboxing::get_platform_sandbox(/*windows_sandbox_enabled*/ false)
|
||||
.unwrap_or(SandboxType::None);
|
||||
|
||||
assert_eq!(
|
||||
select_process_exec_tool_sandbox_type(
|
||||
&FileSystemSandboxPolicy::unrestricted(),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
false,
|
||||
/*enforce_managed_network*/ false,
|
||||
),
|
||||
expected
|
||||
);
|
||||
@@ -736,8 +746,8 @@ async fn kill_child_process_group_kills_grandchildren_on_timeout() -> Result<()>
|
||||
&FileSystemSandboxPolicy::from(&SandboxPolicy::new_read_only_policy()),
|
||||
None,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
None,
|
||||
None,
|
||||
/*stdout_stream*/ None,
|
||||
/*after_spawn*/ None,
|
||||
)
|
||||
.await?;
|
||||
assert!(output.timed_out);
|
||||
@@ -798,8 +808,8 @@ async fn process_exec_tool_call_respects_cancellation_token() -> Result<()> {
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
cwd.as_path(),
|
||||
&None,
|
||||
false,
|
||||
None,
|
||||
/*use_legacy_landlock*/ false,
|
||||
/*stdout_stream*/ None,
|
||||
)
|
||||
.await;
|
||||
let output = match result {
|
||||
|
||||
@@ -390,7 +390,7 @@ fn import_skills_returns_only_new_skill_directory_count() {
|
||||
fs::create_dir_all(agents_skills.join("skill-a")).expect("create existing target");
|
||||
|
||||
let copied_count = service_for_paths(claude_home, codex_home)
|
||||
.import_skills(None)
|
||||
.import_skills(/*cwd*/ None)
|
||||
.expect("import skills");
|
||||
|
||||
assert_eq!(copied_count, 1);
|
||||
|
||||
@@ -115,10 +115,10 @@ fn is_mutating_event_filters_non_mutating_event_kinds() {
|
||||
fn register_dedupes_by_path_and_scope() {
|
||||
let watcher = Arc::new(FileWatcher::noop());
|
||||
let (subscriber, _rx) = watcher.add_subscriber();
|
||||
let _first = subscriber.register_path(path("/tmp/skills"), false);
|
||||
let _second = subscriber.register_path(path("/tmp/skills"), false);
|
||||
let _third = subscriber.register_path(path("/tmp/skills"), true);
|
||||
let _fourth = subscriber.register_path(path("/tmp/other-skills"), true);
|
||||
let _first = subscriber.register_path(path("/tmp/skills"), /*recursive*/ false);
|
||||
let _second = subscriber.register_path(path("/tmp/skills"), /*recursive*/ false);
|
||||
let _third = subscriber.register_path(path("/tmp/skills"), /*recursive*/ true);
|
||||
let _fourth = subscriber.register_path(path("/tmp/other-skills"), /*recursive*/ true);
|
||||
|
||||
assert_eq!(
|
||||
watcher.watch_counts_for_test(&path("/tmp/skills")),
|
||||
@@ -134,7 +134,7 @@ fn register_dedupes_by_path_and_scope() {
|
||||
fn watch_registration_drop_unregisters_paths() {
|
||||
let watcher = Arc::new(FileWatcher::noop());
|
||||
let (subscriber, _rx) = watcher.add_subscriber();
|
||||
let registration = subscriber.register_path(path("/tmp/skills"), true);
|
||||
let registration = subscriber.register_path(path("/tmp/skills"), /*recursive*/ true);
|
||||
|
||||
drop(registration);
|
||||
|
||||
@@ -146,7 +146,7 @@ fn subscriber_drop_unregisters_paths() {
|
||||
let watcher = Arc::new(FileWatcher::noop());
|
||||
let registration = {
|
||||
let (subscriber, _rx) = watcher.add_subscriber();
|
||||
subscriber.register_path(path("/tmp/skills"), true)
|
||||
subscriber.register_path(path("/tmp/skills"), /*recursive*/ true)
|
||||
};
|
||||
|
||||
assert_eq!(watcher.watch_counts_for_test(&path("/tmp/skills")), None);
|
||||
@@ -174,8 +174,8 @@ fn recursive_registration_downgrades_to_non_recursive_after_drop() {
|
||||
|
||||
let watcher = Arc::new(FileWatcher::new().expect("watcher"));
|
||||
let (subscriber, _rx) = watcher.add_subscriber();
|
||||
let non_recursive = subscriber.register_path(root.clone(), false);
|
||||
let recursive = subscriber.register_path(root.clone(), true);
|
||||
let non_recursive = subscriber.register_path(root.clone(), /*recursive*/ false);
|
||||
let recursive = subscriber.register_path(root.clone(), /*recursive*/ true);
|
||||
|
||||
{
|
||||
let inner = watcher.inner.as_ref().expect("watcher inner");
|
||||
@@ -209,7 +209,7 @@ fn unregister_holds_state_lock_until_unwatch_finishes() {
|
||||
let watcher = Arc::new(FileWatcher::new().expect("watcher"));
|
||||
let (unregister_subscriber, _unregister_rx) = watcher.add_subscriber();
|
||||
let (register_subscriber, _register_rx) = watcher.add_subscriber();
|
||||
let registration = unregister_subscriber.register_path(root.clone(), true);
|
||||
let registration = unregister_subscriber.register_path(root.clone(), /*recursive*/ true);
|
||||
|
||||
let inner = watcher.inner.as_ref().expect("watcher inner");
|
||||
let inner_guard = inner.lock().expect("inner lock");
|
||||
@@ -229,7 +229,8 @@ fn unregister_holds_state_lock_until_unwatch_finishes() {
|
||||
|
||||
let register_root = root.clone();
|
||||
let register_thread = std::thread::spawn(move || {
|
||||
let registration = register_subscriber.register_path(register_root, false);
|
||||
let registration =
|
||||
register_subscriber.register_path(register_root, /*recursive*/ false);
|
||||
(register_subscriber, registration)
|
||||
});
|
||||
|
||||
@@ -257,8 +258,8 @@ async fn matching_subscribers_are_notified() {
|
||||
let watcher = Arc::new(FileWatcher::noop());
|
||||
let (skills_subscriber, skills_rx) = watcher.add_subscriber();
|
||||
let (plugins_subscriber, plugins_rx) = watcher.add_subscriber();
|
||||
let _skills = skills_subscriber.register_path(path("/tmp/skills"), true);
|
||||
let _plugins = plugins_subscriber.register_path(path("/tmp/plugins"), true);
|
||||
let _skills = skills_subscriber.register_path(path("/tmp/skills"), /*recursive*/ true);
|
||||
let _plugins = plugins_subscriber.register_path(path("/tmp/plugins"), /*recursive*/ true);
|
||||
let mut skills_rx = ThrottledWatchReceiver::new(skills_rx, TEST_THROTTLE_INTERVAL);
|
||||
let mut plugins_rx = ThrottledWatchReceiver::new(plugins_rx, TEST_THROTTLE_INTERVAL);
|
||||
|
||||
@@ -285,7 +286,7 @@ async fn matching_subscribers_are_notified() {
|
||||
async fn non_recursive_watch_ignores_grandchildren() {
|
||||
let watcher = Arc::new(FileWatcher::noop());
|
||||
let (subscriber, rx) = watcher.add_subscriber();
|
||||
let _registration = subscriber.register_path(path("/tmp/skills"), false);
|
||||
let _registration = subscriber.register_path(path("/tmp/skills"), /*recursive*/ false);
|
||||
let mut rx = ThrottledWatchReceiver::new(rx, TEST_THROTTLE_INTERVAL);
|
||||
|
||||
watcher
|
||||
@@ -300,7 +301,8 @@ async fn non_recursive_watch_ignores_grandchildren() {
|
||||
async fn ancestor_events_notify_child_watches() {
|
||||
let watcher = Arc::new(FileWatcher::noop());
|
||||
let (subscriber, rx) = watcher.add_subscriber();
|
||||
let _registration = subscriber.register_path(path("/tmp/skills/rust/SKILL.md"), false);
|
||||
let _registration =
|
||||
subscriber.register_path(path("/tmp/skills/rust/SKILL.md"), /*recursive*/ false);
|
||||
let mut rx = ThrottledWatchReceiver::new(rx, TEST_THROTTLE_INTERVAL);
|
||||
|
||||
watcher.send_paths_for_test(vec![path("/tmp/skills")]).await;
|
||||
@@ -321,7 +323,7 @@ async fn ancestor_events_notify_child_watches() {
|
||||
async fn spawn_event_loop_filters_non_mutating_events() {
|
||||
let watcher = Arc::new(FileWatcher::noop());
|
||||
let (subscriber, rx) = watcher.add_subscriber();
|
||||
let _registration = subscriber.register_path(path("/tmp/skills"), true);
|
||||
let _registration = subscriber.register_path(path("/tmp/skills"), /*recursive*/ true);
|
||||
let mut rx = ThrottledWatchReceiver::new(rx, TEST_THROTTLE_INTERVAL);
|
||||
let (raw_tx, raw_rx) = mpsc::unbounded_channel();
|
||||
watcher.spawn_event_loop_for_test(raw_rx);
|
||||
|
||||
@@ -72,7 +72,7 @@ async fn create_test_git_repo(temp_dir: &TempDir) -> PathBuf {
|
||||
#[tokio::test]
|
||||
async fn test_recent_commits_non_git_directory_returns_empty() {
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
||||
let entries = recent_commits(temp_dir.path(), 10).await;
|
||||
let entries = recent_commits(temp_dir.path(), /*limit*/ 10).await;
|
||||
assert!(entries.is_empty(), "expected no commits outside a git repo");
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ async fn test_recent_commits_orders_and_limits() {
|
||||
.expect("git commit 3");
|
||||
|
||||
// Request the latest 3 commits; should be our three changes in reverse time order.
|
||||
let entries = recent_commits(&repo_path, 3).await;
|
||||
let entries = recent_commits(&repo_path, /*limit*/ 3).await;
|
||||
assert_eq!(entries.len(), 3);
|
||||
assert_eq!(entries[0].subject, "third change");
|
||||
assert_eq!(entries[1].subject, "second change");
|
||||
|
||||
@@ -746,9 +746,13 @@ mod tests {
|
||||
#[test]
|
||||
fn guardian_review_session_config_change_invalidates_cached_session() {
|
||||
let parent_config = crate::config::test_config();
|
||||
let cached_spawn_config =
|
||||
build_guardian_review_session_config(&parent_config, None, "active-model", None)
|
||||
.expect("cached guardian config");
|
||||
let cached_spawn_config = build_guardian_review_session_config(
|
||||
&parent_config,
|
||||
/*live_network_config*/ None,
|
||||
"active-model",
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.expect("cached guardian config");
|
||||
let cached_reuse_key =
|
||||
GuardianReviewSessionReuseKey::from_spawn_config(&cached_spawn_config);
|
||||
|
||||
@@ -757,9 +761,9 @@ mod tests {
|
||||
Some("https://guardian.example.invalid/v1".to_string());
|
||||
let next_spawn_config = build_guardian_review_session_config(
|
||||
&changed_parent_config,
|
||||
None,
|
||||
/*live_network_config*/ None,
|
||||
"active-model",
|
||||
None,
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.expect("next guardian config");
|
||||
let next_reuse_key = GuardianReviewSessionReuseKey::from_spawn_config(&next_spawn_config);
|
||||
@@ -775,7 +779,7 @@ mod tests {
|
||||
async fn run_before_review_deadline_times_out_before_future_completes() {
|
||||
let outcome = run_before_review_deadline(
|
||||
tokio::time::Instant::now() + Duration::from_millis(10),
|
||||
None,
|
||||
/*external_cancel*/ None,
|
||||
async {
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
},
|
||||
@@ -816,7 +820,7 @@ mod tests {
|
||||
|
||||
let outcome = run_before_review_deadline_with_cancel(
|
||||
tokio::time::Instant::now() + Duration::from_millis(10),
|
||||
None,
|
||||
/*external_cancel*/ None,
|
||||
&cancel_token,
|
||||
async {
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
@@ -862,7 +866,7 @@ mod tests {
|
||||
|
||||
let outcome = run_before_review_deadline_with_cancel(
|
||||
tokio::time::Instant::now() + Duration::from_secs(1),
|
||||
None,
|
||||
/*external_cancel*/ None,
|
||||
&cancel_token,
|
||||
async { 42usize },
|
||||
)
|
||||
|
||||
@@ -251,7 +251,7 @@ fn collect_guardian_transcript_entries_includes_recent_tool_calls_and_output() {
|
||||
fn guardian_truncate_text_keeps_prefix_suffix_and_xml_marker() {
|
||||
let content = "prefix ".repeat(200) + &" suffix".repeat(200);
|
||||
|
||||
let truncated = guardian_truncate_text(&content, 20);
|
||||
let truncated = guardian_truncate_text(&content, /*token_cap*/ 20);
|
||||
|
||||
assert!(truncated.starts_with("prefix"));
|
||||
assert!(truncated.contains("<truncated omitted_approx_tokens=\""));
|
||||
@@ -392,7 +392,7 @@ async fn cancelled_guardian_review_emits_terminal_abort_without_warning() {
|
||||
patch: "*** Begin Patch\n*** Update File: guardian.txt\n@@\n+hello\n*** End Patch"
|
||||
.to_string(),
|
||||
},
|
||||
None,
|
||||
/*retry_reason*/ None,
|
||||
cancel_token,
|
||||
)
|
||||
.await;
|
||||
@@ -556,7 +556,7 @@ async fn guardian_review_request_layout_matches_model_visible_request_snapshot()
|
||||
Arc::clone(&turn),
|
||||
prompt,
|
||||
guardian_output_schema(),
|
||||
None,
|
||||
/*external_cancel*/ None,
|
||||
)
|
||||
.await;
|
||||
let GuardianReviewOutcome::Completed(Ok(assessment)) = outcome else {
|
||||
@@ -634,7 +634,7 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
||||
Arc::clone(&turn),
|
||||
first_prompt,
|
||||
guardian_output_schema(),
|
||||
None,
|
||||
/*external_cancel*/ None,
|
||||
)
|
||||
.await;
|
||||
let second_prompt = build_guardian_prompt_items(
|
||||
@@ -659,7 +659,7 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
||||
Arc::clone(&turn),
|
||||
second_prompt,
|
||||
guardian_output_schema(),
|
||||
None,
|
||||
/*external_cancel*/ None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -772,7 +772,7 @@ async fn guardian_review_surfaces_responses_api_errors_in_rejection_reason() ->
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the reviewed docs fix.".to_string()),
|
||||
},
|
||||
None,
|
||||
/*retry_reason*/ None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -884,7 +884,7 @@ async fn guardian_parallel_reviews_fork_from_last_committed_trunk_history() -> a
|
||||
justification: Some("Inspect repo state before proceeding.".to_string()),
|
||||
};
|
||||
assert_eq!(
|
||||
review_approval_request(&session, &turn, initial_request, None).await,
|
||||
review_approval_request(&session, &turn, initial_request, /*retry_reason*/ None).await,
|
||||
ReviewDecision::Approved
|
||||
);
|
||||
|
||||
@@ -988,7 +988,7 @@ fn guardian_review_session_config_preserves_parent_network_proxy() {
|
||||
|
||||
let guardian_config = build_guardian_review_session_config_for_test(
|
||||
&parent_config,
|
||||
None,
|
||||
/*live_network_config*/ None,
|
||||
"parent-active-model",
|
||||
Some(codex_protocol::openai_models::ReasoningEffort::Low),
|
||||
)
|
||||
@@ -1019,9 +1019,13 @@ fn guardian_review_session_config_overrides_parent_developer_instructions() {
|
||||
parent_config.developer_instructions =
|
||||
Some("parent or managed config should not replace guardian policy".to_string());
|
||||
|
||||
let guardian_config =
|
||||
build_guardian_review_session_config_for_test(&parent_config, None, "active-model", None)
|
||||
.expect("guardian config");
|
||||
let guardian_config = build_guardian_review_session_config_for_test(
|
||||
&parent_config,
|
||||
/*live_network_config*/ None,
|
||||
"active-model",
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.expect("guardian config");
|
||||
|
||||
assert_eq!(
|
||||
guardian_config.developer_instructions,
|
||||
@@ -1040,7 +1044,7 @@ fn guardian_review_session_config_uses_live_network_proxy_state() {
|
||||
parent_config.permissions.network = Some(
|
||||
NetworkProxySpec::from_config_and_constraints(
|
||||
parent_network,
|
||||
None,
|
||||
/*requirements*/ None,
|
||||
parent_config.permissions.sandbox_policy.get(),
|
||||
)
|
||||
.expect("parent network proxy spec"),
|
||||
@@ -1056,7 +1060,7 @@ fn guardian_review_session_config_uses_live_network_proxy_state() {
|
||||
&parent_config,
|
||||
Some(live_network.clone()),
|
||||
"active-model",
|
||||
None,
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.expect("guardian config");
|
||||
|
||||
@@ -1065,7 +1069,7 @@ fn guardian_review_session_config_uses_live_network_proxy_state() {
|
||||
Some(
|
||||
NetworkProxySpec::from_config_and_constraints(
|
||||
live_network,
|
||||
None,
|
||||
/*requirements*/ None,
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
)
|
||||
.expect("live network proxy spec")
|
||||
@@ -1087,9 +1091,13 @@ fn guardian_review_session_config_rejects_pinned_collab_feature() {
|
||||
)
|
||||
.expect("managed features");
|
||||
|
||||
let err =
|
||||
build_guardian_review_session_config_for_test(&parent_config, None, "active-model", None)
|
||||
.expect_err("guardian config should fail when collab is pinned on");
|
||||
let err = build_guardian_review_session_config_for_test(
|
||||
&parent_config,
|
||||
/*live_network_config*/ None,
|
||||
"active-model",
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.expect_err("guardian config should fail when collab is pinned on");
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
@@ -1102,9 +1110,13 @@ fn guardian_review_session_config_uses_parent_active_model_instead_of_hardcoded_
|
||||
let mut parent_config = test_config();
|
||||
parent_config.model = Some("configured-model".to_string());
|
||||
|
||||
let guardian_config =
|
||||
build_guardian_review_session_config_for_test(&parent_config, None, "active-model", None)
|
||||
.expect("guardian config");
|
||||
let guardian_config = build_guardian_review_session_config_for_test(
|
||||
&parent_config,
|
||||
/*live_network_config*/ None,
|
||||
"active-model",
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.expect("guardian config");
|
||||
|
||||
assert_eq!(guardian_config.model, Some("active-model".to_string()));
|
||||
}
|
||||
@@ -1135,9 +1147,13 @@ fn guardian_review_session_config_uses_requirements_guardian_override() {
|
||||
)
|
||||
.expect("load config");
|
||||
|
||||
let guardian_config =
|
||||
build_guardian_review_session_config_for_test(&parent_config, None, "active-model", None)
|
||||
.expect("guardian config");
|
||||
let guardian_config = build_guardian_review_session_config_for_test(
|
||||
&parent_config,
|
||||
/*live_network_config*/ None,
|
||||
"active-model",
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.expect("guardian config");
|
||||
|
||||
assert_eq!(
|
||||
guardian_config.developer_instructions,
|
||||
@@ -1163,9 +1179,13 @@ fn guardian_review_session_config_uses_default_guardian_policy_without_requireme
|
||||
)
|
||||
.expect("load config");
|
||||
|
||||
let guardian_config =
|
||||
build_guardian_review_session_config_for_test(&parent_config, None, "active-model", None)
|
||||
.expect("guardian config");
|
||||
let guardian_config = build_guardian_review_session_config_for_test(
|
||||
&parent_config,
|
||||
/*live_network_config*/ None,
|
||||
"active-model",
|
||||
/*reasoning_effort*/ None,
|
||||
)
|
||||
.expect("guardian config");
|
||||
|
||||
assert_eq!(
|
||||
guardian_config.developer_instructions,
|
||||
|
||||
@@ -209,7 +209,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolve_oauth_scopes_prefers_configured_over_discovered() {
|
||||
let resolved = resolve_oauth_scopes(
|
||||
None,
|
||||
/*explicit_scopes*/ None,
|
||||
Some(vec!["configured".to_string()]),
|
||||
Some(vec!["discovered".to_string()]),
|
||||
);
|
||||
@@ -225,7 +225,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resolve_oauth_scopes_uses_discovered_when_needed() {
|
||||
let resolved = resolve_oauth_scopes(None, None, Some(vec!["discovered".to_string()]));
|
||||
let resolved = resolve_oauth_scopes(
|
||||
/*explicit_scopes*/ None,
|
||||
/*configured_scopes*/ None,
|
||||
Some(vec!["discovered".to_string()]),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolved,
|
||||
@@ -238,7 +242,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resolve_oauth_scopes_preserves_explicitly_empty_configured_scopes() {
|
||||
let resolved = resolve_oauth_scopes(None, Some(Vec::new()), Some(vec!["ignored".into()]));
|
||||
let resolved = resolve_oauth_scopes(
|
||||
/*explicit_scopes*/ None,
|
||||
Some(Vec::new()),
|
||||
Some(vec!["ignored".into()]),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolved,
|
||||
@@ -251,7 +259,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resolve_oauth_scopes_falls_back_to_empty() {
|
||||
let resolved = resolve_oauth_scopes(None, None, None);
|
||||
let resolved = resolve_oauth_scopes(
|
||||
/*explicit_scopes*/ None, /*configured_scopes*/ None,
|
||||
/*discovered_scopes*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolved,
|
||||
|
||||
@@ -167,7 +167,12 @@ fn codex_apps_server_config_uses_legacy_codex_apps_path() {
|
||||
let mut config = crate::config::test_config();
|
||||
config.chatgpt_base_url = "https://chatgpt.com".to_string();
|
||||
|
||||
let mut servers = with_codex_apps_mcp(HashMap::new(), false, None, &config);
|
||||
let mut servers = with_codex_apps_mcp(
|
||||
HashMap::new(),
|
||||
/*connectors_enabled*/ false,
|
||||
/*auth*/ None,
|
||||
&config,
|
||||
);
|
||||
assert!(!servers.contains_key(CODEX_APPS_MCP_SERVER_NAME));
|
||||
|
||||
config
|
||||
@@ -175,7 +180,9 @@ fn codex_apps_server_config_uses_legacy_codex_apps_path() {
|
||||
.enable(Feature::Apps)
|
||||
.expect("test config should allow apps");
|
||||
|
||||
servers = with_codex_apps_mcp(servers, true, None, &config);
|
||||
servers = with_codex_apps_mcp(
|
||||
servers, /*connectors_enabled*/ true, /*auth*/ None, &config,
|
||||
);
|
||||
let server = servers
|
||||
.get(CODEX_APPS_MCP_SERVER_NAME)
|
||||
.expect("codex apps should be present when apps is enabled");
|
||||
@@ -252,7 +259,7 @@ async fn effective_mcp_servers_include_plugins_without_overriding_user_config()
|
||||
.expect("test config should accept MCP servers");
|
||||
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(config.codex_home.clone())));
|
||||
let effective = mcp_manager.effective_servers(&config, None);
|
||||
let effective = mcp_manager.effective_servers(&config, /*auth*/ None);
|
||||
|
||||
let sample = effective.get("sample").expect("user server should exist");
|
||||
let docs = effective.get("docs").expect("plugin server should exist");
|
||||
|
||||
@@ -562,7 +562,7 @@ fn mcp_init_error_display_prompts_for_login_when_auth_required() {
|
||||
let server_name = "example";
|
||||
let err: StartupOutcomeError = anyhow::anyhow!("Auth required for server").into();
|
||||
|
||||
let display = mcp_init_error_display(server_name, None, &err);
|
||||
let display = mcp_init_error_display(server_name, /*entry*/ None, &err);
|
||||
|
||||
let expected = format!(
|
||||
"The {server_name} MCP server is not logged in. Run `codex mcp login {server_name}`."
|
||||
@@ -609,7 +609,7 @@ fn mcp_init_error_display_includes_startup_timeout_hint() {
|
||||
let server_name = "slow";
|
||||
let err: StartupOutcomeError = anyhow::anyhow!("request timed out").into();
|
||||
|
||||
let display = mcp_init_error_display(server_name, None, &err);
|
||||
let display = mcp_init_error_display(server_name, /*entry*/ None, &err);
|
||||
|
||||
assert_eq!(
|
||||
"MCP client for `slow` timed out after 10 seconds. Add or adjust `startup_timeout_sec` in your config.toml:\n[mcp_servers.slow]\nstartup_timeout_sec = XX",
|
||||
|
||||
@@ -330,7 +330,7 @@ mod tests {
|
||||
&templates,
|
||||
"codex_apps",
|
||||
Some("github"),
|
||||
None,
|
||||
/*connector_name*/ None,
|
||||
Some("add_comment"),
|
||||
Some(&json!({})),
|
||||
);
|
||||
@@ -361,7 +361,7 @@ mod tests {
|
||||
&templates,
|
||||
"codex_apps",
|
||||
Some("calendar"),
|
||||
None,
|
||||
/*connector_name*/ None,
|
||||
Some("create_event"),
|
||||
Some(&json!({})),
|
||||
),
|
||||
|
||||
@@ -72,13 +72,13 @@ fn prompt_options(
|
||||
|
||||
#[test]
|
||||
fn approval_required_when_read_only_false_and_destructive() {
|
||||
let annotations = annotations(Some(false), Some(true), None);
|
||||
let annotations = annotations(Some(false), Some(true), /*open_world*/ None);
|
||||
assert_eq!(requires_mcp_tool_approval(Some(&annotations)), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn approval_required_when_read_only_false_and_open_world() {
|
||||
let annotations = annotations(Some(false), None, Some(true));
|
||||
let annotations = annotations(Some(false), /*destructive*/ None, Some(true));
|
||||
assert_eq!(requires_mcp_tool_approval(Some(&annotations)), true);
|
||||
}
|
||||
|
||||
@@ -90,12 +90,16 @@ fn approval_required_when_destructive_even_if_read_only_true() {
|
||||
|
||||
#[test]
|
||||
fn approval_required_when_annotations_are_absent() {
|
||||
assert_eq!(requires_mcp_tool_approval(None), true);
|
||||
assert_eq!(requires_mcp_tool_approval(/*annotations*/ None), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn approval_not_required_when_read_only_and_other_hints_are_absent() {
|
||||
let annotations = annotations(Some(true), None, None);
|
||||
let annotations = annotations(
|
||||
Some(true),
|
||||
/*destructive*/ None,
|
||||
/*open_world*/ None,
|
||||
);
|
||||
assert_eq!(requires_mcp_tool_approval(Some(&annotations)), false);
|
||||
}
|
||||
|
||||
@@ -187,7 +191,9 @@ async fn approval_elicitation_request_uses_message_override_and_preserves_tool_p
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
"create_event",
|
||||
Some("Calendar"),
|
||||
prompt_options(true, true),
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ true, /*allow_persistent_approval*/ true,
|
||||
),
|
||||
Some("Allow Calendar to create an event?"),
|
||||
);
|
||||
|
||||
@@ -221,7 +227,9 @@ async fn approval_elicitation_request_uses_message_override_and_preserves_tool_p
|
||||
]),
|
||||
question,
|
||||
message_override: Some("Allow Calendar to create an event?"),
|
||||
prompt_options: prompt_options(true, true),
|
||||
prompt_options: prompt_options(
|
||||
/*allow_session_remember*/ true, /*allow_persistent_approval*/ true,
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -279,9 +287,11 @@ fn custom_mcp_tool_question_mentions_server_name() {
|
||||
"q".to_string(),
|
||||
"custom_server",
|
||||
"run_action",
|
||||
None,
|
||||
prompt_options(false, false),
|
||||
None,
|
||||
/*connector_name*/ None,
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ false, /*allow_persistent_approval*/ false,
|
||||
),
|
||||
/*question_override*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(question.header, "Approve app tool call?");
|
||||
@@ -305,9 +315,11 @@ fn codex_apps_tool_question_uses_fallback_app_label() {
|
||||
"q".to_string(),
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
"run_action",
|
||||
None,
|
||||
prompt_options(true, true),
|
||||
None,
|
||||
/*connector_name*/ None,
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ true, /*allow_persistent_approval*/ true,
|
||||
),
|
||||
/*question_override*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -323,8 +335,10 @@ fn trusted_codex_apps_tool_question_offers_always_allow() {
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
"run_action",
|
||||
Some("Calendar"),
|
||||
prompt_options(true, true),
|
||||
None,
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ true, /*allow_persistent_approval*/ true,
|
||||
),
|
||||
/*question_override*/ None,
|
||||
);
|
||||
let options = question.options.expect("options");
|
||||
|
||||
@@ -363,8 +377,12 @@ fn codex_apps_tool_question_without_elicitation_omits_always_allow() {
|
||||
CODEX_APPS_MCP_SERVER_NAME,
|
||||
"run_action",
|
||||
Some("Calendar"),
|
||||
mcp_tool_approval_prompt_options(Some(&session_key), Some(&persistent_key), false),
|
||||
None,
|
||||
mcp_tool_approval_prompt_options(
|
||||
Some(&session_key),
|
||||
Some(&persistent_key),
|
||||
/*tool_call_mcp_elicitation_enabled*/ false,
|
||||
),
|
||||
/*question_override*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -388,9 +406,11 @@ fn custom_mcp_tool_question_offers_session_remember_and_always_allow() {
|
||||
"q".to_string(),
|
||||
"custom_server",
|
||||
"run_action",
|
||||
None,
|
||||
prompt_options(true, true),
|
||||
None,
|
||||
/*connector_name*/ None,
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ true, /*allow_persistent_approval*/ true,
|
||||
),
|
||||
/*question_override*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -423,11 +443,15 @@ fn custom_servers_support_session_and_persistent_approval() {
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
session_mcp_tool_approval_key(&invocation, None, AppToolApproval::Auto),
|
||||
session_mcp_tool_approval_key(&invocation, /*metadata*/ None, AppToolApproval::Auto),
|
||||
Some(expected.clone())
|
||||
);
|
||||
assert_eq!(
|
||||
persistent_mcp_tool_approval_key(&invocation, None, AppToolApproval::Auto),
|
||||
persistent_mcp_tool_approval_key(
|
||||
&invocation,
|
||||
/*metadata*/ None,
|
||||
AppToolApproval::Auto
|
||||
),
|
||||
Some(expected)
|
||||
);
|
||||
}
|
||||
@@ -439,7 +463,13 @@ fn codex_apps_connectors_support_persistent_approval() {
|
||||
tool: "calendar/list_events".to_string(),
|
||||
arguments: None,
|
||||
};
|
||||
let metadata = approval_metadata(Some("calendar"), Some("Calendar"), None, None, None);
|
||||
let metadata = approval_metadata(
|
||||
Some("calendar"),
|
||||
Some("Calendar"),
|
||||
/*connector_description*/ None,
|
||||
/*tool_title*/ None,
|
||||
/*tool_description*/ None,
|
||||
);
|
||||
let expected = McpToolApprovalKey {
|
||||
server: CODEX_APPS_MCP_SERVER_NAME.to_string(),
|
||||
connector_id: Some("calendar".to_string()),
|
||||
@@ -475,7 +505,8 @@ fn sanitize_mcp_tool_result_for_model_rewrites_image_content() {
|
||||
meta: None,
|
||||
});
|
||||
|
||||
let got = sanitize_mcp_tool_result_for_model(false, result).expect("sanitized result");
|
||||
let got = sanitize_mcp_tool_result_for_model(/*supports_image_input*/ false, result)
|
||||
.expect("sanitized result");
|
||||
|
||||
assert_eq!(
|
||||
got.content,
|
||||
@@ -505,8 +536,11 @@ fn sanitize_mcp_tool_result_for_model_preserves_image_when_supported() {
|
||||
meta: Some(serde_json::json!({"k": "v"})),
|
||||
};
|
||||
|
||||
let got =
|
||||
sanitize_mcp_tool_result_for_model(true, Ok(original.clone())).expect("unsanitized result");
|
||||
let got = sanitize_mcp_tool_result_for_model(
|
||||
/*supports_image_input*/ true,
|
||||
Ok(original.clone()),
|
||||
)
|
||||
.expect("unsanitized result");
|
||||
|
||||
assert_eq!(got, original);
|
||||
}
|
||||
@@ -606,10 +640,12 @@ fn approval_elicitation_meta_marks_tool_approvals() {
|
||||
assert_eq!(
|
||||
build_mcp_tool_approval_elicitation_meta(
|
||||
"custom_server",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
prompt_options(false, false),
|
||||
/*metadata*/ None,
|
||||
/*tool_params*/ None,
|
||||
/*tool_params_display*/ None,
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ false, /*allow_persistent_approval*/ false
|
||||
),
|
||||
),
|
||||
Some(serde_json::json!({
|
||||
MCP_TOOL_APPROVAL_KIND_KEY: MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL,
|
||||
@@ -623,15 +659,17 @@ fn approval_elicitation_meta_merges_session_and_always_persist_for_custom_server
|
||||
build_mcp_tool_approval_elicitation_meta(
|
||||
"custom_server",
|
||||
Some(&approval_metadata(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*connector_id*/ None,
|
||||
/*connector_name*/ None,
|
||||
/*connector_description*/ None,
|
||||
Some("Run Action"),
|
||||
Some("Runs the selected action."),
|
||||
)),
|
||||
Some(&serde_json::json!({"id": 1})),
|
||||
None,
|
||||
prompt_options(true, true),
|
||||
/*tool_params_display*/ None,
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ true, /*allow_persistent_approval*/ true
|
||||
),
|
||||
),
|
||||
Some(serde_json::json!({
|
||||
MCP_TOOL_APPROVAL_KIND_KEY: MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL,
|
||||
@@ -742,11 +780,11 @@ fn prepare_arc_request_action_serializes_mcp_tool_call_shape() {
|
||||
let action = prepare_arc_request_action(
|
||||
&invocation,
|
||||
Some(&approval_metadata(
|
||||
None,
|
||||
/*connector_id*/ None,
|
||||
Some("Playwright"),
|
||||
None,
|
||||
/*connector_description*/ None,
|
||||
Some("Navigate"),
|
||||
None,
|
||||
/*tool_description*/ None,
|
||||
)),
|
||||
);
|
||||
|
||||
@@ -796,8 +834,10 @@ fn approval_elicitation_meta_includes_connector_source_for_codex_apps() {
|
||||
Some(&serde_json::json!({
|
||||
"calendar_id": "primary",
|
||||
})),
|
||||
None,
|
||||
prompt_options(false, false),
|
||||
/*tool_params_display*/ None,
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ false, /*allow_persistent_approval*/ false
|
||||
),
|
||||
),
|
||||
Some(serde_json::json!({
|
||||
MCP_TOOL_APPROVAL_KIND_KEY: MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL,
|
||||
@@ -829,8 +869,10 @@ fn approval_elicitation_meta_merges_session_and_always_persist_with_connector_so
|
||||
Some(&serde_json::json!({
|
||||
"calendar_id": "primary",
|
||||
})),
|
||||
None,
|
||||
prompt_options(true, true),
|
||||
/*tool_params_display*/ None,
|
||||
prompt_options(
|
||||
/*allow_session_remember*/ true, /*allow_persistent_approval*/ true
|
||||
),
|
||||
),
|
||||
Some(serde_json::json!({
|
||||
MCP_TOOL_APPROVAL_KIND_KEY: MCP_TOOL_APPROVAL_KIND_MCP_TOOL_CALL,
|
||||
@@ -1170,7 +1212,11 @@ async fn approve_mode_skips_when_annotations_do_not_require_approval() {
|
||||
arguments: None,
|
||||
};
|
||||
let metadata = McpToolApprovalMetadata {
|
||||
annotations: Some(annotations(Some(true), None, None)),
|
||||
annotations: Some(annotations(
|
||||
Some(true),
|
||||
/*destructive*/ None,
|
||||
/*open_world*/ None,
|
||||
)),
|
||||
connector_id: None,
|
||||
connector_name: None,
|
||||
connector_description: None,
|
||||
@@ -1233,7 +1279,11 @@ async fn guardian_mode_skips_auto_when_annotations_do_not_require_approval() {
|
||||
arguments: None,
|
||||
};
|
||||
let metadata = McpToolApprovalMetadata {
|
||||
annotations: Some(annotations(Some(true), None, None)),
|
||||
annotations: Some(annotations(
|
||||
Some(true),
|
||||
/*destructive*/ None,
|
||||
/*open_world*/ None,
|
||||
)),
|
||||
connector_id: None,
|
||||
connector_name: None,
|
||||
connector_description: None,
|
||||
@@ -1268,7 +1318,11 @@ async fn prompt_mode_waits_for_approval_when_annotations_do_not_require_approval
|
||||
arguments: None,
|
||||
};
|
||||
let metadata = McpToolApprovalMetadata {
|
||||
annotations: Some(annotations(Some(true), None, None)),
|
||||
annotations: Some(annotations(
|
||||
Some(true),
|
||||
/*destructive*/ None,
|
||||
/*open_world*/ None,
|
||||
)),
|
||||
connector_id: None,
|
||||
connector_name: None,
|
||||
connector_description: None,
|
||||
|
||||
@@ -29,7 +29,7 @@ fn fixed_thread_id() -> ThreadId {
|
||||
#[test]
|
||||
fn rollout_summary_file_stem_uses_uuid_timestamp_and_hash_when_slug_missing() {
|
||||
let thread_id = fixed_thread_id();
|
||||
let memory = stage1_output_with_slug(thread_id, None);
|
||||
let memory = stage1_output_with_slug(thread_id, /*rollout_slug*/ None);
|
||||
|
||||
assert_eq!(rollout_summary_file_stem(&memory), FIXED_PREFIX);
|
||||
assert_eq!(
|
||||
|
||||
@@ -526,8 +526,8 @@ mod phase2 {
|
||||
thread_id,
|
||||
self.session.conversation_id,
|
||||
source_updated_at,
|
||||
3_600,
|
||||
64,
|
||||
/*lease_seconds*/ 3_600,
|
||||
/*max_running_jobs*/ 64,
|
||||
)
|
||||
.await
|
||||
.expect("claim stage-1 job");
|
||||
@@ -543,7 +543,7 @@ mod phase2 {
|
||||
source_updated_at,
|
||||
"raw memory",
|
||||
"rollout summary",
|
||||
None,
|
||||
/*rollout_slug*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("mark stage-1 success"),
|
||||
@@ -571,24 +571,24 @@ mod phase2 {
|
||||
|
||||
#[test]
|
||||
fn completion_watermark_never_regresses_below_claimed_input_watermark() {
|
||||
let stage1_output = stage1_output_with_source_updated_at(123);
|
||||
let stage1_output = stage1_output_with_source_updated_at(/*source_updated_at*/ 123);
|
||||
|
||||
let completion = phase2::get_watermark(1_000, &[stage1_output]);
|
||||
let completion = phase2::get_watermark(/*claimed_watermark*/ 1_000, &[stage1_output]);
|
||||
pretty_assertions::assert_eq!(completion, 1_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completion_watermark_uses_claimed_watermark_when_there_are_no_memories() {
|
||||
let completion = phase2::get_watermark(777, &[]);
|
||||
let completion = phase2::get_watermark(/*claimed_watermark*/ 777, &[]);
|
||||
pretty_assertions::assert_eq!(completion, 777);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completion_watermark_uses_latest_memory_timestamp_when_it_is_newer() {
|
||||
let older = stage1_output_with_source_updated_at(123);
|
||||
let newer = stage1_output_with_source_updated_at(456);
|
||||
let older = stage1_output_with_source_updated_at(/*source_updated_at*/ 123);
|
||||
let newer = stage1_output_with_source_updated_at(/*source_updated_at*/ 456);
|
||||
|
||||
let completion = phase2::get_watermark(200, &[older, newer]);
|
||||
let completion = phase2::get_watermark(/*claimed_watermark*/ 200, &[older, newer]);
|
||||
pretty_assertions::assert_eq!(completion, 456);
|
||||
}
|
||||
|
||||
@@ -608,12 +608,12 @@ mod phase2 {
|
||||
let harness = DispatchHarness::new().await;
|
||||
harness
|
||||
.state_db
|
||||
.enqueue_global_consolidation(123)
|
||||
.enqueue_global_consolidation(/*input_watermark*/ 123)
|
||||
.await
|
||||
.expect("enqueue global consolidation");
|
||||
let claimed = harness
|
||||
.state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 3_600)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 3_600)
|
||||
.await
|
||||
.expect("claim running global lock");
|
||||
assert!(
|
||||
@@ -625,7 +625,7 @@ mod phase2 {
|
||||
|
||||
let running_claim = harness
|
||||
.state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 3_600)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 3_600)
|
||||
.await
|
||||
.expect("claim while lock is still running");
|
||||
pretty_assertions::assert_eq!(running_claim, Phase2JobClaimOutcome::SkippedRunning);
|
||||
@@ -641,7 +641,7 @@ mod phase2 {
|
||||
|
||||
let stale_claim = harness
|
||||
.state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 0)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 0)
|
||||
.await
|
||||
.expect("claim stale global lock");
|
||||
assert!(
|
||||
@@ -653,7 +653,7 @@ mod phase2 {
|
||||
|
||||
let post_dispatch_claim = harness
|
||||
.state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 3_600)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 3_600)
|
||||
.await
|
||||
.expect("claim after stale lock dispatch");
|
||||
assert!(
|
||||
@@ -759,7 +759,7 @@ mod phase2 {
|
||||
|
||||
harness
|
||||
.state_db
|
||||
.enqueue_global_consolidation(999)
|
||||
.enqueue_global_consolidation(/*input_watermark*/ 999)
|
||||
.await
|
||||
.expect("enqueue global consolidation");
|
||||
|
||||
@@ -801,7 +801,7 @@ mod phase2 {
|
||||
);
|
||||
let next_claim = harness
|
||||
.state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 3_600)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 3_600)
|
||||
.await
|
||||
.expect("claim global job after empty consolidation success");
|
||||
pretty_assertions::assert_eq!(next_claim, Phase2JobClaimOutcome::SkippedNotDirty);
|
||||
@@ -817,7 +817,7 @@ mod phase2 {
|
||||
let harness = DispatchHarness::new().await;
|
||||
harness
|
||||
.state_db
|
||||
.enqueue_global_consolidation(99)
|
||||
.enqueue_global_consolidation(/*input_watermark*/ 99)
|
||||
.await
|
||||
.expect("enqueue global consolidation");
|
||||
let mut constrained_config = harness.config.as_ref().clone();
|
||||
@@ -828,7 +828,7 @@ mod phase2 {
|
||||
|
||||
let retry_claim = harness
|
||||
.state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 3_600)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 3_600)
|
||||
.await
|
||||
.expect("claim global job after sandbox policy failure");
|
||||
pretty_assertions::assert_eq!(retry_claim, Phase2JobClaimOutcome::SkippedNotDirty);
|
||||
@@ -840,7 +840,7 @@ mod phase2 {
|
||||
#[tokio::test]
|
||||
async fn dispatch_marks_job_for_retry_when_syncing_artifacts_fails() {
|
||||
let harness = DispatchHarness::new().await;
|
||||
harness.seed_stage1_output(100).await;
|
||||
harness.seed_stage1_output(/*source_updated_at*/ 100).await;
|
||||
let root = memory_root(&harness.config.codex_home);
|
||||
tokio::fs::write(&root, "not a directory")
|
||||
.await
|
||||
@@ -850,7 +850,7 @@ mod phase2 {
|
||||
|
||||
let retry_claim = harness
|
||||
.state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 3_600)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 3_600)
|
||||
.await
|
||||
.expect("claim global job after sync failure");
|
||||
pretty_assertions::assert_eq!(retry_claim, Phase2JobClaimOutcome::SkippedNotDirty);
|
||||
@@ -862,7 +862,7 @@ mod phase2 {
|
||||
#[tokio::test]
|
||||
async fn dispatch_marks_job_for_retry_when_rebuilding_raw_memories_fails() {
|
||||
let harness = DispatchHarness::new().await;
|
||||
harness.seed_stage1_output(100).await;
|
||||
harness.seed_stage1_output(/*source_updated_at*/ 100).await;
|
||||
let root = memory_root(&harness.config.codex_home);
|
||||
tokio::fs::create_dir_all(raw_memories_file(&root))
|
||||
.await
|
||||
@@ -872,7 +872,7 @@ mod phase2 {
|
||||
|
||||
let retry_claim = harness
|
||||
.state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 3_600)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 3_600)
|
||||
.await
|
||||
.expect("claim global job after rebuild failure");
|
||||
pretty_assertions::assert_eq!(retry_claim, Phase2JobClaimOutcome::SkippedNotDirty);
|
||||
@@ -917,7 +917,13 @@ mod phase2 {
|
||||
.expect("upsert thread metadata");
|
||||
|
||||
let claim = state_db
|
||||
.try_claim_stage1_job(thread_id, session.conversation_id, 100, 3_600, 64)
|
||||
.try_claim_stage1_job(
|
||||
thread_id,
|
||||
session.conversation_id,
|
||||
/*source_updated_at*/ 100,
|
||||
/*lease_seconds*/ 3_600,
|
||||
/*max_running_jobs*/ 64,
|
||||
)
|
||||
.await
|
||||
.expect("claim stage-1 job");
|
||||
let ownership_token = match claim {
|
||||
@@ -929,10 +935,10 @@ mod phase2 {
|
||||
.mark_stage1_job_succeeded(
|
||||
thread_id,
|
||||
&ownership_token,
|
||||
100,
|
||||
/*source_updated_at*/ 100,
|
||||
"raw memory",
|
||||
"rollout summary",
|
||||
None,
|
||||
/*rollout_slug*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("mark stage-1 success"),
|
||||
@@ -942,7 +948,7 @@ mod phase2 {
|
||||
phase2::run(&session, Arc::clone(&config)).await;
|
||||
|
||||
let retry_claim = state_db
|
||||
.try_claim_global_phase2_job(ThreadId::new(), 3_600)
|
||||
.try_claim_global_phase2_job(ThreadId::new(), /*lease_seconds*/ 3_600)
|
||||
.await
|
||||
.expect("claim global job after spawn failure");
|
||||
pretty_assertions::assert_eq!(
|
||||
|
||||
@@ -37,8 +37,8 @@ async fn lookup_reads_history_entries() {
|
||||
let (log_id, count) = history_metadata_for_file(&history_path).await;
|
||||
assert_eq!(count, entries.len());
|
||||
|
||||
let second_entry =
|
||||
lookup_history_entry(&history_path, log_id, 1).expect("fetch second history entry");
|
||||
let second_entry = lookup_history_entry(&history_path, log_id, /*offset*/ 1)
|
||||
.expect("fetch second history entry");
|
||||
assert_eq!(second_entry, entries[1]);
|
||||
}
|
||||
|
||||
@@ -80,8 +80,8 @@ async fn lookup_uses_stable_log_id_after_appends() {
|
||||
)
|
||||
.expect("append history entry");
|
||||
|
||||
let fetched =
|
||||
lookup_history_entry(&history_path, log_id, 1).expect("lookup appended history entry");
|
||||
let fetched = lookup_history_entry(&history_path, log_id, /*offset*/ 1)
|
||||
.expect("lookup appended history entry");
|
||||
assert_eq!(fetched, appended);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,10 @@ fn default_mode_instructions_replace_mode_names_placeholder() {
|
||||
let expected_snippet = format!("Known mode names are {known_mode_names}.");
|
||||
assert!(default_instructions.contains(&expected_snippet));
|
||||
|
||||
let expected_availability_message =
|
||||
request_user_input_availability_message(ModeKind::Default, true);
|
||||
let expected_availability_message = request_user_input_availability_message(
|
||||
ModeKind::Default,
|
||||
/*default_mode_request_user_input*/ true,
|
||||
);
|
||||
assert!(default_instructions.contains(&expected_availability_message));
|
||||
assert!(default_instructions.contains("prefer using the `request_user_input` tool"));
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ async fn get_model_info_tracks_fallback_usage() {
|
||||
let manager = ModelsManager::new(
|
||||
codex_home.path().to_path_buf(),
|
||||
auth_manager,
|
||||
None,
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
);
|
||||
let known_slug = manager
|
||||
@@ -175,7 +175,7 @@ async fn get_model_info_uses_custom_catalog() {
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
let mut overlay = remote_model("gpt-overlay", "Overlay", 0);
|
||||
let mut overlay = remote_model("gpt-overlay", "Overlay", /*priority*/ 0);
|
||||
overlay.supports_image_detail_original = true;
|
||||
|
||||
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
|
||||
@@ -208,7 +208,7 @@ async fn get_model_info_matches_namespaced_suffix() {
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
let mut remote = remote_model("gpt-image", "Image", 0);
|
||||
let mut remote = remote_model("gpt-image", "Image", /*priority*/ 0);
|
||||
remote.supports_image_detail_original = true;
|
||||
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
|
||||
let manager = ModelsManager::new(
|
||||
@@ -240,7 +240,7 @@ async fn get_model_info_rejects_multi_segment_namespace_suffix_matching() {
|
||||
let manager = ModelsManager::new(
|
||||
codex_home.path().to_path_buf(),
|
||||
auth_manager,
|
||||
None,
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
);
|
||||
let known_slug = manager
|
||||
@@ -262,8 +262,8 @@ async fn get_model_info_rejects_multi_segment_namespace_suffix_matching() {
|
||||
async fn refresh_available_models_sorts_by_priority() {
|
||||
let server = MockServer::start().await;
|
||||
let remote_models = vec![
|
||||
remote_model("priority-low", "Low", 1),
|
||||
remote_model("priority-high", "High", 0),
|
||||
remote_model("priority-low", "Low", /*priority*/ 1),
|
||||
remote_model("priority-high", "High", /*priority*/ 0),
|
||||
];
|
||||
let models_mock = mount_models_once(
|
||||
&server,
|
||||
@@ -313,7 +313,7 @@ async fn refresh_available_models_sorts_by_priority() {
|
||||
#[tokio::test]
|
||||
async fn refresh_available_models_uses_cache_when_fresh() {
|
||||
let server = MockServer::start().await;
|
||||
let remote_models = vec![remote_model("cached", "Cached", 5)];
|
||||
let remote_models = vec![remote_model("cached", "Cached", /*priority*/ 5)];
|
||||
let models_mock = mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
@@ -354,7 +354,7 @@ async fn refresh_available_models_uses_cache_when_fresh() {
|
||||
#[tokio::test]
|
||||
async fn refresh_available_models_refetches_when_cache_stale() {
|
||||
let server = MockServer::start().await;
|
||||
let initial_models = vec![remote_model("stale", "Stale", 1)];
|
||||
let initial_models = vec![remote_model("stale", "Stale", /*priority*/ 1)];
|
||||
let initial_mock = mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
@@ -387,7 +387,7 @@ async fn refresh_available_models_refetches_when_cache_stale() {
|
||||
.await
|
||||
.expect("cache manipulation succeeds");
|
||||
|
||||
let updated_models = vec![remote_model("fresh", "Fresh", 9)];
|
||||
let updated_models = vec![remote_model("fresh", "Fresh", /*priority*/ 9)];
|
||||
server.reset().await;
|
||||
let refreshed_mock = mount_models_once(
|
||||
&server,
|
||||
@@ -417,7 +417,7 @@ async fn refresh_available_models_refetches_when_cache_stale() {
|
||||
#[tokio::test]
|
||||
async fn refresh_available_models_refetches_when_version_mismatch() {
|
||||
let server = MockServer::start().await;
|
||||
let initial_models = vec![remote_model("old", "Old", 1)];
|
||||
let initial_models = vec![remote_model("old", "Old", /*priority*/ 1)];
|
||||
let initial_mock = mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
@@ -450,7 +450,7 @@ async fn refresh_available_models_refetches_when_version_mismatch() {
|
||||
.await
|
||||
.expect("cache mutation succeeds");
|
||||
|
||||
let updated_models = vec![remote_model("new", "New", 2)];
|
||||
let updated_models = vec![remote_model("new", "New", /*priority*/ 2)];
|
||||
server.reset().await;
|
||||
let refreshed_mock = mount_models_once(
|
||||
&server,
|
||||
@@ -480,7 +480,11 @@ async fn refresh_available_models_refetches_when_version_mismatch() {
|
||||
#[tokio::test]
|
||||
async fn refresh_available_models_drops_removed_remote_models() {
|
||||
let server = MockServer::start().await;
|
||||
let initial_models = vec![remote_model("remote-old", "Remote Old", 1)];
|
||||
let initial_models = vec![remote_model(
|
||||
"remote-old",
|
||||
"Remote Old",
|
||||
/*priority*/ 1,
|
||||
)];
|
||||
let initial_mock = mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
@@ -506,7 +510,11 @@ async fn refresh_available_models_drops_removed_remote_models() {
|
||||
.expect("initial refresh succeeds");
|
||||
|
||||
server.reset().await;
|
||||
let refreshed_models = vec![remote_model("remote-new", "Remote New", 1)];
|
||||
let refreshed_models = vec![remote_model(
|
||||
"remote-new",
|
||||
"Remote New",
|
||||
/*priority*/ 1,
|
||||
)];
|
||||
let refreshed_mock = mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
@@ -550,7 +558,7 @@ async fn refresh_available_models_skips_network_without_chatgpt_auth() {
|
||||
let models_mock = mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
models: vec![remote_model(dynamic_slug, "No Auth", 1)],
|
||||
models: vec![remote_model(dynamic_slug, "No Auth", /*priority*/ 1)],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
@@ -558,7 +566,7 @@ async fn refresh_available_models_skips_network_without_chatgpt_auth() {
|
||||
let codex_home = tempdir().expect("temp dir");
|
||||
let auth_manager = Arc::new(AuthManager::new(
|
||||
codex_home.path().to_path_buf(),
|
||||
false,
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
AuthCredentialsStoreMode::File,
|
||||
));
|
||||
let provider = provider_for(server.uri());
|
||||
@@ -621,7 +629,7 @@ fn models_request_telemetry_emits_auth_env_feedback_tags_on_failure() {
|
||||
.unwrap(),
|
||||
);
|
||||
telemetry.on_request(
|
||||
1,
|
||||
/*attempt*/ 1,
|
||||
Some(StatusCode::UNAUTHORIZED),
|
||||
Some(&TransportError::Http {
|
||||
status: StatusCode::UNAUTHORIZED,
|
||||
@@ -695,8 +703,10 @@ fn build_available_models_picks_default_after_hiding_hidden_models() {
|
||||
provider,
|
||||
);
|
||||
|
||||
let hidden_model = remote_model_with_visibility("hidden", "Hidden", 0, "hide");
|
||||
let visible_model = remote_model_with_visibility("visible", "Visible", 1, "list");
|
||||
let hidden_model =
|
||||
remote_model_with_visibility("hidden", "Hidden", /*priority*/ 0, "hide");
|
||||
let visible_model =
|
||||
remote_model_with_visibility("visible", "Visible", /*priority*/ 1, "list");
|
||||
|
||||
let expected_hidden = ModelPreset::from(hidden_model.clone());
|
||||
let mut expected_visible = ModelPreset::from(visible_model.clone());
|
||||
|
||||
@@ -120,7 +120,7 @@ fn execpolicy_network_rules_overlay_network_lists() {
|
||||
"blocked.example.com",
|
||||
NetworkRuleProtocol::Https,
|
||||
Decision::Allow,
|
||||
None,
|
||||
/*justification*/ None,
|
||||
)
|
||||
.expect("allow rule should be valid");
|
||||
exec_policy
|
||||
@@ -128,7 +128,7 @@ fn execpolicy_network_rules_overlay_network_lists() {
|
||||
"api.example.com",
|
||||
NetworkRuleProtocol::Http,
|
||||
Decision::Forbidden,
|
||||
None,
|
||||
/*justification*/ None,
|
||||
)
|
||||
.expect("deny rule should be valid");
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ fn image_detail_original_feature_enables_explicit_original_without_force() {
|
||||
Some(ImageDetail::Original)
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_output_image_detail(&features, &model_info, None),
|
||||
normalize_output_image_detail(&features, &model_info, /*detail*/ None),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,10 @@ fn load_plugins_loads_default_skills_and_mcp_servers() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
let outcome = load_plugins_from_config(&plugin_config_toml(true, true), codex_home.path());
|
||||
let outcome = load_plugins_from_config(
|
||||
&plugin_config_toml(/*enabled*/ true, /*plugins_feature_enabled*/ true),
|
||||
codex_home.path(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
outcome.plugins(),
|
||||
@@ -340,7 +343,10 @@ fn capability_summary_sanitizes_plugin_descriptions_to_one_line() {
|
||||
"---\nname: sample-search\ndescription: search sample data\n---\n",
|
||||
);
|
||||
|
||||
let outcome = load_plugins_from_config(&plugin_config_toml(true, true), codex_home.path());
|
||||
let outcome = load_plugins_from_config(
|
||||
&plugin_config_toml(/*enabled*/ true, /*plugins_feature_enabled*/ true),
|
||||
codex_home.path(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
outcome.plugins()[0].manifest_description.as_deref(),
|
||||
@@ -375,7 +381,10 @@ fn capability_summary_truncates_overlong_plugin_descriptions() {
|
||||
"---\nname: sample-search\ndescription: search sample data\n---\n",
|
||||
);
|
||||
|
||||
let outcome = load_plugins_from_config(&plugin_config_toml(true, true), codex_home.path());
|
||||
let outcome = load_plugins_from_config(
|
||||
&plugin_config_toml(/*enabled*/ true, /*plugins_feature_enabled*/ true),
|
||||
codex_home.path(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
outcome.plugins()[0].manifest_description.as_deref(),
|
||||
@@ -455,7 +464,10 @@ fn load_plugins_uses_manifest_configured_component_paths() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
let outcome = load_plugins_from_config(&plugin_config_toml(true, true), codex_home.path());
|
||||
let outcome = load_plugins_from_config(
|
||||
&plugin_config_toml(/*enabled*/ true, /*plugins_feature_enabled*/ true),
|
||||
codex_home.path(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
outcome.plugins()[0].skill_roots,
|
||||
@@ -562,7 +574,10 @@ fn load_plugins_ignores_manifest_component_paths_without_dot_slash() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
let outcome = load_plugins_from_config(&plugin_config_toml(true, true), codex_home.path());
|
||||
let outcome = load_plugins_from_config(
|
||||
&plugin_config_toml(/*enabled*/ true, /*plugins_feature_enabled*/ true),
|
||||
codex_home.path(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
outcome.plugins()[0].skill_roots,
|
||||
@@ -622,7 +637,12 @@ fn load_plugins_preserves_disabled_plugins_without_effective_contributions() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
let outcome = load_plugins_from_config(&plugin_config_toml(false, true), codex_home.path());
|
||||
let outcome = load_plugins_from_config(
|
||||
&plugin_config_toml(
|
||||
/*enabled*/ false, /*plugins_feature_enabled*/ true,
|
||||
),
|
||||
codex_home.path(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
outcome.plugins(),
|
||||
@@ -831,7 +851,9 @@ fn load_plugins_returns_empty_when_feature_disabled() {
|
||||
);
|
||||
write_file(
|
||||
&codex_home.path().join(CONFIG_TOML_FILE),
|
||||
&plugin_config_toml(true, false),
|
||||
&plugin_config_toml(
|
||||
/*enabled*/ true, /*plugins_feature_enabled*/ false,
|
||||
),
|
||||
);
|
||||
|
||||
let config = load_config_blocking(codex_home.path(), codex_home.path());
|
||||
@@ -1326,7 +1348,7 @@ plugins = false
|
||||
|
||||
let config = load_config(tmp.path(), tmp.path()).await;
|
||||
let outcome = PluginsManager::new(tmp.path().to_path_buf())
|
||||
.sync_plugins_from_remote(&config, None, /*additive_only*/ false)
|
||||
.sync_plugins_from_remote(&config, /*auth*/ None, /*additive_only*/ false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -2110,10 +2132,13 @@ plugins = true
|
||||
|
||||
let mut config = load_config(tmp.path(), tmp.path()).await;
|
||||
config.chatgpt_base_url = format!("{}/backend-api/", server.uri());
|
||||
let manager = PluginsManager::new_with_restriction_product(tmp.path().to_path_buf(), None);
|
||||
let manager = PluginsManager::new_with_restriction_product(
|
||||
tmp.path().to_path_buf(),
|
||||
/*restriction_product*/ None,
|
||||
);
|
||||
|
||||
let featured_plugin_ids = manager
|
||||
.featured_plugin_ids_for_config(&config, None)
|
||||
.featured_plugin_ids_for_config(&config, /*auth*/ None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -2219,7 +2244,7 @@ fn load_plugins_ignores_project_config_files() {
|
||||
);
|
||||
write_file(
|
||||
&project_root.join(".codex/config.toml"),
|
||||
&plugin_config_toml(true, true),
|
||||
&plugin_config_toml(/*enabled*/ true, /*plugins_feature_enabled*/ true),
|
||||
);
|
||||
|
||||
let stack = ConfigLayerStack::new(
|
||||
@@ -2227,7 +2252,10 @@ fn load_plugins_ignores_project_config_files() {
|
||||
ConfigLayerSource::Project {
|
||||
dot_codex_folder: AbsolutePathBuf::try_from(project_root.join(".codex")).unwrap(),
|
||||
},
|
||||
toml::from_str(&plugin_config_toml(true, true)).expect("project config should parse"),
|
||||
toml::from_str(&plugin_config_toml(
|
||||
/*enabled*/ true, /*plugins_feature_enabled*/ true,
|
||||
))
|
||||
.expect("project config should parse"),
|
||||
)],
|
||||
ConfigRequirements::default(),
|
||||
ConfigRequirementsToml::default(),
|
||||
|
||||
@@ -342,7 +342,7 @@ fn list_marketplaces_dedupes_multiple_roots_in_same_repo() {
|
||||
AbsolutePathBuf::try_from(repo_root.clone()).unwrap(),
|
||||
AbsolutePathBuf::try_from(nested_root).unwrap(),
|
||||
],
|
||||
None,
|
||||
/*home_dir*/ None,
|
||||
)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
@@ -397,10 +397,12 @@ fn list_marketplaces_reads_marketplace_display_name() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let marketplaces =
|
||||
list_marketplaces_with_home(&[AbsolutePathBuf::try_from(repo_root).unwrap()], None)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
let marketplaces = list_marketplaces_with_home(
|
||||
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
|
||||
/*home_dir*/ None,
|
||||
)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
|
||||
assert_eq!(
|
||||
marketplaces[0].interface,
|
||||
@@ -458,7 +460,7 @@ fn list_marketplaces_skips_marketplaces_that_fail_to_load() {
|
||||
AbsolutePathBuf::try_from(valid_repo_root).unwrap(),
|
||||
AbsolutePathBuf::try_from(invalid_repo_root).unwrap(),
|
||||
],
|
||||
None,
|
||||
/*home_dir*/ None,
|
||||
)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
@@ -503,7 +505,7 @@ fn list_marketplaces_reports_marketplace_load_errors() {
|
||||
AbsolutePathBuf::try_from(valid_repo_root).unwrap(),
|
||||
AbsolutePathBuf::try_from(invalid_repo_root).unwrap(),
|
||||
],
|
||||
None,
|
||||
/*home_dir*/ None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -566,10 +568,12 @@ fn list_marketplaces_resolves_plugin_interface_paths_to_absolute() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let marketplaces =
|
||||
list_marketplaces_with_home(&[AbsolutePathBuf::try_from(repo_root).unwrap()], None)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
let marketplaces = list_marketplaces_with_home(
|
||||
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
|
||||
/*home_dir*/ None,
|
||||
)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
|
||||
assert_eq!(
|
||||
marketplaces[0].plugins[0].policy.installation,
|
||||
@@ -634,10 +638,12 @@ fn list_marketplaces_ignores_legacy_top_level_policy_fields() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let marketplaces =
|
||||
list_marketplaces_with_home(&[AbsolutePathBuf::try_from(repo_root).unwrap()], None)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
let marketplaces = list_marketplaces_with_home(
|
||||
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
|
||||
/*home_dir*/ None,
|
||||
)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
|
||||
assert_eq!(
|
||||
marketplaces[0].plugins[0].policy.installation,
|
||||
@@ -690,10 +696,12 @@ fn list_marketplaces_ignores_plugin_interface_assets_without_dot_slash() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let marketplaces =
|
||||
list_marketplaces_with_home(&[AbsolutePathBuf::try_from(repo_root).unwrap()], None)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
let marketplaces = list_marketplaces_with_home(
|
||||
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
|
||||
/*home_dir*/ None,
|
||||
)
|
||||
.unwrap()
|
||||
.marketplaces;
|
||||
|
||||
assert_eq!(
|
||||
marketplaces[0].plugins[0].interface,
|
||||
|
||||
@@ -75,7 +75,9 @@ async fn make_config_with_project_root_markers(
|
||||
async fn no_doc_file_returns_none() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
|
||||
let res = get_user_instructions(&make_config(&tmp, 4096, None).await).await;
|
||||
let res =
|
||||
get_user_instructions(&make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await)
|
||||
.await;
|
||||
assert!(
|
||||
res.is_none(),
|
||||
"Expected None when AGENTS.md is absent and no system instructions provided"
|
||||
@@ -89,9 +91,10 @@ async fn doc_smaller_than_limit_is_returned() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
fs::write(tmp.path().join("AGENTS.md"), "hello world").unwrap();
|
||||
|
||||
let res = get_user_instructions(&make_config(&tmp, 4096, None).await)
|
||||
.await
|
||||
.expect("doc expected");
|
||||
let res =
|
||||
get_user_instructions(&make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await)
|
||||
.await
|
||||
.expect("doc expected");
|
||||
|
||||
assert_eq!(
|
||||
res, "hello world",
|
||||
@@ -108,7 +111,7 @@ async fn doc_larger_than_limit_is_truncated() {
|
||||
let huge = "A".repeat(LIMIT * 2); // 2 KiB
|
||||
fs::write(tmp.path().join("AGENTS.md"), &huge).unwrap();
|
||||
|
||||
let res = get_user_instructions(&make_config(&tmp, LIMIT, None).await)
|
||||
let res = get_user_instructions(&make_config(&tmp, LIMIT, /*instructions*/ None).await)
|
||||
.await
|
||||
.expect("doc expected");
|
||||
|
||||
@@ -137,7 +140,7 @@ async fn finds_doc_in_repo_root() {
|
||||
std::fs::create_dir_all(&nested).unwrap();
|
||||
|
||||
// Build config pointing at the nested dir.
|
||||
let mut cfg = make_config(&repo, 4096, None).await;
|
||||
let mut cfg = make_config(&repo, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
cfg.cwd = nested.abs();
|
||||
|
||||
let res = get_user_instructions(&cfg).await.expect("doc expected");
|
||||
@@ -150,7 +153,8 @@ async fn zero_byte_limit_disables_docs() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
fs::write(tmp.path().join("AGENTS.md"), "something").unwrap();
|
||||
|
||||
let res = get_user_instructions(&make_config(&tmp, 0, None).await).await;
|
||||
let res =
|
||||
get_user_instructions(&make_config(&tmp, /*limit*/ 0, /*instructions*/ None).await).await;
|
||||
assert!(
|
||||
res.is_none(),
|
||||
"With limit 0 the function should return None"
|
||||
@@ -160,7 +164,7 @@ async fn zero_byte_limit_disables_docs() {
|
||||
#[tokio::test]
|
||||
async fn js_repl_instructions_are_appended_when_enabled() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let mut cfg = make_config(&tmp, 4096, None).await;
|
||||
let mut cfg = make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
cfg.features
|
||||
.enable(Feature::JsRepl)
|
||||
.expect("test config should allow js_repl");
|
||||
@@ -175,7 +179,7 @@ async fn js_repl_instructions_are_appended_when_enabled() {
|
||||
#[tokio::test]
|
||||
async fn js_repl_tools_only_instructions_are_feature_gated() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let mut cfg = make_config(&tmp, 4096, None).await;
|
||||
let mut cfg = make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
let mut features = cfg.features.get().clone();
|
||||
features
|
||||
.enable(Feature::JsRepl)
|
||||
@@ -194,7 +198,7 @@ async fn js_repl_tools_only_instructions_are_feature_gated() {
|
||||
#[tokio::test]
|
||||
async fn js_repl_image_detail_original_does_not_change_instructions() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let mut cfg = make_config(&tmp, 4096, None).await;
|
||||
let mut cfg = make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
let mut features = cfg.features.get().clone();
|
||||
features
|
||||
.enable(Feature::JsRepl)
|
||||
@@ -219,7 +223,7 @@ async fn merges_existing_instructions_with_project_doc() {
|
||||
|
||||
const INSTRUCTIONS: &str = "base instructions";
|
||||
|
||||
let res = get_user_instructions(&make_config(&tmp, 4096, Some(INSTRUCTIONS)).await)
|
||||
let res = get_user_instructions(&make_config(&tmp, /*limit*/ 4096, Some(INSTRUCTIONS)).await)
|
||||
.await
|
||||
.expect("should produce a combined instruction string");
|
||||
|
||||
@@ -236,7 +240,8 @@ async fn keeps_existing_instructions_when_doc_missing() {
|
||||
|
||||
const INSTRUCTIONS: &str = "some instructions";
|
||||
|
||||
let res = get_user_instructions(&make_config(&tmp, 4096, Some(INSTRUCTIONS)).await).await;
|
||||
let res =
|
||||
get_user_instructions(&make_config(&tmp, /*limit*/ 4096, Some(INSTRUCTIONS)).await).await;
|
||||
|
||||
assert_eq!(res, Some(INSTRUCTIONS.to_string()));
|
||||
}
|
||||
@@ -262,7 +267,7 @@ async fn concatenates_root_and_cwd_docs() {
|
||||
std::fs::create_dir_all(&nested).unwrap();
|
||||
fs::write(nested.join("AGENTS.md"), "crate doc").unwrap();
|
||||
|
||||
let mut cfg = make_config(&repo, 4096, None).await;
|
||||
let mut cfg = make_config(&repo, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
cfg.cwd = nested.abs();
|
||||
|
||||
let res = get_user_instructions(&cfg).await.expect("doc expected");
|
||||
@@ -279,7 +284,13 @@ async fn project_root_markers_are_honored_for_agents_discovery() {
|
||||
fs::create_dir_all(nested.join(".git")).unwrap();
|
||||
fs::write(nested.join("AGENTS.md"), "child doc").unwrap();
|
||||
|
||||
let mut cfg = make_config_with_project_root_markers(&root, 4096, None, &[".codex-root"]).await;
|
||||
let mut cfg = make_config_with_project_root_markers(
|
||||
&root,
|
||||
/*limit*/ 4096,
|
||||
/*instructions*/ None,
|
||||
&[".codex-root"],
|
||||
)
|
||||
.await;
|
||||
cfg.cwd = nested.abs();
|
||||
|
||||
let discovery = discover_project_doc_paths(&cfg).expect("discover paths");
|
||||
@@ -302,7 +313,7 @@ async fn agents_local_md_preferred() {
|
||||
fs::write(tmp.path().join(DEFAULT_PROJECT_DOC_FILENAME), "versioned").unwrap();
|
||||
fs::write(tmp.path().join(LOCAL_PROJECT_DOC_FILENAME), "local").unwrap();
|
||||
|
||||
let cfg = make_config(&tmp, 4096, None).await;
|
||||
let cfg = make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
|
||||
let res = get_user_instructions(&cfg)
|
||||
.await
|
||||
@@ -324,7 +335,13 @@ async fn uses_configured_fallback_when_agents_missing() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
fs::write(tmp.path().join("EXAMPLE.md"), "example instructions").unwrap();
|
||||
|
||||
let cfg = make_config_with_fallback(&tmp, 4096, None, &["EXAMPLE.md"]).await;
|
||||
let cfg = make_config_with_fallback(
|
||||
&tmp,
|
||||
/*limit*/ 4096,
|
||||
/*instructions*/ None,
|
||||
&["EXAMPLE.md"],
|
||||
)
|
||||
.await;
|
||||
|
||||
let res = get_user_instructions(&cfg)
|
||||
.await
|
||||
@@ -340,7 +357,13 @@ async fn agents_md_preferred_over_fallbacks() {
|
||||
fs::write(tmp.path().join("AGENTS.md"), "primary").unwrap();
|
||||
fs::write(tmp.path().join("EXAMPLE.md"), "secondary").unwrap();
|
||||
|
||||
let cfg = make_config_with_fallback(&tmp, 4096, None, &["EXAMPLE.md", ".example.md"]).await;
|
||||
let cfg = make_config_with_fallback(
|
||||
&tmp,
|
||||
/*limit*/ 4096,
|
||||
/*instructions*/ None,
|
||||
&["EXAMPLE.md", ".example.md"],
|
||||
)
|
||||
.await;
|
||||
|
||||
let res = get_user_instructions(&cfg)
|
||||
.await
|
||||
@@ -364,7 +387,7 @@ async fn skills_are_not_appended_to_project_doc() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
fs::write(tmp.path().join("AGENTS.md"), "base doc").unwrap();
|
||||
|
||||
let cfg = make_config(&tmp, 4096, None).await;
|
||||
let cfg = make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
create_skill(
|
||||
cfg.codex_home.clone(),
|
||||
"pdf-processing",
|
||||
@@ -380,7 +403,7 @@ async fn skills_are_not_appended_to_project_doc() {
|
||||
#[tokio::test]
|
||||
async fn apps_feature_does_not_emit_user_instructions_by_itself() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
let mut cfg = make_config(&tmp, 4096, None).await;
|
||||
let mut cfg = make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
cfg.features
|
||||
.enable(Feature::Apps)
|
||||
.expect("test config should allow apps");
|
||||
@@ -394,7 +417,7 @@ async fn apps_feature_does_not_append_to_project_doc_user_instructions() {
|
||||
let tmp = tempfile::tempdir().expect("tempdir");
|
||||
fs::write(tmp.path().join("AGENTS.md"), "base doc").unwrap();
|
||||
|
||||
let mut cfg = make_config(&tmp, 4096, None).await;
|
||||
let mut cfg = make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
cfg.features
|
||||
.enable(Feature::Apps)
|
||||
.expect("test config should allow apps");
|
||||
|
||||
@@ -47,7 +47,7 @@ fn thread_metadata(cwd: &str, title: &str, first_user_message: &str) -> ThreadMe
|
||||
fn workspace_section_requires_meaningful_structure() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
assert_eq!(
|
||||
build_workspace_section_with_user_root(cwd.path(), None),
|
||||
build_workspace_section_with_user_root(cwd.path(), /*user_root*/ None),
|
||||
None
|
||||
);
|
||||
}
|
||||
@@ -58,8 +58,8 @@ fn workspace_section_includes_tree_when_entries_exist() {
|
||||
fs::create_dir(cwd.path().join("docs")).expect("create docs dir");
|
||||
fs::write(cwd.path().join("README.md"), "hello").expect("write readme");
|
||||
|
||||
let section =
|
||||
build_workspace_section_with_user_root(cwd.path(), None).expect("workspace section");
|
||||
let section = build_workspace_section_with_user_root(cwd.path(), /*user_root*/ None)
|
||||
.expect("workspace section");
|
||||
assert!(section.contains("Working directory tree:"));
|
||||
assert!(section.contains("- docs/"));
|
||||
assert!(section.contains("- README.md"));
|
||||
|
||||
@@ -32,7 +32,7 @@ fn default_linux_sandbox_uses_platform_sandbox_tag() {
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
WindowsSandboxLevel::Disabled,
|
||||
);
|
||||
let expected = get_platform_sandbox(false)
|
||||
let expected = get_platform_sandbox(/*windows_sandbox_enabled*/ false)
|
||||
.map(SandboxType::as_metric_tag)
|
||||
.unwrap_or("none");
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
@@ -269,9 +269,15 @@ async fn snapshot_shell_does_not_inherit_stdin() -> Result<()> {
|
||||
"HOME=\"{home_display}\"; export HOME; {}",
|
||||
bash_snapshot_script()
|
||||
);
|
||||
let output = run_script_with_timeout(&shell, &script, Duration::from_secs(2), true, home)
|
||||
.await
|
||||
.context("run snapshot command")?;
|
||||
let output = run_script_with_timeout(
|
||||
&shell,
|
||||
&script,
|
||||
Duration::from_secs(2),
|
||||
/*use_login_shell*/ true,
|
||||
home,
|
||||
)
|
||||
.await
|
||||
.context("run snapshot command")?;
|
||||
let read_status = fs::read_to_string(&read_status_path)
|
||||
.await
|
||||
.context("read stdin probe status")?;
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::process::Command;
|
||||
#[test]
|
||||
#[cfg(target_os = "macos")]
|
||||
fn detects_zsh() {
|
||||
let zsh_shell = get_shell(ShellType::Zsh, None).unwrap();
|
||||
let zsh_shell = get_shell(ShellType::Zsh, /*path*/ None).unwrap();
|
||||
|
||||
let shell_path = zsh_shell.shell_path;
|
||||
|
||||
@@ -24,7 +24,7 @@ fn fish_fallback_to_zsh() {
|
||||
|
||||
#[test]
|
||||
fn detects_bash() {
|
||||
let bash_shell = get_shell(ShellType::Bash, None).unwrap();
|
||||
let bash_shell = get_shell(ShellType::Bash, /*path*/ None).unwrap();
|
||||
let shell_path = bash_shell.shell_path;
|
||||
|
||||
assert!(
|
||||
@@ -35,7 +35,7 @@ fn detects_bash() {
|
||||
|
||||
#[test]
|
||||
fn detects_sh() {
|
||||
let sh_shell = get_shell(ShellType::Sh, None).unwrap();
|
||||
let sh_shell = get_shell(ShellType::Sh, /*path*/ None).unwrap();
|
||||
let shell_path = sh_shell.shell_path;
|
||||
assert!(
|
||||
shell_path.file_name().and_then(|name| name.to_str()) == Some("sh"),
|
||||
@@ -48,23 +48,47 @@ fn can_run_on_shell_test() {
|
||||
let cmd = "echo \"Works\"";
|
||||
if cfg!(windows) {
|
||||
assert!(shell_works(
|
||||
get_shell(ShellType::PowerShell, None),
|
||||
get_shell(ShellType::PowerShell, /*path*/ None),
|
||||
"Out-String 'Works'",
|
||||
true,
|
||||
/*required*/ true,
|
||||
));
|
||||
assert!(shell_works(
|
||||
get_shell(ShellType::Cmd, /*path*/ None),
|
||||
cmd,
|
||||
/*required*/ true,
|
||||
));
|
||||
assert!(shell_works(
|
||||
Some(ultimate_fallback_shell()),
|
||||
cmd,
|
||||
/*required*/ true
|
||||
));
|
||||
assert!(shell_works(get_shell(ShellType::Cmd, None), cmd, true,));
|
||||
assert!(shell_works(Some(ultimate_fallback_shell()), cmd, true));
|
||||
} else {
|
||||
assert!(shell_works(Some(ultimate_fallback_shell()), cmd, true));
|
||||
assert!(shell_works(get_shell(ShellType::Zsh, None), cmd, false));
|
||||
assert!(shell_works(get_shell(ShellType::Bash, None), cmd, true));
|
||||
assert!(shell_works(get_shell(ShellType::Sh, None), cmd, true));
|
||||
assert!(shell_works(
|
||||
Some(ultimate_fallback_shell()),
|
||||
cmd,
|
||||
/*required*/ true
|
||||
));
|
||||
assert!(shell_works(
|
||||
get_shell(ShellType::Zsh, /*path*/ None),
|
||||
cmd,
|
||||
/*required*/ false
|
||||
));
|
||||
assert!(shell_works(
|
||||
get_shell(ShellType::Bash, /*path*/ None),
|
||||
cmd,
|
||||
/*required*/ true
|
||||
));
|
||||
assert!(shell_works(
|
||||
get_shell(ShellType::Sh, /*path*/ None),
|
||||
cmd,
|
||||
/*required*/ true
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn shell_works(shell: Option<Shell>, command: &str, required: bool) -> bool {
|
||||
if let Some(shell) = shell {
|
||||
let args = shell.derive_exec_args(command, false);
|
||||
let args = shell.derive_exec_args(command, /*use_login_shell*/ false);
|
||||
let output = Command::new(args[0].clone())
|
||||
.args(&args[1..])
|
||||
.output()
|
||||
@@ -85,11 +109,11 @@ fn derive_exec_args() {
|
||||
shell_snapshot: empty_shell_snapshot_receiver(),
|
||||
};
|
||||
assert_eq!(
|
||||
test_bash_shell.derive_exec_args("echo hello", false),
|
||||
test_bash_shell.derive_exec_args("echo hello", /*use_login_shell*/ false),
|
||||
vec!["/bin/bash", "-c", "echo hello"]
|
||||
);
|
||||
assert_eq!(
|
||||
test_bash_shell.derive_exec_args("echo hello", true),
|
||||
test_bash_shell.derive_exec_args("echo hello", /*use_login_shell*/ true),
|
||||
vec!["/bin/bash", "-lc", "echo hello"]
|
||||
);
|
||||
|
||||
@@ -99,11 +123,11 @@ fn derive_exec_args() {
|
||||
shell_snapshot: empty_shell_snapshot_receiver(),
|
||||
};
|
||||
assert_eq!(
|
||||
test_zsh_shell.derive_exec_args("echo hello", false),
|
||||
test_zsh_shell.derive_exec_args("echo hello", /*use_login_shell*/ false),
|
||||
vec!["/bin/zsh", "-c", "echo hello"]
|
||||
);
|
||||
assert_eq!(
|
||||
test_zsh_shell.derive_exec_args("echo hello", true),
|
||||
test_zsh_shell.derive_exec_args("echo hello", /*use_login_shell*/ true),
|
||||
vec!["/bin/zsh", "-lc", "echo hello"]
|
||||
);
|
||||
|
||||
@@ -113,11 +137,11 @@ fn derive_exec_args() {
|
||||
shell_snapshot: empty_shell_snapshot_receiver(),
|
||||
};
|
||||
assert_eq!(
|
||||
test_powershell_shell.derive_exec_args("echo hello", false),
|
||||
test_powershell_shell.derive_exec_args("echo hello", /*use_login_shell*/ false),
|
||||
vec!["pwsh.exe", "-NoProfile", "-Command", "echo hello"]
|
||||
);
|
||||
assert_eq!(
|
||||
test_powershell_shell.derive_exec_args("echo hello", true),
|
||||
test_powershell_shell.derive_exec_args("echo hello", /*use_login_shell*/ true),
|
||||
vec!["pwsh.exe", "-Command", "echo hello"]
|
||||
);
|
||||
}
|
||||
@@ -161,7 +185,7 @@ fn finds_powershell() {
|
||||
return;
|
||||
}
|
||||
|
||||
let powershell_shell = get_shell(ShellType::PowerShell, None).unwrap();
|
||||
let powershell_shell = get_shell(ShellType::PowerShell, /*path*/ None).unwrap();
|
||||
let shell_path = powershell_shell.shell_path;
|
||||
|
||||
assert!(shell_path.ends_with("pwsh.exe") || shell_path.ends_with("powershell.exe"));
|
||||
|
||||
@@ -102,7 +102,7 @@ mod tests {
|
||||
let mut rx = skills_watcher.subscribe();
|
||||
let _registration = skills_watcher
|
||||
.subscriber
|
||||
.register_path(PathBuf::from("/tmp/skill"), true);
|
||||
.register_path(PathBuf::from("/tmp/skill"), /*recursive*/ true);
|
||||
|
||||
file_watcher
|
||||
.send_paths_for_test(vec![PathBuf::from("/tmp/skill/SKILL.md")])
|
||||
|
||||
@@ -28,9 +28,10 @@ async fn handle_non_tool_response_item_strips_citations_from_assistant_message()
|
||||
"hello<oai-mem-citation><citation_entries>\nMEMORY.md:1-2|note=[x]\n</citation_entries>\n<rollout_ids>\n019cc2ea-1dff-7902-8d40-c8f6e5d83cc4\n</rollout_ids></oai-mem-citation> world",
|
||||
);
|
||||
|
||||
let turn_item = handle_non_tool_response_item(&session, &turn_context, &item, false)
|
||||
.await
|
||||
.expect("assistant message should parse");
|
||||
let turn_item =
|
||||
handle_non_tool_response_item(&session, &turn_context, &item, /*plan_mode*/ false)
|
||||
.await
|
||||
.expect("assistant message should parse");
|
||||
|
||||
let TurnItem::AgentMessage(agent_message) = turn_item else {
|
||||
panic!("expected agent message");
|
||||
@@ -60,7 +61,7 @@ fn last_assistant_message_from_item_strips_citations_and_plan_blocks() {
|
||||
"before<oai-mem-citation>doc1</oai-mem-citation>\n<proposed_plan>\n- x\n</proposed_plan>\nafter",
|
||||
);
|
||||
|
||||
let message = last_assistant_message_from_item(&item, true)
|
||||
let message = last_assistant_message_from_item(&item, /*plan_mode*/ true)
|
||||
.expect("assistant text should remain after stripping");
|
||||
|
||||
assert_eq!(message, "before\nafter");
|
||||
@@ -70,14 +71,20 @@ fn last_assistant_message_from_item_strips_citations_and_plan_blocks() {
|
||||
fn last_assistant_message_from_item_returns_none_for_citation_only_message() {
|
||||
let item = assistant_output_text("<oai-mem-citation>doc1</oai-mem-citation>");
|
||||
|
||||
assert_eq!(last_assistant_message_from_item(&item, false), None);
|
||||
assert_eq!(
|
||||
last_assistant_message_from_item(&item, /*plan_mode*/ false),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last_assistant_message_from_item_returns_none_for_plan_only_hidden_message() {
|
||||
let item = assistant_output_text("<proposed_plan>\n- x\n</proposed_plan>");
|
||||
|
||||
assert_eq!(last_assistant_message_from_item(&item, true), None);
|
||||
assert_eq!(
|
||||
last_assistant_message_from_item(&item, /*plan_mode*/ true),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -27,5 +27,8 @@ fn large_untracked_warning_disabled_when_threshold_disabled() {
|
||||
ignored_untracked_files: Vec::new(),
|
||||
};
|
||||
|
||||
assert_eq!(format_large_untracked_warning(None, &report), None);
|
||||
assert_eq!(
|
||||
format_large_untracked_warning(/*ignore_large_untracked_dirs*/ None, &report),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ fn test_session_telemetry() -> SessionTelemetry {
|
||||
ThreadId::new(),
|
||||
"gpt-5.1",
|
||||
"gpt-5.1",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*account_id*/ None,
|
||||
/*account_email*/ None,
|
||||
/*auth_mode*/ None,
|
||||
"test_originator".to_string(),
|
||||
false,
|
||||
/*log_user_prompts*/ false,
|
||||
"tty".to_string(),
|
||||
SessionSource::Cli,
|
||||
)
|
||||
@@ -75,7 +75,11 @@ fn metric_point(resource_metrics: &ResourceMetrics) -> (BTreeMap<String, String>
|
||||
fn emit_turn_network_proxy_metric_records_active_turn() {
|
||||
let session_telemetry = test_session_telemetry();
|
||||
|
||||
emit_turn_network_proxy_metric(&session_telemetry, true, ("tmp_mem_enabled", "true"));
|
||||
emit_turn_network_proxy_metric(
|
||||
&session_telemetry,
|
||||
/*network_proxy_active*/ true,
|
||||
("tmp_mem_enabled", "true"),
|
||||
);
|
||||
|
||||
let snapshot = session_telemetry
|
||||
.snapshot_metrics()
|
||||
@@ -96,7 +100,11 @@ fn emit_turn_network_proxy_metric_records_active_turn() {
|
||||
fn emit_turn_network_proxy_metric_records_inactive_turn() {
|
||||
let session_telemetry = test_session_telemetry();
|
||||
|
||||
emit_turn_network_proxy_metric(&session_telemetry, false, ("tmp_mem_enabled", "false"));
|
||||
emit_turn_network_proxy_metric(
|
||||
&session_telemetry,
|
||||
/*network_proxy_active*/ false,
|
||||
("tmp_mem_enabled", "false"),
|
||||
);
|
||||
|
||||
let snapshot = session_telemetry
|
||||
.snapshot_metrics()
|
||||
|
||||
@@ -75,7 +75,7 @@ fn truncates_before_requested_user_message() {
|
||||
.collect();
|
||||
let truncated = truncate_before_nth_user_message(
|
||||
InitialHistory::Forked(initial),
|
||||
1,
|
||||
/*n*/ 1,
|
||||
&SnapshotTurnState {
|
||||
ends_mid_turn: false,
|
||||
active_turn_id: None,
|
||||
@@ -100,7 +100,7 @@ fn truncates_before_requested_user_message() {
|
||||
.collect();
|
||||
let truncated2 = truncate_before_nth_user_message(
|
||||
InitialHistory::Forked(initial2.clone()),
|
||||
2,
|
||||
/*n*/ 2,
|
||||
&SnapshotTurnState {
|
||||
ends_mid_turn: false,
|
||||
active_turn_id: None,
|
||||
@@ -210,7 +210,7 @@ async fn ignores_session_prefix_messages_when_truncating() {
|
||||
|
||||
let truncated = truncate_before_nth_user_message(
|
||||
InitialHistory::Forked(rollout_items),
|
||||
1,
|
||||
/*n*/ 1,
|
||||
&SnapshotTurnState {
|
||||
ends_mid_turn: false,
|
||||
active_turn_id: None,
|
||||
|
||||
@@ -61,7 +61,8 @@ fn truncates_rollout_from_start_before_nth_user_only() {
|
||||
.map(RolloutItem::ResponseItem)
|
||||
.collect();
|
||||
|
||||
let truncated = truncate_rollout_before_nth_user_message_from_start(&rollout, 1);
|
||||
let truncated =
|
||||
truncate_rollout_before_nth_user_message_from_start(&rollout, /*n_from_start*/ 1);
|
||||
let expected = vec![
|
||||
RolloutItem::ResponseItem(items[0].clone()),
|
||||
RolloutItem::ResponseItem(items[1].clone()),
|
||||
@@ -72,7 +73,8 @@ fn truncates_rollout_from_start_before_nth_user_only() {
|
||||
serde_json::to_value(&expected).unwrap()
|
||||
);
|
||||
|
||||
let truncated2 = truncate_rollout_before_nth_user_message_from_start(&rollout, 2);
|
||||
let truncated2 =
|
||||
truncate_rollout_before_nth_user_message_from_start(&rollout, /*n_from_start*/ 2);
|
||||
assert_eq!(
|
||||
serde_json::to_value(&truncated2).unwrap(),
|
||||
serde_json::to_value(&rollout).unwrap()
|
||||
@@ -113,7 +115,10 @@ fn truncates_rollout_from_start_applies_thread_rollback_markers() {
|
||||
|
||||
// Effective user history after applying rollback(1) is: u1, u3, u4.
|
||||
// So n_from_start=2 should cut before u4 (not u3).
|
||||
let truncated = truncate_rollout_before_nth_user_message_from_start(&rollout_items, 2);
|
||||
let truncated = truncate_rollout_before_nth_user_message_from_start(
|
||||
&rollout_items,
|
||||
/*n_from_start*/ 2,
|
||||
);
|
||||
let expected = rollout_items[..7].to_vec();
|
||||
assert_eq!(
|
||||
serde_json::to_value(&truncated).unwrap(),
|
||||
@@ -136,7 +141,10 @@ async fn ignores_session_prefix_messages_when_truncating_rollout_from_start() {
|
||||
.map(RolloutItem::ResponseItem)
|
||||
.collect();
|
||||
|
||||
let truncated = truncate_rollout_before_nth_user_message_from_start(&rollout_items, 1);
|
||||
let truncated = truncate_rollout_before_nth_user_message_from_start(
|
||||
&rollout_items,
|
||||
/*n_from_start*/ 1,
|
||||
);
|
||||
let expected: Vec<RolloutItem> = vec![
|
||||
RolloutItem::ResponseItem(items[0].clone()),
|
||||
RolloutItem::ResponseItem(items[1].clone()),
|
||||
|
||||
@@ -58,7 +58,7 @@ async fn emit_js_repl_exec_end_sends_event() {
|
||||
turn.as_ref(),
|
||||
"call-1",
|
||||
"hello",
|
||||
None,
|
||||
/*error*/ None,
|
||||
Duration::from_millis(12),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -34,9 +34,11 @@ async fn lists_directory_entries() {
|
||||
symlink(dir_path.join("entry.txt"), &link_path).expect("create symlink");
|
||||
}
|
||||
|
||||
let entries = list_dir_slice(dir_path, 1, 20, 3)
|
||||
.await
|
||||
.expect("list directory");
|
||||
let entries = list_dir_slice(
|
||||
dir_path, /*offset*/ 1, /*limit*/ 20, /*depth*/ 3,
|
||||
)
|
||||
.await
|
||||
.expect("list directory");
|
||||
|
||||
#[cfg(unix)]
|
||||
let expected = vec![
|
||||
@@ -68,9 +70,11 @@ async fn errors_when_offset_exceeds_entries() {
|
||||
.await
|
||||
.expect("create sub dir");
|
||||
|
||||
let err = list_dir_slice(dir_path, 10, 1, 2)
|
||||
.await
|
||||
.expect_err("offset exceeds entries");
|
||||
let err = list_dir_slice(
|
||||
dir_path, /*offset*/ 10, /*limit*/ 1, /*depth*/ 2,
|
||||
)
|
||||
.await
|
||||
.expect_err("offset exceeds entries");
|
||||
assert_eq!(
|
||||
err,
|
||||
FunctionCallError::RespondToModel("offset exceeds directory entry count".to_string())
|
||||
@@ -95,17 +99,21 @@ async fn respects_depth_parameter() {
|
||||
.await
|
||||
.expect("write deeper");
|
||||
|
||||
let entries_depth_one = list_dir_slice(dir_path, 1, 10, 1)
|
||||
.await
|
||||
.expect("list depth 1");
|
||||
let entries_depth_one = list_dir_slice(
|
||||
dir_path, /*offset*/ 1, /*limit*/ 10, /*depth*/ 1,
|
||||
)
|
||||
.await
|
||||
.expect("list depth 1");
|
||||
assert_eq!(
|
||||
entries_depth_one,
|
||||
vec!["nested/".to_string(), "root.txt".to_string(),]
|
||||
);
|
||||
|
||||
let entries_depth_two = list_dir_slice(dir_path, 1, 20, 2)
|
||||
.await
|
||||
.expect("list depth 2");
|
||||
let entries_depth_two = list_dir_slice(
|
||||
dir_path, /*offset*/ 1, /*limit*/ 20, /*depth*/ 2,
|
||||
)
|
||||
.await
|
||||
.expect("list depth 2");
|
||||
assert_eq!(
|
||||
entries_depth_two,
|
||||
vec![
|
||||
@@ -116,9 +124,11 @@ async fn respects_depth_parameter() {
|
||||
]
|
||||
);
|
||||
|
||||
let entries_depth_three = list_dir_slice(dir_path, 1, 30, 3)
|
||||
.await
|
||||
.expect("list depth 3");
|
||||
let entries_depth_three = list_dir_slice(
|
||||
dir_path, /*offset*/ 1, /*limit*/ 30, /*depth*/ 3,
|
||||
)
|
||||
.await
|
||||
.expect("list depth 3");
|
||||
assert_eq!(
|
||||
entries_depth_three,
|
||||
vec![
|
||||
@@ -148,9 +158,11 @@ async fn paginates_in_sorted_order() {
|
||||
.await
|
||||
.expect("write b child");
|
||||
|
||||
let first_page = list_dir_slice(dir_path, 1, 2, 2)
|
||||
.await
|
||||
.expect("list page one");
|
||||
let first_page = list_dir_slice(
|
||||
dir_path, /*offset*/ 1, /*limit*/ 2, /*depth*/ 2,
|
||||
)
|
||||
.await
|
||||
.expect("list page one");
|
||||
assert_eq!(
|
||||
first_page,
|
||||
vec![
|
||||
@@ -160,9 +172,11 @@ async fn paginates_in_sorted_order() {
|
||||
]
|
||||
);
|
||||
|
||||
let second_page = list_dir_slice(dir_path, 3, 2, 2)
|
||||
.await
|
||||
.expect("list page two");
|
||||
let second_page = list_dir_slice(
|
||||
dir_path, /*offset*/ 3, /*limit*/ 2, /*depth*/ 2,
|
||||
)
|
||||
.await
|
||||
.expect("list page two");
|
||||
assert_eq!(
|
||||
second_page,
|
||||
vec!["b/".to_string(), " b_child.txt".to_string()]
|
||||
@@ -183,7 +197,7 @@ async fn handles_large_limit_without_overflow() {
|
||||
.await
|
||||
.expect("write gamma");
|
||||
|
||||
let entries = list_dir_slice(dir_path, 2, usize::MAX, 1)
|
||||
let entries = list_dir_slice(dir_path, /*offset*/ 2, usize::MAX, /*depth*/ 1)
|
||||
.await
|
||||
.expect("list without overflow");
|
||||
assert_eq!(
|
||||
@@ -204,9 +218,11 @@ async fn indicates_truncated_results() {
|
||||
.expect("write file");
|
||||
}
|
||||
|
||||
let entries = list_dir_slice(dir_path, 1, 25, 1)
|
||||
.await
|
||||
.expect("list directory");
|
||||
let entries = list_dir_slice(
|
||||
dir_path, /*offset*/ 1, /*limit*/ 25, /*depth*/ 1,
|
||||
)
|
||||
.await
|
||||
.expect("list directory");
|
||||
assert_eq!(entries.len(), 26);
|
||||
assert_eq!(
|
||||
entries.last(),
|
||||
@@ -226,7 +242,10 @@ async fn truncation_respects_sorted_order() -> anyhow::Result<()> {
|
||||
tokio::fs::write(nested.join("child.txt"), b"child").await?;
|
||||
tokio::fs::write(deeper.join("grandchild.txt"), b"deep").await?;
|
||||
|
||||
let entries_depth_three = list_dir_slice(dir_path, 1, 3, 3).await?;
|
||||
let entries_depth_three = list_dir_slice(
|
||||
dir_path, /*offset*/ 1, /*limit*/ 3, /*depth*/ 3,
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
entries_depth_three,
|
||||
vec![
|
||||
|
||||
@@ -261,7 +261,7 @@ mod tests {
|
||||
let cwd = tempdir().expect("tempdir");
|
||||
|
||||
let normalized = normalize_and_validate_additional_permissions(
|
||||
false,
|
||||
/*additional_permissions_allowed*/ false,
|
||||
AskForApproval::Granular(GranularApprovalConfig {
|
||||
sandbox_approval: true,
|
||||
rules: true,
|
||||
@@ -271,7 +271,7 @@ mod tests {
|
||||
}),
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
Some(network_permissions()),
|
||||
true,
|
||||
/*permissions_preapproved*/ true,
|
||||
cwd.path(),
|
||||
)
|
||||
.expect("preapproved permissions should be allowed");
|
||||
@@ -284,11 +284,11 @@ mod tests {
|
||||
let cwd = tempdir().expect("tempdir");
|
||||
|
||||
let err = normalize_and_validate_additional_permissions(
|
||||
false,
|
||||
/*additional_permissions_allowed*/ false,
|
||||
AskForApproval::OnRequest,
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
Some(network_permissions()),
|
||||
false,
|
||||
/*permissions_preapproved*/ false,
|
||||
cwd.path(),
|
||||
)
|
||||
.expect_err("fresh inline permission requests should remain disabled");
|
||||
@@ -305,7 +305,7 @@ mod tests {
|
||||
let granted_permissions = file_system_permissions(cwd.path());
|
||||
let implicit_permissions = implicit_granted_permissions(
|
||||
SandboxPermissions::UseDefault,
|
||||
None,
|
||||
/*additional_permissions*/ None,
|
||||
&EffectiveAdditionalPermissions {
|
||||
sandbox_permissions: SandboxPermissions::WithAdditionalPermissions,
|
||||
additional_permissions: Some(granted_permissions.clone()),
|
||||
|
||||
@@ -82,7 +82,7 @@ fn parse_agent_id(id: &str) -> ThreadId {
|
||||
fn thread_manager() -> ThreadManager {
|
||||
ThreadManager::with_models_provider_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
built_in_model_providers(/* openai_base_url */ None)["openai"].clone(),
|
||||
built_in_model_providers(/* openai_base_url */ /*openai_base_url*/ None)["openai"].clone(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -247,7 +247,8 @@ async fn spawn_agent_uses_explorer_role_and_preserves_approval_policy() {
|
||||
let manager = thread_manager();
|
||||
session.services.agent_control = manager.agent_control();
|
||||
let mut config = (*turn.config).clone();
|
||||
let provider = built_in_model_providers(/* openai_base_url */ None)["ollama"].clone();
|
||||
let provider =
|
||||
built_in_model_providers(/* openai_base_url */ /*openai_base_url*/ None)["ollama"].clone();
|
||||
config.model_provider_id = "ollama".to_string();
|
||||
config.model_provider = provider.clone();
|
||||
config
|
||||
@@ -962,7 +963,7 @@ async fn multi_agent_v2_send_message_interrupts_busy_child_without_triggering_tu
|
||||
AgentPath::try_from("/root/worker").expect("agent path"),
|
||||
Vec::new(),
|
||||
"continue".to_string(),
|
||||
false,
|
||||
/*trigger_turn*/ false,
|
||||
),
|
||||
);
|
||||
let saw_user_message = history_items.iter().any(|item| {
|
||||
@@ -1094,7 +1095,7 @@ async fn multi_agent_v2_assign_task_interrupts_busy_child_without_losing_message
|
||||
AgentPath::try_from("/root/worker").expect("agent path"),
|
||||
Vec::new(),
|
||||
"continue".to_string(),
|
||||
true,
|
||||
/*trigger_turn*/ true,
|
||||
),
|
||||
);
|
||||
let saw_user_message = history_items.iter().any(|item| {
|
||||
@@ -1636,8 +1637,8 @@ async fn resume_agent_restores_closed_agent_and_accepts_send_input() {
|
||||
phase: None,
|
||||
})]),
|
||||
AuthManager::from_auth_for_testing(CodexAuth::from_api_key("dummy")),
|
||||
false,
|
||||
None,
|
||||
/*persist_extended_history*/ false,
|
||||
/*parent_trace*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("start thread");
|
||||
@@ -2557,7 +2558,7 @@ async fn build_agent_resume_config_clears_base_instructions() {
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("approval policy set");
|
||||
|
||||
let config = build_agent_resume_config(&turn, 0).expect("resume config");
|
||||
let config = build_agent_resume_config(&turn, /*child_depth*/ 0).expect("resume config");
|
||||
|
||||
let mut expected = (*turn.config).clone();
|
||||
expected.base_instructions = None;
|
||||
|
||||
@@ -12,23 +12,38 @@ fn request_user_input_mode_availability_defaults_to_plan_only() {
|
||||
#[test]
|
||||
fn request_user_input_unavailable_messages_respect_default_mode_feature_flag() {
|
||||
assert_eq!(
|
||||
request_user_input_unavailable_message(ModeKind::Plan, false),
|
||||
request_user_input_unavailable_message(
|
||||
ModeKind::Plan,
|
||||
/*default_mode_request_user_input*/ false
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
request_user_input_unavailable_message(ModeKind::Default, false),
|
||||
request_user_input_unavailable_message(
|
||||
ModeKind::Default,
|
||||
/*default_mode_request_user_input*/ false
|
||||
),
|
||||
Some("request_user_input is unavailable in Default mode".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
request_user_input_unavailable_message(ModeKind::Default, true),
|
||||
request_user_input_unavailable_message(
|
||||
ModeKind::Default,
|
||||
/*default_mode_request_user_input*/ true
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
request_user_input_unavailable_message(ModeKind::Execute, false),
|
||||
request_user_input_unavailable_message(
|
||||
ModeKind::Execute,
|
||||
/*default_mode_request_user_input*/ false
|
||||
),
|
||||
Some("request_user_input is unavailable in Execute mode".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
request_user_input_unavailable_message(ModeKind::PairProgramming, false),
|
||||
request_user_input_unavailable_message(
|
||||
ModeKind::PairProgramming,
|
||||
/*default_mode_request_user_input*/ false
|
||||
),
|
||||
Some("request_user_input is unavailable in Pair Programming mode".to_string())
|
||||
);
|
||||
}
|
||||
@@ -36,11 +51,11 @@ fn request_user_input_unavailable_messages_respect_default_mode_feature_flag() {
|
||||
#[test]
|
||||
fn request_user_input_tool_description_mentions_available_modes() {
|
||||
assert_eq!(
|
||||
request_user_input_tool_description(false),
|
||||
request_user_input_tool_description(/*default_mode_request_user_input*/ false),
|
||||
"Request user input for one to three short questions and wait for the response. This tool is only available in Plan mode.".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
request_user_input_tool_description(true),
|
||||
request_user_input_tool_description(/*default_mode_request_user_input*/ true),
|
||||
"Request user input for one to three short questions and wait for the response. This tool is only available in Default or Plan mode.".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,12 +63,12 @@ fn commands_generated_by_shell_command_handler_can_be_matched_by_is_known_safe_c
|
||||
}
|
||||
|
||||
fn assert_safe(shell: &Shell, command: &str) {
|
||||
assert!(is_known_safe_command(
|
||||
&shell.derive_exec_args(command, /* use_login_shell */ true)
|
||||
));
|
||||
assert!(is_known_safe_command(
|
||||
&shell.derive_exec_args(command, /* use_login_shell */ false)
|
||||
));
|
||||
assert!(is_known_safe_command(&shell.derive_exec_args(
|
||||
command, /* use_login_shell */ /*use_login_shell*/ true
|
||||
)));
|
||||
assert!(is_known_safe_command(&shell.derive_exec_args(
|
||||
command, /* use_login_shell */ /*use_login_shell*/ false
|
||||
)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -82,7 +82,9 @@ async fn shell_command_handler_to_exec_params_uses_session_shell_and_turn_contex
|
||||
let sandbox_permissions = SandboxPermissions::RequireEscalated;
|
||||
let justification = Some("because tests".to_string());
|
||||
|
||||
let expected_command = session.user_shell().derive_exec_args(&command, true);
|
||||
let expected_command = session
|
||||
.user_shell()
|
||||
.derive_exec_args(&command, /*use_login_shell*/ true);
|
||||
let expected_cwd = turn_context.resolve_path(workdir.clone());
|
||||
let expected_env = create_env(
|
||||
&turn_context.shell_environment_policy,
|
||||
@@ -105,7 +107,7 @@ async fn shell_command_handler_to_exec_params_uses_session_shell_and_turn_contex
|
||||
&session,
|
||||
&turn_context,
|
||||
session.conversation_id,
|
||||
true,
|
||||
/*allow_login_shell*/ true,
|
||||
)
|
||||
.expect("login shells should be allowed");
|
||||
|
||||
@@ -132,17 +134,24 @@ fn shell_command_handler_respects_explicit_login_flag() {
|
||||
shell_snapshot,
|
||||
};
|
||||
|
||||
let login_command = ShellCommandHandler::base_command(&shell, "echo login shell", true);
|
||||
let login_command = ShellCommandHandler::base_command(
|
||||
&shell,
|
||||
"echo login shell",
|
||||
/*use_login_shell*/ true,
|
||||
);
|
||||
assert_eq!(
|
||||
login_command,
|
||||
shell.derive_exec_args("echo login shell", true)
|
||||
shell.derive_exec_args("echo login shell", /*use_login_shell*/ true)
|
||||
);
|
||||
|
||||
let non_login_command =
|
||||
ShellCommandHandler::base_command(&shell, "echo non login shell", false);
|
||||
let non_login_command = ShellCommandHandler::base_command(
|
||||
&shell,
|
||||
"echo non login shell",
|
||||
/*use_login_shell*/ false,
|
||||
);
|
||||
assert_eq!(
|
||||
non_login_command,
|
||||
shell.derive_exec_args("echo non login shell", false)
|
||||
shell.derive_exec_args("echo non login shell", /*use_login_shell*/ false)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -165,20 +174,23 @@ async fn shell_command_handler_defaults_to_non_login_when_disallowed() {
|
||||
&session,
|
||||
&turn_context,
|
||||
session.conversation_id,
|
||||
false,
|
||||
/*allow_login_shell*/ false,
|
||||
)
|
||||
.expect("non-login shells should still be allowed");
|
||||
|
||||
assert_eq!(
|
||||
exec_params.command,
|
||||
session.user_shell().derive_exec_args("echo hello", false)
|
||||
session
|
||||
.user_shell()
|
||||
.derive_exec_args("echo hello", /*use_login_shell*/ false)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shell_command_handler_rejects_login_when_disallowed() {
|
||||
let err = ShellCommandHandler::resolve_use_login_shell(Some(true), false)
|
||||
.expect_err("explicit login should be rejected");
|
||||
let err =
|
||||
ShellCommandHandler::resolve_use_login_shell(Some(true), /*allow_login_shell*/ false)
|
||||
.expect_err("explicit login should be rejected");
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
|
||||
@@ -31,7 +31,7 @@ fn test_get_command_uses_default_shell_when_unspecified() -> anyhow::Result<()>
|
||||
&args,
|
||||
Arc::new(default_user_shell()),
|
||||
&UnifiedExecShellMode::Direct,
|
||||
true,
|
||||
/*allow_login_shell*/ true,
|
||||
)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
|
||||
@@ -52,7 +52,7 @@ fn test_get_command_respects_explicit_bash_shell() -> anyhow::Result<()> {
|
||||
&args,
|
||||
Arc::new(default_user_shell()),
|
||||
&UnifiedExecShellMode::Direct,
|
||||
true,
|
||||
/*allow_login_shell*/ true,
|
||||
)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
|
||||
@@ -78,7 +78,7 @@ fn test_get_command_respects_explicit_powershell_shell() -> anyhow::Result<()> {
|
||||
&args,
|
||||
Arc::new(default_user_shell()),
|
||||
&UnifiedExecShellMode::Direct,
|
||||
true,
|
||||
/*allow_login_shell*/ true,
|
||||
)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
|
||||
@@ -98,7 +98,7 @@ fn test_get_command_respects_explicit_cmd_shell() -> anyhow::Result<()> {
|
||||
&args,
|
||||
Arc::new(default_user_shell()),
|
||||
&UnifiedExecShellMode::Direct,
|
||||
true,
|
||||
/*allow_login_shell*/ true,
|
||||
)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
|
||||
@@ -115,7 +115,7 @@ fn test_get_command_rejects_explicit_login_when_disallowed() -> anyhow::Result<(
|
||||
&args,
|
||||
Arc::new(default_user_shell()),
|
||||
&UnifiedExecShellMode::Direct,
|
||||
false,
|
||||
/*allow_login_shell*/ false,
|
||||
)
|
||||
.expect_err("explicit login should be rejected");
|
||||
|
||||
@@ -144,8 +144,13 @@ fn test_get_command_ignores_explicit_shell_in_zsh_fork_mode() -> anyhow::Result<
|
||||
})?,
|
||||
});
|
||||
|
||||
let command = get_command(&args, Arc::new(default_user_shell()), &shell_mode, true)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let command = get_command(
|
||||
&args,
|
||||
Arc::new(default_user_shell()),
|
||||
&shell_mode,
|
||||
/*allow_login_shell*/ true,
|
||||
)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
|
||||
@@ -47,13 +47,19 @@ fn node_version_parses_v_prefix_and_suffix() {
|
||||
#[test]
|
||||
fn truncate_utf8_prefix_by_bytes_preserves_character_boundaries() {
|
||||
let input = "aé🙂z";
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, 0), "");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, 1), "a");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, 2), "a");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, 3), "aé");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, 6), "aé");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, 7), "aé🙂");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, 8), "aé🙂z");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, /*max_bytes*/ 0), "");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, /*max_bytes*/ 1), "a");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, /*max_bytes*/ 2), "a");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, /*max_bytes*/ 3), "aé");
|
||||
assert_eq!(truncate_utf8_prefix_by_bytes(input, /*max_bytes*/ 6), "aé");
|
||||
assert_eq!(
|
||||
truncate_utf8_prefix_by_bytes(input, /*max_bytes*/ 7),
|
||||
"aé🙂"
|
||||
);
|
||||
assert_eq!(
|
||||
truncate_utf8_prefix_by_bytes(input, /*max_bytes*/ 8),
|
||||
"aé🙂z"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -203,7 +209,7 @@ async fn wait_for_exec_tool_calls_map_drains_inflight_calls_without_hanging() {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reset_waits_for_exec_lock_before_clearing_exec_tool_calls() {
|
||||
let manager = JsReplManager::new(None, Vec::new())
|
||||
let manager = JsReplManager::new(/*node_path*/ None, Vec::new())
|
||||
.await
|
||||
.expect("manager should initialize");
|
||||
let permit = manager
|
||||
@@ -300,8 +306,11 @@ async fn emitted_image_content_item_does_not_force_original_when_enabled() {
|
||||
.expect("test turn features should allow feature update");
|
||||
turn.model_info.supports_image_detail_original = true;
|
||||
|
||||
let content_item =
|
||||
emitted_image_content_item(&turn, "data:image/png;base64,AAA".to_string(), None);
|
||||
let content_item = emitted_image_content_item(
|
||||
&turn,
|
||||
"data:image/png;base64,AAA".to_string(),
|
||||
/*detail*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
content_item,
|
||||
@@ -427,7 +436,7 @@ fn summarize_tool_call_error_marks_error_payload() {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reset_clears_inflight_exec_tool_calls_without_waiting() {
|
||||
let manager = JsReplManager::new(None, Vec::new())
|
||||
let manager = JsReplManager::new(/*node_path*/ None, Vec::new())
|
||||
.await
|
||||
.expect("manager should initialize");
|
||||
let exec_id = Uuid::new_v4().to_string();
|
||||
@@ -460,7 +469,7 @@ async fn reset_clears_inflight_exec_tool_calls_without_waiting() {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reset_aborts_inflight_exec_tool_tasks() {
|
||||
let manager = JsReplManager::new(None, Vec::new())
|
||||
let manager = JsReplManager::new(/*node_path*/ None, Vec::new())
|
||||
.await
|
||||
.expect("manager should initialize");
|
||||
let exec_id = Uuid::new_v4().to_string();
|
||||
@@ -621,14 +630,14 @@ async fn interrupt_turn_exec_clears_matching_submitted_exec() -> anyhow::Result<
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let manager = JsReplManager::new(None, Vec::new())
|
||||
let manager = JsReplManager::new(/*node_path*/ None, Vec::new())
|
||||
.await
|
||||
.expect("manager should initialize");
|
||||
let (_session, turn) = make_session_and_context().await;
|
||||
let turn = Arc::new(turn);
|
||||
let dependency_env = HashMap::new();
|
||||
let mut state = manager
|
||||
.start_kernel(Arc::clone(&turn), &dependency_env, None)
|
||||
.start_kernel(Arc::clone(&turn), &dependency_env, /*thread_id*/ None)
|
||||
.await
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let child = Arc::clone(&state.child);
|
||||
@@ -667,14 +676,14 @@ async fn interrupt_turn_exec_resets_matching_pending_kernel_start() -> anyhow::R
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let manager = JsReplManager::new(None, Vec::new())
|
||||
let manager = JsReplManager::new(/*node_path*/ None, Vec::new())
|
||||
.await
|
||||
.expect("manager should initialize");
|
||||
let (_session, turn) = make_session_and_context().await;
|
||||
let turn = Arc::new(turn);
|
||||
let dependency_env = HashMap::new();
|
||||
let mut state = manager
|
||||
.start_kernel(Arc::clone(&turn), &dependency_env, None)
|
||||
.start_kernel(Arc::clone(&turn), &dependency_env, /*thread_id*/ None)
|
||||
.await
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
state.top_level_exec_state = TopLevelExecState::FreshKernel {
|
||||
@@ -711,14 +720,14 @@ async fn interrupt_turn_exec_does_not_reset_reused_kernel_before_submit() -> any
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let manager = JsReplManager::new(None, Vec::new())
|
||||
let manager = JsReplManager::new(/*node_path*/ None, Vec::new())
|
||||
.await
|
||||
.expect("manager should initialize");
|
||||
let (_session, turn) = make_session_and_context().await;
|
||||
let turn = Arc::new(turn);
|
||||
let dependency_env = HashMap::new();
|
||||
let mut state = manager
|
||||
.start_kernel(Arc::clone(&turn), &dependency_env, None)
|
||||
.start_kernel(Arc::clone(&turn), &dependency_env, /*thread_id*/ None)
|
||||
.await
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
state.top_level_exec_state = TopLevelExecState::ReusedKernelPending {
|
||||
|
||||
@@ -29,7 +29,7 @@ fn handler_looks_up_namespaced_aliases_explicitly() {
|
||||
(namespaced_name, Arc::clone(&namespaced_handler)),
|
||||
]));
|
||||
|
||||
let plain = registry.handler(tool_name, None);
|
||||
let plain = registry.handler(tool_name, /*namespace*/ None);
|
||||
let namespaced = registry.handler(tool_name, Some(namespace));
|
||||
let missing_namespaced = registry.handler(tool_name, Some("mcp__codex_apps__calendar"));
|
||||
|
||||
|
||||
@@ -126,8 +126,8 @@ fn build_sandbox_command_falls_back_to_current_exe_for_apply_patch() {
|
||||
timeout_ms: None,
|
||||
};
|
||||
|
||||
let command =
|
||||
ApplyPatchRuntime::build_sandbox_command(&request, None).expect("build sandbox command");
|
||||
let command = ApplyPatchRuntime::build_sandbox_command(&request, /*codex_self_exe*/ None)
|
||||
.expect("build sandbox command");
|
||||
|
||||
assert_eq!(
|
||||
command.program,
|
||||
|
||||
@@ -95,15 +95,24 @@ fn execve_prompt_rejection_keeps_unmatched_commands_on_sandbox_flag() {
|
||||
#[test]
|
||||
fn approval_sandbox_permissions_only_downgrades_preapproved_additional_permissions() {
|
||||
assert_eq!(
|
||||
super::approval_sandbox_permissions(SandboxPermissions::WithAdditionalPermissions, true),
|
||||
super::approval_sandbox_permissions(
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
/*additional_permissions_preapproved*/ true
|
||||
),
|
||||
SandboxPermissions::UseDefault,
|
||||
);
|
||||
assert_eq!(
|
||||
super::approval_sandbox_permissions(SandboxPermissions::WithAdditionalPermissions, false),
|
||||
super::approval_sandbox_permissions(
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
/*additional_permissions_preapproved*/ false
|
||||
),
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
);
|
||||
assert_eq!(
|
||||
super::approval_sandbox_permissions(SandboxPermissions::RequireEscalated, true),
|
||||
super::approval_sandbox_permissions(
|
||||
SandboxPermissions::RequireEscalated,
|
||||
/*additional_permissions_preapproved*/ true
|
||||
),
|
||||
SandboxPermissions::RequireEscalated,
|
||||
);
|
||||
}
|
||||
@@ -278,7 +287,7 @@ fn shell_request_escalation_execution_is_explicit() {
|
||||
&sandbox_policy,
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
None,
|
||||
/*additional_permissions*/ None,
|
||||
),
|
||||
EscalationExecution::TurnDefault,
|
||||
);
|
||||
@@ -288,7 +297,7 @@ fn shell_request_escalation_execution_is_explicit() {
|
||||
&sandbox_policy,
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
None,
|
||||
/*additional_permissions*/ None,
|
||||
),
|
||||
EscalationExecution::Unsandboxed,
|
||||
);
|
||||
@@ -466,7 +475,7 @@ fn intercepted_exec_policy_treats_preapproved_additional_permissions_as_default(
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
sandbox_permissions: super::approval_sandbox_permissions(
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
true,
|
||||
/*additional_permissions_preapproved*/ true,
|
||||
),
|
||||
enable_shell_wrapper_parsing: false,
|
||||
},
|
||||
|
||||
@@ -2389,7 +2389,13 @@ pub(crate) fn build_specs(
|
||||
app_tools: Option<HashMap<String, ToolInfo>>,
|
||||
dynamic_tools: &[DynamicToolSpec],
|
||||
) -> ToolRegistryBuilder {
|
||||
build_specs_with_discoverable_tools(config, mcp_tools, app_tools, None, dynamic_tools)
|
||||
build_specs_with_discoverable_tools(
|
||||
config,
|
||||
mcp_tools,
|
||||
app_tools,
|
||||
/*discoverable_tools*/ None,
|
||||
dynamic_tools,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn build_specs_with_discoverable_tools(
|
||||
|
||||
@@ -220,22 +220,22 @@ fn model_info_from_models_json(slug: &str) -> ModelInfo {
|
||||
#[test]
|
||||
fn unified_exec_is_blocked_for_windows_sandboxed_policies_only() {
|
||||
assert!(!unified_exec_allowed_in_environment(
|
||||
true,
|
||||
/*is_windows*/ true,
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
WindowsSandboxLevel::RestrictedToken,
|
||||
));
|
||||
assert!(!unified_exec_allowed_in_environment(
|
||||
true,
|
||||
/*is_windows*/ true,
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
WindowsSandboxLevel::RestrictedToken,
|
||||
));
|
||||
assert!(unified_exec_allowed_in_environment(
|
||||
true,
|
||||
/*is_windows*/ true,
|
||||
&SandboxPolicy::DangerFullAccess,
|
||||
WindowsSandboxLevel::RestrictedToken,
|
||||
));
|
||||
assert!(unified_exec_allowed_in_environment(
|
||||
true,
|
||||
/*is_windows*/ true,
|
||||
&SandboxPolicy::DangerFullAccess,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
));
|
||||
@@ -280,7 +280,13 @@ fn test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
// Build actual map name -> spec
|
||||
use std::collections::BTreeMap;
|
||||
@@ -301,7 +307,9 @@ fn test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search() {
|
||||
// Build expected from the same helpers used by the builder.
|
||||
let mut expected: BTreeMap<String, ToolSpec> = BTreeMap::from([]);
|
||||
for spec in [
|
||||
create_exec_command_tool(true, false),
|
||||
create_exec_command_tool(
|
||||
/*allow_login_shell*/ true, /*exec_permission_approvals_enabled*/ false,
|
||||
),
|
||||
create_write_stdin_tool(),
|
||||
PLAN_TOOL.clone(),
|
||||
create_request_user_input_tool(CollaborationModesConfig::default()),
|
||||
@@ -376,7 +384,13 @@ fn test_build_specs_collab_tools_enabled() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_contains_tool_names(
|
||||
&tools,
|
||||
&["spawn_agent", "send_input", "wait_agent", "close_agent"],
|
||||
@@ -402,7 +416,13 @@ fn test_build_specs_multi_agent_v2_uses_task_names_and_hides_resume() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_contains_tool_names(
|
||||
&tools,
|
||||
&[
|
||||
@@ -554,7 +574,13 @@ fn test_build_specs_enable_fanout_enables_agent_jobs_and_collab_tools() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_contains_tool_names(
|
||||
&tools,
|
||||
&[
|
||||
@@ -584,7 +610,13 @@ fn view_image_tool_omits_detail_without_original_detail_feature() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let view_image = find_tool(&tools, VIEW_IMAGE_TOOL_NAME);
|
||||
let ToolSpec::Function(ResponsesApiTool { parameters, .. }) = &view_image.spec else {
|
||||
panic!("view_image should be a function tool");
|
||||
@@ -613,7 +645,13 @@ fn view_image_tool_includes_detail_with_original_detail_feature() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let view_image = find_tool(&tools, VIEW_IMAGE_TOOL_NAME);
|
||||
let ToolSpec::Function(ResponsesApiTool { parameters, .. }) = &view_image.spec else {
|
||||
panic!("view_image should be a function tool");
|
||||
@@ -652,7 +690,13 @@ fn test_build_specs_agent_job_worker_tools_enabled() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_contains_tool_names(
|
||||
&tools,
|
||||
&[
|
||||
@@ -683,7 +727,13 @@ fn request_user_input_description_reflects_default_mode_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let request_user_input_tool = find_tool(&tools, "request_user_input");
|
||||
assert_eq!(
|
||||
request_user_input_tool.spec,
|
||||
@@ -701,7 +751,13 @@ fn request_user_input_description_reflects_default_mode_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let request_user_input_tool = find_tool(&tools, "request_user_input");
|
||||
assert_eq!(
|
||||
request_user_input_tool.spec,
|
||||
@@ -726,7 +782,13 @@ fn request_permissions_requires_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_lacks_tool_name(&tools, "request_permissions");
|
||||
|
||||
let mut features = Features::with_defaults();
|
||||
@@ -741,7 +803,13 @@ fn request_permissions_requires_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let request_permissions_tool = find_tool(&tools, "request_permissions");
|
||||
assert_eq!(
|
||||
request_permissions_tool.spec,
|
||||
@@ -765,7 +833,13 @@ fn request_permissions_tool_is_independent_from_additional_permissions() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
assert_lacks_tool_name(&tools, "request_permissions");
|
||||
}
|
||||
@@ -786,7 +860,13 @@ fn get_memory_requires_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert!(
|
||||
!tools.iter().any(|t| t.spec.name() == "get_memory"),
|
||||
"get_memory should be disabled when memory_tool feature is off"
|
||||
@@ -809,7 +889,13 @@ fn js_repl_requires_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
assert!(
|
||||
!tools.iter().any(|tool| tool.spec.name() == "js_repl"),
|
||||
@@ -838,7 +924,13 @@ fn js_repl_enabled_adds_tools() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_contains_tool_names(&tools, &["js_repl", "js_repl_reset"]);
|
||||
}
|
||||
|
||||
@@ -864,7 +956,13 @@ fn image_generation_tools_require_feature_and_supported_model() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (default_tools, _) = build_specs(&default_tools_config, None, None, &[]).build();
|
||||
let (default_tools, _) = build_specs(
|
||||
&default_tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert!(
|
||||
!default_tools
|
||||
.iter()
|
||||
@@ -881,7 +979,13 @@ fn image_generation_tools_require_feature_and_supported_model() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (supported_tools, _) = build_specs(&supported_tools_config, None, None, &[]).build();
|
||||
let (supported_tools, _) = build_specs(
|
||||
&supported_tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_contains_tool_names(&supported_tools, &["image_generation"]);
|
||||
let image_generation_tool = find_tool(&supported_tools, "image_generation");
|
||||
assert_eq!(
|
||||
@@ -901,7 +1005,13 @@ fn image_generation_tools_require_feature_and_supported_model() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert!(
|
||||
!tools
|
||||
.iter()
|
||||
@@ -992,7 +1102,13 @@ fn web_search_mode_cached_sets_external_web_access_false() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
let tool = find_tool(&tools, "web_search");
|
||||
assert_eq!(
|
||||
@@ -1023,7 +1139,13 @@ fn web_search_mode_live_sets_external_web_access_true() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
let tool = find_tool(&tools, "web_search");
|
||||
assert_eq!(
|
||||
@@ -1068,7 +1190,13 @@ fn web_search_config_is_forwarded_to_tool_spec() {
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
})
|
||||
.with_web_search_config(Some(web_search_config.clone()));
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
let tool = find_tool(&tools, "web_search");
|
||||
assert_eq!(
|
||||
@@ -1105,7 +1233,13 @@ fn web_search_tool_type_text_and_image_sets_search_content_types() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
let tool = find_tool(&tools, "web_search");
|
||||
assert_eq!(
|
||||
@@ -1140,7 +1274,13 @@ fn mcp_resource_tools_are_hidden_without_mcp_servers() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
assert!(
|
||||
!tools.iter().any(|tool| matches!(
|
||||
@@ -1166,7 +1306,13 @@ fn mcp_resource_tools_are_included_when_mcp_servers_are_present() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, Some(HashMap::new()), None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
Some(HashMap::new()),
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
assert_contains_tool_names(
|
||||
&tools,
|
||||
@@ -1406,7 +1552,13 @@ fn test_build_specs_default_shell_present() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, Some(HashMap::new()), None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
Some(HashMap::new()),
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
// Only check the shell variant and a couple of core tools.
|
||||
let mut subset = vec!["exec_command", "write_stdin", "update_plan"];
|
||||
@@ -1496,7 +1648,13 @@ fn test_parallel_support_flags() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
assert!(find_tool(&tools, "exec_command").supports_parallel_tool_calls);
|
||||
assert!(!find_tool(&tools, "write_stdin").supports_parallel_tool_calls);
|
||||
@@ -1518,7 +1676,13 @@ fn test_test_model_info_includes_sync_tool() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
|
||||
assert!(
|
||||
tools
|
||||
@@ -1568,7 +1732,7 @@ fn test_build_specs_mcp_tools_converted() {
|
||||
}),
|
||||
),
|
||||
)])),
|
||||
None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
@@ -1653,7 +1817,7 @@ fn test_build_specs_mcp_tools_sorted_by_name() {
|
||||
),
|
||||
]);
|
||||
|
||||
let (tools, _) = build_specs(&tools_config, Some(tools_map), None, &[]).build();
|
||||
let (tools, _) = build_specs(&tools_config, Some(tools_map), /*app_tools*/ None, &[]).build();
|
||||
|
||||
// Only assert that the MCP tools themselves are sorted by fully-qualified name.
|
||||
let mcp_names: Vec<_> = tools
|
||||
@@ -1827,7 +1991,13 @@ fn search_tool_requires_model_capability_and_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, app_tools.clone(), &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
app_tools.clone(),
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_lacks_tool_name(&tools, TOOL_SEARCH_TOOL_NAME);
|
||||
|
||||
let available_models = Vec::new();
|
||||
@@ -1840,7 +2010,13 @@ fn search_tool_requires_model_capability_and_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, app_tools.clone(), &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
app_tools.clone(),
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
assert_lacks_tool_name(&tools, TOOL_SEARCH_TOOL_NAME);
|
||||
|
||||
let mut features = Features::with_defaults();
|
||||
@@ -1855,7 +2031,7 @@ fn search_tool_requires_model_capability_and_feature_flag() {
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, app_tools, &[]).build();
|
||||
let (tools, _) = build_specs(&tools_config, /*mcp_tools*/ None, app_tools, &[]).build();
|
||||
assert_contains_tool_names(&tools, &[TOOL_SEARCH_TOOL_NAME]);
|
||||
}
|
||||
|
||||
@@ -1879,8 +2055,8 @@ fn tool_suggest_is_not_registered_without_feature_flag() {
|
||||
});
|
||||
let (tools, _) = build_specs_with_discoverable_tools(
|
||||
&tools_config,
|
||||
None,
|
||||
None,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
Some(vec![discoverable_connector(
|
||||
"connector_2128aebfecb84f64a069897515042a44",
|
||||
"Google Calendar",
|
||||
@@ -1919,8 +2095,8 @@ fn tool_suggest_can_be_registered_without_search_tool() {
|
||||
});
|
||||
let (tools, _) = build_specs_with_discoverable_tools(
|
||||
&tools_config,
|
||||
None,
|
||||
None,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
Some(vec![discoverable_connector(
|
||||
"connector_2128aebfecb84f64a069897515042a44",
|
||||
"Google Calendar",
|
||||
@@ -1974,8 +2150,8 @@ fn tool_suggest_requires_apps_and_plugins_features() {
|
||||
});
|
||||
let (tools, _) = build_specs_with_discoverable_tools(
|
||||
&tools_config,
|
||||
None,
|
||||
None,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
discoverable_tools.clone(),
|
||||
&[],
|
||||
)
|
||||
@@ -2007,7 +2183,13 @@ fn search_tool_description_handles_no_enabled_apps() {
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
|
||||
let (tools, _) = build_specs(&tools_config, None, Some(HashMap::new()), &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
Some(HashMap::new()),
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let search_tool = find_tool(&tools, TOOL_SEARCH_TOOL_NAME);
|
||||
let ToolSpec::ToolSearch { description, .. } = &search_tool.spec else {
|
||||
panic!("expected tool_search tool");
|
||||
@@ -2036,7 +2218,7 @@ fn search_tool_description_falls_back_to_connector_name_without_description() {
|
||||
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
None,
|
||||
/*mcp_tools*/ None,
|
||||
Some(HashMap::from([(
|
||||
"mcp__codex_apps__calendar_create_event".to_string(),
|
||||
ToolInfo {
|
||||
@@ -2085,7 +2267,7 @@ fn search_tool_registers_namespaced_app_tool_aliases() {
|
||||
|
||||
let (_, registry) = build_specs(
|
||||
&tools_config,
|
||||
None,
|
||||
/*mcp_tools*/ None,
|
||||
Some(HashMap::from([
|
||||
(
|
||||
"mcp__codex_apps__calendar_create_event".to_string(),
|
||||
@@ -2128,8 +2310,8 @@ fn search_tool_registers_namespaced_app_tool_aliases() {
|
||||
|
||||
let alias = tool_handler_key("_create_event", Some("mcp__codex_apps__calendar"));
|
||||
|
||||
assert!(registry.has_handler(TOOL_SEARCH_TOOL_NAME, None));
|
||||
assert!(registry.has_handler(alias.as_str(), None));
|
||||
assert!(registry.has_handler(TOOL_SEARCH_TOOL_NAME, /*namespace*/ None));
|
||||
assert!(registry.has_handler(alias.as_str(), /*namespace*/ None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2174,8 +2356,8 @@ fn tool_suggest_description_lists_discoverable_tools() {
|
||||
|
||||
let (tools, _) = build_specs_with_discoverable_tools(
|
||||
&tools_config,
|
||||
None,
|
||||
None,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
Some(discoverable_tools),
|
||||
&[],
|
||||
)
|
||||
@@ -2269,7 +2451,7 @@ fn test_mcp_tool_property_missing_type_defaults_to_string() {
|
||||
}),
|
||||
),
|
||||
)])),
|
||||
None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
@@ -2327,7 +2509,7 @@ fn test_mcp_tool_integer_normalized_to_number() {
|
||||
}),
|
||||
),
|
||||
)])),
|
||||
None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
@@ -2384,7 +2566,7 @@ fn test_mcp_tool_array_without_items_gets_default_string_items() {
|
||||
}),
|
||||
),
|
||||
)])),
|
||||
None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
@@ -2445,7 +2627,7 @@ fn test_mcp_tool_anyof_defaults_to_string() {
|
||||
}),
|
||||
),
|
||||
)])),
|
||||
None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
@@ -2473,7 +2655,7 @@ fn test_mcp_tool_anyof_defaults_to_string() {
|
||||
|
||||
#[test]
|
||||
fn test_shell_tool() {
|
||||
let tool = super::create_shell_tool(false);
|
||||
let tool = super::create_shell_tool(/*exec_permission_approvals_enabled*/ false);
|
||||
let ToolSpec::Function(ResponsesApiTool {
|
||||
description, name, ..
|
||||
}) = &tool
|
||||
@@ -2506,7 +2688,9 @@ Examples of valid command strings:
|
||||
|
||||
#[test]
|
||||
fn test_exec_command_tool_windows_description_includes_shell_safety_guidance() {
|
||||
let tool = super::create_exec_command_tool(true, false);
|
||||
let tool = super::create_exec_command_tool(
|
||||
/*allow_login_shell*/ true, /*exec_permission_approvals_enabled*/ false,
|
||||
);
|
||||
let ToolSpec::Function(ResponsesApiTool {
|
||||
description, name, ..
|
||||
}) = &tool
|
||||
@@ -2529,7 +2713,7 @@ fn test_exec_command_tool_windows_description_includes_shell_safety_guidance() {
|
||||
|
||||
#[test]
|
||||
fn shell_tool_with_request_permission_includes_additional_permissions() {
|
||||
let tool = super::create_shell_tool(true);
|
||||
let tool = super::create_shell_tool(/*exec_permission_approvals_enabled*/ true);
|
||||
let ToolSpec::Function(ResponsesApiTool { parameters, .. }) = tool else {
|
||||
panic!("expected function tool");
|
||||
};
|
||||
@@ -2609,7 +2793,9 @@ fn request_permissions_tool_includes_full_permission_schema() {
|
||||
|
||||
#[test]
|
||||
fn test_shell_command_tool() {
|
||||
let tool = super::create_shell_command_tool(true, false);
|
||||
let tool = super::create_shell_command_tool(
|
||||
/*allow_login_shell*/ true, /*exec_permission_approvals_enabled*/ false,
|
||||
);
|
||||
let ToolSpec::Function(ResponsesApiTool {
|
||||
description, name, ..
|
||||
}) = &tool
|
||||
@@ -2686,7 +2872,7 @@ fn test_get_openai_tools_mcp_tools_with_additional_properties_schema() {
|
||||
}),
|
||||
),
|
||||
)])),
|
||||
None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
@@ -2766,7 +2952,13 @@ fn code_mode_augments_builtin_tool_descriptions_with_typed_sample() {
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let ToolSpec::Function(ResponsesApiTool { description, .. }) =
|
||||
&find_tool(&tools, "view_image").spec
|
||||
else {
|
||||
@@ -2814,7 +3006,7 @@ fn code_mode_augments_mcp_tool_descriptions_with_namespaced_sample() {
|
||||
}),
|
||||
),
|
||||
)])),
|
||||
None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
@@ -2863,7 +3055,13 @@ fn code_mode_only_exec_description_includes_full_nested_tool_details() {
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let ToolSpec::Freeform(FreeformTool { description, .. }) = &find_tool(&tools, "exec").spec
|
||||
else {
|
||||
panic!("expected freeform tool");
|
||||
@@ -2895,7 +3093,13 @@ fn code_mode_exec_description_omits_nested_tool_details_when_not_code_mode_only(
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
});
|
||||
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
)
|
||||
.build();
|
||||
let ToolSpec::Freeform(FreeformTool { description, .. }) = &find_tool(&tools, "exec").spec
|
||||
else {
|
||||
panic!("expected freeform tool");
|
||||
|
||||
@@ -6,11 +6,13 @@ use pretty_assertions::assert_eq;
|
||||
fn split_valid_utf8_prefix_respects_max_bytes_for_ascii() {
|
||||
let mut buf = b"hello word!".to_vec();
|
||||
|
||||
let first = split_valid_utf8_prefix_with_max(&mut buf, 5).expect("expected prefix");
|
||||
let first =
|
||||
split_valid_utf8_prefix_with_max(&mut buf, /*max_bytes*/ 5).expect("expected prefix");
|
||||
assert_eq!(first, b"hello".to_vec());
|
||||
assert_eq!(buf, b" word!".to_vec());
|
||||
|
||||
let second = split_valid_utf8_prefix_with_max(&mut buf, 5).expect("expected prefix");
|
||||
let second =
|
||||
split_valid_utf8_prefix_with_max(&mut buf, /*max_bytes*/ 5).expect("expected prefix");
|
||||
assert_eq!(second, b" word".to_vec());
|
||||
assert_eq!(buf, b"!".to_vec());
|
||||
}
|
||||
@@ -20,7 +22,8 @@ fn split_valid_utf8_prefix_avoids_splitting_utf8_codepoints() {
|
||||
// "é" is 2 bytes in UTF-8. With a max of 3 bytes, we should only emit 1 char (2 bytes).
|
||||
let mut buf = "ééé".as_bytes().to_vec();
|
||||
|
||||
let first = split_valid_utf8_prefix_with_max(&mut buf, 3).expect("expected prefix");
|
||||
let first =
|
||||
split_valid_utf8_prefix_with_max(&mut buf, /*max_bytes*/ 3).expect("expected prefix");
|
||||
assert_eq!(std::str::from_utf8(&first).unwrap(), "é");
|
||||
assert_eq!(buf, "éé".as_bytes().to_vec());
|
||||
}
|
||||
@@ -29,7 +32,8 @@ fn split_valid_utf8_prefix_avoids_splitting_utf8_codepoints() {
|
||||
fn split_valid_utf8_prefix_makes_progress_on_invalid_utf8() {
|
||||
let mut buf = vec![0xff, b'a', b'b'];
|
||||
|
||||
let first = split_valid_utf8_prefix_with_max(&mut buf, 2).expect("expected prefix");
|
||||
let first =
|
||||
split_valid_utf8_prefix_with_max(&mut buf, /*max_bytes*/ 2).expect("expected prefix");
|
||||
assert_eq!(first, vec![0xff]);
|
||||
assert_eq!(buf, b"ab".to_vec());
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn keeps_prefix_and_suffix_when_over_budget() {
|
||||
let mut buf = HeadTailBuffer::new(10);
|
||||
let mut buf = HeadTailBuffer::new(/*max_bytes*/ 10);
|
||||
|
||||
buf.push_chunk(b"0123456789".to_vec());
|
||||
assert_eq!(buf.omitted_bytes(), 0);
|
||||
@@ -20,7 +20,7 @@ fn keeps_prefix_and_suffix_when_over_budget() {
|
||||
|
||||
#[test]
|
||||
fn max_bytes_zero_drops_everything() {
|
||||
let mut buf = HeadTailBuffer::new(0);
|
||||
let mut buf = HeadTailBuffer::new(/*max_bytes*/ 0);
|
||||
buf.push_chunk(b"abc".to_vec());
|
||||
|
||||
assert_eq!(buf.retained_bytes(), 0);
|
||||
@@ -31,7 +31,7 @@ fn max_bytes_zero_drops_everything() {
|
||||
|
||||
#[test]
|
||||
fn head_budget_zero_keeps_only_last_byte_in_tail() {
|
||||
let mut buf = HeadTailBuffer::new(1);
|
||||
let mut buf = HeadTailBuffer::new(/*max_bytes*/ 1);
|
||||
buf.push_chunk(b"abc".to_vec());
|
||||
|
||||
assert_eq!(buf.retained_bytes(), 1);
|
||||
@@ -41,7 +41,7 @@ fn head_budget_zero_keeps_only_last_byte_in_tail() {
|
||||
|
||||
#[test]
|
||||
fn draining_resets_state() {
|
||||
let mut buf = HeadTailBuffer::new(10);
|
||||
let mut buf = HeadTailBuffer::new(/*max_bytes*/ 10);
|
||||
buf.push_chunk(b"0123456789".to_vec());
|
||||
buf.push_chunk(b"ab".to_vec());
|
||||
|
||||
@@ -55,7 +55,7 @@ fn draining_resets_state() {
|
||||
|
||||
#[test]
|
||||
fn chunk_larger_than_tail_budget_keeps_only_tail_end() {
|
||||
let mut buf = HeadTailBuffer::new(10);
|
||||
let mut buf = HeadTailBuffer::new(/*max_bytes*/ 10);
|
||||
buf.push_chunk(b"0123456789".to_vec());
|
||||
|
||||
// Tail budget is 5 bytes. This chunk should replace the tail and keep only its last 5 bytes.
|
||||
@@ -69,7 +69,7 @@ fn chunk_larger_than_tail_budget_keeps_only_tail_end() {
|
||||
|
||||
#[test]
|
||||
fn fills_head_then_tail_across_multiple_chunks() {
|
||||
let mut buf = HeadTailBuffer::new(10);
|
||||
let mut buf = HeadTailBuffer::new(/*max_bytes*/ 10);
|
||||
|
||||
// Fill the 5-byte head budget across multiple chunks.
|
||||
buf.push_chunk(b"01".to_vec());
|
||||
|
||||
@@ -33,7 +33,15 @@ async fn exec_command(
|
||||
yield_time_ms: u64,
|
||||
workdir: Option<PathBuf>,
|
||||
) -> Result<ExecCommandToolOutput, UnifiedExecError> {
|
||||
exec_command_with_tty(session, turn, cmd, yield_time_ms, workdir, true).await
|
||||
exec_command_with_tty(
|
||||
session,
|
||||
turn,
|
||||
cmd,
|
||||
yield_time_ms,
|
||||
workdir,
|
||||
/*tty*/ true,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn shell_env() -> HashMap<String, String> {
|
||||
@@ -227,14 +235,17 @@ async fn unified_exec_persists_across_requests() -> anyhow::Result<()> {
|
||||
|
||||
let (session, turn) = test_session_and_turn().await;
|
||||
|
||||
let open_shell = exec_command(&session, &turn, "bash -i", 2_500, None).await?;
|
||||
let open_shell = exec_command(
|
||||
&session, &turn, "bash -i", /*yield_time_ms*/ 2_500, /*workdir*/ None,
|
||||
)
|
||||
.await?;
|
||||
let process_id = open_shell.process_id.expect("expected process_id");
|
||||
|
||||
write_stdin(
|
||||
&session,
|
||||
process_id,
|
||||
"export CODEX_INTERACTIVE_SHELL_VAR=codex\n",
|
||||
2_500,
|
||||
/*yield_time_ms*/ 2_500,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -242,7 +253,7 @@ async fn unified_exec_persists_across_requests() -> anyhow::Result<()> {
|
||||
&session,
|
||||
process_id,
|
||||
"echo $CODEX_INTERACTIVE_SHELL_VAR\n",
|
||||
2_500,
|
||||
/*yield_time_ms*/ 2_500,
|
||||
)
|
||||
.await?;
|
||||
assert!(
|
||||
@@ -259,14 +270,17 @@ async fn multi_unified_exec_sessions() -> anyhow::Result<()> {
|
||||
|
||||
let (session, turn) = test_session_and_turn().await;
|
||||
|
||||
let shell_a = exec_command(&session, &turn, "bash -i", 2_500, None).await?;
|
||||
let shell_a = exec_command(
|
||||
&session, &turn, "bash -i", /*yield_time_ms*/ 2_500, /*workdir*/ None,
|
||||
)
|
||||
.await?;
|
||||
let session_a = shell_a.process_id.expect("expected process id");
|
||||
|
||||
write_stdin(
|
||||
&session,
|
||||
session_a,
|
||||
"export CODEX_INTERACTIVE_SHELL_VAR=codex\n",
|
||||
2_500,
|
||||
/*yield_time_ms*/ 2_500,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -274,8 +288,8 @@ async fn multi_unified_exec_sessions() -> anyhow::Result<()> {
|
||||
&session,
|
||||
&turn,
|
||||
"echo $CODEX_INTERACTIVE_SHELL_VAR",
|
||||
2_500,
|
||||
None,
|
||||
/*yield_time_ms*/ 2_500,
|
||||
/*workdir*/ None,
|
||||
)
|
||||
.await?;
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
@@ -292,7 +306,7 @@ async fn multi_unified_exec_sessions() -> anyhow::Result<()> {
|
||||
&session,
|
||||
shell_a.process_id.expect("expected process id"),
|
||||
"echo $CODEX_INTERACTIVE_SHELL_VAR\n",
|
||||
2_500,
|
||||
/*yield_time_ms*/ 2_500,
|
||||
)
|
||||
.await?;
|
||||
assert!(
|
||||
@@ -311,14 +325,17 @@ async fn unified_exec_timeouts() -> anyhow::Result<()> {
|
||||
|
||||
let (session, turn) = test_session_and_turn().await;
|
||||
|
||||
let open_shell = exec_command(&session, &turn, "bash -i", 2_500, None).await?;
|
||||
let open_shell = exec_command(
|
||||
&session, &turn, "bash -i", /*yield_time_ms*/ 2_500, /*workdir*/ None,
|
||||
)
|
||||
.await?;
|
||||
let process_id = open_shell.process_id.expect("expected process id");
|
||||
|
||||
write_stdin(
|
||||
&session,
|
||||
process_id,
|
||||
format!("export CODEX_INTERACTIVE_SHELL_VAR={TEST_VAR_VALUE}\n").as_str(),
|
||||
2_500,
|
||||
/*yield_time_ms*/ 2_500,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -326,7 +343,7 @@ async fn unified_exec_timeouts() -> anyhow::Result<()> {
|
||||
&session,
|
||||
process_id,
|
||||
"sleep 5 && echo $CODEX_INTERACTIVE_SHELL_VAR\n",
|
||||
10,
|
||||
/*yield_time_ms*/ 10,
|
||||
)
|
||||
.await?;
|
||||
assert!(
|
||||
@@ -336,7 +353,7 @@ async fn unified_exec_timeouts() -> anyhow::Result<()> {
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(7)).await;
|
||||
|
||||
let out_3 = write_stdin(&session, process_id, "", 100).await?;
|
||||
let out_3 = write_stdin(&session, process_id, "", /*yield_time_ms*/ 100).await?;
|
||||
|
||||
assert!(
|
||||
out_3.truncated_output().contains(TEST_VAR_VALUE),
|
||||
@@ -351,12 +368,12 @@ async fn unified_exec_pause_blocks_yield_timeout() -> anyhow::Result<()> {
|
||||
skip_if_sandbox!(Ok(()));
|
||||
|
||||
let (session, turn) = test_session_and_turn().await;
|
||||
session.set_out_of_band_elicitation_pause_state(true);
|
||||
session.set_out_of_band_elicitation_pause_state(/*paused*/ true);
|
||||
|
||||
let paused_session = Arc::clone(&session);
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
paused_session.set_out_of_band_elicitation_pause_state(false);
|
||||
paused_session.set_out_of_band_elicitation_pause_state(/*paused*/ false);
|
||||
});
|
||||
|
||||
let started = tokio::time::Instant::now();
|
||||
@@ -364,8 +381,8 @@ async fn unified_exec_pause_blocks_yield_timeout() -> anyhow::Result<()> {
|
||||
&session,
|
||||
&turn,
|
||||
"sleep 1 && echo unified-exec-done",
|
||||
250,
|
||||
None,
|
||||
/*yield_time_ms*/ 250,
|
||||
/*workdir*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -390,7 +407,14 @@ async fn unified_exec_pause_blocks_yield_timeout() -> anyhow::Result<()> {
|
||||
async fn requests_with_large_timeout_are_capped() -> anyhow::Result<()> {
|
||||
let (session, turn) = test_session_and_turn().await;
|
||||
|
||||
let result = exec_command(&session, &turn, "echo codex", 120_000, None).await?;
|
||||
let result = exec_command(
|
||||
&session,
|
||||
&turn,
|
||||
"echo codex",
|
||||
/*yield_time_ms*/ 120_000,
|
||||
/*workdir*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert!(result.process_id.is_some());
|
||||
assert!(result.truncated_output().contains("codex"));
|
||||
@@ -402,7 +426,14 @@ async fn requests_with_large_timeout_are_capped() -> anyhow::Result<()> {
|
||||
#[ignore] // Ignored while we have a better way to test this.
|
||||
async fn completed_commands_do_not_persist_sessions() -> anyhow::Result<()> {
|
||||
let (session, turn) = test_session_and_turn().await;
|
||||
let result = exec_command(&session, &turn, "echo codex", 2_500, None).await?;
|
||||
let result = exec_command(
|
||||
&session,
|
||||
&turn,
|
||||
"echo codex",
|
||||
/*yield_time_ms*/ 2_500,
|
||||
/*workdir*/ None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
result.process_id.is_some(),
|
||||
@@ -430,14 +461,17 @@ async fn reusing_completed_process_returns_unknown_process() -> anyhow::Result<(
|
||||
|
||||
let (session, turn) = test_session_and_turn().await;
|
||||
|
||||
let open_shell = exec_command(&session, &turn, "bash -i", 2_500, None).await?;
|
||||
let open_shell = exec_command(
|
||||
&session, &turn, "bash -i", /*yield_time_ms*/ 2_500, /*workdir*/ None,
|
||||
)
|
||||
.await?;
|
||||
let process_id = open_shell.process_id.expect("expected process id");
|
||||
|
||||
write_stdin(&session, process_id, "exit\n", 2_500).await?;
|
||||
write_stdin(&session, process_id, "exit\n", /*yield_time_ms*/ 2_500).await?;
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||
|
||||
let err = write_stdin(&session, process_id, "", 100)
|
||||
let err = write_stdin(&session, process_id, "", /*yield_time_ms*/ 100)
|
||||
.await
|
||||
.expect_err("expected unknown process error");
|
||||
|
||||
@@ -475,9 +509,9 @@ async fn completed_pipe_commands_preserve_exit_code() -> anyhow::Result<()> {
|
||||
let environment = codex_exec_server::Environment::default();
|
||||
let process = UnifiedExecProcessManager::default()
|
||||
.open_session_with_exec_env(
|
||||
1234,
|
||||
/*process_id*/ 1234,
|
||||
&request,
|
||||
false,
|
||||
/*tty*/ false,
|
||||
Box::new(NoopSpawnLifecycle),
|
||||
&environment,
|
||||
)
|
||||
@@ -517,9 +551,9 @@ async fn unified_exec_uses_remote_exec_server_when_configured() -> anyhow::Resul
|
||||
let manager = UnifiedExecProcessManager::default();
|
||||
let process = manager
|
||||
.open_session_with_exec_env(
|
||||
1234,
|
||||
/*process_id*/ 1234,
|
||||
&request,
|
||||
true,
|
||||
/*tty*/ true,
|
||||
Box::new(NoopSpawnLifecycle),
|
||||
remote_test_env.environment(),
|
||||
)
|
||||
@@ -541,7 +575,7 @@ async fn unified_exec_uses_remote_exec_server_when_configured() -> anyhow::Resul
|
||||
&output_closed,
|
||||
&output_closed_notify,
|
||||
&cancellation_token,
|
||||
None,
|
||||
/*pause_state*/ None,
|
||||
Instant::now() + Duration::from_millis(2_500),
|
||||
)
|
||||
.await;
|
||||
@@ -571,9 +605,9 @@ async fn remote_exec_server_rejects_inherited_fd_launches() -> anyhow::Result<()
|
||||
let manager = UnifiedExecProcessManager::default();
|
||||
let err = manager
|
||||
.open_session_with_exec_env(
|
||||
1234,
|
||||
/*process_id*/ 1234,
|
||||
&request,
|
||||
true,
|
||||
/*tty*/ true,
|
||||
Box::new(TestSpawnLifecycle {
|
||||
inherited_fds: vec![42],
|
||||
}),
|
||||
|
||||
@@ -36,7 +36,7 @@ fn unified_exec_env_overrides_existing_values() {
|
||||
|
||||
#[test]
|
||||
fn exec_server_process_id_matches_unified_exec_process_id() {
|
||||
assert_eq!(exec_server_process_id(4321), "4321");
|
||||
assert_eq!(exec_server_process_id(/*process_id*/ 4321), "4321");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -232,9 +232,9 @@ fn emit_feedback_auth_recovery_tags_clears_stale_401_fields() {
|
||||
"done",
|
||||
"recovery_not_run",
|
||||
Some("req-401-b"),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
/*auth_cf_ray*/ None,
|
||||
/*auth_error*/ None,
|
||||
/*auth_error_code*/ None,
|
||||
);
|
||||
|
||||
let tags = tags.lock().unwrap().clone();
|
||||
@@ -440,7 +440,7 @@ fn resume_command_prefers_name_over_id() {
|
||||
#[test]
|
||||
fn resume_command_with_only_id() {
|
||||
let thread_id = ThreadId::from_string("123e4567-e89b-12d3-a456-426614174000").unwrap();
|
||||
let command = resume_command(None, Some(thread_id));
|
||||
let command = resume_command(/*thread_name*/ None, Some(thread_id));
|
||||
assert_eq!(
|
||||
command,
|
||||
Some("codex resume 123e4567-e89b-12d3-a456-426614174000".to_string())
|
||||
@@ -449,21 +449,21 @@ fn resume_command_with_only_id() {
|
||||
|
||||
#[test]
|
||||
fn resume_command_with_no_name_or_id() {
|
||||
let command = resume_command(None, None);
|
||||
let command = resume_command(/*thread_name*/ None, /*thread_id*/ None);
|
||||
assert_eq!(command, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resume_command_quotes_thread_name_when_needed() {
|
||||
let command = resume_command(Some("-starts-with-dash"), None);
|
||||
let command = resume_command(Some("-starts-with-dash"), /*thread_id*/ None);
|
||||
assert_eq!(
|
||||
command,
|
||||
Some("codex resume -- -starts-with-dash".to_string())
|
||||
);
|
||||
|
||||
let command = resume_command(Some("two words"), None);
|
||||
let command = resume_command(Some("two words"), /*thread_id*/ None);
|
||||
assert_eq!(command, Some("codex resume 'two words'".to_string()));
|
||||
|
||||
let command = resume_command(Some("quote'case"), None);
|
||||
let command = resume_command(Some("quote'case"), /*thread_id*/ None);
|
||||
assert_eq!(command, Some("codex resume \"quote'case\"".to_string()));
|
||||
}
|
||||
|
||||
@@ -52,8 +52,11 @@ fn elevated_wins_when_both_flags_are_enabled() {
|
||||
#[test]
|
||||
fn legacy_mode_prefers_elevated() {
|
||||
let mut entries = BTreeMap::new();
|
||||
entries.insert("experimental_windows_sandbox".to_string(), true);
|
||||
entries.insert("elevated_windows_sandbox".to_string(), true);
|
||||
entries.insert(
|
||||
"experimental_windows_sandbox".to_string(),
|
||||
/*value*/ true,
|
||||
);
|
||||
entries.insert("elevated_windows_sandbox".to_string(), /*value*/ true);
|
||||
|
||||
assert_eq!(
|
||||
legacy_windows_sandbox_mode_from_entries(&entries),
|
||||
@@ -64,7 +67,10 @@ fn legacy_mode_prefers_elevated() {
|
||||
#[test]
|
||||
fn legacy_mode_supports_alias_key() {
|
||||
let mut entries = BTreeMap::new();
|
||||
entries.insert("enable_experimental_windows_sandbox".to_string(), true);
|
||||
entries.insert(
|
||||
"enable_experimental_windows_sandbox".to_string(),
|
||||
/*value*/ true,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
legacy_windows_sandbox_mode_from_entries(&entries),
|
||||
@@ -98,7 +104,10 @@ fn resolve_windows_sandbox_mode_prefers_profile_windows() {
|
||||
#[test]
|
||||
fn resolve_windows_sandbox_mode_falls_back_to_legacy_keys() {
|
||||
let mut entries = BTreeMap::new();
|
||||
entries.insert("experimental_windows_sandbox".to_string(), true);
|
||||
entries.insert(
|
||||
"experimental_windows_sandbox".to_string(),
|
||||
/*value*/ true,
|
||||
);
|
||||
let cfg = ConfigToml {
|
||||
features: Some(FeaturesToml { entries }),
|
||||
..Default::default()
|
||||
@@ -113,7 +122,10 @@ fn resolve_windows_sandbox_mode_falls_back_to_legacy_keys() {
|
||||
#[test]
|
||||
fn resolve_windows_sandbox_mode_profile_legacy_false_blocks_top_level_legacy_true() {
|
||||
let mut profile_entries = BTreeMap::new();
|
||||
profile_entries.insert("experimental_windows_sandbox".to_string(), false);
|
||||
profile_entries.insert(
|
||||
"experimental_windows_sandbox".to_string(),
|
||||
/*value*/ false,
|
||||
);
|
||||
let profile = ConfigProfile {
|
||||
features: Some(FeaturesToml {
|
||||
entries: profile_entries,
|
||||
@@ -122,7 +134,10 @@ fn resolve_windows_sandbox_mode_profile_legacy_false_blocks_top_level_legacy_tru
|
||||
};
|
||||
|
||||
let mut cfg_entries = BTreeMap::new();
|
||||
cfg_entries.insert("experimental_windows_sandbox".to_string(), true);
|
||||
cfg_entries.insert(
|
||||
"experimental_windows_sandbox".to_string(),
|
||||
/*value*/ true,
|
||||
);
|
||||
let cfg = ConfigToml {
|
||||
features: Some(FeaturesToml {
|
||||
entries: cfg_entries,
|
||||
|
||||
Reference in New Issue
Block a user