use agent_client_protocol::schema::v1::{
PermissionOption, PermissionOptionKind, ToolCallUpdate, ToolCallUpdateFields,
};
use pretty_assertions::assert_eq;
use serde_json::json;
use super::*;
use crate::{agent::CursorTool, cursor_runtime::acp_config::fence};
#[test]
fn permission_decision_table() {
use CursorTool as T;
use ToolKind as K;
let cases = [
("tool", Some(K::Execute), T::Shell),
("web_fetch_0", Some(K::Fetch), T::WebFetch),
("tool", Some(K::Edit), T::Edit),
("tool", Some(K::Delete), T::Edit),
("tool", Some(K::Move), T::ApplyAgentDiff),
("tool", Some(K::Read), T::Read),
("tool", Some(K::Search), T::Grep),
("web_search_q1", Some(K::Search), T::WebSearch),
("tool", Some(K::Other), T::Mcp),
];
for (id, kind, member) in cases {
let request = RequestPermissionRequest::new(
"session",
ToolCallUpdate::new(
id,
ToolCallUpdateFields::new().kind(kind).title("server: tool"),
),
vec![PermissionOption::new(
"allow-once",
"Allow once",
PermissionOptionKind::AllowOnce,
)],
);
let other = if member == T::Grep { T::Read } else { T::Grep };
for (tools, expected) in [
(vec![member], PermissionDecision::AllowOnce),
(vec![], PermissionDecision::Reject),
(vec![other], PermissionDecision::Reject),
] {
assert_eq!(
decide(
&fence(PermissionMode::Bypass, &tools),
PermissionMode::Bypass,
&request
),
expected,
"kind {kind:?}, tools {tools:?}"
);
}
let full = fence(PermissionMode::Bypass, T::ALL);
for mode in [
PermissionMode::Plan,
PermissionMode::Auto,
PermissionMode::AllowEdits,
PermissionMode::Supervised,
] {
assert_eq!(
decide(&full, mode, &request),
PermissionDecision::Reject,
"mode {mode:?}, kind {kind:?}"
);
}
assert_eq!(
decide(
&fence(PermissionMode::Plan, T::ALL),
PermissionMode::Bypass,
&request
),
PermissionDecision::Reject
);
}
}
#[test]
fn only_described_other_requests_count_as_mcp() {
let full_mcp = fence(
PermissionMode::Bypass,
&[
CursorTool::Mcp,
CursorTool::ListMcpResources,
CursorTool::ReadMcpResource,
],
);
let future_kind: ToolKind = serde_json::from_value(json!("future_kind")).unwrap();
for (kind, title, expected) in [
(
Some(ToolKind::Other),
"server: tool",
PermissionDecision::AllowOnce,
),
(
Some(future_kind),
"server: tool",
PermissionDecision::AllowOnce,
),
(
Some(ToolKind::Other),
"Unknown operation",
PermissionDecision::Reject,
),
(Some(ToolKind::Other), ": tool", PermissionDecision::Reject),
(
Some(ToolKind::Think),
"server: tool",
PermissionDecision::Reject,
),
(
Some(ToolKind::SwitchMode),
"server: tool",
PermissionDecision::Reject,
),
(None, "server: tool", PermissionDecision::Reject),
] {
let request = RequestPermissionRequest::new(
"session",
ToolCallUpdate::new("tool", ToolCallUpdateFields::new().kind(kind).title(title)),
vec![PermissionOption::new(
"allow-once",
"Allow once",
PermissionOptionKind::AllowOnce,
)],
);
assert_eq!(
decide(&full_mcp, PermissionMode::Bypass, &request),
expected,
"kind {kind:?}, title {title}"
);
}
}
#[test]
fn ask_question_permission_fallback_is_skipped() {
let request = RequestPermissionRequest::new(
"session",
ToolCallUpdate::new(
"question",
ToolCallUpdateFields::new().kind(ToolKind::Other),
),
vec![
PermissionOption::new("answer-a", "A", PermissionOptionKind::AllowOnce),
PermissionOption::new("answer-b", "B", PermissionOptionKind::AllowOnce),
PermissionOption::new(
"__ask_question_skip__",
"Skip",
PermissionOptionKind::RejectOnce,
),
],
);
for mode in [PermissionMode::Bypass, PermissionMode::Plan] {
assert_eq!(
decide(&fence(mode, CursorTool::ALL), mode, &request),
PermissionDecision::Reject
);
}
}