mirror of
https://github.com/openai/codex.git
synced 2026-11-03 19:53:25 +00:00
feat: drop dependency to write path
This commit is contained in:
@@ -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:?}"),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user