Skip to content

Commit 1b49134

Browse files
authored
Feature: Auto-approval for specific tools (#26)
* feat(console_ui.py): enhance tool approval prompt to include options for session and forever approval feat(tool_manager.py): load persistent auto-approved tools from config to maintain user preferences across sessions feat(config_management.py): add methods to manage auto-approval tools in global config for better user experience * chore(tool_manager.py): remove commented-out code for loading auto-approved tools to clean up the codebase chore(config_management.py): remove commented-out code for ensuring auto_approval_tools key to enhance code clarity * chore(console_ui.py): remove commented-out code for clarity and maintainability chore(tool_manager.py): remove commented-out explanation to streamline code readability * refactor(tool_manager.py): move _load_persistent_auto_approved_tools method to the end of the class for better readability and organization * fix(console_ui.py): update prompt text for tool approval options to improve clarity fix(console_ui.py): simplify approved text messages for better readability fix(config_management.py): return a default configuration structure with global settings when config file is missing or invalid
1 parent 3790c1c commit 1b49134

File tree

3 files changed

+67
-13
lines changed

3 files changed

+67
-13
lines changed

AgentCrew/modules/chat/console_ui.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ def display_tool_confirmation_request(self, tool_info):
451451
# Use Rich to print the prompt but still need to use input() for user interaction
452452
self.console.print(
453453
Text(
454-
"\nAllow this tool to run? [y]es/[n]o/[all] future calls: ",
454+
"\nAllow this tool to run? [y]es/[n]o/[a]ll in this session/[f]orever (this and future sessions): ",
455455
style=RICH_STYLE_YELLOW,
456456
),
457457
end="",
@@ -471,20 +471,31 @@ def display_tool_confirmation_request(self, tool_info):
471471
confirmation_id, {"action": "deny"}
472472
)
473473
break
474-
elif response in ["all", "a"]:
474+
elif response in ["a", "all"]:
475475
self.message_handler.resolve_tool_confirmation(
476476
confirmation_id, {"action": "approve_all"}
477477
)
478478
approved_text = Text(
479-
"✓ Approved all future calls to '", style=RICH_STYLE_YELLOW
479+
f"✓ Approved all future calls to '{tool_use['name']}' for this session.", style=RICH_STYLE_YELLOW
480480
)
481-
approved_text.append(tool_use["name"])
482-
approved_text.append("' for this session.")
483481
self.console.print(approved_text)
484482
break
483+
elif response in ["f", "forever"]:
484+
from AgentCrew.modules.config import ConfigManagement
485+
config_manager = ConfigManagement()
486+
config_manager.write_auto_approval_tools(tool_use["name"], add=True)
487+
488+
self.message_handler.resolve_tool_confirmation(
489+
confirmation_id, {"action": "approve_all"}
490+
)
491+
saved_text = Text(
492+
f"✓ Tool '{tool_use['name']}' will be auto-approved forever.", style=RICH_STYLE_YELLOW
493+
)
494+
self.console.print(saved_text)
495+
break
485496
else:
486497
self.console.print(
487-
Text("Please enter 'y', 'n', or 'all'.", style=RICH_STYLE_YELLOW)
498+
Text("Please enter 'y', 'n', 'a', or 'f'.", style=RICH_STYLE_YELLOW)
488499
)
489500
self._start_input_thread()
490501

AgentCrew/modules/chat/message/tool_manager.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import asyncio
33

44
from AgentCrew.modules import logger
5+
from AgentCrew.modules.config import ConfigManagement
56
from AgentCrew.modules.llm.message import MessageTransformer
67

78

@@ -13,11 +14,18 @@ def __init__(self, message_handler):
1314

1415
if isinstance(message_handler, MessageHandler):
1516
self.message_handler = message_handler
16-
self._auto_approved_tools = set() # Track tools approved for all future calls
17+
18+
self._auto_approved_tools = self._load_persistent_auto_approved_tools()
19+
1720
self._pending_confirmations = {} # Store futures for confirmation requests
1821
self._next_confirmation_id = 0 # ID counter for confirmation requests
1922
self.yolo_mode = False # Enable/disable auto-approval mode
2023

24+
def _load_persistent_auto_approved_tools(self):
25+
"""Load persistent auto-approved tools from config."""
26+
config_manager = ConfigManagement()
27+
return set(config_manager.get_auto_approval_tools())
28+
2129
async def execute_tool(self, tool_use: Dict[str, Any]):
2230
"""Execute a tool with proper confirmation flow."""
2331
tool_name = tool_use["name"]
@@ -62,7 +70,6 @@ async def execute_tool(self, tool_use: Dict[str, Any]):
6270
)
6371
return
6472

