Skip to content

Commit 7d2eb93

Browse files
committed
tui/exec: show effective workspace roots in summaries
1 parent 1d60544 commit 7d2eb93

5 files changed

Lines changed: 119 additions & 11 deletions

File tree

codex-rs/exec/src/event_processor_with_human_output.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ fn config_summary_entries(
420420
config: &Config,
421421
session_configured_event: &SessionConfiguredEvent,
422422
) -> Vec<(&'static str, String)> {
423+
let permission_profile = config.permissions.effective_permission_profile();
423424
let mut entries = vec![
424425
("workdir", config.cwd.display().to_string()),
425426
("model", session_configured_event.model.clone()),
@@ -434,9 +435,9 @@ fn config_summary_entries(
434435
(
435436
"sandbox",
436437
summarize_permission_profile(
437-
&config.permissions.effective_permission_profile(),
438+
&permission_profile,
438439
&config.cwd,
439-
config.permissions.user_visible_workspace_roots(),
440+
config.effective_workspace_roots().as_slice(),
440441
),
441442
),
442443
];

codex-rs/exec/src/event_processor_with_human_output_tests.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@ use codex_app_server_protocol::ServerNotification;
22
use codex_app_server_protocol::ThreadItem;
33
use codex_app_server_protocol::Turn;
44
use codex_app_server_protocol::TurnStatus;
5+
use codex_core::config::ConfigBuilder;
6+
use codex_protocol::SessionId;
7+
use codex_protocol::ThreadId;
58
use codex_protocol::models::PermissionProfile;
69
use codex_protocol::permissions::FileSystemAccessMode;
710
use codex_protocol::permissions::FileSystemPath;
811
use codex_protocol::permissions::FileSystemSandboxEntry;
912
use codex_protocol::permissions::FileSystemSandboxPolicy;
1013
use codex_protocol::permissions::NetworkSandboxPolicy;
14+
use codex_protocol::protocol::AskForApproval;
15+
use codex_protocol::protocol::SessionConfiguredEvent;
1116
use codex_utils_absolute_path::test_support::PathBufExt;
1217
use codex_utils_absolute_path::test_support::test_path_buf;
1318
use codex_utils_sandbox_summary::summarize_permission_profile;
1419
use owo_colors::Style;
1520
use pretty_assertions::assert_eq;
1621

1722
use super::EventProcessorWithHumanOutput;
23+
use super::config_summary_entries;
1824
use super::final_message_from_turn_items;
1925
use super::reasoning_text;
2026
use super::should_print_final_message_to_stdout;
@@ -168,6 +174,70 @@ fn summarizes_managed_read_only_permission_profile() {
168174
);
169175
}
170176

177+
#[tokio::test]
178+
async fn config_summary_entries_include_runtime_workspace_roots() {
179+
let codex_home = tempfile::tempdir().expect("create codex home");
180+
let cwd = tempfile::tempdir().expect("create cwd");
181+
let extra_root = tempfile::tempdir().expect("create extra root");
182+
let mut config = ConfigBuilder::default()
183+
.codex_home(codex_home.path().to_path_buf())
184+
.fallback_cwd(Some(cwd.path().to_path_buf()))
185+
.build()
186+
.await
187+
.expect("build default config");
188+
let cwd = cwd.path().to_path_buf().abs();
189+
let extra_root = extra_root.path().to_path_buf().abs();
190+
let expected_extra_root =
191+
std::fs::canonicalize(extra_root.as_path()).expect("canonicalize extra root");
192+
config.cwd = cwd.clone();
193+
config.workspace_roots = vec![cwd.clone(), extra_root];
194+
config
195+
.permissions
196+
.set_workspace_roots(config.workspace_roots.clone());
197+
config
198+
.permissions
199+
.set_permission_profile(PermissionProfile::workspace_write_with(
200+
&[],
201+
NetworkSandboxPolicy::Restricted,
202+
/*exclude_tmpdir_env_var*/ true,
203+
/*exclude_slash_tmp*/ true,
204+
))
205+
.expect("set permission profile");
206+
207+
let session_configured_event = SessionConfiguredEvent {
208+
session_id: SessionId::new(),
209+
thread_id: ThreadId::new(),
210+
forked_from_id: None,
211+
thread_source: None,
212+
thread_name: None,
213+
model: "gpt-5.4".to_string(),
214+
model_provider_id: config.model_provider_id.clone(),
215+
service_tier: None,
216+
approval_policy: AskForApproval::Never,
217+
approvals_reviewer: config.approvals_reviewer,
218+
permission_profile: config.permissions.effective_permission_profile(),
219+
active_permission_profile: None,
220+
cwd,
221+
reasoning_effort: None,
222+
initial_messages: None,
223+
network_proxy: None,
224+
rollout_path: None,
225+
};
226+
227+
let summary_entries = config_summary_entries(&config, &session_configured_event);
228+
assert!(
229+
summary_entries.iter().any(|(key, value)| {
230+
*key == "sandbox"
231+
&& *value
232+
== format!(
233+
"workspace-write [workdir, {}]",
234+
expected_extra_root.display()
235+
)
236+
}),
237+
"expected runtime workspace root in sandbox summary: {summary_entries:?}"
238+
);
239+
}
240+
171241
#[test]
172242
fn final_message_from_turn_items_uses_latest_agent_message() {
173243
let message = final_message_from_turn_items(&[

codex-rs/tui/src/chatwidget/status_surfaces.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -902,11 +902,9 @@ fn permissions_display(config: &Config) -> String {
902902
}
903903

904904
let permission_profile = config.permissions.effective_permission_profile();
905-
let summary = summarize_permission_profile(
906-
&permission_profile,
907-
&config.cwd,
908-
config.permissions.workspace_roots(),
909-
);
905+
let workspace_roots = config.effective_workspace_roots();
906+
let summary =
907+
summarize_permission_profile(&permission_profile, &config.cwd, workspace_roots.as_slice());
910908
if let Some(details) = summary.strip_prefix("read-only")
911909
&& !details.contains("(network access enabled)")
912910
{

codex-rs/tui/src/status/card.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ impl StatusHistoryCell {
256256
) -> (Self, StatusHistoryHandle) {
257257
let approval_policy = AskForApproval::from(config.permissions.approval_policy.value());
258258
let permission_profile = config.permissions.effective_permission_profile();
259-
let workspace_roots = config.permissions.user_visible_workspace_roots();
259+
let workspace_roots = config.effective_workspace_roots();
260260
let mut config_entries = vec![
261261
("workdir", config.cwd.display().to_string()),
262262
("model", model_name.to_string()),
@@ -267,7 +267,11 @@ impl StatusHistoryCell {
267267
),
268268
(
269269
"sandbox",
270-
summarize_permission_profile(&permission_profile, &config.cwd, workspace_roots),
270+
summarize_permission_profile(
271+
&permission_profile,
272+
&config.cwd,
273+
workspace_roots.as_slice(),
274+
),
271275
),
272276
];
273277
if config.model_provider.wire_api == WireApi::Responses {
@@ -291,8 +295,9 @@ impl StatusHistoryCell {
291295
.map(|(_, v)| v.clone())
292296
.unwrap_or_else(|| "<unknown>".to_string());
293297
let active_permission_profile = config.permissions.active_permission_profile();
294-
let sandbox = status_permission_summary(&permission_profile, &config.cwd, workspace_roots);
295-
let workspace_root_suffix = workspace_root_suffix(workspace_roots, &config.cwd);
298+
let sandbox =
299+
status_permission_summary(&permission_profile, &config.cwd, workspace_roots.as_slice());
300+
let workspace_root_suffix = workspace_root_suffix(workspace_roots.as_slice(), &config.cwd);
296301
let approval = status_approval_label(approval_policy, config.approvals_reviewer, &approval);
297302
let permissions = status_permissions_label(
298303
active_permission_profile.as_ref(),

codex-rs/tui/src/status/tests.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,40 @@ async fn status_permissions_workspace_roots_show_additional_directories() {
463463
);
464464
}
465465

466+
#[tokio::test]
467+
async fn status_permissions_workspace_roots_include_profile_defined_directories() {
468+
let temp_home = TempDir::new().expect("temp home");
469+
let mut config = test_config(&temp_home).await;
470+
set_workspace_cwd(&mut config, test_path_buf("/workspace/tests").abs());
471+
config
472+
.permissions
473+
.approval_policy
474+
.set(AskForApproval::OnRequest.to_core())
475+
.expect("set approval policy");
476+
let profile_root = test_path_buf("/workspace/shared").abs();
477+
config
478+
.permissions
479+
.set_permission_profile_from_session_snapshot_with_profile_workspace_roots(
480+
PermissionProfile::workspace_write_with(
481+
std::slice::from_ref(&profile_root),
482+
NetworkSandboxPolicy::Restricted,
483+
/*exclude_tmpdir_env_var*/ false,
484+
/*exclude_slash_tmp*/ false,
485+
),
486+
Some(ActivePermissionProfile::new(":workspace")),
487+
vec![profile_root.clone()],
488+
)
489+
.expect("set permission profile");
490+
491+
assert_eq!(
492+
permissions_text_for(&config),
493+
Some(format!(
494+
"Workspace [{}] (on-request)",
495+
profile_root.display()
496+
))
497+
);
498+
}
499+
466500
#[tokio::test]
467501
async fn status_permissions_broadened_workspace_profile_shows_builtin_label() {
468502
let temp_home = TempDir::new().expect("temp home");

0 commit comments

Comments
 (0)