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:
Won Park authored and copyberry committed 2026-08-05 08:04:15 +00:00
1 parent 3171166881
commit 757c151a0e
35 files changed
+536 -39

No files matched your search

+27 -8
View File
@@ -1150,11 +1150,15 @@ impl App {
.await;
}
AppEvent::UpdateModel(model) => {
self.chat_widget.set_model(&model);
self.sync_active_thread_model_setting(app_server, model)
.await;
self.sync_active_thread_service_tier_to_cached_session()
.await;
let model_changed = self.chat_widget.current_model() != model
|| self.chat_widget.current_collaboration_mode().model() != model;
if model_changed {
self.chat_widget.set_model(&model);
self.sync_active_thread_model_setting(app_server, model, /*effort*/ None)
.await;
self.sync_active_thread_service_tier_to_cached_session()
.await;
}
}
AppEvent::UpdatePersonality(personality) => {
self.on_update_personality(personality);
@@ -1178,12 +1182,22 @@ impl App {
self.chat_widget.open_advanced_reasoning_popup(model);
}
AppEvent::ApplyAdvancedReasoning { model, effort } => {
let model_changed = self.chat_widget.current_model() != model
|| self.chat_widget.current_collaboration_mode().model() != model;
let default_effort =
self.on_apply_advanced_reasoning(model.as_str(), effort.clone());
if let Some(mut params) =
self.active_thread_model_setting_update_params(model.clone())
if model_changed {
self.sync_active_thread_model_setting(
app_server,
model.clone(),
Some(effort.clone()),
)
.await;
} else if let Some(mut params) =
self.active_thread_reasoning_setting_update_params(Some(effort.clone()))
{
params.effort = Some(effort.clone());
params.collaboration_mode =
Some(self.chat_widget.effective_collaboration_mode());
self.send_thread_settings_update(app_server, params).await;
}
self.sync_active_thread_service_tier_to_cached_session()
@@ -1756,6 +1770,11 @@ impl App {
}
}
}
AppEvent::CyberModelAutoReviewNotice => {
self.chat_widget.add_warning_message(
"Cyber models default to \"Approve for me\" for safety reasons.".to_string(),
);
}
AppEvent::PluginUninstallLoaded {
cwd,
plugin_id: _plugin_id,