From 09279a428659cf41824737d3e0c97bcc19a8885a Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Fri, 10 Apr 2026 13:24:57 -0700 Subject: feat: Client-tool execution + permission system (#3370) Adds client-side tool execution to Atuin AI, starting with `atuin_history`. The server can request tool calls, which are executed locally with a permission system, and results are sent back to continue the conversation. --- crates/atuin-ai/src/permissions/resolver.rs | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 crates/atuin-ai/src/permissions/resolver.rs (limited to 'crates/atuin-ai/src/permissions/resolver.rs') diff --git a/crates/atuin-ai/src/permissions/resolver.rs b/crates/atuin-ai/src/permissions/resolver.rs new file mode 100644 index 00000000..dc4f83bf --- /dev/null +++ b/crates/atuin-ai/src/permissions/resolver.rs @@ -0,0 +1,31 @@ +use std::path::PathBuf; + +use eyre::Result; + +use crate::permissions::check::{PermissionChecker, PermissionRequest, PermissionResponse}; +use crate::permissions::walker::PermissionWalker; +use crate::permissions::writer; +use crate::tools::ClientToolCall; + +/// Resolves permissions for client tool calls by walking the filesystem to find permission files, +pub(crate) struct PermissionResolver { + checker: PermissionChecker, +} + +impl PermissionResolver { + /// Create a new resolver that walks from `working_dir` to root for project + /// permissions, and also checks the global permissions file. + pub async fn new(working_dir: PathBuf) -> Result { + let global_file = writer::global_permissions_path(); + let mut walker = PermissionWalker::new(working_dir, Some(global_file)); + walker.walk().await?; + let checker = PermissionChecker::new(walker.rules().to_owned()); + Ok(Self { checker }) + } + + /// Check whether `tool` is allowed, denied, or needs user confirmation. + pub async fn check(&self, tool: &ClientToolCall) -> Result { + let request = PermissionRequest::new(tool); + self.checker.check(&request).await + } +} -- cgit v1.3.1