use std::sync::mpsc::Sender;
use agent_client_protocol::schema::v1::{PermissionOptionKind, RequestPermissionRequest};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PermissionDecision {
Cancelled,
Selected(String),
}
#[derive(Clone, Debug)]
pub struct PendingPermission {
pub tool_call_id: String,
pub title: String,
pub options: Vec<PermissionOptionView>,
pub selected: usize,
pub responder: Sender<PermissionDecision>,
}
impl PendingPermission {
pub fn from_request(request: &RequestPermissionRequest, responder: Sender<PermissionDecision>) -> Self {
let title = request
.tool_call
.fields
.title
.clone()
.unwrap_or_else(|| request.tool_call.tool_call_id.to_string());
let options = request
.options
.iter()
.map(|option| PermissionOptionView {
id: option.option_id.to_string(),
name: option.name.clone(),
kind: PermissionKindView::from(option.kind),
})
.collect();
Self { tool_call_id: request.tool_call.tool_call_id.to_string(), title, options, selected: 0, responder }
}
pub fn move_up(&mut self) {
self.selected = self.selected.saturating_sub(1);
}
pub fn move_down(&mut self) {
self.selected = (self.selected + 1).min(self.options.len().saturating_sub(1));
}
pub fn select(&self) -> Option<PermissionDecision> {
let option = self.options.get(self.selected)?;
let decision = PermissionDecision::Selected(option.id.clone());
let _ = self.responder.send(decision.clone());
Some(decision)
}
pub fn cancel(&self) -> PermissionDecision {
let decision = PermissionDecision::Cancelled;
let _ = self.responder.send(decision.clone());
decision
}
}
impl PartialEq for PendingPermission {
fn eq(&self, other: &Self) -> bool {
self.tool_call_id == other.tool_call_id
&& self.title == other.title
&& self.options == other.options
&& self.selected == other.selected
}
}
impl Eq for PendingPermission {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PermissionOptionView {
pub id: String,
pub name: String,
pub kind: PermissionKindView,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PermissionKindView {
AllowOnce,
AllowAlways,
RejectOnce,
RejectAlways,
Unknown,
}
impl PermissionKindView {
pub fn label(self) -> &'static str {
match self {
PermissionKindView::AllowOnce => "allow once",
PermissionKindView::AllowAlways => "allow always",
PermissionKindView::RejectOnce => "reject once",
PermissionKindView::RejectAlways => "reject always",
PermissionKindView::Unknown => "unknown",
}
}
}
impl From<PermissionOptionKind> for PermissionKindView {
fn from(value: PermissionOptionKind) -> Self {
match value {
PermissionOptionKind::AllowOnce => PermissionKindView::AllowOnce,
PermissionOptionKind::AllowAlways => PermissionKindView::AllowAlways,
PermissionOptionKind::RejectOnce => PermissionKindView::RejectOnce,
PermissionOptionKind::RejectAlways => PermissionKindView::RejectAlways,
_ => PermissionKindView::Unknown,
}
}
}