feat: AI based permission checks
This commit is contained in:
@@ -1225,12 +1225,34 @@ impl App {
|
||||
self.agent_tools = Some((session_id, Arc::new(Mutex::new(tools))));
|
||||
}
|
||||
let tools = Arc::clone(&self.agent_tools.as_ref().unwrap().1);
|
||||
let approval_mode = match self.permission_mode {
|
||||
PermissionMode::Heuristic => crate::agent::ShellApprovalMode::Heuristic,
|
||||
PermissionMode::Ai => {
|
||||
let effective = crate::settings::effective_settings(
|
||||
self.config.model,
|
||||
&self.config.generation,
|
||||
&self.config.runtime,
|
||||
&models_path(),
|
||||
)?;
|
||||
let service = self
|
||||
.generation_service
|
||||
.as_ref()
|
||||
.ok_or_else(|| "The model runtime is unavailable.".to_owned())?;
|
||||
crate::agent::ShellApprovalMode::Ai(Box::new(crate::agent::AiRiskClassifier::new(
|
||||
service.clone(),
|
||||
effective.engine,
|
||||
effective.turn,
|
||||
transient_cache_path(),
|
||||
Duration::from_secs(self.config.idle_timeout_minutes.max(1) as u64 * 60),
|
||||
)))
|
||||
}
|
||||
};
|
||||
self.tool_cards = calls
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(crate::agent::ToolCard::parsing)
|
||||
.collect();
|
||||
self.active_tools = Some(crate::agent::execute_async(tools, calls));
|
||||
self.active_tools = Some(crate::agent::execute_async(tools, calls, approval_mode));
|
||||
self.activity = Some("Parsing tool calls…".into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::sync::RwLock;
|
||||
#[derive(Clone)]
|
||||
pub(super) struct PreferenceDraft {
|
||||
pub(super) model: ModelChoice,
|
||||
pub(super) default_permission_mode: PermissionMode,
|
||||
pub(super) legacy_mtp_enabled: bool,
|
||||
pub(super) dspark_enabled: bool,
|
||||
pub(super) idle_timeout_minutes: String,
|
||||
@@ -63,6 +64,7 @@ impl PreferenceDraft {
|
||||
let speculative = &runtime.speculative;
|
||||
Self {
|
||||
model: config.model,
|
||||
default_permission_mode: config.default_permission_mode,
|
||||
legacy_mtp_enabled: speculative.legacy_mtp_enabled,
|
||||
dspark_enabled: speculative.dspark_enabled,
|
||||
idle_timeout_minutes: config.idle_timeout_minutes.to_string(),
|
||||
@@ -407,6 +409,7 @@ impl App {
|
||||
};
|
||||
let config = Config {
|
||||
model: self.preference_draft.model,
|
||||
default_permission_mode: self.preference_draft.default_permission_mode,
|
||||
idle_timeout_minutes,
|
||||
a2ui_enabled: self.preference_draft.a2ui_enabled,
|
||||
endpoint: EndpointConfig {
|
||||
@@ -524,6 +527,10 @@ impl App {
|
||||
}
|
||||
self.preference_error = None;
|
||||
}
|
||||
Message::PreferencePermissionModeChanged(mode) => {
|
||||
self.preference_draft.default_permission_mode = mode;
|
||||
self.preference_error = None;
|
||||
}
|
||||
Message::PreferenceLegacyMtpChanged(enabled) => {
|
||||
self.preference_draft.legacy_mtp_enabled =
|
||||
self.preference_draft.model.supports_dspark() && enabled;
|
||||
|
||||
@@ -171,6 +171,7 @@ impl App {
|
||||
self.drafts.entry(project_id).or_insert(title);
|
||||
self.remember_project(project_id);
|
||||
self.selected_session = None;
|
||||
self.permission_mode = self.config.default_permission_mode;
|
||||
self.conversation.clear();
|
||||
self.chat_follow_tail = true;
|
||||
self.context_notice = None;
|
||||
@@ -211,7 +212,7 @@ impl App {
|
||||
.database
|
||||
.as_mut()
|
||||
.ok_or_else(|| "The project database is unavailable.".to_owned())?;
|
||||
let session = database.create_session(project_id, &title)?;
|
||||
let session = database.create_session(project_id, &title, self.permission_mode)?;
|
||||
self.drafts.remove(&project_id);
|
||||
self.remember_project(project_id);
|
||||
self.selected_session = Some(session.id);
|
||||
|
||||
@@ -12,7 +12,9 @@ use super::{
|
||||
ModelDownload, ModelOperation, PreferenceSection, ProjectChoice, chat_scroll_id, composer_id,
|
||||
models_path, preferences_scroll_id,
|
||||
};
|
||||
use crate::config::{GIT_DIFF_ALGORITHMS, GIT_DIFF_LAYOUTS, GIT_DIFF_WHITESPACE_MODES};
|
||||
use crate::config::{
|
||||
GIT_DIFF_ALGORITHMS, GIT_DIFF_LAYOUTS, GIT_DIFF_WHITESPACE_MODES, PERMISSION_MODES,
|
||||
};
|
||||
use crate::database::{ProjectWithSessions, Session, SessionState};
|
||||
use crate::model::{
|
||||
self, DownloadPhase, MODEL_CHOICES, ManagedArtifact, ManagedArtifactState, ModelChoice,
|
||||
|
||||
@@ -334,6 +334,13 @@ impl App {
|
||||
Space::new().width(Length::Fill),
|
||||
project_control,
|
||||
branch_control,
|
||||
pick_list(
|
||||
&PERMISSION_MODES[..],
|
||||
Some(self.permission_mode),
|
||||
Message::PermissionModeChanged,
|
||||
)
|
||||
.text_size(12)
|
||||
.padding([2, 6]),
|
||||
icon(ICON_MODEL, 16),
|
||||
text(self.config.model.to_string()).size(12),
|
||||
action,
|
||||
|
||||
@@ -126,6 +126,20 @@ impl App {
|
||||
.on_toggle(Message::PreferenceA2uiChanged),
|
||||
"Lets the local model build validated native charts, tables, forms and other interactive chat surfaces. Turning it off removes the A2UI catalog from the system prompt.",
|
||||
),
|
||||
row![
|
||||
hint(
|
||||
text("Default shell permission mode").size(13).width(Length::Fill),
|
||||
"Heuristic uses the built-in command classifier. AI based asks the local model once before each shell command and prompts when it reports risk.",
|
||||
),
|
||||
pick_list(
|
||||
&PERMISSION_MODES[..],
|
||||
Some(self.preference_draft.default_permission_mode),
|
||||
Message::PreferencePermissionModeChanged,
|
||||
)
|
||||
.width(180),
|
||||
]
|
||||
.spacing(12)
|
||||
.align_y(Alignment::Center),
|
||||
]
|
||||
.spacing(10),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user