chore(tui) Simplify /status Permissions (#11290)
## Summary Consolidate `/status` Permissions lines into a simpler view. It should only show "Default," "Full Access," or "Custom" (with specifics) ## Testing - [x] many snapshots updated
This commit is contained in:
parent
ad9a540ab0
commit
30cdfce1a5
13 changed files with 100 additions and 47 deletions
|
|
@ -5434,21 +5434,7 @@ impl ChatWidget {
|
|||
current_sandbox: &SandboxPolicy,
|
||||
preset: &ApprovalPreset,
|
||||
) -> bool {
|
||||
if current_approval != preset.approval {
|
||||
return false;
|
||||
}
|
||||
matches!(
|
||||
(&preset.sandbox, current_sandbox),
|
||||
(SandboxPolicy::ReadOnly, SandboxPolicy::ReadOnly)
|
||||
| (
|
||||
SandboxPolicy::DangerFullAccess,
|
||||
SandboxPolicy::DangerFullAccess
|
||||
)
|
||||
| (
|
||||
SandboxPolicy::WorkspaceWrite { .. },
|
||||
SandboxPolicy::WorkspaceWrite { .. }
|
||||
)
|
||||
)
|
||||
current_approval == preset.approval && *current_sandbox == preset.sandbox
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
|
|
|||
|
|
@ -4176,7 +4176,7 @@ async fn approvals_selection_popup_snapshot_windows_degraded_sandbox() {
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn preset_matching_ignores_extra_writable_roots() {
|
||||
async fn preset_matching_requires_exact_workspace_write_settings() {
|
||||
let preset = builtin_approval_presets()
|
||||
.into_iter()
|
||||
.find(|p| p.id == "auto")
|
||||
|
|
@ -4189,8 +4189,8 @@ async fn preset_matching_ignores_extra_writable_roots() {
|
|||
};
|
||||
|
||||
assert!(
|
||||
ChatWidget::preset_matches_current(AskForApproval::OnRequest, ¤t_sandbox, &preset),
|
||||
"WorkspaceWrite with extra roots should still match the Agent preset"
|
||||
!ChatWidget::preset_matches_current(AskForApproval::OnRequest, ¤t_sandbox, &preset),
|
||||
"WorkspaceWrite with extra roots should not match the Default preset"
|
||||
);
|
||||
assert!(
|
||||
!ChatWidget::preset_matches_current(AskForApproval::Never, ¤t_sandbox, &preset),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use chrono::DateTime;
|
|||
use chrono::Local;
|
||||
use codex_core::WireApi;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::NetworkAccess;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::protocol::TokenUsage;
|
||||
|
|
@ -63,8 +64,7 @@ struct StatusHistoryCell {
|
|||
model_name: String,
|
||||
model_details: Vec<String>,
|
||||
directory: PathBuf,
|
||||
approval: String,
|
||||
sandbox: String,
|
||||
permissions: String,
|
||||
agents_summary: String,
|
||||
collaboration_mode: Option<String>,
|
||||
model_provider: Option<String>,
|
||||
|
|
@ -194,6 +194,10 @@ impl StatusHistoryCell {
|
|||
let sandbox = match config.sandbox_policy.get() {
|
||||
SandboxPolicy::DangerFullAccess => "danger-full-access".to_string(),
|
||||
SandboxPolicy::ReadOnly => "read-only".to_string(),
|
||||
SandboxPolicy::WorkspaceWrite {
|
||||
network_access: true,
|
||||
..
|
||||
} => "workspace-write with network access".to_string(),
|
||||
SandboxPolicy::WorkspaceWrite { .. } => "workspace-write".to_string(),
|
||||
SandboxPolicy::ExternalSandbox { network_access } => {
|
||||
if matches!(network_access, NetworkAccess::Enabled) {
|
||||
|
|
@ -203,6 +207,17 @@ impl StatusHistoryCell {
|
|||
}
|
||||
}
|
||||
};
|
||||
let permissions = if config.approval_policy.value() == AskForApproval::OnRequest
|
||||
&& *config.sandbox_policy.get() == SandboxPolicy::new_workspace_write_policy()
|
||||
{
|
||||
"Default".to_string()
|
||||
} else if config.approval_policy.value() == AskForApproval::Never
|
||||
&& *config.sandbox_policy.get() == SandboxPolicy::DangerFullAccess
|
||||
{
|
||||
"Full Access".to_string()
|
||||
} else {
|
||||
format!("Custom ({sandbox}, {approval})")
|
||||
};
|
||||
let agents_summary = compose_agents_summary(config);
|
||||
let model_provider = format_model_provider(config);
|
||||
let account = compose_account_display(auth_manager, plan_type);
|
||||
|
|
@ -235,8 +250,7 @@ impl StatusHistoryCell {
|
|||
model_name,
|
||||
model_details,
|
||||
directory: config.cwd.clone(),
|
||||
approval,
|
||||
sandbox,
|
||||
permissions,
|
||||
agents_summary,
|
||||
collaboration_mode: collaboration_mode.map(ToString::to_string),
|
||||
model_provider,
|
||||
|
|
@ -416,11 +430,10 @@ impl HistoryCell for StatusHistoryCell {
|
|||
}
|
||||
});
|
||||
|
||||
let mut labels: Vec<String> =
|
||||
vec!["Model", "Directory", "Approval", "Sandbox", "Agents.md"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect();
|
||||
let mut labels: Vec<String> = vec!["Model", "Directory", "Permissions", "Agents.md"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect();
|
||||
let mut seen: BTreeSet<String> = labels.iter().cloned().collect();
|
||||
let thread_name = self.thread_name.as_deref().filter(|name| !name.is_empty());
|
||||
|
||||
|
|
@ -483,8 +496,7 @@ impl HistoryCell for StatusHistoryCell {
|
|||
lines.push(formatter.line("Model provider", vec![Span::from(model_provider.clone())]));
|
||||
}
|
||||
lines.push(formatter.line("Directory", vec![Span::from(directory_value)]));
|
||||
lines.push(formatter.line("Approval", vec![Span::from(self.approval.clone())]));
|
||||
lines.push(formatter.line("Sandbox", vec![Span::from(self.sandbox.clone())]));
|
||||
lines.push(formatter.line("Permissions", vec![Span::from(self.permissions.clone())]));
|
||||
lines.push(formatter.line("Agents.md", vec![Span::from(self.agents_summary.clone())]));
|
||||
|
||||
if let Some(account_value) = account_value {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex (reasoning none, summaries auto) │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: read-only │
|
||||
│ Permissions: Custom (read-only, on-request) │
|
||||
│ Agents.md: <none> │
|
||||
│ │
|
||||
│ Token usage: 1.05K total (700 input + 350 output) │
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex (reasoning none, summaries auto) │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: read-only │
|
||||
│ Permissions: Custom (read-only, on-request) │
|
||||
│ Agents.md: <none> │
|
||||
│ │
|
||||
│ Token usage: 2K total (1.4K input + 600 output) │
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: read-only │
|
||||
│ Permissions: Custom (read-only, on-request) │
|
||||
│ Agents.md: <none> │
|
||||
│ Session: 0f0f3c13-6cf9-4aa4-8b80-7d49c2f1be2e │
|
||||
│ Forked from: e9f18a88-8081-4e51-9d4e-8af5cde2d8dd │
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: read-only │
|
||||
│ Permissions: Custom (read-only, on-request) │
|
||||
│ Agents.md: <none> │
|
||||
│ │
|
||||
│ Token usage: 1.2K total (800 input + 400 output) │
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex-max (reasoning high, summaries detailed) │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: workspace-write │
|
||||
│ Permissions: Default │
|
||||
│ Agents.md: <none> │
|
||||
│ │
|
||||
│ Token usage: 1.9K total (1K input + 900 output) │
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: read-only │
|
||||
│ Permissions: Custom (read-only, on-request) │
|
||||
│ Agents.md: <none> │
|
||||
│ │
|
||||
│ Token usage: 750 total (500 input + 250 output) │
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: read-only │
|
||||
│ Permissions: Custom (read-only, on-request) │
|
||||
│ Agents.md: <none> │
|
||||
│ │
|
||||
│ Token usage: 750 total (500 input + 250 output) │
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex-max (reasoning none, summaries auto) │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: read-only │
|
||||
│ Permissions: Custom (read-only, on-request) │
|
||||
│ Agents.md: <none> │
|
||||
│ │
|
||||
│ Token usage: 1.9K total (1K input + 900 output) │
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ expression: sanitized
|
|||
│ │
|
||||
│ Model: gpt-5.1-codex-max (reasoning high, summaries de │
|
||||
│ Directory: [[workspace]] │
|
||||
│ Approval: on-request │
|
||||
│ Sandbox: read-only │
|
||||
│ Permissions: Custom (read-only, on-request) │
|
||||
│ Agents.md: <none> │
|
||||
│ │
|
||||
│ Token usage: 1.9K total (1K input + 900 output) │
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use chrono::Utc;
|
|||
use codex_core::AuthManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::CreditsSnapshot;
|
||||
use codex_core::protocol::RateLimitSnapshot;
|
||||
use codex_core::protocol::RateLimitWindow;
|
||||
|
|
@ -17,6 +18,7 @@ use codex_protocol::ThreadId;
|
|||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use insta::assert_snapshot;
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
|
|
@ -167,6 +169,68 @@ async fn status_snapshot_includes_reasoning_details() {
|
|||
assert_snapshot!(sanitized);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_permissions_non_default_workspace_write_is_custom() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
config
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: Vec::new(),
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
})
|
||||
.expect("set sandbox policy");
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage::default();
|
||||
let captured_at = chrono::Local
|
||||
.with_ymd_and_hms(2024, 1, 2, 3, 4, 5)
|
||||
.single()
|
||||
.expect("timestamp");
|
||||
let model_slug = codex_core::test_support::get_model_offline(config.model.as_deref());
|
||||
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
&usage,
|
||||
&None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
captured_at,
|
||||
&model_slug,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let rendered_lines = render_lines(&composite.display_lines(80));
|
||||
let permissions_line = rendered_lines
|
||||
.iter()
|
||||
.find(|line| line.contains("Permissions:"))
|
||||
.expect("permissions line");
|
||||
let permissions_text = permissions_line
|
||||
.split("Permissions:")
|
||||
.nth(1)
|
||||
.map(str::trim)
|
||||
.map(|text| text.trim_end_matches('│'))
|
||||
.map(str::trim);
|
||||
|
||||
assert_eq!(
|
||||
permissions_text,
|
||||
Some("Custom (workspace-write with network access, on-request)")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_snapshot_includes_forked_from() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue