Skip to content

Commit 73dd563

Browse files
committed
copy/functionality update
1 parent e83d61e commit 73dd563

16 files changed

+250
-102
lines changed

codex-rs/tui/src/app.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -924,10 +924,10 @@ impl App {
924924
self.chat_widget.add_info_message(
925925
match mode {
926926
WindowsSandboxEnableMode::Elevated => {
927-
"Enabled elevated Windows sandbox.".to_string()
927+
"Enabled elevated agent sandbox.".to_string()
928928
}
929929
WindowsSandboxEnableMode::Legacy => {
930-
"Enabled degraded Windows sandbox.".to_string()
930+
"Enabled non-elevated agent sandbox.".to_string()
931931
}
932932
},
933933
None,

codex-rs/tui/src/app_event.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ pub(crate) enum WindowsSandboxEnableMode {
2525
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2626
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
2727
pub(crate) enum WindowsSandboxFallbackReason {
28-
DeclinedElevation,
2928
ElevationFailed,
3029
}
3130

codex-rs/tui/src/bottom_pane/list_selection_view.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::render::RectExt as _;
2020
use crate::render::renderable::ColumnRenderable;
2121
use crate::render::renderable::Renderable;
2222
use crate::style::user_message_style;
23+
use super::selection_popup_common::wrap_styled_line;
2324

2425
use super::CancellationEvent;
2526
use super::bottom_pane_view::BottomPaneView;
@@ -438,8 +439,10 @@ impl Renderable for ListSelectionView {
438439
if self.is_searchable {
439440
height = height.saturating_add(1);
440441
}
441-
if self.footer_note.is_some() {
442-
height = height.saturating_add(1);
442+
if let Some(note) = &self.footer_note {
443+
let note_width = width.saturating_sub(2);
444+
let note_lines = wrap_styled_line(note, note_width);
445+
height = height.saturating_add(note_lines.len() as u16);
443446
}
444447
if self.footer_hint.is_some() {
445448
height = height.saturating_add(1);
@@ -452,8 +455,13 @@ impl Renderable for ListSelectionView {
452455
return;
453456
}
454457

455-
let footer_rows =
456-
u16::from(self.footer_note.is_some()) + u16::from(self.footer_hint.is_some());
458+
let note_width = area.width.saturating_sub(2);
459+
let note_lines = self
460+
.footer_note
461+
.as_ref()
462+
.map(|note| wrap_styled_line(note, note_width));
463+
let note_height = note_lines.as_ref().map_or(0, |lines| lines.len() as u16);
464+
let footer_rows = note_height + u16::from(self.footer_hint.is_some());
457465
let [content_area, footer_area] =
458466
Layout::vertical([Constraint::Fill(1), Constraint::Length(footer_rows)]).areas(area);
459467

@@ -525,19 +533,30 @@ impl Renderable for ListSelectionView {
525533

526534
if footer_area.height > 0 {
527535
let [note_area, hint_area] = Layout::vertical([
528-
Constraint::Length(if self.footer_note.is_some() { 1 } else { 0 }),
536+
Constraint::Length(note_height),
529537
Constraint::Length(if self.footer_hint.is_some() { 1 } else { 0 }),
530538
])
531539
.areas(footer_area);
532540

533-
if let Some(note) = &self.footer_note {
541+
if let Some(lines) = note_lines {
534542
let note_area = Rect {
535543
x: note_area.x + 2,
536544
y: note_area.y,
537545
width: note_area.width.saturating_sub(2),
538546
height: note_area.height,
539547
};
540-
note.clone().render(note_area, buf);
548+
for (idx, line) in lines.iter().enumerate() {
549+
if idx as u16 >= note_area.height {
550+
break;
551+
}
552+
let line_area = Rect {
553+
x: note_area.x,
554+
y: note_area.y + idx as u16,
555+
width: note_area.width,
556+
height: 1,
557+
};
558+
line.clone().render(line_area, buf);
559+
}
541560
}
542561

543562
if let Some(hint) = &self.footer_hint {

codex-rs/tui/src/bottom_pane/selection_popup_common.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ pub(crate) struct GenericDisplayRow {
2626
pub wrap_indent: Option<usize>, // optional indent for wrapped lines
2727
}
2828

29+
pub(crate) fn wrap_styled_line<'a>(line: &'a Line<'a>, width: u16) -> Vec<Line<'a>> {
30+
use crate::wrapping::RtOptions;
31+
use crate::wrapping::word_wrap_line;
32+
33+
let width = width.max(1) as usize;
34+
let opts = RtOptions::new(width)
35+
.initial_indent(Line::from(""))
36+
.subsequent_indent(Line::from(""));
37+
word_wrap_line(line, opts)
38+
}
39+
2940
fn line_width(line: &Line<'_>) -> usize {
3041
line.iter()
3142
.map(|span| UnicodeWidthStr::width(span.content.as_ref()))

codex-rs/tui/src/chatwidget.rs

Lines changed: 77 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2816,7 +2816,7 @@ impl ChatWidget {
28162816
let is_current =
28172817
Self::preset_matches_current(current_approval, current_sandbox, &preset);
28182818
let name = if preset.id == "auto" && windows_degraded_sandbox_enabled {
2819-
"Agent (degraded)".to_string()
2819+
"Agent (non-elevated sandbox)".to_string()
28202820
} else {
28212821
preset.label.to_string()
28222822
};
@@ -2897,8 +2897,8 @@ impl ChatWidget {
28972897

28982898
let footer_note = show_elevate_sandbox_hint.then(|| {
28992899
vec![
2900-
"To upgrade to the elevated sandbox, run ".dim(),
2901-
"/elevate-sandbox".cyan(),
2900+
"The non-elevated sandbox protects your files and prevents network access under most circumstances. However, it carries greater risk if prompt injected. To upgrade to the elevated sandbox, run ".dim(),
2901+
"/setup-elevated-sandbox".cyan(),
29022902
".".dim(),
29032903
]
29042904
.into()
@@ -3230,40 +3230,59 @@ impl ChatWidget {
32303230
return;
32313231
}
32323232

3233+
let current_approval = self.config.approval_policy.value();
3234+
let current_sandbox = self.config.sandbox_policy.get();
3235+
let presets = builtin_approval_presets();
3236+
let stay_full_access = presets
3237+
.iter()
3238+
.find(|preset| preset.id == "full-access")
3239+
.is_some_and(|preset| {
3240+
Self::preset_matches_current(current_approval, current_sandbox, preset)
3241+
});
3242+
let stay_actions = if stay_full_access {
3243+
Vec::new()
3244+
} else {
3245+
presets
3246+
.iter()
3247+
.find(|preset| preset.id == "read-only")
3248+
.map(|preset| {
3249+
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
3250+
})
3251+
.unwrap_or_default()
3252+
};
3253+
let stay_label = if stay_full_access {
3254+
"Stay in Agent Full Access".to_string()
3255+
} else {
3256+
"Stay in Read-Only".to_string()
3257+
};
3258+
32333259
let mut header = ColumnRenderable::new();
32343260
header.push(*Box::new(
32353261
Paragraph::new(vec![
3236-
line!["Codex works best in Agent mode.".bold()],
3237-
line!["To use Agent mode on Windows, we need to configure the sandbox."],
3238-
line!["This setup requires elevation. Do you accept?"],
3262+
line!["Set Up Agent Sandbox".bold()],
3263+
line![""],
3264+
line!["Agent mode uses an experimental Windows sandbox that protects your files and prevents network access by default."],
32393265
line!["Learn more: https://developers.openai.com/codex/windows"],
32403266
])
32413267
.wrap(Wrap { trim: false }),
32423268
));
32433269

3244-
let preset_accept = preset.clone();
3245-
let preset_decline = preset;
32463270
let items = vec![
32473271
SelectionItem {
3248-
name: "Yes, I accept".to_string(),
3272+
name: "Set up agent sandbox (requires elevation)".to_string(),
32493273
description: None,
32503274
actions: vec![Box::new(move |tx| {
32513275
tx.send(AppEvent::BeginWindowsSandboxElevatedSetup {
3252-
preset: preset_accept.clone(),
3276+
preset: preset.clone(),
32533277
});
32543278
})],
32553279
dismiss_on_select: true,
32563280
..Default::default()
32573281
},
32583282
SelectionItem {
3259-
name: "No, I do not accept".to_string(),
3283+
name: stay_label,
32603284
description: None,
3261-
actions: vec![Box::new(move |tx| {
3262-
tx.send(AppEvent::OpenWindowsSandboxFallbackPrompt {
3263-
preset: preset_decline.clone(),
3264-
reason: WindowsSandboxFallbackReason::DeclinedElevation,
3265-
});
3266-
})],
3285+
actions: stay_actions,
32673286
dismiss_on_select: true,
32683287
..Default::default()
32693288
},
@@ -3289,53 +3308,75 @@ impl ChatWidget {
32893308
) {
32903309
use ratatui_macros::line;
32913310

3311+
let _ = reason;
3312+
3313+
let current_approval = self.config.approval_policy.value();
3314+
let current_sandbox = self.config.sandbox_policy.get();
3315+
let presets = builtin_approval_presets();
3316+
let stay_full_access = presets
3317+
.iter()
3318+
.find(|preset| preset.id == "full-access")
3319+
.is_some_and(|preset| {
3320+
Self::preset_matches_current(current_approval, current_sandbox, preset)
3321+
});
3322+
let stay_actions = if stay_full_access {
3323+
Vec::new()
3324+
} else {
3325+
presets
3326+
.iter()
3327+
.find(|preset| preset.id == "read-only")
3328+
.map(|preset| {
3329+
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
3330+
})
3331+
.unwrap_or_default()
3332+
};
3333+
let stay_label = if stay_full_access {
3334+
"Stay in Agent Full Access".to_string()
3335+
} else {
3336+
"Stay in Read-Only".to_string()
3337+
};
3338+
32923339
let mut lines = Vec::new();
3293-
if reason == WindowsSandboxFallbackReason::ElevationFailed {
3294-
lines.push(line!["The elevated setup did not complete.".bold()]);
3295-
}
3296-
lines.push(line![
3297-
"You can still use a degraded sandbox without elevation."
3298-
]);
3299-
lines.push(line!["It is less watertight, but still secure."]);
3340+
lines.push(line!["Use Non-Elevated Sandbox?".bold()]);
3341+
lines.push(line![""]);
33003342
lines.push(line![
3301-
"Learn more: https://developers.openai.com/codex/windows"
3343+
"Elevation failed. You can also use a non-elevated sandbox, which protects your files and prevents network access under most circumstances. However, it carries greater risk if prompt injected."
33023344
]);
3345+
lines.push(line!["Learn more: https://developers.openai.com/codex/windows"]);
33033346

33043347
let mut header = ColumnRenderable::new();
33053348
header.push(*Box::new(Paragraph::new(lines).wrap(Wrap { trim: false })));
33063349

3307-
let preset_retry = preset.clone();
3308-
let preset_degraded = preset;
3350+
let elevated_preset = preset.clone();
3351+
let legacy_preset = preset.clone();
33093352
let items = vec![
33103353
SelectionItem {
3311-
name: "Try elevated setup again".to_string(),
3354+
name: "Try elevated agent sandbox setup again".to_string(),
33123355
description: None,
33133356
actions: vec![Box::new(move |tx| {
33143357
tx.send(AppEvent::BeginWindowsSandboxElevatedSetup {
3315-
preset: preset_retry.clone(),
3358+
preset: elevated_preset.clone(),
33163359
});
33173360
})],
33183361
dismiss_on_select: true,
33193362
..Default::default()
33203363
},
33213364
SelectionItem {
3322-
name: "Use degraded sandbox".to_string(),
3365+
name: "Use non-elevated agent sandbox".to_string(),
33233366
description: None,
33243367
actions: vec![Box::new(move |tx| {
33253368
tx.send(AppEvent::EnableWindowsSandboxForAgentMode {
3326-
preset: preset_degraded.clone(),
3369+
preset: legacy_preset.clone(),
33273370
mode: WindowsSandboxEnableMode::Legacy,
33283371
});
33293372
})],
33303373
dismiss_on_select: true,
33313374
..Default::default()
33323375
},
33333376
SelectionItem {
3334-
name: "Use no sandbox".to_string(),
3377+
name: stay_label,
33353378
description: None,
3336-
actions: vec![Box::new(|tx| {
3337-
tx.send(AppEvent::OpenApprovalsPopup);
3338-
})],
3379+
actions: stay_actions,
33393380
dismiss_on_select: true,
33403381
..Default::default()
33413382
},
@@ -3384,8 +3425,7 @@ impl ChatWidget {
33843425
self.bottom_pane.ensure_status_indicator();
33853426
self.bottom_pane.set_interrupt_hint_visible(false);
33863427
self.set_status_header(
3387-
"Setting up the elevated Windows sandbox (this may take a minute or more). You'll stay in your current mode until it's done."
3388-
.to_string(),
3428+
"Setting up agent sandbox. This can take a minute.".to_string(),
33893429
);
33903430
self.request_redraw();
33913431
}

codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__approvals_selection_popup@windows_degraded.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ expression: popup
1111
commands with network access. Exercise caution when
1212
using.
1313

14-
To upgrade to the elevated sandbox, run /elevate-sandbox.
14+
To upgrade to the elevated sandbox, run /setup-elevated-sandbox.
1515
Press enter to confirm or esc to go back

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,8 +2083,12 @@ async fn startup_prompts_for_windows_sandbox_when_agent_requested() {
20832083
"expected startup prompt to explain elevation: {popup}"
20842084
);
20852085
assert!(
2086-
popup.contains("Yes, I accept"),
2087-
"expected startup prompt to offer accepting elevation: {popup}"
2086+
popup.contains("Set up agent sandbox"),
2087+
"expected startup prompt to offer agent sandbox setup: {popup}"
2088+
);
2089+
assert!(
2090+
popup.contains("Stay in"),
2091+
"expected startup prompt to offer staying in current mode: {popup}"
20882092
);
20892093

20902094
set_windows_sandbox_enabled(true);

codex-rs/tui/src/slash_command.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub enum SlashCommand {
1414
// more frequently used commands should be listed first.
1515
Model,
1616
Approvals,
17+
#[strum(serialize = "setup-elevated-sandbox")]
1718
ElevateSandbox,
1819
Experimental,
1920
Skills,
@@ -55,7 +56,7 @@ impl SlashCommand {
5556
SlashCommand::Ps => "list background terminals",
5657
SlashCommand::Model => "choose what model and reasoning effort to use",
5758
SlashCommand::Approvals => "choose what Codex can do without approval",
58-
SlashCommand::ElevateSandbox => "upgrade to the elevated Windows sandbox",
59+
SlashCommand::ElevateSandbox => "set up elevated agent sandbox",
5960
SlashCommand::Experimental => "toggle beta features",
6061
SlashCommand::Mcp => "list configured MCP tools",
6162
SlashCommand::Logout => "log out of Codex",

codex-rs/tui2/src/app.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,10 +1696,10 @@ impl App {
16961696
self.chat_widget.add_info_message(
16971697
match mode {
16981698
WindowsSandboxEnableMode::Elevated => {
1699-
"Enabled elevated Windows sandbox.".to_string()
1699+
"Enabled elevated agent sandbox.".to_string()
17001700
}
17011701
WindowsSandboxEnableMode::Legacy => {
1702-
"Enabled degraded Windows sandbox.".to_string()
1702+
"Enabled non-elevated agent sandbox.".to_string()
17031703
}
17041704
},
17051705
None,

codex-rs/tui2/src/app_event.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ pub(crate) enum WindowsSandboxEnableMode {
2424
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2525
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
2626
pub(crate) enum WindowsSandboxFallbackReason {
27-
DeclinedElevation,
2827
ElevationFailed,
2928
}
3029

0 commit comments

Comments
 (0)