@@ -3,7 +3,7 @@ use agent_client_protocol_schema::ProtocolVersion;
33use serde:: { Deserialize , Serialize } ;
44use serde_json:: Value ;
55use std:: collections:: HashMap ;
6- use std:: sync:: atomic:: { AtomicBool , Ordering } ;
6+ use std:: sync:: atomic:: { AtomicBool , AtomicU8 , Ordering } ;
77use std:: sync:: Arc ;
88use std:: path:: PathBuf ;
99use tokio:: process:: Command ;
@@ -15,6 +15,58 @@ use anyhow::{Result, anyhow};
1515use crate :: utils:: relative_to_current_dir;
1616use crate :: acp_history:: AcpHistoryManager ;
1717
18+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
19+ #[ repr( u8 ) ]
20+ pub enum AcpPermissionMode {
21+ Ask = 0 ,
22+ FullAccess = 1 ,
23+ }
24+
25+ impl AcpPermissionMode {
26+ pub fn from_env ( ) -> Self {
27+ let raw = std:: env:: var ( "ANYCODE_ACP_PERMISSION_MODE" )
28+ . unwrap_or_else ( |_| "full_access" . to_string ( ) ) ;
29+ Self :: from_str ( & raw ) . unwrap_or_else ( || {
30+ error ! (
31+ "Unknown ANYCODE_ACP_PERMISSION_MODE value '{}', defaulting to full_access" ,
32+ raw
33+ ) ;
34+ Self :: FullAccess
35+ } )
36+ }
37+
38+ pub fn from_str ( value : & str ) -> Option < Self > {
39+ match value. to_lowercase ( ) . as_str ( ) {
40+ "ask" => Some ( Self :: Ask ) ,
41+ "full_access" | "full-access" | "fullaccess" => Some ( Self :: FullAccess ) ,
42+ _ => None ,
43+ }
44+ }
45+
46+ pub fn as_str ( self ) -> & ' static str {
47+ match self {
48+ Self :: Ask => "ask" ,
49+ Self :: FullAccess => "full_access" ,
50+ }
51+ }
52+
53+ pub fn is_full_access ( self ) -> bool {
54+ matches ! ( self , Self :: FullAccess )
55+ }
56+
57+ pub fn as_atomic ( self ) -> u8 {
58+ self as u8
59+ }
60+
61+ pub fn from_atomic ( value : u8 ) -> Self {
62+ match value {
63+ 0 => Self :: Ask ,
64+ 1 => Self :: FullAccess ,
65+ _ => Self :: FullAccess ,
66+ }
67+ }
68+ }
69+
1870#[ derive( Debug , Clone , Serialize , Deserialize ) ]
1971pub struct AcpUserMessage {
2072 pub content : String ,
@@ -130,6 +182,7 @@ pub struct PermissionResponse {
130182
131183struct AcpClientImpl {
132184 agent_id : String ,
185+ permission_mode : Arc < AtomicU8 > ,
133186 message_sender : broadcast:: Sender < AcpMessage > ,
134187 history : Arc < tokio:: sync:: Mutex < Vec < AcpMessage > > > ,
135188 /// Pending permission requests waiting for user response
@@ -143,6 +196,40 @@ impl Client for AcpClientImpl {
143196 args : acp:: RequestPermissionRequest ,
144197 ) -> acp:: Result < acp:: RequestPermissionResponse > {
145198 info ! ( "request_permission called for agent {}: {:?}" , self . agent_id, args) ;
199+
200+ let permission_mode = AcpPermissionMode :: from_atomic ( self . permission_mode . load ( Ordering :: Relaxed ) ) ;
201+ if permission_mode. is_full_access ( ) {
202+ let selected_option = args. options . iter ( ) . find ( |opt| {
203+ let name = opt. name . to_lowercase ( ) ;
204+ name. contains ( "allow" )
205+ || name. contains ( "approve" )
206+ || name. contains ( "accept" )
207+ || name. contains ( "grant" )
208+ || name. contains ( "yes" )
209+ || name. contains ( "continue" )
210+ || name. contains ( "proceed" )
211+ } ) . or_else ( || args. options . first ( ) ) ;
212+
213+ if let Some ( option) = selected_option {
214+ info ! (
215+ "Auto-approving permission for agent {} in full_access mode: {}" ,
216+ self . agent_id,
217+ option. name
218+ ) ;
219+ let selected_outcome = acp:: SelectedPermissionOutcome :: new ( option. option_id . clone ( ) ) ;
220+ return Ok ( acp:: RequestPermissionResponse :: new (
221+ acp:: RequestPermissionOutcome :: Selected ( selected_outcome) ,
222+ ) ) ;
223+ }
224+
225+ error ! (
226+ "Full access mode but no permission options were returned for agent {}" ,
227+ self . agent_id
228+ ) ;
229+ return Ok ( acp:: RequestPermissionResponse :: new (
230+ acp:: RequestPermissionOutcome :: Cancelled ,
231+ ) ) ;
232+ }
146233
147234 // Handle tool_call if present
148235 let tool_call_update = & args. tool_call ;
@@ -729,6 +816,7 @@ pub type PendingPermissionsMap = Arc<tokio::sync::Mutex<HashMap<String, tokio::s
729816pub struct AcpAgent {
730817 agent_id : String ,
731818 agent_name : String ,
819+ permission_mode : Arc < AtomicU8 > ,
732820 connection : Option < acp:: ClientSideConnection > ,
733821 session_id : Option < acp:: SessionId > ,
734822 ready : Arc < AtomicBool > ,
@@ -744,7 +832,7 @@ pub struct AcpAgent {
744832}
745833
746834impl AcpAgent {
747- pub fn new ( agent_id : String , agent_name : String ) -> Self {
835+ pub fn new ( agent_id : String , agent_name : String , permission_mode : Arc < AtomicU8 > ) -> Self {
748836 // Initialize history manager with current working directory and agent ID
749837 let project_root = std:: env:: current_dir ( ) . unwrap_or_else ( |_| PathBuf :: from ( "." ) ) ;
750838 let mut history_manager = AcpHistoryManager :: new ( & project_root, & agent_id) ;
@@ -755,6 +843,7 @@ impl AcpAgent {
755843 Self {
756844 agent_id,
757845 agent_name,
846+ permission_mode,
758847 connection : None ,
759848 session_id : None ,
760849 ready : Arc :: new ( AtomicBool :: new ( false ) ) ,
@@ -794,6 +883,7 @@ impl AcpAgent {
794883 let history_clone = self . history . clone ( ) ;
795884 let message_sender_clone = history_tx. clone ( ) ;
796885 let pending_permissions_clone = self . pending_permissions . clone ( ) ;
886+ let permission_mode_clone = self . permission_mode . clone ( ) ;
797887
798888 let local_set_handle = tokio:: task:: spawn_blocking ( move || {
799889 tokio:: runtime:: Handle :: current ( ) . block_on ( async {
@@ -805,6 +895,7 @@ impl AcpAgent {
805895 history_clone,
806896 message_sender_clone,
807897 pending_permissions_clone,
898+ permission_mode_clone,
808899 stdin,
809900 stdout,
810901 stderr,
@@ -864,6 +955,7 @@ impl AcpAgent {
864955 history : Arc < tokio:: sync:: Mutex < Vec < AcpMessage > > > ,
865956 message_sender : broadcast:: Sender < AcpMessage > ,
866957 pending_permissions : PendingPermissionsMap ,
958+ permission_mode : Arc < AtomicU8 > ,
867959 stdin : tokio:: process:: ChildStdin ,
868960 stdout : tokio:: process:: ChildStdout ,
869961 stderr : tokio:: process:: ChildStderr ,
@@ -878,6 +970,7 @@ impl AcpAgent {
878970 // Create client implementation
879971 let client_impl = AcpClientImpl {
880972 agent_id : agent_id. clone ( ) ,
973+ permission_mode,
881974 message_sender : message_sender. clone ( ) ,
882975 history,
883976 pending_permissions,
@@ -1247,15 +1340,25 @@ impl AcpAgent {
12471340
12481341pub struct AcpManager {
12491342 agents : HashMap < String , AcpAgent > ,
1343+ permission_mode : Arc < AtomicU8 > ,
12501344}
12511345
12521346impl AcpManager {
1253- pub fn new ( ) -> Self {
1347+ pub fn new ( permission_mode : AcpPermissionMode ) -> Self {
12541348 Self {
12551349 agents : HashMap :: new ( ) ,
1350+ permission_mode : Arc :: new ( AtomicU8 :: new ( permission_mode. as_atomic ( ) ) ) ,
12561351 }
12571352 }
12581353
1354+ pub fn get_permission_mode ( & self ) -> AcpPermissionMode {
1355+ AcpPermissionMode :: from_atomic ( self . permission_mode . load ( Ordering :: Relaxed ) )
1356+ }
1357+
1358+ pub fn set_permission_mode ( & self , mode : AcpPermissionMode ) {
1359+ self . permission_mode . store ( mode. as_atomic ( ) , Ordering :: Relaxed ) ;
1360+ }
1361+
12591362 /// Start agent by agent_id and agent_name. Returns an error if the agent already exists.
12601363 pub async fn start_agent (
12611364 & mut self , agent_id : String , agent_name : String , cmd : & str , args : & [ String ] ,
@@ -1264,7 +1367,7 @@ impl AcpManager {
12641367 return Err ( anyhow:: anyhow!( "Agent {} already running" , agent_id) ) ;
12651368 }
12661369
1267- let mut agent = AcpAgent :: new ( agent_id. clone ( ) , agent_name. clone ( ) ) ;
1370+ let mut agent = AcpAgent :: new ( agent_id. clone ( ) , agent_name. clone ( ) , self . permission_mode . clone ( ) ) ;
12681371
12691372 info ! ( "Starting ACP agent {} with command: {} {:?}" , agent_id, cmd, args) ;
12701373 agent. start ( cmd, args) . await ?;
0 commit comments