feat: livrable ticket #96 — effective permissions pour assistants de ticket

This commit is contained in:
2026-07-27 13:37:08 +02:00
parent 865f90eafd
commit e74a9703f9
6 changed files with 343 additions and 29 deletions

View File

@ -9,8 +9,9 @@ use domain::ports::{
use domain::profile::StructuredAdapter;
use domain::AgentProfile;
use domain::{
AgentToolPolicy, AgentToolPolicyStore, AssistantContextProvider, DomainEvent, EventBus,
IssueRef, IssueStore, NetworkPolicy, ProfileId, ProfileStore, Project, SessionId,
AgentToolPolicy, AgentToolPolicyStore, AssistantContextProvider, DomainEvent,
EffectivePermissions, EventBus, IssueRef, IssueStore, NetworkPolicy, PermissionStore,
ProfileId, ProfileStore, Project, SessionId,
};
use crate::terminal::StructuredSessions;
@ -26,6 +27,7 @@ pub struct OpenTicketAssistant {
structured: Arc<StructuredSessions>,
policies: Arc<dyn AgentToolPolicyStore>,
events: Arc<dyn EventBus>,
permissions: Option<Arc<dyn PermissionStore>>,
system_permissions: Option<Arc<dyn SystemPermissionStore>>,
}
@ -73,10 +75,21 @@ impl OpenTicketAssistant {
structured,
policies,
events,
permissions: None,
system_permissions: None,
}
}
/// Wires the project agent permission store used for assistant launches.
///
/// Ticket assistants are not agents, so only project defaults are resolved;
/// agent overrides are deliberately ignored.
#[must_use]
pub fn with_permission_store(mut self, store: Arc<dyn PermissionStore>) -> Self {
self.permissions = Some(store);
self
}
/// Wires the project system permission store used for assistant launches.
#[must_use]
pub fn with_system_permission_store(mut self, store: Arc<dyn SystemPermissionStore>) -> Self {
@ -115,6 +128,9 @@ impl OpenTicketAssistant {
.structured
.ticket_assistant_requester(input.issue_ref)
.is_some();
let permissions = self
.resolve_project_default_permissions(&input.project)
.await?;
let policy = AgentToolPolicy::new(
vec![
"idea_ticket_read".to_owned(),
@ -138,6 +154,7 @@ impl OpenTicketAssistant {
&profile,
&prepared,
&requester,
permissions.as_ref(),
)
.await
{
@ -227,6 +244,20 @@ impl OpenTicketAssistant {
let doc = store.load_system_permissions(project).await?;
Ok(doc.project_default.and_then(|set| set.network))
}
async fn resolve_project_default_permissions(
&self,
project: &Project,
) -> Result<Option<EffectivePermissions>, AppError> {
let Some(store) = &self.permissions else {
return Ok(None);
};
let doc = store.load_permissions(project).await?;
Ok(domain::permission::resolve(
doc.project_defaults.as_ref(),
None,
))
}
}
fn codex_network_access(network: Option<NetworkPolicy>) -> bool {