mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
tui/exec: show effective workspace roots in summaries
This commit is contained in:
@@ -420,6 +420,7 @@ fn config_summary_entries(
|
|||||||
config: &Config,
|
config: &Config,
|
||||||
session_configured_event: &SessionConfiguredEvent,
|
session_configured_event: &SessionConfiguredEvent,
|
||||||
) -> Vec<(&'static str, String)> {
|
) -> Vec<(&'static str, String)> {
|
||||||
|
let permission_profile = config.permissions.effective_permission_profile();
|
||||||
let mut entries = vec![
|
let mut entries = vec![
|
||||||
("workdir", config.cwd.display().to_string()),
|
("workdir", config.cwd.display().to_string()),
|
||||||
("model", session_configured_event.model.clone()),
|
("model", session_configured_event.model.clone()),
|
||||||
@@ -434,9 +435,9 @@ fn config_summary_entries(
|
|||||||
(
|
(
|
||||||
"sandbox",
|
"sandbox",
|
||||||
summarize_permission_profile(
|
summarize_permission_profile(
|
||||||
&config.permissions.effective_permission_profile(),
|
&permission_profile,
|
||||||
&config.cwd,
|
&config.cwd,
|
||||||
config.permissions.user_visible_workspace_roots(),
|
config.effective_workspace_roots().as_slice(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -2,12 +2,17 @@ use codex_app_server_protocol::ServerNotification;
|
|||||||
use codex_app_server_protocol::ThreadItem;
|
use codex_app_server_protocol::ThreadItem;
|
||||||
use codex_app_server_protocol::Turn;
|
use codex_app_server_protocol::Turn;
|
||||||
use codex_app_server_protocol::TurnStatus;
|
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::models::PermissionProfile;
|
||||||
use codex_protocol::permissions::FileSystemAccessMode;
|
use codex_protocol::permissions::FileSystemAccessMode;
|
||||||
use codex_protocol::permissions::FileSystemPath;
|
use codex_protocol::permissions::FileSystemPath;
|
||||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
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::PathBufExt;
|
||||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||||
use codex_utils_sandbox_summary::summarize_permission_profile;
|
use codex_utils_sandbox_summary::summarize_permission_profile;
|
||||||
@@ -15,6 +20,7 @@ use owo_colors::Style;
|
|||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::EventProcessorWithHumanOutput;
|
use super::EventProcessorWithHumanOutput;
|
||||||
|
use super::config_summary_entries;
|
||||||
use super::final_message_from_turn_items;
|
use super::final_message_from_turn_items;
|
||||||
use super::reasoning_text;
|
use super::reasoning_text;
|
||||||
use super::should_print_final_message_to_stdout;
|
use super::should_print_final_message_to_stdout;
|
||||||
@@ -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]
|
#[test]
|
||||||
fn final_message_from_turn_items_uses_latest_agent_message() {
|
fn final_message_from_turn_items_uses_latest_agent_message() {
|
||||||
let message = final_message_from_turn_items(&[
|
let message = final_message_from_turn_items(&[
|
||||||
|
|||||||
@@ -902,11 +902,9 @@ fn permissions_display(config: &Config) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let permission_profile = config.permissions.effective_permission_profile();
|
let permission_profile = config.permissions.effective_permission_profile();
|
||||||
let summary = summarize_permission_profile(
|
let workspace_roots = config.effective_workspace_roots();
|
||||||
&permission_profile,
|
let summary =
|
||||||
&config.cwd,
|
summarize_permission_profile(&permission_profile, &config.cwd, workspace_roots.as_slice());
|
||||||
config.permissions.workspace_roots(),
|
|
||||||
);
|
|
||||||
if let Some(details) = summary.strip_prefix("read-only")
|
if let Some(details) = summary.strip_prefix("read-only")
|
||||||
&& !details.contains("(network access enabled)")
|
&& !details.contains("(network access enabled)")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ impl StatusHistoryCell {
|
|||||||
) -> (Self, StatusHistoryHandle) {
|
) -> (Self, StatusHistoryHandle) {
|
||||||
let approval_policy = AskForApproval::from(config.permissions.approval_policy.value());
|
let approval_policy = AskForApproval::from(config.permissions.approval_policy.value());
|
||||||
let permission_profile = config.permissions.effective_permission_profile();
|
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![
|
let mut config_entries = vec![
|
||||||
("workdir", config.cwd.display().to_string()),
|
("workdir", config.cwd.display().to_string()),
|
||||||
("model", model_name.to_string()),
|
("model", model_name.to_string()),
|
||||||
@@ -267,7 +267,11 @@ impl StatusHistoryCell {
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"sandbox",
|
"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 {
|
if config.model_provider.wire_api == WireApi::Responses {
|
||||||
@@ -291,8 +295,9 @@ impl StatusHistoryCell {
|
|||||||
.map(|(_, v)| v.clone())
|
.map(|(_, v)| v.clone())
|
||||||
.unwrap_or_else(|| "<unknown>".to_string());
|
.unwrap_or_else(|| "<unknown>".to_string());
|
||||||
let active_permission_profile = config.permissions.active_permission_profile();
|
let active_permission_profile = config.permissions.active_permission_profile();
|
||||||
let sandbox = status_permission_summary(&permission_profile, &config.cwd, workspace_roots);
|
let sandbox =
|
||||||
let workspace_root_suffix = workspace_root_suffix(workspace_roots, &config.cwd);
|
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 approval = status_approval_label(approval_policy, config.approvals_reviewer, &approval);
|
||||||
let permissions = status_permissions_label(
|
let permissions = status_permissions_label(
|
||||||
active_permission_profile.as_ref(),
|
active_permission_profile.as_ref(),
|
||||||
|
|||||||
@@ -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]
|
#[tokio::test]
|
||||||
async fn status_permissions_broadened_workspace_profile_shows_builtin_label() {
|
async fn status_permissions_broadened_workspace_profile_shows_builtin_label() {
|
||||||
let temp_home = TempDir::new().expect("temp home");
|
let temp_home = TempDir::new().expect("temp home");
|
||||||
|
|||||||
Reference in New Issue
Block a user