Move workspace roots onto thread/session state and stop using active permission profile modifications as an overlay for writable roots. Existing app-server threads now preserve their persisted PermissionProfile value across resume, fork, and turn updates; permissions requests on existing threads only update the active named profile after validating it exists. Workspace roots can be updated independently, and SandboxPolicy::WorkspaceWrite no longer stores its own writable_roots.

This commit is contained in:
Michael Bolin
2026-05-11 13:31:36 -07:00
parent cf6342b75b
commit 50719c6d17
140 changed files with 2297 additions and 2053 deletions
+1
View File
@@ -22,6 +22,7 @@ codex-login = { workspace = true }
codex-otel = { workspace = true }
codex-protocol = { workspace = true }
codex-state = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-path = { workspace = true }
codex-utils-string = { workspace = true }
serde = { workspace = true, features = ["derive"] }
+17
View File
@@ -2,10 +2,13 @@ use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use codex_utils_absolute_path::AbsolutePathBuf;
pub trait RolloutConfigView {
fn codex_home(&self) -> &Path;
fn sqlite_home(&self) -> &Path;
fn cwd(&self) -> &Path;
fn workspace_roots(&self) -> &[AbsolutePathBuf];
fn model_provider_id(&self) -> &str;
fn generate_memories(&self) -> bool;
}
@@ -15,6 +18,7 @@ pub struct RolloutConfig {
pub codex_home: PathBuf,
pub sqlite_home: PathBuf,
pub cwd: PathBuf,
pub workspace_roots: Vec<AbsolutePathBuf>,
pub model_provider_id: String,
pub generate_memories: bool,
}
@@ -27,6 +31,7 @@ impl RolloutConfig {
codex_home: view.codex_home().to_path_buf(),
sqlite_home: view.sqlite_home().to_path_buf(),
cwd: view.cwd().to_path_buf(),
workspace_roots: view.workspace_roots().to_vec(),
model_provider_id: view.model_provider_id().to_string(),
generate_memories: view.generate_memories(),
}
@@ -46,6 +51,10 @@ impl RolloutConfigView for RolloutConfig {
self.cwd.as_path()
}
fn workspace_roots(&self) -> &[AbsolutePathBuf] {
&self.workspace_roots
}
fn model_provider_id(&self) -> &str {
self.model_provider_id.as_str()
}
@@ -68,6 +77,10 @@ impl<T: RolloutConfigView + ?Sized> RolloutConfigView for &T {
(*self).cwd()
}
fn workspace_roots(&self) -> &[AbsolutePathBuf] {
(*self).workspace_roots()
}
fn model_provider_id(&self) -> &str {
(*self).model_provider_id()
}
@@ -90,6 +103,10 @@ impl<T: RolloutConfigView + ?Sized> RolloutConfigView for Arc<T> {
self.as_ref().cwd()
}
fn workspace_roots(&self) -> &[AbsolutePathBuf] {
self.as_ref().workspace_roots()
}
fn model_provider_id(&self) -> &str {
self.as_ref().model_provider_id()
}
+3
View File
@@ -37,6 +37,7 @@ async fn extract_metadata_from_rollout_uses_session_meta() {
forked_from_id: None,
timestamp: "2026-01-27T12:34:56Z".to_string(),
cwd: dir.path().to_path_buf(),
workspace_roots: Vec::new(),
originator: "cli".to_string(),
cli_version: "0.0.0".to_string(),
source: SessionSource::default(),
@@ -89,6 +90,7 @@ async fn extract_metadata_from_rollout_returns_latest_memory_mode() {
forked_from_id: None,
timestamp: "2026-01-27T12:34:56Z".to_string(),
cwd: dir.path().to_path_buf(),
workspace_roots: Vec::new(),
originator: "cli".to_string(),
cli_version: "0.0.0".to_string(),
source: SessionSource::default(),
@@ -349,6 +351,7 @@ fn write_rollout_in_sessions_with_cwd(
forked_from_id: None,
timestamp: event_ts.to_string(),
cwd,
workspace_roots: Vec::new(),
originator: "cli".to_string(),
cli_version: "0.0.0".to_string(),
source: SessionSource::default(),
+1
View File
@@ -697,6 +697,7 @@ impl RolloutRecorder {
forked_from_id,
timestamp,
cwd: config.cwd().to_path_buf(),
workspace_roots: config.workspace_roots().to_vec(),
originator: originator().value,
cli_version: env!("CARGO_PKG_VERSION").to_string(),
agent_nickname: source.get_nickname(),
+4
View File
@@ -32,6 +32,7 @@ fn test_config(codex_home: &Path) -> RolloutConfig {
codex_home: codex_home.to_path_buf(),
sqlite_home: codex_home.to_path_buf(),
cwd: codex_home.to_path_buf(),
workspace_roots: Vec::new(),
model_provider_id: "test-provider".to_string(),
generate_memories: true,
}
@@ -88,6 +89,7 @@ async fn state_db_init_backfills_before_returning() -> anyhow::Result<()> {
forked_from_id: None,
timestamp: "2026-01-27T12:34:56Z".to_string(),
cwd: home.path().to_path_buf(),
workspace_roots: Vec::new(),
originator: "test".to_string(),
cli_version: "test".to_string(),
source: SessionSource::Cli,
@@ -1348,11 +1350,13 @@ async fn resume_candidate_matches_cwd_reads_latest_turn_context() -> std::io::Re
turn_id: Some("turn-1".to_string()),
trace_id: None,
cwd: latest_cwd.clone(),
workspace_roots: Vec::new(),
current_date: None,
timezone: None,
approval_policy: AskForApproval::Never,
sandbox_policy: SandboxPolicy::new_read_only_policy(),
permission_profile: None,
active_permission_profile: None,
network: None,
file_system_sandbox_policy: None,
model: "test-model".to_string(),
@@ -29,6 +29,7 @@ fn write_rollout_with_metadata(path: &Path, thread_id: ThreadId) -> std::io::Res
forked_from_id: None,
timestamp,
cwd: ".".into(),
workspace_roots: Vec::new(),
originator: "test_originator".into(),
cli_version: "test_version".into(),
source: SessionSource::Cli,
+1
View File
@@ -1380,6 +1380,7 @@ async fn test_updated_at_uses_file_mtime() -> Result<()> {
forked_from_id: None,
timestamp: ts.to_string(),
cwd: ".".into(),
workspace_roots: Vec::new(),
originator: "test_originator".into(),
cli_version: "test_version".into(),
source: SessionSource::VSCode,