mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Use AbsolutePathBuf in skill loading and codex_home (#17407)
Helps with FS migration later
This commit is contained in:
committed by
GitHub
Unverified
parent
d25a9822a7
commit
ac82443d07
@@ -91,7 +91,7 @@ impl AgentControlHarness {
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
std::sync::Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
@@ -905,7 +905,7 @@ async fn spawn_agent_respects_max_threads_limit() {
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
std::sync::Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
@@ -959,7 +959,7 @@ async fn spawn_agent_releases_slot_after_shutdown() {
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
std::sync::Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
@@ -1004,7 +1004,7 @@ async fn spawn_agent_limit_shared_across_clones() {
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
std::sync::Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
@@ -1051,7 +1051,7 @@ async fn resume_agent_respects_max_threads_limit() {
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
std::sync::Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
@@ -1109,7 +1109,7 @@ async fn resume_agent_releases_slot_after_resume_failure() {
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
std::sync::Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
@@ -1506,7 +1506,7 @@ async fn resume_thread_subagent_restores_stored_nickname_and_role() {
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
std::sync::Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::skills_load_input_from_config;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_utils_absolute_path::test_support::PathExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
@@ -652,10 +653,8 @@ 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(),
|
||||
/*bundled_skills_enabled*/ true,
|
||||
);
|
||||
let skills_manager =
|
||||
SkillsManager::new(home.path().abs(), /*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);
|
||||
|
||||
+14
-14
@@ -649,7 +649,7 @@ impl Codex {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name,
|
||||
@@ -1912,7 +1912,7 @@ impl Session {
|
||||
tx
|
||||
} else {
|
||||
ShellSnapshot::start_snapshotting(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
conversation_id,
|
||||
session_configuration.cwd.to_path_buf(),
|
||||
&mut default_shell,
|
||||
@@ -2164,7 +2164,7 @@ impl Session {
|
||||
INITIAL_SUBMIT_ID.to_owned(),
|
||||
tx_event.clone(),
|
||||
sandbox_state,
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
codex_apps_tools_cache_key(auth),
|
||||
tool_plugin_provenance,
|
||||
)
|
||||
@@ -4516,7 +4516,7 @@ impl Session {
|
||||
turn_context.sub_id.clone(),
|
||||
self.get_tx_event(),
|
||||
sandbox_state,
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
codex_apps_tools_cache_key(auth.as_ref()),
|
||||
tool_plugin_provenance,
|
||||
)
|
||||
@@ -4884,7 +4884,6 @@ mod handlers {
|
||||
use crate::codex::SessionSettingsUpdate;
|
||||
use crate::codex::SteerInputError;
|
||||
|
||||
use crate::SkillError;
|
||||
use crate::codex::spawn_review_thread;
|
||||
use crate::config::Config;
|
||||
use crate::config_loader::CloudRequirementsLoader;
|
||||
@@ -4915,6 +4914,7 @@ mod handlers {
|
||||
use codex_protocol::protocol::ReviewDecision;
|
||||
use codex_protocol::protocol::ReviewRequest;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
use codex_protocol::protocol::SkillErrorInfo;
|
||||
use codex_protocol::protocol::SkillsListEntry;
|
||||
use codex_protocol::protocol::ThreadNameUpdatedEvent;
|
||||
use codex_protocol::protocol::ThreadRolledBackEvent;
|
||||
@@ -5371,7 +5371,7 @@ mod handlers {
|
||||
let mut skills = Vec::new();
|
||||
let empty_cli_overrides: &[(String, toml::Value)] = &[];
|
||||
for cwd in cwds {
|
||||
let cwd_abs = match AbsolutePathBuf::try_from(cwd.as_path()) {
|
||||
let cwd_abs = match AbsolutePathBuf::relative_to_current_dir(cwd.as_path()) {
|
||||
Ok(path) => path,
|
||||
Err(err) => {
|
||||
let message = err.to_string();
|
||||
@@ -5379,17 +5379,17 @@ mod handlers {
|
||||
skills.push(SkillsListEntry {
|
||||
cwd: cwd_for_entry.clone(),
|
||||
skills: Vec::new(),
|
||||
errors: super::errors_to_info(&[SkillError {
|
||||
errors: vec![SkillErrorInfo {
|
||||
path: cwd_for_entry,
|
||||
message,
|
||||
}]),
|
||||
}],
|
||||
});
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let config_layer_stack = match load_config_layers_state(
|
||||
&codex_home,
|
||||
Some(cwd_abs),
|
||||
Some(cwd_abs.clone()),
|
||||
empty_cli_overrides,
|
||||
LoaderOverrides::default(),
|
||||
CloudRequirementsLoader::default(),
|
||||
@@ -5403,10 +5403,10 @@ mod handlers {
|
||||
skills.push(SkillsListEntry {
|
||||
cwd: cwd_for_entry.clone(),
|
||||
skills: Vec::new(),
|
||||
errors: super::errors_to_info(&[SkillError {
|
||||
errors: vec![SkillErrorInfo {
|
||||
path: cwd_for_entry,
|
||||
message,
|
||||
}]),
|
||||
}],
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -5416,7 +5416,7 @@ mod handlers {
|
||||
config.features.enabled(Feature::Plugins),
|
||||
);
|
||||
let skills_input = crate::SkillsLoadInput::new(
|
||||
cwd.clone(),
|
||||
cwd_abs,
|
||||
effective_skill_roots,
|
||||
config_layer_stack,
|
||||
config.bundled_skills_enabled(),
|
||||
@@ -5959,7 +5959,7 @@ async fn spawn_review_thread(
|
||||
|
||||
fn skills_to_info(
|
||||
skills: &[SkillMetadata],
|
||||
disabled_paths: &HashSet<PathBuf>,
|
||||
disabled_paths: &HashSet<AbsolutePathBuf>,
|
||||
) -> Vec<ProtocolSkillMetadata> {
|
||||
skills
|
||||
.iter()
|
||||
@@ -6005,7 +6005,7 @@ fn errors_to_info(errors: &[SkillError]) -> Vec<SkillErrorInfo> {
|
||||
errors
|
||||
.iter()
|
||||
.map(|err| SkillErrorInfo {
|
||||
path: err.path.clone(),
|
||||
path: err.path.to_path_buf(),
|
||||
message: err.message.clone(),
|
||||
})
|
||||
.collect()
|
||||
|
||||
@@ -1984,7 +1984,7 @@ async fn set_rate_limits_retains_previous_credits() {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2086,7 +2086,7 @@ async fn set_rate_limits_updates_plan_type_when_present() {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2438,7 +2438,7 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2550,7 +2550,7 @@ enabled = false
|
||||
"custom".to_string(),
|
||||
crate::config::AgentRoleConfig {
|
||||
description: None,
|
||||
config_file: Some(role_path),
|
||||
config_file: Some(role_path.to_path_buf()),
|
||||
nickname_candidates: None,
|
||||
},
|
||||
);
|
||||
@@ -2663,7 +2663,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
||||
|
||||
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
auth_manager.clone(),
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
@@ -2701,7 +2701,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2716,7 +2716,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
||||
|
||||
let (tx_event, _rx_event) = async_channel::unbounded();
|
||||
let (agent_status_tx, _agent_status_rx) = watch::channel(AgentStatus::PendingInit);
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone()));
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.to_path_buf()));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
let skills_manager = Arc::new(SkillsManager::new(
|
||||
config.codex_home.clone(),
|
||||
@@ -2763,7 +2763,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
let conversation_id = ThreadId::default();
|
||||
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
auth_manager.clone(),
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
@@ -2805,7 +2805,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2830,7 +2830,7 @@ 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 plugins_manager = Arc::new(PluginsManager::new(config.codex_home.to_path_buf()));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
let skills_manager = Arc::new(SkillsManager::new(
|
||||
config.codex_home.clone(),
|
||||
@@ -3608,7 +3608,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
||||
let conversation_id = ThreadId::default();
|
||||
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
auth_manager.clone(),
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
@@ -3650,7 +3650,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -3675,7 +3675,7 @@ 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 plugins_manager = Arc::new(PluginsManager::new(config.codex_home.to_path_buf()));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
let skills_manager = Arc::new(SkillsManager::new(
|
||||
config.codex_home.clone(),
|
||||
|
||||
@@ -95,7 +95,7 @@ async fn guardian_allows_shell_additional_permissions_requests_past_policy_valid
|
||||
config.model_provider.base_url = Some(format!("{}/v1", server.uri()));
|
||||
let config = Arc::new(config);
|
||||
let models_manager = Arc::new(crate::test_support::models_manager_with_provider(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::clone(&session.services.auth_manager),
|
||||
config.model_provider.clone(),
|
||||
));
|
||||
@@ -417,12 +417,12 @@ async fn guardian_subagent_does_not_inherit_parent_exec_policy_rules() {
|
||||
|
||||
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
auth_manager.clone(),
|
||||
/*model_catalog*/ None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone()));
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.to_path_buf()));
|
||||
let skills_manager = Arc::new(SkillsManager::new(
|
||||
config.codex_home.clone(),
|
||||
/*bundled_skills_enabled*/ true,
|
||||
|
||||
@@ -125,7 +125,7 @@ fn load_config_normalizes_relative_cwd_override() -> std::io::Result<()> {
|
||||
cwd: Some(PathBuf::from("nested")),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.abs().into_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(config.cwd, expected_cwd);
|
||||
@@ -141,7 +141,7 @@ fn load_config_records_global_agents_path() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs().into_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -168,7 +168,7 @@ fn load_config_records_preferred_global_agents_override_path() -> std::io::Resul
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs().into_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -247,7 +247,7 @@ consolidation_model = "gpt-5"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
memories_cfg,
|
||||
ConfigOverrides::default(),
|
||||
tempdir().expect("tempdir").path().to_path_buf(),
|
||||
tempdir().expect("tempdir").abs(),
|
||||
)
|
||||
.expect("load config from memories settings");
|
||||
assert_eq!(
|
||||
@@ -379,7 +379,7 @@ fn runtime_config_defaults_model_availability_nux() {
|
||||
let cfg = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
tempdir().expect("tempdir").path().to_path_buf(),
|
||||
tempdir().expect("tempdir").abs(),
|
||||
)
|
||||
.expect("load config");
|
||||
|
||||
@@ -494,7 +494,7 @@ fn permissions_profiles_network_populates_runtime_network_proxy_spec() -> std::i
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
let network = config
|
||||
.permissions
|
||||
@@ -544,7 +544,7 @@ fn permissions_profiles_network_disabled_by_default_does_not_start_proxy() -> st
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert!(config.permissions.network.is_none());
|
||||
@@ -592,7 +592,7 @@ fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::io::Re
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
let memories_root = codex_home.path().join("memories").abs();
|
||||
@@ -673,7 +673,7 @@ fn permissions_profiles_require_default_permissions() -> std::io::Result<()> {
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.expect_err("missing default_permissions should be rejected");
|
||||
|
||||
@@ -715,7 +715,7 @@ fn permissions_profiles_reject_writes_outside_workspace_root() -> std::io::Resul
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.expect_err("writes outside the workspace root should be rejected");
|
||||
|
||||
@@ -760,7 +760,7 @@ fn permissions_profiles_reject_nested_entries_for_non_project_roots() -> std::io
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.expect_err("nested entries outside :project_roots should be rejected");
|
||||
|
||||
@@ -789,7 +789,7 @@ fn load_workspace_permission_profile(profile: PermissionProfileToml) -> std::io:
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -957,7 +957,7 @@ fn permissions_profiles_reject_project_root_parent_traversal() -> std::io::Resul
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.expect_err("parent traversal should be rejected for project root subpaths");
|
||||
|
||||
@@ -1001,7 +1001,7 @@ fn permissions_profiles_allow_network_enablement() -> std::io::Result<()> {
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert!(
|
||||
@@ -1229,7 +1229,7 @@ exclude_slash_tmp = true
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
let sandbox_policy = config.permissions.sandbox_policy.get();
|
||||
@@ -1409,7 +1409,7 @@ fn add_dir_override_extends_workspace_writable_roots() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
overrides,
|
||||
temp_dir.path().to_path_buf(),
|
||||
temp_dir.path().abs(),
|
||||
)?;
|
||||
|
||||
let expected_backend = backend.abs();
|
||||
@@ -1447,7 +1447,7 @@ fn sqlite_home_defaults_to_codex_home_for_workspace_write() -> std::io::Result<(
|
||||
sandbox_mode: Some(SandboxMode::WorkspaceWrite),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(config.sqlite_home, codex_home.path().to_path_buf());
|
||||
@@ -1471,7 +1471,7 @@ fn workspace_write_always_includes_memories_root_once() -> std::io::Result<()> {
|
||||
sandbox_mode: Some(SandboxMode::WorkspaceWrite),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
if cfg!(target_os = "windows") {
|
||||
@@ -1513,7 +1513,7 @@ fn config_defaults_to_file_cli_auth_store_mode() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -1535,7 +1535,7 @@ fn config_resolves_explicit_keyring_auth_store_mode() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -1557,7 +1557,7 @@ fn config_resolves_default_oauth_store_mode() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -1633,7 +1633,7 @@ fn feedback_enabled_defaults_to_true() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(config.feedback_enabled, true);
|
||||
@@ -1795,7 +1795,7 @@ fn profile_sandbox_mode_overrides_base() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert!(matches!(
|
||||
@@ -1828,11 +1828,7 @@ fn cli_override_takes_precedence_over_profile_sandbox_mode() -> std::io::Result<
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
overrides,
|
||||
codex_home.path().to_path_buf(),
|
||||
)?;
|
||||
let config = Config::load_from_base_config_with_overrides(cfg, overrides, codex_home.abs())?;
|
||||
|
||||
if cfg!(target_os = "windows") {
|
||||
assert!(matches!(
|
||||
@@ -1862,7 +1858,7 @@ fn feature_table_overrides_legacy_flags() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert!(!config.features.enabled(Feature::ApplyPatchFreeform));
|
||||
@@ -1883,7 +1879,7 @@ fn legacy_toggles_map_to_features() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert!(config.features.enabled(Feature::ApplyPatchFreeform));
|
||||
@@ -1910,7 +1906,7 @@ fn responses_websocket_features_do_not_change_wire_api() -> std::io::Result<()>
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(config.model_provider.wire_api, WireApi::Responses);
|
||||
@@ -1930,7 +1926,7 @@ fn config_honors_explicit_file_oauth_store_mode() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -1975,7 +1971,7 @@ async fn managed_config_overrides_oauth_store_mode() -> anyhow::Result<()> {
|
||||
let final_config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
assert_eq!(
|
||||
final_config.mcp_oauth_credentials_store_mode,
|
||||
@@ -2199,7 +2195,7 @@ fn to_mcp_config_preserves_apps_feature_from_config() -> std::io::Result<()> {
|
||||
let mut config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
|
||||
@@ -3231,8 +3227,8 @@ impl PrecedenceTestFixture {
|
||||
self.cwd.path().to_path_buf()
|
||||
}
|
||||
|
||||
fn codex_home(&self) -> PathBuf {
|
||||
self.codex_home.path().to_path_buf()
|
||||
fn codex_home(&self) -> AbsolutePathBuf {
|
||||
self.codex_home.abs()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3247,7 +3243,7 @@ fn cli_override_sets_compact_prompt() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
overrides,
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -3277,11 +3273,7 @@ fn loads_compact_prompt_from_file() -> std::io::Result<()> {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
overrides,
|
||||
codex_home.path().to_path_buf(),
|
||||
)?;
|
||||
let config = Config::load_from_base_config_with_overrides(cfg, overrides, codex_home.abs())?;
|
||||
|
||||
assert_eq!(
|
||||
config.compact_prompt.as_deref(),
|
||||
@@ -3312,7 +3304,7 @@ fn load_config_uses_requirements_guardian_policy_config() -> std::io::Result<()>
|
||||
cwd: Some(codex_home.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
config_layer_stack,
|
||||
)?;
|
||||
|
||||
@@ -3343,7 +3335,7 @@ fn load_config_ignores_empty_requirements_guardian_policy_config() -> std::io::R
|
||||
cwd: Some(codex_home.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
config_layer_stack,
|
||||
)?;
|
||||
|
||||
@@ -3376,7 +3368,7 @@ fn load_config_rejects_missing_agent_role_config_file() -> std::io::Result<()> {
|
||||
let result = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
);
|
||||
let err = result.expect_err("missing role config file should be rejected");
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
@@ -4244,7 +4236,7 @@ fn load_config_normalizes_agent_role_nickname_candidates() -> std::io::Result<()
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -4282,7 +4274,7 @@ fn load_config_rejects_empty_agent_role_nickname_candidates() -> std::io::Result
|
||||
let result = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
);
|
||||
let err = result.expect_err("empty nickname candidates should be rejected");
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
@@ -4317,7 +4309,7 @@ fn load_config_rejects_duplicate_agent_role_nickname_candidates() -> std::io::Re
|
||||
let result = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
);
|
||||
let err = result.expect_err("duplicate nickname candidates should be rejected");
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
@@ -4352,7 +4344,7 @@ fn load_config_rejects_unsafe_agent_role_nickname_candidates() -> std::io::Resul
|
||||
let result = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
);
|
||||
let err = result.expect_err("unsafe nickname candidates should be rejected");
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
@@ -4383,7 +4375,7 @@ fn model_catalog_json_loads_from_path() -> std::io::Result<()> {
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(config.model_catalog, Some(catalog));
|
||||
@@ -4404,7 +4396,7 @@ fn model_catalog_json_rejects_empty_catalog() -> std::io::Result<()> {
|
||||
let err = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.expect_err("empty custom catalog should fail config load");
|
||||
|
||||
@@ -4590,8 +4582,8 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
codex_home: fixture.codex_home(),
|
||||
sqlite_home: fixture.codex_home(),
|
||||
log_dir: fixture.codex_home().join("log"),
|
||||
sqlite_home: fixture.codex_home().to_path_buf(),
|
||||
log_dir: fixture.codex_home().join("log").to_path_buf(),
|
||||
config_layer_stack: Default::default(),
|
||||
startup_warnings: Vec::new(),
|
||||
history: History::default(),
|
||||
@@ -4739,8 +4731,8 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
codex_home: fixture.codex_home(),
|
||||
sqlite_home: fixture.codex_home(),
|
||||
log_dir: fixture.codex_home().join("log"),
|
||||
sqlite_home: fixture.codex_home().to_path_buf(),
|
||||
log_dir: fixture.codex_home().join("log").to_path_buf(),
|
||||
config_layer_stack: Default::default(),
|
||||
startup_warnings: Vec::new(),
|
||||
history: History::default(),
|
||||
@@ -4886,8 +4878,8 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
codex_home: fixture.codex_home(),
|
||||
sqlite_home: fixture.codex_home(),
|
||||
log_dir: fixture.codex_home().join("log"),
|
||||
sqlite_home: fixture.codex_home().to_path_buf(),
|
||||
log_dir: fixture.codex_home().join("log").to_path_buf(),
|
||||
config_layer_stack: Default::default(),
|
||||
startup_warnings: Vec::new(),
|
||||
history: History::default(),
|
||||
@@ -5019,8 +5011,8 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
codex_home: fixture.codex_home(),
|
||||
sqlite_home: fixture.codex_home(),
|
||||
log_dir: fixture.codex_home().join("log"),
|
||||
sqlite_home: fixture.codex_home().to_path_buf(),
|
||||
log_dir: fixture.codex_home().join("log").to_path_buf(),
|
||||
config_layer_stack: Default::default(),
|
||||
startup_warnings: Vec::new(),
|
||||
history: History::default(),
|
||||
@@ -5321,7 +5313,7 @@ fn test_load_config_rejects_legacy_ollama_chat_provider_with_helpful_error() ->
|
||||
let result = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
);
|
||||
assert!(result.is_err());
|
||||
let error = result.unwrap_err();
|
||||
@@ -5584,7 +5576,7 @@ mcp_oauth_callback_port = 5678
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(config.mcp_oauth_callback_port, Some(5678));
|
||||
@@ -5605,7 +5597,7 @@ allow_login_shell = false
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert!(!config.permissions.allow_login_shell);
|
||||
@@ -5625,7 +5617,7 @@ mcp_oauth_callback_url = "https://example.com/callback"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -5655,7 +5647,7 @@ fn test_untrusted_project_gets_unless_trusted_approval_policy() -> anyhow::Resul
|
||||
cwd: Some(test_path.to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
// Verify that untrusted projects get UnlessTrusted approval policy
|
||||
@@ -6389,7 +6381,7 @@ discoverables = [
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -6428,7 +6420,7 @@ experimental_realtime_start_instructions = "start instructions from config"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -6456,7 +6448,7 @@ experimental_realtime_ws_base_url = "http://127.0.0.1:8011"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -6484,7 +6476,7 @@ experimental_realtime_ws_backend_prompt = "prompt from config"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -6512,7 +6504,7 @@ experimental_realtime_ws_startup_context = "startup context from config"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -6540,7 +6532,7 @@ experimental_realtime_ws_model = "realtime-test-model"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -6564,7 +6556,7 @@ voice = "marin"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -6604,7 +6596,7 @@ voice = "cedar"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -6641,7 +6633,7 @@ speaker = "Desk Speakers"
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
)?;
|
||||
|
||||
assert_eq!(config.realtime_audio.microphone.as_deref(), Some("USB Mic"));
|
||||
|
||||
@@ -174,7 +174,7 @@ pub(crate) fn test_config() -> Config {
|
||||
Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
AbsolutePathBuf::from_absolute_path(codex_home.path()).expect("temp dir should resolve"),
|
||||
)
|
||||
.expect("load default test config")
|
||||
}
|
||||
@@ -425,7 +425,7 @@ pub struct Config {
|
||||
|
||||
/// Directory containing all Codex state (defaults to `~/.codex` but can be
|
||||
/// overridden by the `CODEX_HOME` environment variable).
|
||||
pub codex_home: PathBuf,
|
||||
pub codex_home: AbsolutePathBuf,
|
||||
|
||||
/// Directory where Codex stores the SQLite state DB.
|
||||
pub sqlite_home: PathBuf,
|
||||
@@ -616,7 +616,7 @@ impl Default for MultiAgentV2Config {
|
||||
|
||||
impl AuthManagerConfig for Config {
|
||||
fn codex_home(&self) -> PathBuf {
|
||||
self.codex_home.clone()
|
||||
self.codex_home.to_path_buf()
|
||||
}
|
||||
|
||||
fn cli_auth_credentials_store_mode(&self) -> AuthCredentialsStoreMode {
|
||||
@@ -678,7 +678,10 @@ impl ConfigBuilder {
|
||||
cloud_requirements,
|
||||
fallback_cwd,
|
||||
} = self;
|
||||
let codex_home = codex_home.map_or_else(find_codex_home, std::io::Result::Ok)?;
|
||||
let codex_home = match codex_home {
|
||||
Some(codex_home) => AbsolutePathBuf::from_absolute_path(codex_home)?,
|
||||
None => find_codex_home()?,
|
||||
};
|
||||
let cli_overrides = cli_overrides.unwrap_or_default();
|
||||
let mut harness_overrides = harness_overrides.unwrap_or_default();
|
||||
let loader_overrides = loader_overrides.unwrap_or_default();
|
||||
@@ -753,7 +756,7 @@ impl Config {
|
||||
|
||||
McpConfig {
|
||||
chatgpt_base_url: self.chatgpt_base_url.clone(),
|
||||
codex_home: self.codex_home.clone(),
|
||||
codex_home: self.codex_home.to_path_buf(),
|
||||
mcp_oauth_credentials_store_mode: self.mcp_oauth_credentials_store_mode,
|
||||
mcp_oauth_callback_port: self.mcp_oauth_callback_port,
|
||||
mcp_oauth_callback_url: self.mcp_oauth_callback_url.clone(),
|
||||
@@ -784,7 +787,10 @@ impl Config {
|
||||
cli_overrides: Vec<(String, TomlValue)>,
|
||||
) -> std::io::Result<Self> {
|
||||
let codex_home = find_codex_home()?;
|
||||
Self::load_default_with_cli_overrides_for_codex_home(codex_home, cli_overrides)
|
||||
Self::load_default_with_cli_overrides_for_codex_home(
|
||||
codex_home.to_path_buf(),
|
||||
cli_overrides,
|
||||
)
|
||||
}
|
||||
|
||||
/// Load a default configuration for a specific Codex home without reading
|
||||
@@ -801,6 +807,7 @@ impl Config {
|
||||
})?;
|
||||
let cli_layer = crate::config_loader::build_cli_overrides_layer(&cli_overrides);
|
||||
crate::config_loader::merge_toml_values(&mut merged, &cli_layer);
|
||||
let codex_home = AbsolutePathBuf::from_absolute_path_checked(codex_home)?;
|
||||
let config_toml = deserialize_config_toml_with_base(merged, &codex_home)?;
|
||||
Self::load_config_with_layer_stack(
|
||||
config_toml,
|
||||
@@ -1406,7 +1413,7 @@ impl Config {
|
||||
fn load_from_base_config_with_overrides(
|
||||
cfg: ConfigToml,
|
||||
overrides: ConfigOverrides,
|
||||
codex_home: PathBuf,
|
||||
codex_home: AbsolutePathBuf,
|
||||
) -> std::io::Result<Self> {
|
||||
// Note this ignores requirements.toml enforcement for tests.
|
||||
let config_layer_stack = ConfigLayerStack::default();
|
||||
@@ -1416,7 +1423,7 @@ impl Config {
|
||||
pub(crate) fn load_config_with_layer_stack(
|
||||
cfg: ConfigToml,
|
||||
overrides: ConfigOverrides,
|
||||
codex_home: PathBuf,
|
||||
codex_home: AbsolutePathBuf,
|
||||
config_layer_stack: ConfigLayerStack,
|
||||
) -> std::io::Result<Self> {
|
||||
validate_model_providers(&cfg.model_providers)
|
||||
@@ -1916,11 +1923,7 @@ impl Config {
|
||||
.log_dir
|
||||
.as_ref()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.unwrap_or_else(|| {
|
||||
let mut p = codex_home.clone();
|
||||
p.push("log");
|
||||
p
|
||||
});
|
||||
.unwrap_or_else(|| codex_home.join("log").to_path_buf());
|
||||
let sqlite_home = cfg
|
||||
.sqlite_home
|
||||
.as_ref()
|
||||
@@ -2338,7 +2341,7 @@ fn toml_uses_deprecated_instructions_file(value: &TomlValue) -> bool {
|
||||
/// value will be canonicalized and this function will Err otherwise.
|
||||
/// - If `CODEX_HOME` is not set, this function does not verify that the
|
||||
/// directory exists.
|
||||
pub fn find_codex_home() -> std::io::Result<PathBuf> {
|
||||
pub fn find_codex_home() -> std::io::Result<AbsolutePathBuf> {
|
||||
codex_utils_home_dir::find_codex_home()
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ fn restricted_read_implicitly_allows_helper_executables() -> std::io::Result<()>
|
||||
main_execve_wrapper_exe: Some(execve_wrapper),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home,
|
||||
AbsolutePathBuf::from_absolute_path(&codex_home)?,
|
||||
)?;
|
||||
|
||||
let expected_zsh = AbsolutePathBuf::try_from(zsh_path)?;
|
||||
|
||||
@@ -199,7 +199,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status(
|
||||
});
|
||||
}
|
||||
let cache_key = accessible_connectors_cache_key(config, auth.as_ref());
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone()));
|
||||
let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.to_path_buf()));
|
||||
let mcp_manager = McpManager::new(Arc::clone(&plugins_manager));
|
||||
let tool_plugin_provenance = mcp_manager.tool_plugin_provenance(config);
|
||||
if !force_refetch && let Some(cached_connectors) = read_cached_accessible_connectors(&cache_key)
|
||||
@@ -242,7 +242,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status(
|
||||
INITIAL_SUBMIT_ID.to_owned(),
|
||||
tx_event,
|
||||
sandbox_state,
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
codex_apps_tools_cache_key(auth.as_ref()),
|
||||
ToolPluginProvenance::default(),
|
||||
)
|
||||
@@ -396,7 +396,7 @@ fn filter_tool_suggest_discoverable_connectors(
|
||||
}
|
||||
|
||||
fn tool_suggest_connector_ids(config: &Config) -> HashSet<String> {
|
||||
let mut connector_ids = PluginsManager::new(config.codex_home.clone())
|
||||
let mut connector_ids = PluginsManager::new(config.codex_home.to_path_buf())
|
||||
.plugins_for_config(config)
|
||||
.capability_summaries()
|
||||
.iter()
|
||||
|
||||
@@ -45,6 +45,7 @@ use core_test_support::responses::start_mock_server;
|
||||
use core_test_support::skip_if_no_network;
|
||||
use core_test_support::streaming_sse::StreamingSseChunk;
|
||||
use core_test_support::streaming_sse::start_streaming_sse_server;
|
||||
use core_test_support::test_path_buf;
|
||||
use insta::Settings;
|
||||
use insta::assert_snapshot;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -76,7 +77,7 @@ async fn guardian_test_session_and_turn_with_base_url(
|
||||
config.user_instructions = None;
|
||||
let config = Arc::new(config);
|
||||
let models_manager = Arc::new(test_support::models_manager_with_provider(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::clone(&session.services.auth_manager),
|
||||
config.model_provider.clone(),
|
||||
));
|
||||
@@ -621,13 +622,8 @@ fn guardian_approval_request_to_json_renders_mcp_tool_call_shape() -> serde_json
|
||||
|
||||
#[test]
|
||||
fn guardian_assessment_action_redacts_apply_patch_patch_text() {
|
||||
let (cwd, file) = if cfg!(windows) {
|
||||
(r"C:\tmp", r"C:\tmp\guardian.txt")
|
||||
} else {
|
||||
("/tmp", "/tmp/guardian.txt")
|
||||
};
|
||||
let cwd = PathBuf::from(cwd);
|
||||
let file = PathBuf::from(file).abs();
|
||||
let cwd = test_path_buf("/tmp");
|
||||
let file = test_path_buf("/tmp/guardian.txt").abs();
|
||||
let action = GuardianApprovalRequest::ApplyPatch {
|
||||
id: "patch-1".to_string(),
|
||||
cwd: cwd.clone(),
|
||||
@@ -658,8 +654,8 @@ fn guardian_request_turn_id_prefers_network_access_owner_turn() {
|
||||
};
|
||||
let apply_patch = GuardianApprovalRequest::ApplyPatch {
|
||||
id: "patch-1".to_string(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
files: vec![PathBuf::from("/tmp/guardian.txt").abs()],
|
||||
cwd: test_path_buf("/tmp"),
|
||||
files: vec![test_path_buf("/tmp/guardian.txt").abs()],
|
||||
patch: "*** Begin Patch\n*** Update File: guardian.txt\n@@\n+hello\n*** End Patch"
|
||||
.to_string(),
|
||||
};
|
||||
@@ -686,8 +682,8 @@ async fn cancelled_guardian_review_emits_terminal_abort_without_warning() {
|
||||
"review-cancelled-guardian".to_string(),
|
||||
GuardianApprovalRequest::ApplyPatch {
|
||||
id: "patch-1".to_string(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
files: vec![PathBuf::from("/tmp/guardian.txt").abs()],
|
||||
cwd: test_path_buf("/tmp"),
|
||||
files: vec![test_path_buf("/tmp/guardian.txt").abs()],
|
||||
patch: "*** Begin Patch\n*** Update File: guardian.txt\n@@\n+hello\n*** End Patch"
|
||||
.to_string(),
|
||||
},
|
||||
@@ -873,7 +869,7 @@ async fn guardian_review_request_layout_matches_model_visible_request_snapshot()
|
||||
config.model_provider.base_url = Some(format!("{}/v1", server.uri()));
|
||||
let config = Arc::new(config);
|
||||
let models_manager = Arc::new(test_support::models_manager_with_provider(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::clone(&session.services.auth_manager),
|
||||
config.model_provider.clone(),
|
||||
));
|
||||
@@ -1239,7 +1235,7 @@ async fn guardian_review_surfaces_responses_api_errors_in_rejection_reason() ->
|
||||
config.user_instructions = None;
|
||||
let config = Arc::new(config);
|
||||
let models_manager = Arc::new(test_support::models_manager_with_provider(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::clone(&session.services.auth_manager),
|
||||
config.model_provider.clone(),
|
||||
));
|
||||
@@ -1714,7 +1710,7 @@ fn guardian_review_session_config_uses_requirements_guardian_policy_config() {
|
||||
cwd: Some(workspace.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
config_layer_stack,
|
||||
)
|
||||
.expect("load config");
|
||||
@@ -1748,7 +1744,7 @@ fn guardian_review_session_config_uses_default_guardian_policy_without_requireme
|
||||
cwd: Some(workspace.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.path().to_path_buf(),
|
||||
codex_home.abs(),
|
||||
config_layer_stack,
|
||||
)
|
||||
.expect("load config");
|
||||
|
||||
@@ -1545,7 +1545,7 @@ async fn persist_custom_mcp_tool_approval(
|
||||
if !servers.contains_key(server) {
|
||||
anyhow::bail!("MCP server `{server}` is not configured in config.toml");
|
||||
}
|
||||
config.codex_home.clone()
|
||||
config.codex_home.to_path_buf()
|
||||
};
|
||||
|
||||
ConfigEditsBuilder::new(&config_folder)
|
||||
|
||||
@@ -1313,7 +1313,7 @@ async fn guardian_mode_skips_auto_when_annotations_do_not_require_approval() {
|
||||
config.approvals_reviewer = ApprovalsReviewer::GuardianSubagent;
|
||||
let config = Arc::new(config);
|
||||
let models_manager = Arc::new(crate::test_support::models_manager_with_provider(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::clone(&session.services.auth_manager),
|
||||
config.model_provider.clone(),
|
||||
));
|
||||
@@ -1388,7 +1388,7 @@ async fn guardian_mode_mcp_denial_returns_rationale_message() {
|
||||
config.approvals_reviewer = ApprovalsReviewer::GuardianSubagent;
|
||||
let config = Arc::new(config);
|
||||
let models_manager = Arc::new(crate::test_support::models_manager_with_provider(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::clone(&session.services.auth_manager),
|
||||
config.model_provider.clone(),
|
||||
));
|
||||
@@ -1836,7 +1836,7 @@ async fn approve_mode_routes_arc_ask_user_to_guardian_when_guardian_reviewer_is_
|
||||
config.approvals_reviewer = ApprovalsReviewer::GuardianSubagent;
|
||||
let config = Arc::new(config);
|
||||
let models_manager = Arc::new(crate::test_support::models_manager_with_provider(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::clone(&session.services.auth_manager),
|
||||
config.model_provider.clone(),
|
||||
));
|
||||
|
||||
@@ -435,7 +435,6 @@ mod phase2 {
|
||||
use codex_state::Phase2JobClaimOutcome;
|
||||
use codex_state::Stage1Output;
|
||||
use codex_state::ThreadMetadataBuilder;
|
||||
use core_test_support::PathBufExt;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -469,12 +468,14 @@ mod phase2 {
|
||||
async fn new() -> Self {
|
||||
let codex_home = tempfile::tempdir().expect("create temp codex home");
|
||||
let mut config = test_config();
|
||||
config.codex_home = codex_home.path().to_path_buf();
|
||||
config.cwd = config.codex_home.abs();
|
||||
config.codex_home =
|
||||
codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(codex_home.path())
|
||||
.expect("codex home is absolute");
|
||||
config.cwd = config.codex_home.clone();
|
||||
let config = Arc::new(config);
|
||||
|
||||
let state_db = codex_state::StateRuntime::init(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
config.model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
@@ -483,7 +484,7 @@ mod phase2 {
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
std::sync::Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
@@ -507,7 +508,8 @@ mod phase2 {
|
||||
thread_id,
|
||||
self.config
|
||||
.codex_home
|
||||
.join(format!("rollout-{thread_id}.jsonl")),
|
||||
.join(format!("rollout-{thread_id}.jsonl"))
|
||||
.to_path_buf(),
|
||||
Utc::now(),
|
||||
SessionSource::Cli,
|
||||
);
|
||||
@@ -890,12 +892,14 @@ mod phase2 {
|
||||
async fn dispatch_marks_job_for_retry_when_spawn_agent_fails() {
|
||||
let codex_home = tempfile::tempdir().expect("create temp codex home");
|
||||
let mut config = test_config();
|
||||
config.codex_home = codex_home.path().to_path_buf();
|
||||
config.cwd = config.codex_home.abs();
|
||||
config.codex_home =
|
||||
codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(codex_home.path())
|
||||
.expect("codex home is absolute");
|
||||
config.cwd = config.codex_home.clone();
|
||||
let config = Arc::new(config);
|
||||
|
||||
let state_db = codex_state::StateRuntime::init(
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
config.model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
@@ -909,7 +913,10 @@ mod phase2 {
|
||||
let thread_id = ThreadId::new();
|
||||
let mut metadata_builder = ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
config.codex_home.join(format!("rollout-{thread_id}.jsonl")),
|
||||
config
|
||||
.codex_home
|
||||
.join(format!("rollout-{thread_id}.jsonl"))
|
||||
.to_path_buf(),
|
||||
Utc::now(),
|
||||
SessionSource::Cli,
|
||||
);
|
||||
|
||||
@@ -61,9 +61,7 @@ pub struct HistoryEntry {
|
||||
}
|
||||
|
||||
fn history_filepath(config: &Config) -> PathBuf {
|
||||
let mut path = config.codex_home.clone();
|
||||
path.push(HISTORY_FILENAME);
|
||||
path
|
||||
config.codex_home.join(HISTORY_FILENAME).to_path_buf()
|
||||
}
|
||||
|
||||
/// Append a `text` entry associated with `conversation_id` to the history file.
|
||||
|
||||
@@ -83,7 +83,7 @@ pub fn build_provider(
|
||||
OtelProvider::from(&OtelSettings {
|
||||
service_name: service_name.to_string(),
|
||||
service_version: service_version.to_string(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
environment: config.otel.environment.to_string(),
|
||||
exporter,
|
||||
trace_exporter,
|
||||
|
||||
@@ -29,7 +29,7 @@ pub(crate) fn list_tool_suggest_discoverable_plugins(
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let plugins_manager = PluginsManager::new(config.codex_home.clone());
|
||||
let plugins_manager = PluginsManager::new(config.codex_home.to_path_buf());
|
||||
let configured_plugin_ids = config
|
||||
.tool_suggest
|
||||
.discoverables
|
||||
|
||||
@@ -168,7 +168,7 @@ pub struct PluginDetail {
|
||||
pub installed: bool,
|
||||
pub enabled: bool,
|
||||
pub skills: Vec<SkillMetadata>,
|
||||
pub disabled_skill_paths: HashSet<PathBuf>,
|
||||
pub disabled_skill_paths: HashSet<AbsolutePathBuf>,
|
||||
pub apps: Vec<AppConnectorId>,
|
||||
pub mcp_server_names: Vec<String>,
|
||||
}
|
||||
@@ -423,7 +423,7 @@ impl PluginsManager {
|
||||
&self,
|
||||
config_layer_stack: &ConfigLayerStack,
|
||||
plugins_feature_enabled: bool,
|
||||
) -> Vec<PathBuf> {
|
||||
) -> Vec<AbsolutePathBuf> {
|
||||
if !plugins_feature_enabled {
|
||||
return Vec::new();
|
||||
}
|
||||
@@ -587,7 +587,7 @@ impl PluginsManager {
|
||||
if let Some(analytics_events_client) = analytics_events_client {
|
||||
analytics_events_client.track_plugin_installed(plugin_telemetry_metadata_from_root(
|
||||
&result.plugin_id,
|
||||
result.installed_path.as_path(),
|
||||
&result.installed_path,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -983,7 +983,7 @@ impl PluginsManager {
|
||||
let manifest_paths = &manifest.paths;
|
||||
let skill_config_rules = skill_config_rules_from_stack(&config.config_layer_stack);
|
||||
let resolved_skills = load_plugin_skills(
|
||||
source_path.as_path(),
|
||||
&source_path,
|
||||
manifest_paths,
|
||||
self.restriction_product,
|
||||
&skill_config_rules,
|
||||
@@ -1061,7 +1061,7 @@ impl PluginsManager {
|
||||
roots: &[AbsolutePathBuf],
|
||||
) {
|
||||
let mut roots = roots.to_vec();
|
||||
roots.sort_unstable_by(|left, right| left.as_path().cmp(right.as_path()));
|
||||
roots.sort_unstable();
|
||||
roots.dedup();
|
||||
if roots.is_empty() {
|
||||
return;
|
||||
@@ -1238,7 +1238,7 @@ impl PluginsManager {
|
||||
{
|
||||
roots.push(curated_repo_root);
|
||||
}
|
||||
roots.sort_unstable_by(|left, right| left.as_path().cmp(right.as_path()));
|
||||
roots.sort_unstable();
|
||||
roots.dedup();
|
||||
roots
|
||||
}
|
||||
@@ -1703,9 +1703,9 @@ fn load_plugin(
|
||||
.map(str::to_string)
|
||||
.or_else(|| Some(manifest.name.clone()));
|
||||
loaded_plugin.manifest_description = manifest.description.clone();
|
||||
loaded_plugin.skill_roots = plugin_skill_roots(plugin_root.as_path(), manifest_paths);
|
||||
loaded_plugin.skill_roots = plugin_skill_roots(&plugin_root, manifest_paths);
|
||||
let resolved_skills = load_plugin_skills(
|
||||
plugin_root.as_path(),
|
||||
&plugin_root,
|
||||
manifest_paths,
|
||||
restriction_product,
|
||||
skill_config_rules,
|
||||
@@ -1734,7 +1734,7 @@ fn load_plugin(
|
||||
|
||||
struct ResolvedPluginSkills {
|
||||
skills: Vec<SkillMetadata>,
|
||||
disabled_skill_paths: HashSet<PathBuf>,
|
||||
disabled_skill_paths: HashSet<AbsolutePathBuf>,
|
||||
had_errors: bool,
|
||||
}
|
||||
|
||||
@@ -1750,7 +1750,7 @@ impl ResolvedPluginSkills {
|
||||
}
|
||||
|
||||
fn load_plugin_skills(
|
||||
plugin_root: &Path,
|
||||
plugin_root: &AbsolutePathBuf,
|
||||
manifest_paths: &PluginManifestPaths,
|
||||
restriction_product: Option<Product>,
|
||||
skill_config_rules: &SkillConfigRules,
|
||||
@@ -1778,17 +1778,20 @@ fn load_plugin_skills(
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_skill_roots(plugin_root: &Path, manifest_paths: &PluginManifestPaths) -> Vec<PathBuf> {
|
||||
fn plugin_skill_roots(
|
||||
plugin_root: &AbsolutePathBuf,
|
||||
manifest_paths: &PluginManifestPaths,
|
||||
) -> Vec<AbsolutePathBuf> {
|
||||
let mut paths = default_skill_roots(plugin_root);
|
||||
if let Some(path) = &manifest_paths.skills {
|
||||
paths.push(path.to_path_buf());
|
||||
paths.push(path.clone());
|
||||
}
|
||||
paths.sort_unstable();
|
||||
paths.dedup();
|
||||
paths
|
||||
}
|
||||
|
||||
fn default_skill_roots(plugin_root: &Path) -> Vec<PathBuf> {
|
||||
fn default_skill_roots(plugin_root: &AbsolutePathBuf) -> Vec<AbsolutePathBuf> {
|
||||
let skills_dir = plugin_root.join(DEFAULT_SKILLS_DIR_NAME);
|
||||
if skills_dir.is_dir() {
|
||||
vec![skills_dir]
|
||||
@@ -1815,8 +1818,8 @@ fn default_mcp_config_paths(plugin_root: &Path) -> Vec<AbsolutePathBuf> {
|
||||
{
|
||||
paths.push(default_path);
|
||||
}
|
||||
paths.sort_unstable_by(|left, right| left.as_path().cmp(right.as_path()));
|
||||
paths.dedup_by(|left, right| left.as_path() == right.as_path());
|
||||
paths.sort_unstable();
|
||||
paths.dedup();
|
||||
paths
|
||||
}
|
||||
|
||||
@@ -1848,8 +1851,8 @@ fn default_app_config_paths(plugin_root: &Path) -> Vec<AbsolutePathBuf> {
|
||||
{
|
||||
paths.push(default_path);
|
||||
}
|
||||
paths.sort_unstable_by(|left, right| left.as_path().cmp(right.as_path()));
|
||||
paths.dedup_by(|left, right| left.as_path() == right.as_path());
|
||||
paths.sort_unstable();
|
||||
paths.dedup();
|
||||
paths
|
||||
}
|
||||
|
||||
@@ -1894,18 +1897,18 @@ fn load_apps_from_paths(
|
||||
|
||||
pub fn plugin_telemetry_metadata_from_root(
|
||||
plugin_id: &PluginId,
|
||||
plugin_root: &Path,
|
||||
plugin_root: &AbsolutePathBuf,
|
||||
) -> PluginTelemetryMetadata {
|
||||
let Some(manifest) = load_plugin_manifest(plugin_root) else {
|
||||
let Some(manifest) = load_plugin_manifest(plugin_root.as_path()) else {
|
||||
return PluginTelemetryMetadata::from_plugin_id(plugin_id);
|
||||
};
|
||||
|
||||
let manifest_paths = &manifest.paths;
|
||||
let has_skills = !plugin_skill_roots(plugin_root, manifest_paths).is_empty();
|
||||
let mut mcp_server_names = Vec::new();
|
||||
for path in plugin_mcp_config_paths(plugin_root, manifest_paths) {
|
||||
for path in plugin_mcp_config_paths(plugin_root.as_path(), manifest_paths) {
|
||||
mcp_server_names.extend(
|
||||
load_mcp_servers_from_file(plugin_root, &path)
|
||||
load_mcp_servers_from_file(plugin_root.as_path(), &path)
|
||||
.mcp_servers
|
||||
.into_keys(),
|
||||
);
|
||||
@@ -1921,7 +1924,7 @@ pub fn plugin_telemetry_metadata_from_root(
|
||||
description: None,
|
||||
has_skills,
|
||||
mcp_server_names,
|
||||
app_connector_ids: load_plugin_apps(plugin_root),
|
||||
app_connector_ids: load_plugin_apps(plugin_root.as_path()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -1951,7 +1954,7 @@ pub fn installed_plugin_telemetry_metadata(
|
||||
return PluginTelemetryMetadata::from_plugin_id(plugin_id);
|
||||
};
|
||||
|
||||
plugin_telemetry_metadata_from_root(plugin_id, plugin_root.as_path())
|
||||
plugin_telemetry_metadata_from_root(plugin_id, &plugin_root)
|
||||
}
|
||||
|
||||
fn load_mcp_servers_from_file(
|
||||
|
||||
@@ -17,6 +17,7 @@ use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_config::types::McpServerTransportConfig;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_protocol::protocol::Product;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fs;
|
||||
use tempfile::TempDir;
|
||||
@@ -164,7 +165,7 @@ fn load_plugins_loads_default_skills_and_mcp_servers() {
|
||||
),
|
||||
root: AbsolutePathBuf::try_from(plugin_root.clone()).unwrap(),
|
||||
enabled: true,
|
||||
skill_roots: vec![plugin_root.join("skills")],
|
||||
skill_roots: vec![plugin_root.join("skills").abs()],
|
||||
disabled_skill_paths: HashSet::new(),
|
||||
has_enabled_skills: true,
|
||||
mcp_servers: HashMap::from([(
|
||||
@@ -205,7 +206,7 @@ fn load_plugins_loads_default_skills_and_mcp_servers() {
|
||||
);
|
||||
assert_eq!(
|
||||
outcome.effective_skill_roots(),
|
||||
vec![plugin_root.join("skills")]
|
||||
vec![plugin_root.join("skills").abs()]
|
||||
);
|
||||
assert_eq!(outcome.effective_mcp_servers().len(), 1);
|
||||
assert_eq!(
|
||||
@@ -243,7 +244,9 @@ enabled = false
|
||||
enabled = true
|
||||
"#;
|
||||
let outcome = load_plugins_from_config(config_toml, codex_home.path());
|
||||
let skill_path = dunce::canonicalize(skill_path).expect("skill path should canonicalize");
|
||||
let skill_path = dunce::canonicalize(skill_path)
|
||||
.expect("skill path should canonicalize")
|
||||
.abs();
|
||||
|
||||
assert_eq!(
|
||||
outcome.plugins()[0].disabled_skill_paths,
|
||||
@@ -325,7 +328,7 @@ fn plugin_telemetry_metadata_uses_default_mcp_config_path() {
|
||||
|
||||
let metadata = plugin_telemetry_metadata_from_root(
|
||||
&PluginId::parse("sample@test").expect("plugin id should parse"),
|
||||
&plugin_root,
|
||||
&plugin_root.abs(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -490,8 +493,8 @@ fn load_plugins_uses_manifest_configured_component_paths() {
|
||||
assert_eq!(
|
||||
outcome.plugins()[0].skill_roots,
|
||||
vec![
|
||||
plugin_root.join("custom-skills"),
|
||||
plugin_root.join("skills")
|
||||
plugin_root.join("custom-skills").abs(),
|
||||
plugin_root.join("skills").abs()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -599,7 +602,7 @@ fn load_plugins_ignores_manifest_component_paths_without_dot_slash() {
|
||||
|
||||
assert_eq!(
|
||||
outcome.plugins()[0].skill_roots,
|
||||
vec![plugin_root.join("skills")]
|
||||
vec![plugin_root.join("skills").abs()]
|
||||
);
|
||||
assert_eq!(
|
||||
outcome.plugins()[0].mcp_servers,
|
||||
@@ -799,7 +802,7 @@ fn capability_index_filters_inactive_and_zero_capability_plugins() {
|
||||
};
|
||||
let outcome = PluginLoadOutcome::from_plugins(vec![
|
||||
LoadedPlugin {
|
||||
skill_roots: vec![codex_home.path().join("skills-plugin/skills")],
|
||||
skill_roots: vec![codex_home.path().join("skills-plugin/skills").abs()],
|
||||
has_enabled_skills: true,
|
||||
..plugin("skills@test", "skills-plugin", "skills-plugin")
|
||||
},
|
||||
@@ -816,7 +819,7 @@ fn capability_index_filters_inactive_and_zero_capability_plugins() {
|
||||
plugin("empty@test", "empty-plugin", "empty-plugin"),
|
||||
LoadedPlugin {
|
||||
enabled: false,
|
||||
skill_roots: vec![codex_home.path().join("disabled-plugin/skills")],
|
||||
skill_roots: vec![codex_home.path().join("disabled-plugin/skills").abs()],
|
||||
apps: vec![connector("connector_hidden")],
|
||||
..plugin("disabled@test", "disabled-plugin", "disabled-plugin")
|
||||
},
|
||||
|
||||
@@ -248,14 +248,14 @@ pub async fn discover_project_doc_paths(
|
||||
if !project_root_markers.is_empty() {
|
||||
for ancestor in dir.ancestors() {
|
||||
for marker in &project_root_markers {
|
||||
let marker_path = AbsolutePathBuf::try_from(ancestor.join(marker))?;
|
||||
let marker_path = ancestor.join(marker);
|
||||
let marker_exists = match fs.get_metadata(&marker_path, /*sandbox*/ None).await {
|
||||
Ok(_) => true,
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => false,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
if marker_exists {
|
||||
project_root = Some(AbsolutePathBuf::try_from(ancestor.to_path_buf())?);
|
||||
project_root = Some(ancestor.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ async fn skills_are_not_appended_to_project_doc() {
|
||||
|
||||
let cfg = make_config(&tmp, /*limit*/ 4096, /*instructions*/ None).await;
|
||||
create_skill(
|
||||
cfg.codex_home.clone(),
|
||||
cfg.codex_home.to_path_buf(),
|
||||
"pdf-processing",
|
||||
"extract from pdfs",
|
||||
);
|
||||
|
||||
@@ -107,7 +107,8 @@ mod tests {
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let cwd = tempfile::tempdir().expect("create cwd");
|
||||
let mut config = test_config();
|
||||
config.codex_home = codex_home.path().to_path_buf();
|
||||
config.codex_home =
|
||||
AbsolutePathBuf::from_absolute_path(codex_home.path()).expect("codex home is absolute");
|
||||
config.cwd = AbsolutePathBuf::try_from(cwd.path().to_path_buf()).expect("absolute cwd");
|
||||
config.user_instructions = Some("Project-specific test instructions".to_string());
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::codex::Session;
|
||||
@@ -15,6 +13,7 @@ use codex_protocol::protocol::SkillScope;
|
||||
use codex_protocol::request_user_input::RequestUserInputArgs;
|
||||
use codex_protocol::request_user_input::RequestUserInputQuestion;
|
||||
use codex_protocol::request_user_input::RequestUserInputResponse;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use tracing::warn;
|
||||
|
||||
pub use codex_core_skills::SkillDependencyInfo;
|
||||
@@ -43,10 +42,10 @@ pub use codex_core_skills::system;
|
||||
|
||||
pub(crate) fn skills_load_input_from_config(
|
||||
config: &Config,
|
||||
effective_skill_roots: Vec<PathBuf>,
|
||||
effective_skill_roots: Vec<AbsolutePathBuf>,
|
||||
) -> SkillsLoadInput {
|
||||
SkillsLoadInput::new(
|
||||
config.cwd.clone().to_path_buf(),
|
||||
config.cwd.clone(),
|
||||
effective_skill_roots,
|
||||
config.config_layer_stack.clone(),
|
||||
config.bundled_skills_enabled(),
|
||||
@@ -172,7 +171,7 @@ pub(crate) async fn maybe_emit_implicit_skill_invocation(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
command: &str,
|
||||
workdir: &Path,
|
||||
workdir: &AbsolutePathBuf,
|
||||
) {
|
||||
let Some(candidate) = detect_implicit_skill_invocation_for_command(
|
||||
turn_context.turn_skills.outcome.as_ref(),
|
||||
@@ -184,7 +183,7 @@ pub(crate) async fn maybe_emit_implicit_skill_invocation(
|
||||
let invocation = SkillInvocation {
|
||||
skill_name: candidate.name,
|
||||
skill_scope: candidate.scope,
|
||||
skill_path: candidate.path_to_skills_md,
|
||||
skill_path: candidate.path_to_skills_md.to_path_buf(),
|
||||
invocation_type: InvocationType::Implicit,
|
||||
};
|
||||
let skill_scope = match invocation.skill_scope {
|
||||
|
||||
@@ -67,7 +67,7 @@ impl SkillsWatcher {
|
||||
.skill_roots_for_config(&skills_input)
|
||||
.into_iter()
|
||||
.map(|root| WatchPath {
|
||||
path: root.path,
|
||||
path: root.path.into_path_buf(),
|
||||
recursive: true,
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -45,6 +45,7 @@ use codex_protocol::protocol::TurnAbortReason;
|
||||
use codex_protocol::protocol::TurnAbortedEvent;
|
||||
use codex_protocol::protocol::W3cTraceContext;
|
||||
use codex_state::DirectionalThreadSpawnEdgeStatus;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use futures::StreamExt;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use std::collections::HashMap;
|
||||
@@ -234,7 +235,7 @@ impl ThreadManager {
|
||||
.unwrap_or_else(|| ModelProviderInfo::create_openai_provider(/*base_url*/ None));
|
||||
let (thread_created_tx, _) = broadcast::channel(THREAD_CREATED_CHANNEL_CAPACITY);
|
||||
let plugins_manager = Arc::new(PluginsManager::new_with_restriction_product(
|
||||
codex_home.clone(),
|
||||
codex_home.to_path_buf(),
|
||||
restriction_product,
|
||||
));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
@@ -249,7 +250,7 @@ impl ThreadManager {
|
||||
threads: Arc::new(RwLock::new(HashMap::new())),
|
||||
thread_created_tx,
|
||||
models_manager: Arc::new(ModelsManager::new_with_provider(
|
||||
codex_home,
|
||||
codex_home.to_path_buf(),
|
||||
auth_manager.clone(),
|
||||
config.model_catalog.clone(),
|
||||
collaboration_modes_config,
|
||||
@@ -303,6 +304,10 @@ impl ThreadManager {
|
||||
) -> Self {
|
||||
set_thread_manager_test_mode_for_tests(/*enabled*/ true);
|
||||
let auth_manager = AuthManager::from_auth_for_testing(auth);
|
||||
let skills_codex_home = match AbsolutePathBuf::from_absolute_path_checked(&codex_home) {
|
||||
Ok(codex_home) => codex_home,
|
||||
Err(err) => panic!("test codex_home should be absolute: {err}"),
|
||||
};
|
||||
let (thread_created_tx, _) = broadcast::channel(THREAD_CREATED_CHANNEL_CAPACITY);
|
||||
let restriction_product = SessionSource::Exec.restriction_product();
|
||||
let plugins_manager = Arc::new(PluginsManager::new_with_restriction_product(
|
||||
@@ -311,7 +316,7 @@ impl ThreadManager {
|
||||
));
|
||||
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
|
||||
let skills_manager = Arc::new(SkillsManager::new_with_restriction_product(
|
||||
codex_home.clone(),
|
||||
skills_codex_home,
|
||||
/*bundled_skills_enabled*/ true,
|
||||
restriction_product,
|
||||
));
|
||||
|
||||
@@ -12,6 +12,7 @@ use codex_protocol::openai_models::ModelsResponse;
|
||||
use codex_protocol::protocol::AgentMessageEvent;
|
||||
use codex_protocol::protocol::TurnStartedEvent;
|
||||
use codex_protocol::protocol::UserMessageEvent;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::PathExt;
|
||||
use core_test_support::responses::mount_models_once;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -237,14 +238,14 @@ async fn ignores_session_prefix_messages_when_truncating() {
|
||||
async fn shutdown_all_threads_bounded_submits_shutdown_to_every_thread() {
|
||||
let temp_dir = tempdir().expect("tempdir");
|
||||
let mut config = test_config();
|
||||
config.codex_home = temp_dir.path().join("codex-home");
|
||||
config.codex_home = temp_dir.path().join("codex-home").abs();
|
||||
config.cwd = config.codex_home.abs();
|
||||
std::fs::create_dir_all(&config.codex_home).expect("create codex home");
|
||||
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::new(codex_exec_server::EnvironmentManager::new(
|
||||
/*exec_server_url*/ None,
|
||||
)),
|
||||
@@ -279,7 +280,7 @@ async fn new_uses_configured_openai_provider_for_model_refresh() {
|
||||
|
||||
let temp_dir = tempdir().expect("tempdir");
|
||||
let mut config = test_config();
|
||||
config.codex_home = temp_dir.path().join("codex-home");
|
||||
config.codex_home = temp_dir.path().join("codex-home").abs();
|
||||
config.cwd = config.codex_home.abs();
|
||||
std::fs::create_dir_all(&config.codex_home).expect("create codex home");
|
||||
config.model_catalog = None;
|
||||
@@ -422,7 +423,7 @@ fn mixed_response_and_legacy_user_event_history_is_mid_turn() {
|
||||
async fn interrupted_fork_snapshot_does_not_synthesize_turn_id_for_legacy_history() {
|
||||
let temp_dir = tempdir().expect("tempdir");
|
||||
let mut config = test_config();
|
||||
config.codex_home = temp_dir.path().join("codex-home");
|
||||
config.codex_home = temp_dir.path().join("codex-home").abs();
|
||||
config.cwd = config.codex_home.abs();
|
||||
std::fs::create_dir_all(&config.codex_home).expect("create codex home");
|
||||
|
||||
@@ -525,7 +526,7 @@ async fn interrupted_fork_snapshot_does_not_synthesize_turn_id_for_legacy_histor
|
||||
async fn interrupted_fork_snapshot_preserves_explicit_turn_id() {
|
||||
let temp_dir = tempdir().expect("tempdir");
|
||||
let mut config = test_config();
|
||||
config.codex_home = temp_dir.path().join("codex-home");
|
||||
config.codex_home = temp_dir.path().join("codex-home").abs();
|
||||
config.cwd = config.codex_home.abs();
|
||||
std::fs::create_dir_all(&config.codex_home).expect("create codex home");
|
||||
|
||||
@@ -618,7 +619,7 @@ async fn interrupted_fork_snapshot_preserves_explicit_turn_id() {
|
||||
async fn interrupted_fork_snapshot_uses_persisted_mid_turn_history_without_live_source() {
|
||||
let temp_dir = tempdir().expect("tempdir");
|
||||
let mut config = test_config();
|
||||
config.codex_home = temp_dir.path().join("codex-home");
|
||||
config.codex_home = temp_dir.path().join("codex-home").abs();
|
||||
config.cwd = config.codex_home.abs();
|
||||
std::fs::create_dir_all(&config.codex_home).expect("create codex home");
|
||||
|
||||
|
||||
@@ -96,7 +96,11 @@ async fn install_role_with_model_override(turn: &mut TurnContext) -> String {
|
||||
tokio::fs::create_dir_all(&turn.config.codex_home)
|
||||
.await
|
||||
.expect("codex home should be created");
|
||||
let role_config_path = turn.config.codex_home.join("fork-context-role.toml");
|
||||
let role_config_path = turn
|
||||
.config
|
||||
.codex_home
|
||||
.as_path()
|
||||
.join("fork-context-role.toml");
|
||||
tokio::fs::write(
|
||||
&role_config_path,
|
||||
r#"model = "gpt-5-role-override"
|
||||
|
||||
Reference in New Issue
Block a user