Simplify Galaxy permissions and preserve explicit user authorization

This commit is contained in:
2026-10-02 01:58:37 -05:00
parent 5a70995502
commit 2796fdd114
37 changed files with 1818 additions and 3530 deletions
+39 -3
View File
@@ -51,7 +51,7 @@ use self::execute::{
RunningActionPhase, TryExecuteResult,
};
use super::history_model::BlocklistAIHistoryEvent;
use super::BlocklistAIHistoryModel;
use super::{BlocklistAIHistoryModel, BlocklistAIPermissions};
use crate::ai::agent::conversation::{AIConversationId, ConversationStatus};
use crate::ai::agent::{
AIAgentAction, AIAgentActionId, AIAgentActionResult, AIAgentActionResultType,
@@ -878,6 +878,19 @@ impl BlocklistAIActionModel {
}
});
let profiles =
crate::ai::execution_profiles::profiles::AIExecutionProfilesModel::handle(ctx);
ctx.subscribe_to_model(&profiles, |_, _, _, ctx| {
// Re-evaluate queued tools after a mode/profile change. Defer until the
// profile edit and any current approval callback have completed.
ctx.spawn(futures::future::ready(()), |me, (), ctx| {
let conversations = me.pending_actions.keys().copied().collect::<Vec<_>>();
for conversation_id in conversations {
me.try_to_execute_available_actions(conversation_id, ctx);
}
});
});
let history_model = BlocklistAIHistoryModel::handle(ctx);
ctx.subscribe_to_model(&history_model, |me, _, event, ctx| match event {
BlocklistAIHistoryEvent::RemoveConversation {
@@ -1674,6 +1687,15 @@ impl BlocklistAIActionModel {
return None;
}
}
if BlocklistAIPermissions::as_ref(ctx).get_permission_mode(ctx, Some(self.terminal_view_id))
== crate::ai::execution_profiles::PermissionMode::ReadOnly
&& !risk_review::is_read_only_inspection(
&self.pending_actions[&conversation_id][idx].action,
)
{
let candidate = self.pending_actions[&conversation_id][idx].clone();
self.review_pending_action(&candidate, conversation_id, is_user_initiated, ctx)?;
}
if let Some(in_flight) = self.deferred_provider_preprocessing.get_mut(&key) {
if *in_flight {
return None;
@@ -1980,6 +2002,12 @@ impl BlocklistAIActionModel {
{
self.deferred_provider_preprocessing
.insert((conversation_id, action.id.clone()), false);
} else if BlocklistAIPermissions::as_ref(ctx)
.get_permission_mode(ctx, Some(self.terminal_view_id))
== crate::ai::execution_profiles::PermissionMode::ReadOnly
&& !risk_review::is_read_only_inspection(&action.action)
{
// A prohibited action must not perform even preprocessing in Read Only.
} else {
preprocess_future.push(self.preprocess_action(action, conversation_id, ctx));
}
@@ -2760,6 +2788,10 @@ fn provider_action_correlations(
#[derive(Debug, Clone)]
pub enum BlocklistAIActionEvent {
PermissionReviewChanged {
conversation_id: AIConversationId,
action_id: AIAgentActionId,
},
/// Emitted when the action with the given ID is enqueued for execution.
QueuedAction {
action_id: AIAgentActionId,
@@ -2803,7 +2835,8 @@ pub enum BlocklistAIActionEvent {
impl BlocklistAIActionEvent {
pub fn action_id(&self) -> &AIAgentActionId {
match self {
BlocklistAIActionEvent::QueuedAction { action_id, .. }
BlocklistAIActionEvent::PermissionReviewChanged { action_id, .. }
| BlocklistAIActionEvent::QueuedAction { action_id, .. }
| BlocklistAIActionEvent::ActionBlockedOnUserConfirmation { action_id, .. }
| BlocklistAIActionEvent::ExecutingAction { action_id, .. }
| BlocklistAIActionEvent::FinishedAction { action_id, .. } => action_id,
@@ -2816,7 +2849,10 @@ impl BlocklistAIActionEvent {
pub fn conversation_id(&self) -> Option<AIConversationId> {
match self {
BlocklistAIActionEvent::ActionBlockedOnUserConfirmation {
BlocklistAIActionEvent::PermissionReviewChanged {
conversation_id, ..
}
| BlocklistAIActionEvent::ActionBlockedOnUserConfirmation {
conversation_id, ..
}
| BlocklistAIActionEvent::ExecutingAction {