Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions codex-rs/exec/src/event_processor_with_human_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ fn config_summary_entries(
config: &Config,
session_configured_event: &SessionConfiguredEvent,
) -> Vec<(&'static str, String)> {
let permission_profile = config.permissions.effective_permission_profile();
let mut entries = vec![
("workdir", config.cwd.display().to_string()),
("model", session_configured_event.model.clone()),
Expand All @@ -434,9 +435,9 @@ fn config_summary_entries(
(
"sandbox",
summarize_permission_profile(
&config.permissions.effective_permission_profile(),
&permission_profile,
&config.cwd,
config.permissions.user_visible_workspace_roots(),
config.effective_workspace_roots().as_slice(),
),
),
];
Expand Down
71 changes: 71 additions & 0 deletions codex-rs/exec/src/event_processor_with_human_output_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ use codex_app_server_protocol::ServerNotification;
use codex_app_server_protocol::ThreadItem;
use codex_app_server_protocol::Turn;
use codex_app_server_protocol::TurnStatus;
use codex_core::config::ConfigBuilder;
use codex_protocol::SessionId;
use codex_protocol::ThreadId;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::SessionConfiguredEvent;
use codex_utils_absolute_path::test_support::PathBufExt;
use codex_utils_absolute_path::test_support::test_path_buf;
use codex_utils_sandbox_summary::summarize_permission_profile;
use owo_colors::Style;
use pretty_assertions::assert_eq;

use super::EventProcessorWithHumanOutput;
use super::config_summary_entries;
use super::final_message_from_turn_items;
use super::reasoning_text;
use super::should_print_final_message_to_stdout;
Expand Down Expand Up @@ -168,6 +174,71 @@ fn summarizes_managed_read_only_permission_profile() {
);
}

#[tokio::test]
async fn config_summary_entries_include_runtime_workspace_roots() {
let codex_home = tempfile::tempdir().expect("create codex home");
let cwd = tempfile::tempdir().expect("create cwd");
let extra_root = tempfile::tempdir().expect("create extra root");
let mut config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.fallback_cwd(Some(cwd.path().to_path_buf()))
.build()
.await
.expect("build default config");
let cwd = cwd.path().to_path_buf().abs();
let extra_root = extra_root.path().to_path_buf().abs();
let expected_extra_root_name = extra_root
.file_name()
.expect("extra root should have file name")
.to_string_lossy()
.to_string();
config.cwd = cwd.clone();
config.workspace_roots = vec![cwd.clone(), extra_root];
config
.permissions
.set_workspace_roots(config.workspace_roots.clone());
config
.permissions
.set_permission_profile(PermissionProfile::workspace_write_with(
&[],
NetworkSandboxPolicy::Restricted,
/*exclude_tmpdir_env_var*/ true,
/*exclude_slash_tmp*/ true,
))
.expect("set permission profile");

let session_configured_event = SessionConfiguredEvent {
session_id: SessionId::new(),
thread_id: ThreadId::new(),
forked_from_id: None,
thread_source: None,
thread_name: None,
model: "gpt-5.4".to_string(),
model_provider_id: config.model_provider_id.clone(),
service_tier: None,
approval_policy: AskForApproval::Never,
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.effective_permission_profile(),
active_permission_profile: None,
cwd,
reasoning_effort: None,
initial_messages: None,
network_proxy: None,
rollout_path: None,
};

let summary_entries = config_summary_entries(&config, &session_configured_event);
let sandbox_summary = summary_entries
.iter()
.find_map(|(key, value)| (*key == "sandbox").then_some(value))
.expect("sandbox summary entry");
assert!(
sandbox_summary.starts_with("workspace-write [workdir, ")
&& sandbox_summary.contains(&expected_extra_root_name),
"expected runtime workspace root in sandbox summary: {summary_entries:?}"
);
}

#[test]
fn final_message_from_turn_items_uses_latest_agent_message() {
let message = final_message_from_turn_items(&[
Expand Down
8 changes: 3 additions & 5 deletions codex-rs/tui/src/chatwidget/status_surfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,11 +902,9 @@ fn permissions_display(config: &Config) -> String {
}

let permission_profile = config.permissions.effective_permission_profile();
let summary = summarize_permission_profile(
&permission_profile,
&config.cwd,
config.permissions.workspace_roots(),
);
let workspace_roots = config.effective_workspace_roots();
let summary =
summarize_permission_profile(&permission_profile, &config.cwd, workspace_roots.as_slice());
if let Some(details) = summary.strip_prefix("read-only")
&& !details.contains("(network access enabled)")
{
Expand Down
13 changes: 9 additions & 4 deletions codex-rs/tui/src/status/card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ impl StatusHistoryCell {
) -> (Self, StatusHistoryHandle) {
let approval_policy = AskForApproval::from(config.permissions.approval_policy.value());
let permission_profile = config.permissions.effective_permission_profile();
let workspace_roots = config.permissions.user_visible_workspace_roots();
let workspace_roots = config.effective_workspace_roots();
let mut config_entries = vec![
("workdir", config.cwd.display().to_string()),
("model", model_name.to_string()),
Expand All @@ -267,7 +267,11 @@ impl StatusHistoryCell {
),
(
"sandbox",
summarize_permission_profile(&permission_profile, &config.cwd, workspace_roots),
summarize_permission_profile(
&permission_profile,
&config.cwd,
workspace_roots.as_slice(),
),
),
];
if config.model_provider.wire_api == WireApi::Responses {
Expand All @@ -291,8 +295,9 @@ impl StatusHistoryCell {
.map(|(_, v)| v.clone())
.unwrap_or_else(|| "<unknown>".to_string());
let active_permission_profile = config.permissions.active_permission_profile();
let sandbox = status_permission_summary(&permission_profile, &config.cwd, workspace_roots);
let workspace_root_suffix = workspace_root_suffix(workspace_roots, &config.cwd);
let sandbox =
status_permission_summary(&permission_profile, &config.cwd, workspace_roots.as_slice());
let workspace_root_suffix = workspace_root_suffix(workspace_roots.as_slice(), &config.cwd);
let approval = status_approval_label(approval_policy, config.approvals_reviewer, &approval);
let permissions = status_permissions_label(
active_permission_profile.as_ref(),
Expand Down
34 changes: 34 additions & 0 deletions codex-rs/tui/src/status/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,40 @@ async fn status_permissions_workspace_roots_show_additional_directories() {
);
}

#[tokio::test]
async fn status_permissions_workspace_roots_include_profile_defined_directories() {
let temp_home = TempDir::new().expect("temp home");
let mut config = test_config(&temp_home).await;
set_workspace_cwd(&mut config, test_path_buf("/workspace/tests").abs());
config
.permissions
.approval_policy
.set(AskForApproval::OnRequest.to_core())
.expect("set approval policy");
let profile_root = test_path_buf("/workspace/shared").abs();
config
.permissions
.set_permission_profile_from_session_snapshot_with_profile_workspace_roots(
PermissionProfile::workspace_write_with(
std::slice::from_ref(&profile_root),
NetworkSandboxPolicy::Restricted,
/*exclude_tmpdir_env_var*/ false,
/*exclude_slash_tmp*/ false,
),
Some(ActivePermissionProfile::new(":workspace")),
vec![profile_root.clone()],
)
.expect("set permission profile");

assert_eq!(
permissions_text_for(&config),
Some(format!(
"Workspace [{}] (on-request)",
profile_root.display()
))
);
}

#[tokio::test]
async fn status_permissions_broadened_workspace_profile_shows_builtin_label() {
let temp_home = TempDir::new().expect("temp home");
Expand Down
Loading