feat: drop dependency to write path

This commit is contained in:
jif-oai
2026-05-26 11:38:16 +01:00
parent 3936ed221d
commit 29786ec28f
4 changed files with 48 additions and 105 deletions
+10 -27
View File
@@ -1551,7 +1551,6 @@ async fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::
.await?;
let cwd_root = cwd.path().abs();
let memories_root = codex_home.path().join("memories").abs();
assert_eq!(
config.permissions.file_system_sandbox_policy(),
FileSystemSandboxPolicy::restricted(vec![
@@ -1573,18 +1572,12 @@ async fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: memories_root.clone(),
},
access: FileSystemAccessMode::Write,
},
]),
);
assert_eq!(
&config.legacy_sandbox_policy(),
&SandboxPolicy::WorkspaceWrite {
writable_roots: vec![memories_root],
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
@@ -1813,7 +1806,7 @@ async fn managed_unrestricted_permission_profile_still_enables_network_requireme
}
#[tokio::test]
async fn permission_profile_override_applies_runtime_roots_to_legacy_projection()
async fn permission_profile_override_keeps_workspace_roots_out_of_legacy_projection()
-> std::io::Result<()> {
let codex_home = TempDir::new()?;
let cwd = TempDir::new()?;
@@ -1846,17 +1839,16 @@ async fn permission_profile_override_applies_runtime_roots_to_legacy_projection(
)
.await?;
let memories_root = codex_home.path().join("memories").abs();
assert!(
config
.permissions
.file_system_sandbox_policy()
.can_write_path_with_cwd(memories_root.as_path(), cwd.path())
.can_write_path_with_cwd(cwd.path(), cwd.path())
);
assert_eq!(
&config.legacy_sandbox_policy(),
&SandboxPolicy::WorkspaceWrite {
writable_roots: vec![memories_root],
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
@@ -2751,9 +2743,6 @@ async fn permissions_profiles_allow_direct_write_roots_outside_workspace_root()
config.custom_permission_profile_ids,
vec!["dev".to_string()]
);
let memories_root = AbsolutePathBuf::from_absolute_path(std::fs::canonicalize(
codex_home.path().join("memories"),
)?)?;
assert!(
config
.permissions
@@ -2763,7 +2752,7 @@ async fn permissions_profiles_allow_direct_write_roots_outside_workspace_root()
assert_eq!(
&config.legacy_sandbox_policy(),
&SandboxPolicy::WorkspaceWrite {
writable_roots: vec![external_write_path, memories_root],
writable_roots: vec![external_write_path],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
@@ -4508,13 +4497,13 @@ async fn sqlite_home_defaults_to_codex_home_for_workspace_write() -> std::io::Re
}
#[tokio::test]
async fn workspace_write_always_includes_memories_root_once() -> std::io::Result<()> {
async fn workspace_write_includes_configured_writable_root_once() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let memories_root = codex_home.path().join("memories");
let writable_root = codex_home.path().join("writable").abs();
let config = Config::load_from_base_config_with_overrides(
ConfigToml {
sandbox_workspace_write: Some(SandboxWorkspaceWrite {
writable_roots: vec![memories_root.abs()],
writable_roots: vec![writable_root.clone(), writable_root.clone()],
..Default::default()
}),
..Default::default()
@@ -4533,22 +4522,16 @@ async fn workspace_write_always_includes_memories_root_once() -> std::io::Result
other => panic!("expected read-only policy on Windows, got {other:?}"),
}
} else {
assert!(
memories_root.is_dir(),
"expected memories root directory to exist at {}",
memories_root.display()
);
let expected_memories_root = memories_root.abs();
match &config.legacy_sandbox_policy() {
SandboxPolicy::WorkspaceWrite { writable_roots, .. } => {
assert_eq!(
writable_roots
.iter()
.filter(|root| **root == expected_memories_root)
.filter(|root| **root == writable_root)
.count(),
1,
"expected single writable root entry for {}",
expected_memories_root.display()
writable_root.display()
);
}
other => panic!("expected workspace-write policy, got {other:?}"),
+4 -78
View File
@@ -70,7 +70,6 @@ use codex_git_utils::resolve_root_git_project_for_trust;
use codex_install_context::InstallContext;
use codex_login::AuthManagerConfig;
use codex_mcp::McpConfig;
use codex_memories_read::memory_root;
use codex_model_provider_info::LEGACY_OLLAMA_CHAT_PROVIDER_ID;
use codex_model_provider_info::ModelProviderInfo;
use codex_model_provider_info::OLLAMA_CHAT_PROVIDER_REMOVED_ERROR;
@@ -2620,10 +2619,6 @@ impl Config {
Some(WindowsSandboxModeToml::Unelevated) => WindowsSandboxLevel::RestrictedToken,
None => WindowsSandboxLevel::from_features(&features),
};
let memories_root = memory_root(&codex_home);
std::fs::create_dir_all(&memories_root)?;
let internal_writable_roots = vec![memories_root];
let profiles_are_active = effective_permission_selection.profiles_are_active(
default_permissions_override.as_deref(),
permission_config_syntax,
@@ -2683,8 +2678,8 @@ impl Config {
file_system_sandbox_policy,
mut active_permission_profile,
mut profile_workspace_roots,
) = if let Some(mut permission_profile) = permission_profile {
let (mut file_system_sandbox_policy, network_sandbox_policy) =
) = if let Some(permission_profile) = permission_profile {
let (file_system_sandbox_policy, _network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let configured_network_proxy_config =
if profile_allows_configured_network_proxy(&permission_profile)
@@ -2708,30 +2703,6 @@ impl Config {
} else {
NetworkProxyConfig::default()
};
let materialized_file_system_sandbox_policy = file_system_sandbox_policy
.clone()
.materialize_project_roots_with_workspace_roots(&workspace_roots);
let materialized_permission_profile =
PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&materialized_file_system_sandbox_policy,
network_sandbox_policy,
);
let sandbox_policy = compatibility_sandbox_policy_for_permission_profile(
&materialized_permission_profile,
&materialized_file_system_sandbox_policy,
network_sandbox_policy,
resolved_cwd.as_path(),
);
if matches!(sandbox_policy, SandboxPolicy::WorkspaceWrite { .. }) {
file_system_sandbox_policy = file_system_sandbox_policy
.with_additional_legacy_workspace_writable_roots(&internal_writable_roots);
permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&file_system_sandbox_policy,
network_sandbox_policy,
);
}
(
configured_network_proxy_config,
permission_profile,
@@ -2776,7 +2747,7 @@ impl Config {
dedupe_absolute_paths(&mut configured_workspace_roots);
file_system_sandbox_policy = file_system_sandbox_policy
.with_materialized_project_roots_for_workspace_roots(&configured_workspace_roots);
let mut permission_profile = if let Some(permission_profile) =
let permission_profile = if let Some(permission_profile) =
builtin_permission_profile(default_permissions, builtin_workspace_write_settings)
{
permission_profile
@@ -2786,30 +2757,6 @@ impl Config {
network_sandbox_policy,
)
};
let materialized_file_system_sandbox_policy = file_system_sandbox_policy
.clone()
.materialize_project_roots_with_workspace_roots(&workspace_roots);
let materialized_permission_profile =
PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&materialized_file_system_sandbox_policy,
network_sandbox_policy,
);
let sandbox_policy = compatibility_sandbox_policy_for_permission_profile(
&materialized_permission_profile,
&materialized_file_system_sandbox_policy,
network_sandbox_policy,
resolved_cwd.as_path(),
);
if matches!(sandbox_policy, SandboxPolicy::WorkspaceWrite { .. }) {
file_system_sandbox_policy = file_system_sandbox_policy
.with_additional_legacy_workspace_writable_roots(&internal_writable_roots);
permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&file_system_sandbox_policy,
network_sandbox_policy,
);
}
let active_permission_profile = if using_implicit_builtin_profile
&& default_permissions == BUILT_IN_WORKSPACE_PROFILE
&& cfg.sandbox_workspace_write.is_some()
@@ -2867,29 +2814,8 @@ impl Config {
);
permission_profile = PermissionProfile::read_only();
}
let (mut file_system_sandbox_policy, network_sandbox_policy) =
let (file_system_sandbox_policy, _network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let materialized_file_system_sandbox_policy = permission_profile
.clone()
.materialize_project_roots_with_workspace_roots(&workspace_roots)
.file_system_sandbox_policy();
if matches!(permission_profile.enforcement(), SandboxEnforcement::Managed)
&& materialized_file_system_sandbox_policy.can_write_path_with_cwd(
resolved_cwd.as_path(),
resolved_cwd.as_path(),
)
&& !materialized_file_system_sandbox_policy.has_full_disk_write_access()
{
// Keep Codex runtime write access while storing the runtime
// workspace roots separately on the thread.
file_system_sandbox_policy = file_system_sandbox_policy
.with_additional_legacy_workspace_writable_roots(&internal_writable_roots);
permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&file_system_sandbox_policy,
network_sandbox_policy,
);
}
(
configured_network_proxy_config,
permission_profile,
+4
View File
@@ -50,6 +50,10 @@ pub fn start_memories_startup_task(
tokio::spawn(async move {
let root = memory_root(&config.codex_home);
if let Err(err) = tokio::fs::create_dir_all(&root).await {
warn!("failed creating memories root: {err}");
return;
}
if let Err(err) = seed_extension_instructions(&root).await {
warn!("failed seeding memory extension instructions: {err}");
}
@@ -26,6 +26,21 @@ use tempfile::TempDir;
use tokio::time::Duration;
use tokio::time::Instant;
#[tokio::test]
async fn memories_startup_creates_memory_root() -> anyhow::Result<()> {
let server = start_mock_server().await;
let home = Arc::new(TempDir::new()?);
let memory_root = home.path().join("memories");
let test = build_test_codex(&server, home).await?;
assert!(!memory_root.exists());
trigger_memories_startup(&test).await;
wait_for_dir(&memory_root).await?;
shutdown_test_codex(&test).await?;
Ok(())
}
#[tokio::test]
async fn memories_startup_phase2_tracks_workspace_diff_across_runs() -> anyhow::Result<()> {
let server = start_mock_server().await;
@@ -376,6 +391,21 @@ async fn wait_for_file_removed(path: &Path) -> anyhow::Result<()> {
}
}
async fn wait_for_dir(path: &Path) -> anyhow::Result<()> {
let deadline = Instant::now() + Duration::from_secs(10);
loop {
if tokio::fs::try_exists(path).await? && path.is_dir() {
return Ok(());
}
assert!(
Instant::now() < deadline,
"timed out waiting for {} to be created",
path.display()
);
tokio::time::sleep(Duration::from_millis(50)).await;
}
}
async fn wait_for_request(mock: &ResponseMock, expected_count: usize) -> Vec<ResponsesRequest> {
let deadline = Instant::now() + Duration::from_secs(10);
loop {