65-
# For all other tools, check if confirmation is needed
6673
if not self.yolo_mode and tool_name not in self._auto_approved_tools:
6774
# Request confirmation from the user
6875
confirmation = await self._wait_for_tool_confirmation(tool_use)
@@ -242,4 +249,4 @@ def _post_tool_transfer(self, tool_use, tool_result):
242249

243250
def reset_approved_tools(self):
244251
"""Reset approved tools for a new conversation."""
245-
self._auto_approved_tools = set()
252+
self._auto_approved_tools = self._load_persistent_auto_approved_tools()

AgentCrew/modules/config/config_management.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,30 +230,33 @@ def _get_global_config_file_path(self) -> str:
230230
def read_global_config_data(self) -> Dict[str, Any]:
231231
"""Reads data from the global config.json file."""
232232
config_path = self._get_global_config_file_path()
233+
default_config = {"api_keys": {}, "auto_approval_tools": [], "global_settings": {"theme": "dark", "yolo_mode": False}}
233234
try:
234235
if not os.path.exists(config_path):
235-
return {"api_keys": {}} # Default structure if file doesn't exist
236+
return default_config
236237
with open(config_path, "r", encoding="utf-8") as f:
237238
data = json.load(f)
238239
if not isinstance(data, dict):
239240
logger.warning(
240241
f"Warning: Global config file {config_path} does not contain a valid JSON object. Returning default."
241242
)
242-
return {"api_keys": {}}
243+
return default_config
243244
# Ensure api_keys key exists and is a dict
244245
if "api_keys" not in data or not isinstance(data.get("api_keys"), dict):
245246
data["api_keys"] = {}
247+
if "auto_approval_tools" not in data or not isinstance(data.get("auto_approval_tools"), list):
248+
data["auto_approval_tools"] = []
246249
return data
247250
except json.JSONDecodeError:
248251
logger.warning(
249252
f"Warning: Error decoding global config file {config_path}. Returning default config."
250253
)
251-
return {"api_keys": {}}
254+
return default_config
252255
except Exception as e:
253256
logger.warning(
254257
f"Warning: Could not read global config file {config_path}: {e}. Returning default config."
255258
)
256-
return {"api_keys": {}}
259+
return default_config
257260

258261
def write_global_config_data(self, config_data: Dict[str, Any]) -> None:
259262
"""Writes data to the global config.json file."""
@@ -561,3 +564,36 @@ def get_last_used_agent(self) -> Optional[str]:
561564
"""
562565
last_used = self.get_last_used_settings()
563566
return last_used.get("agent")
567+
568+
def get_auto_approval_tools(self) -> List[str]:
569+
"""
570+
Get the list of auto-approved tools from global config.
571+
572+
Returns:
573+
List of tool names that are auto-approved.
574+
"""
575+
global_config = self.read_global_config_data()
576+
return global_config.get("auto_approval_tools", [])
577+
578+
def write_auto_approval_tools(self, tool_name: str, add: bool = True) -> None:
579+
"""
580+
Add or remove a tool from the auto-approval list in global config.
581+
582+
Args:
583+
tool_name: Name of the tool to add/remove from auto-approval list.
584+
add: True to add the tool, False to remove it.
585+
"""
586+
try:
587+
global_config = self.read_global_config_data()
588+
auto_approval_tools = global_config.get("auto_approval_tools", [])
589+
590+
if add and tool_name not in auto_approval_tools:
591+
auto_approval_tools.append(tool_name)
592+
elif not add and tool_name in auto_approval_tools:
593+
auto_approval_tools.remove(tool_name)
594+
595+
global_config["auto_approval_tools"] = auto_approval_tools
596+
self.write_global_config_data(global_config)
597+
except Exception as e:
598+
action = "add" if add else "remove"
599+
logger.warning(f"Warning: Failed to {action} tool {tool_name} from auto-approval list: {e}")

0 commit comments

Comments
 (0)