use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionUpdate {
pub tool: Option<String>,
pub allow: Option<Vec<String>>,
pub deny: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BridgePermissionResponse {
pub behavior: BridgePermissionBehavior,
#[serde(rename = "updatedInput")]
pub updated_input: Option<serde_json::Value>,
#[serde(rename = "updatedPermissions")]
pub updated_permissions: Option<Vec<PermissionUpdate>>,
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum BridgePermissionBehavior {
Allow,
Deny,
}
pub trait BridgePermissionCallbacks: Send + Sync {
fn send_request(
&self,
request_id: &str,
tool_name: &str,
input: &serde_json::Value,
tool_use_id: &str,
description: &str,
permission_suggestions: Option<&[PermissionUpdate]>,
blocked_path: Option<&str>,
);
fn send_response(&self, request_id: &str, response: &BridgePermissionResponse);
fn cancel_request(&self, request_id: &str);
fn on_response(
&self,
request_id: &str,
handler: Box<dyn Fn(BridgePermissionResponse) + Send + Sync>,
) -> Box<dyn Fn() + Send + Sync>;
}
pub fn is_bridge_permission_response(value: &serde_json::Value) -> bool {
let obj = match value.as_object() {
Some(o) => o,
None => return false,
};
obj.get("behavior")
.and_then(|v| v.as_str())
.map(|s| s == "allow" || s == "deny")
.unwrap_or(false)
}
pub struct InMemoryBridgePermissionCallbacks {
responses: std::sync::Mutex<std::collections::HashMap<String, BridgePermissionResponse>>,
response_handlers: std::sync::Mutex<
std::collections::HashMap<String, Vec<Box<dyn Fn(BridgePermissionResponse) + Send + Sync>>>,
>,
}
impl InMemoryBridgePermissionCallbacks {
pub fn new() -> Self {
Self {
responses: std::sync::Mutex::new(std::collections::HashMap::new()),
response_handlers: std::sync::Mutex::new(std::collections::HashMap::new()),
}
}
}
impl Default for InMemoryBridgePermissionCallbacks {
fn default() -> Self {
Self::new()
}
}
impl BridgePermissionCallbacks for InMemoryBridgePermissionCallbacks {
fn send_request(
&self,
request_id: &str,
tool_name: &str,
input: &serde_json::Value,
tool_use_id: &str,
description: &str,
_permission_suggestions: Option<&[PermissionUpdate]>,
_blocked_path: Option<&str>,
) {
println!(
"[Permission] Request: {} tool={} tool_use_id={} description={} input={}",
request_id, tool_name, tool_use_id, description, input
);
}
fn send_response(&self, request_id: &str, response: &BridgePermissionResponse) {
let mut responses = self.responses.lock().unwrap();
responses.insert(request_id.to_string(), response.clone());
let handlers = self.response_handlers.lock().unwrap();
if let Some(handler_list) = handlers.get(request_id) {
for handler in handler_list {
handler(response.clone());
}
}
}
fn cancel_request(&self, request_id: &str) {
println!("[Permission] Cancel request: {}", request_id);
}
fn on_response(
&self,
request_id: &str,
handler: Box<dyn Fn(BridgePermissionResponse) + Send + Sync>,
) -> Box<dyn Fn() + Send + Sync> {
let mut handlers = self.response_handlers.lock().unwrap();
handlers
.entry(request_id.to_string())
.or_insert_with(Vec::new)
.push(handler);
Box::new(move || {
})
}
}