mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Display YOLO mode permissions if set when launching TUI (#17877)
- When launching the TUI client, if YOLO mode is enabled, display this in the header. - Eligibility is determined by `approval_policy = "never"` and `sandbox_mode = "danger-full-access"` <img width="886" height="230" alt="image" src="https://github.com/user-attachments/assets/d7064778-e32c-4123-8e44-ca0c9016ab09" />
This commit is contained in:
committed by
GitHub
Unverified
parent
d63ba2d5ec
commit
d97bad1272
@@ -1599,6 +1599,7 @@ impl App {
|
||||
self.config.cwd.to_path_buf(),
|
||||
version,
|
||||
)
|
||||
.with_yolo_mode(history_cell::is_yolo_mode(&self.config))
|
||||
.display_lines(width)
|
||||
}
|
||||
|
||||
|
||||
@@ -9722,14 +9722,17 @@ impl ChatWidget {
|
||||
/// Build a placeholder header cell while the session is configuring.
|
||||
fn placeholder_session_header_cell(config: &Config) -> Box<dyn HistoryCell> {
|
||||
let placeholder_style = Style::default().add_modifier(Modifier::DIM | Modifier::ITALIC);
|
||||
Box::new(history_cell::SessionHeaderHistoryCell::new_with_style(
|
||||
DEFAULT_MODEL_DISPLAY_NAME.to_string(),
|
||||
placeholder_style,
|
||||
/*reasoning_effort*/ None,
|
||||
/*show_fast_status*/ false,
|
||||
config.cwd.to_path_buf(),
|
||||
CODEX_CLI_VERSION,
|
||||
))
|
||||
Box::new(
|
||||
history_cell::SessionHeaderHistoryCell::new_with_style(
|
||||
DEFAULT_MODEL_DISPLAY_NAME.to_string(),
|
||||
placeholder_style,
|
||||
/*reasoning_effort*/ None,
|
||||
/*show_fast_status*/ false,
|
||||
config.cwd.to_path_buf(),
|
||||
CODEX_CLI_VERSION,
|
||||
)
|
||||
.with_yolo_mode(history_cell::is_yolo_mode(config)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Merge the real session info cell with any placeholder header to avoid double boxes.
|
||||
|
||||
@@ -61,9 +61,11 @@ use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::plan_tool::PlanItemArg;
|
||||
use codex_protocol::plan_tool::StepStatus;
|
||||
use codex_protocol::plan_tool::UpdatePlanArgs;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
use codex_protocol::protocol::McpAuthStatus;
|
||||
use codex_protocol::protocol::McpInvocation;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_protocol::protocol::SessionConfiguredEvent;
|
||||
use codex_protocol::request_user_input::RequestUserInputAnswer;
|
||||
use codex_protocol::request_user_input::RequestUserInputQuestion;
|
||||
@@ -1186,6 +1188,8 @@ pub(crate) fn new_session_info(
|
||||
let SessionConfiguredEvent {
|
||||
model,
|
||||
reasoning_effort,
|
||||
approval_policy,
|
||||
sandbox_policy,
|
||||
..
|
||||
} = event;
|
||||
// Header box rendered as history (so it appears at the very top)
|
||||
@@ -1195,7 +1199,8 @@ pub(crate) fn new_session_info(
|
||||
show_fast_status,
|
||||
config.cwd.to_path_buf(),
|
||||
CODEX_CLI_VERSION,
|
||||
);
|
||||
)
|
||||
.with_yolo_mode(has_yolo_permissions(approval_policy, &sandbox_policy));
|
||||
let mut parts: Vec<Box<dyn HistoryCell>> = vec![Box::new(header)];
|
||||
|
||||
if is_first_event {
|
||||
@@ -1259,6 +1264,17 @@ pub(crate) fn new_session_info(
|
||||
SessionInfoCell(CompositeHistoryCell { parts })
|
||||
}
|
||||
|
||||
pub(crate) fn is_yolo_mode(config: &Config) -> bool {
|
||||
has_yolo_permissions(
|
||||
config.permissions.approval_policy.value(),
|
||||
config.permissions.sandbox_policy.get(),
|
||||
)
|
||||
}
|
||||
|
||||
fn has_yolo_permissions(approval_policy: AskForApproval, sandbox_policy: &SandboxPolicy) -> bool {
|
||||
approval_policy == AskForApproval::Never && *sandbox_policy == SandboxPolicy::DangerFullAccess
|
||||
}
|
||||
|
||||
pub(crate) fn new_user_prompt(
|
||||
message: String,
|
||||
text_elements: Vec<TextElement>,
|
||||
@@ -1281,6 +1297,7 @@ pub(crate) struct SessionHeaderHistoryCell {
|
||||
reasoning_effort: Option<ReasoningEffortConfig>,
|
||||
show_fast_status: bool,
|
||||
directory: PathBuf,
|
||||
yolo_mode: bool,
|
||||
}
|
||||
|
||||
impl SessionHeaderHistoryCell {
|
||||
@@ -1316,9 +1333,15 @@ impl SessionHeaderHistoryCell {
|
||||
reasoning_effort,
|
||||
show_fast_status,
|
||||
directory,
|
||||
yolo_mode: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_yolo_mode(mut self, yolo_mode: bool) -> Self {
|
||||
self.yolo_mode = yolo_mode;
|
||||
self
|
||||
}
|
||||
|
||||
fn format_directory(&self, max_width: Option<usize>) -> String {
|
||||
Self::format_directory_inner(&self.directory, max_width)
|
||||
}
|
||||
@@ -1377,7 +1400,12 @@ impl HistoryCell for SessionHeaderHistoryCell {
|
||||
const CHANGE_MODEL_HINT_COMMAND: &str = "/model";
|
||||
const CHANGE_MODEL_HINT_EXPLANATION: &str = " to change";
|
||||
const DIR_LABEL: &str = "directory:";
|
||||
let label_width = DIR_LABEL.len();
|
||||
const PERMISSIONS_LABEL: &str = "permissions:";
|
||||
let label_width = if self.yolo_mode {
|
||||
DIR_LABEL.len().max(PERMISSIONS_LABEL.len())
|
||||
} else {
|
||||
DIR_LABEL.len()
|
||||
};
|
||||
|
||||
let model_label = format!(
|
||||
"{model_label:<label_width$}",
|
||||
@@ -1411,13 +1439,21 @@ impl HistoryCell for SessionHeaderHistoryCell {
|
||||
let dir = self.format_directory(Some(dir_max_width));
|
||||
let dir_spans = vec![Span::from(dir_prefix).dim(), Span::from(dir)];
|
||||
|
||||
let lines = vec![
|
||||
let mut lines = vec![
|
||||
make_row(title_spans),
|
||||
make_row(Vec::new()),
|
||||
make_row(model_spans),
|
||||
make_row(dir_spans),
|
||||
];
|
||||
|
||||
if self.yolo_mode {
|
||||
let permissions_label = format!("{PERMISSIONS_LABEL:<label_width$}");
|
||||
lines.push(make_row(vec![
|
||||
Span::from(format!("{permissions_label} ")).dim(),
|
||||
"YOLO mode".magenta().bold(),
|
||||
]));
|
||||
}
|
||||
|
||||
with_border(lines)
|
||||
}
|
||||
}
|
||||
@@ -3956,6 +3992,25 @@ mod tests {
|
||||
assert!(!model_line.contains("fast"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
target_os = "windows",
|
||||
ignore = "snapshot path rendering differs on Windows"
|
||||
)]
|
||||
fn session_header_indicates_yolo_mode() {
|
||||
let cell = SessionHeaderHistoryCell::new(
|
||||
"gpt-5".to_string(),
|
||||
/*reasoning_effort*/ None,
|
||||
/*show_fast_status*/ false,
|
||||
test_path_buf("/tmp/project").abs().to_path_buf(),
|
||||
"test",
|
||||
)
|
||||
.with_yolo_mode(/*yolo_mode*/ true);
|
||||
|
||||
let rendered = render_lines(&cell.display_lines(/*width*/ 80)).join("\n");
|
||||
insta::assert_snapshot!(rendered);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_header_directory_center_truncates() {
|
||||
let mut dir = home_dir().expect("home directory");
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
---
|
||||
source: tui/src/history_cell.rs
|
||||
expression: rendered
|
||||
---
|
||||
╭───────────────────────────────────────╮
|
||||
│ >_ OpenAI Codex (vtest) │
|
||||
│ │
|
||||
│ model: gpt-5 /model to change │
|
||||
│ directory: /tmp/project │
|
||||
│ permissions: YOLO mode │
|
||||
╰───────────────────────────────────────╯
|
||||
Reference in New Issue
Block a user