mirror of
https://github.com/openai/codex.git
synced 2026-11-03 16:23:51 +00:00
Add safer TUI defaults for cyber models (#37055)
## What changed - Propagate the optional `modelSpecialty` field through the model catalog and app-server `model/list` response. - When a newly selected model has the `cyber` specialty, default an active TUI thread to workspace-write permissions with on-request approval. Use auto review when available, otherwise keep the user as reviewer, while respecting configured permission requirements. - Show a notice when auto review is applied and strengthen the full-access warning for cyber models. - Preserve explicitly selected permissions when only reasoning settings change. ## Testing - Cover auto-review defaults, requirement-aware fallback behavior, permission preservation across reasoning changes, and the new warning notice. GitOrigin-RevId: 8f25b0082d3051d9ff91f7355921236d0f82f758
This commit is contained in:
1 parent
3171166881
commit
757c151a0e
35 files changed
+536
-39
No files matched your search
@@ -407,19 +407,45 @@ impl ChatWidget {
|
||||
) {
|
||||
let selected_name = preset.label.to_string();
|
||||
let approval = AskForApproval::from(preset.approval);
|
||||
let mut header_children: Vec<Box<dyn Renderable>> = Vec::new();
|
||||
let is_cyber_model = self.model_catalog.try_list_models().is_ok_and(|models| {
|
||||
models.iter().any(|model| {
|
||||
model.model == self.current_model()
|
||||
&& model.model_specialty.as_deref() == Some("cyber")
|
||||
})
|
||||
});
|
||||
let title_line = Line::from("Enable full access?").bold();
|
||||
let info_line = Line::from(vec![
|
||||
"When Codex runs with full access, it can edit any file on your computer and run commands with network, without your approval. "
|
||||
.into(),
|
||||
"Exercise caution when enabling full access. This significantly increases the risk of data loss, leaks, or unexpected behavior."
|
||||
.fg(Color::Red),
|
||||
]);
|
||||
header_children.push(Box::new(title_line));
|
||||
header_children.push(Box::new(
|
||||
Paragraph::new(vec![info_line]).wrap(Wrap { trim: false }),
|
||||
));
|
||||
let header = ColumnRenderable::with(header_children);
|
||||
let info_lines = if is_cyber_model {
|
||||
let recommendation = if auto_review_available(&self.config) {
|
||||
"We strongly recommend selecting \"Approve for me\" instead, and customizing the reviewer policy for your use case."
|
||||
} else {
|
||||
"We strongly recommend selecting \"Ask for approval\" instead."
|
||||
};
|
||||
vec![
|
||||
Line::default(),
|
||||
Line::from(
|
||||
"When Codex runs with full access, it can edit any file on your computer and run commands with network, without your approval.",
|
||||
),
|
||||
Line::default(),
|
||||
Line::from(vec![
|
||||
"Cyber models carry a higher risk of dangerous actions.".red(),
|
||||
" Ensure proper safeguards are in place before granting full access. ".into(),
|
||||
recommendation.into(),
|
||||
]),
|
||||
]
|
||||
} else {
|
||||
vec![Line::from(vec![
|
||||
"When Codex runs with full access, it can edit any file on your computer and run commands with network, without your approval. "
|
||||
.into(),
|
||||
"Exercise caution when enabling full access. This significantly increases the risk of data loss, leaks, or unexpected behavior."
|
||||
.red(),
|
||||
])]
|
||||
};
|
||||
let header = Paragraph::new(
|
||||
std::iter::once(title_line)
|
||||
.chain(info_lines)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.wrap(Wrap { trim: false });
|
||||
|
||||
let accept_actions = profile_selection.map_or_else(
|
||||
|| {
|
||||
|
||||
@@ -1,6 +1,37 @@
|
||||
use super::*;
|
||||
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS;
|
||||
|
||||
pub(crate) fn auto_review_available(config: &Config) -> bool {
|
||||
cyber_model_approval_reviewer(config) == Some(ApprovalsReviewer::AutoReview)
|
||||
}
|
||||
|
||||
pub(crate) fn cyber_model_approval_reviewer(config: &Config) -> Option<ApprovalsReviewer> {
|
||||
let requirements = config.config_layer_stack.requirements();
|
||||
if requirements
|
||||
.approval_policy
|
||||
.can_set(&AskForApproval::OnRequest.to_core())
|
||||
.is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let reviewer = if config.features.enabled(Feature::GuardianApproval)
|
||||
&& requirements
|
||||
.approvals_reviewer
|
||||
.can_set(&ApprovalsReviewer::AutoReview)
|
||||
.is_ok()
|
||||
{
|
||||
ApprovalsReviewer::AutoReview
|
||||
} else {
|
||||
ApprovalsReviewer::User
|
||||
};
|
||||
requirements
|
||||
.approvals_reviewer
|
||||
.can_set(&reviewer)
|
||||
.is_ok()
|
||||
.then_some(reviewer)
|
||||
}
|
||||
|
||||
impl ChatWidget {
|
||||
pub(super) fn open_permission_profiles_popup(&mut self) {
|
||||
let active_profile_id = self
|
||||
|
||||
@@ -3141,6 +3141,7 @@ async fn model_picker_hides_show_in_picker_false_models_from_cache() {
|
||||
model: slug.to_string(),
|
||||
display_name: slug.to_string(),
|
||||
description: format!("{slug} description"),
|
||||
model_specialty: None,
|
||||
default_reasoning_effort: ReasoningEffortConfig::Medium,
|
||||
supported_reasoning_efforts: vec![ReasoningEffortPreset {
|
||||
effort: ReasoningEffortConfig::Medium,
|
||||
@@ -3635,6 +3636,7 @@ async fn single_reasoning_option_skips_selection() {
|
||||
model: "model-with-single-reasoning".to_string(),
|
||||
display_name: "model-with-single-reasoning".to_string(),
|
||||
description: "".to_string(),
|
||||
model_specialty: None,
|
||||
default_reasoning_effort: ReasoningEffortConfig::High,
|
||||
supported_reasoning_efforts: single_effort,
|
||||
supports_personality: false,
|
||||
|
||||
@@ -106,8 +106,7 @@ impl ChatWidget {
|
||||
Line::from(vec![
|
||||
"We couldn't complete the world-writable scan, so protections cannot be verified. "
|
||||
.into(),
|
||||
format!("The Windows sandbox cannot guarantee protection in {mode_label}.")
|
||||
.fg(Color::Red),
|
||||
format!("The Windows sandbox cannot guarantee protection in {mode_label}.").red(),
|
||||
])
|
||||
} else {
|
||||
Line::from(vec![
|
||||
|
||||
Reference in new issue
Block a user