PraisonAI 2.3.57__tar.gz → 2.3.62__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {praisonai-2.3.57 → praisonai-2.3.62}/PKG-INFO +17 -2
- {praisonai-2.3.57 → praisonai-2.3.62}/PraisonAI.egg-info/PKG-INFO +17 -2
- {praisonai-2.3.57 → praisonai-2.3.62}/PraisonAI.egg-info/SOURCES.txt +1 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/PraisonAI.egg-info/requires.txt +1 -1
- {praisonai-2.3.57 → praisonai-2.3.62}/README.md +15 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/main.py +150 -3
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/deploy.py +1 -1
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/integrations/base.py +6 -3
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/integrations/claude_code.py +12 -12
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/integrations/codex_cli.py +3 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/integrations/cursor_cli.py +3 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/integrations/gemini_cli.py +6 -6
- praisonai-2.3.62/praisonai/version.py +1 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/pyproject.toml +1 -1
- praisonai-2.3.62/tests/test_approval_system.py +221 -0
- praisonai-2.3.57/praisonai/version.py +0 -1
- {praisonai-2.3.57 → praisonai-2.3.62}/PraisonAI.egg-info/dependency_links.txt +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/PraisonAI.egg-info/entry_points.txt +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/PraisonAI.egg-info/top_level.txt +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/__main__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/agents_generator.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/api/call.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/auto.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/chainlit_ui.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/at_mentions.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/auto_memory.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/autonomy_mode.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/base.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/cost_tracker.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/external_agents.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/fast_context.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/flow_display.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/git_integration.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/guardrail.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/handoff.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/image.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/interactive_tui.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/knowledge.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/mcp.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/message_queue.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/metrics.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/n8n.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/repo_map.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/router.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/sandbox_executor.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/session.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/slash_commands.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/telemetry.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/todo.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/tools.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/cli/features/workflow.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/agent_tools.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/diff/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/diff/diff_strategy.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/tools/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/tools/apply_diff.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/tools/execute_command.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/tools/list_files.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/tools/read_file.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/tools/search_replace.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/tools/write_file.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/utils/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/utils/file_utils.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/utils/ignore_utils.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/code/utils/text_utils.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/inbuilt_tools/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/inc/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/inc/config.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/inc/models.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/integrations/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/profiler.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/scheduler.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/setup/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/setup/build.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/setup/post_install.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/setup/setup_conda_env.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/setup.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/test.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/train.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/train_vision.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/agents.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/callbacks.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/chat.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/code.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/colab.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/colab_chainlit.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/components/aicoder.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/context.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/database_config.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/db.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/realtime.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/realtimeclient/__init__.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/realtimeclient/tools.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/ui/sql_alchemy.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/praisonai/upload_vision.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/setup.cfg +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test_agents_playbook.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test_basic.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test_cli_features.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test_custom_tools_fix.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test_double_api_fix.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test_n8n_integration.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test_phase2_refactor.py +0 -0
- {praisonai-2.3.57 → praisonai-2.3.62}/tests/test_runner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PraisonAI
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.62
|
|
4
4
|
Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
|
|
5
5
|
Author: Mervin Praison
|
|
6
6
|
License: MIT
|
|
@@ -11,7 +11,7 @@ Description-Content-Type: text/markdown
|
|
|
11
11
|
Requires-Dist: rich>=13.7
|
|
12
12
|
Requires-Dist: markdown>=3.5
|
|
13
13
|
Requires-Dist: pyparsing>=3.0.0
|
|
14
|
-
Requires-Dist: praisonaiagents>=0.1.
|
|
14
|
+
Requires-Dist: praisonaiagents>=0.1.21
|
|
15
15
|
Requires-Dist: python-dotenv>=0.19.0
|
|
16
16
|
Requires-Dist: instructor>=1.3.3
|
|
17
17
|
Requires-Dist: PyYAML>=6.0
|
|
@@ -1155,6 +1155,20 @@ praisonai "Complex analysis task" --planning --planning-reasoning
|
|
|
1155
1155
|
praisonai "Task" --planning --auto-approve-plan
|
|
1156
1156
|
```
|
|
1157
1157
|
|
|
1158
|
+
### Tool Approval CLI:
|
|
1159
|
+
```bash
|
|
1160
|
+
# Auto-approve ALL tool executions (use with caution!)
|
|
1161
|
+
praisonai "run ls command" --trust
|
|
1162
|
+
|
|
1163
|
+
# Auto-approve tools up to a risk level (prompt for higher)
|
|
1164
|
+
# Levels: low, medium, high, critical
|
|
1165
|
+
praisonai "write to file" --approve-level high # Prompts for critical tools only
|
|
1166
|
+
praisonai "task" --approve-level medium # Prompts for high and critical
|
|
1167
|
+
|
|
1168
|
+
# Default behavior (no flags): prompts for all dangerous tools
|
|
1169
|
+
praisonai "run shell command" # Will prompt for approval
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1158
1172
|
### Memory CLI:
|
|
1159
1173
|
```bash
|
|
1160
1174
|
# Enable memory for agent (persists across sessions)
|
|
@@ -2496,6 +2510,7 @@ PraisonAI provides zero-dependency persistent memory for agents. For detailed ex
|
|
|
2496
2510
|
- 🤝 **Agent Handoffs** - Transfer context between specialised agents
|
|
2497
2511
|
- 🛡️ **Guardrails** - Input/output validation and safety checks
|
|
2498
2512
|
- ✅ **Human Approval** - Require human confirmation for critical actions
|
|
2513
|
+
- 🔐 **Tool Approval CLI** - `--trust` (auto-approve all) and `--approve-level` (risk-based approval)
|
|
2499
2514
|
- 💬 **Sessions Management** - Isolated conversation contexts
|
|
2500
2515
|
- 🔄 **Stateful Agents** - Maintain state across interactions
|
|
2501
2516
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PraisonAI
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.62
|
|
4
4
|
Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
|
|
5
5
|
Author: Mervin Praison
|
|
6
6
|
License: MIT
|
|
@@ -11,7 +11,7 @@ Description-Content-Type: text/markdown
|
|
|
11
11
|
Requires-Dist: rich>=13.7
|
|
12
12
|
Requires-Dist: markdown>=3.5
|
|
13
13
|
Requires-Dist: pyparsing>=3.0.0
|
|
14
|
-
Requires-Dist: praisonaiagents>=0.1.
|
|
14
|
+
Requires-Dist: praisonaiagents>=0.1.21
|
|
15
15
|
Requires-Dist: python-dotenv>=0.19.0
|
|
16
16
|
Requires-Dist: instructor>=1.3.3
|
|
17
17
|
Requires-Dist: PyYAML>=6.0
|
|
@@ -1155,6 +1155,20 @@ praisonai "Complex analysis task" --planning --planning-reasoning
|
|
|
1155
1155
|
praisonai "Task" --planning --auto-approve-plan
|
|
1156
1156
|
```
|
|
1157
1157
|
|
|
1158
|
+
### Tool Approval CLI:
|
|
1159
|
+
```bash
|
|
1160
|
+
# Auto-approve ALL tool executions (use with caution!)
|
|
1161
|
+
praisonai "run ls command" --trust
|
|
1162
|
+
|
|
1163
|
+
# Auto-approve tools up to a risk level (prompt for higher)
|
|
1164
|
+
# Levels: low, medium, high, critical
|
|
1165
|
+
praisonai "write to file" --approve-level high # Prompts for critical tools only
|
|
1166
|
+
praisonai "task" --approve-level medium # Prompts for high and critical
|
|
1167
|
+
|
|
1168
|
+
# Default behavior (no flags): prompts for all dangerous tools
|
|
1169
|
+
praisonai "run shell command" # Will prompt for approval
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1158
1172
|
### Memory CLI:
|
|
1159
1173
|
```bash
|
|
1160
1174
|
# Enable memory for agent (persists across sessions)
|
|
@@ -2496,6 +2510,7 @@ PraisonAI provides zero-dependency persistent memory for agents. For detailed ex
|
|
|
2496
2510
|
- 🤝 **Agent Handoffs** - Transfer context between specialised agents
|
|
2497
2511
|
- 🛡️ **Guardrails** - Input/output validation and safety checks
|
|
2498
2512
|
- ✅ **Human Approval** - Require human confirmation for critical actions
|
|
2513
|
+
- 🔐 **Tool Approval CLI** - `--trust` (auto-approve all) and `--approve-level` (risk-based approval)
|
|
2499
2514
|
- 💬 **Sessions Management** - Isolated conversation contexts
|
|
2500
2515
|
- 🔄 **Stateful Agents** - Maintain state across interactions
|
|
2501
2516
|
|
|
@@ -1058,6 +1058,20 @@ praisonai "Complex analysis task" --planning --planning-reasoning
|
|
|
1058
1058
|
praisonai "Task" --planning --auto-approve-plan
|
|
1059
1059
|
```
|
|
1060
1060
|
|
|
1061
|
+
### Tool Approval CLI:
|
|
1062
|
+
```bash
|
|
1063
|
+
# Auto-approve ALL tool executions (use with caution!)
|
|
1064
|
+
praisonai "run ls command" --trust
|
|
1065
|
+
|
|
1066
|
+
# Auto-approve tools up to a risk level (prompt for higher)
|
|
1067
|
+
# Levels: low, medium, high, critical
|
|
1068
|
+
praisonai "write to file" --approve-level high # Prompts for critical tools only
|
|
1069
|
+
praisonai "task" --approve-level medium # Prompts for high and critical
|
|
1070
|
+
|
|
1071
|
+
# Default behavior (no flags): prompts for all dangerous tools
|
|
1072
|
+
praisonai "run shell command" # Will prompt for approval
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1061
1075
|
### Memory CLI:
|
|
1062
1076
|
```bash
|
|
1063
1077
|
# Enable memory for agent (persists across sessions)
|
|
@@ -2399,6 +2413,7 @@ PraisonAI provides zero-dependency persistent memory for agents. For detailed ex
|
|
|
2399
2413
|
- 🤝 **Agent Handoffs** - Transfer context between specialised agents
|
|
2400
2414
|
- 🛡️ **Guardrails** - Input/output validation and safety checks
|
|
2401
2415
|
- ✅ **Human Approval** - Require human confirmation for critical actions
|
|
2416
|
+
- 🔐 **Tool Approval CLI** - `--trust` (auto-approve all) and `--approve-level` (risk-based approval)
|
|
2402
2417
|
- 💬 **Sessions Management** - Isolated conversation contexts
|
|
2403
2418
|
- 🔄 **Stateful Agents** - Maintain state across interactions
|
|
2404
2419
|
|
|
@@ -68,13 +68,18 @@ AUTOGEN_AVAILABLE = importlib.util.find_spec("autogen") is not None
|
|
|
68
68
|
PRAISONAI_AVAILABLE = importlib.util.find_spec("praisonaiagents") is not None
|
|
69
69
|
TRAIN_AVAILABLE = importlib.util.find_spec("unsloth") is not None
|
|
70
70
|
|
|
71
|
-
logging.basicConfig(level=os.environ.get('LOGLEVEL', '
|
|
71
|
+
logging.basicConfig(level=os.environ.get('LOGLEVEL', 'WARNING'), format='%(asctime)s - %(levelname)s - %(message)s')
|
|
72
72
|
logging.getLogger('alembic').setLevel(logging.ERROR)
|
|
73
73
|
logging.getLogger('gradio').setLevel(logging.ERROR)
|
|
74
74
|
logging.getLogger('gradio').setLevel(os.environ.get('GRADIO_LOGLEVEL', 'WARNING'))
|
|
75
75
|
logging.getLogger('rust_logger').setLevel(logging.WARNING)
|
|
76
76
|
logging.getLogger('duckduckgo').setLevel(logging.ERROR)
|
|
77
77
|
logging.getLogger('_client').setLevel(logging.ERROR)
|
|
78
|
+
# Suppress praisonaiagents INFO logs unless LOGLEVEL is explicitly set to debug/info
|
|
79
|
+
if os.environ.get('LOGLEVEL', '').upper() not in ('DEBUG', 'INFO'):
|
|
80
|
+
logging.getLogger('praisonaiagents').setLevel(logging.WARNING)
|
|
81
|
+
logging.getLogger('praisonaiagents.llm').setLevel(logging.WARNING)
|
|
82
|
+
logging.getLogger('praisonaiagents.llm.llm').setLevel(logging.WARNING)
|
|
78
83
|
|
|
79
84
|
def stream_subprocess(command, env=None):
|
|
80
85
|
"""
|
|
@@ -693,9 +698,18 @@ class PraisonAI:
|
|
|
693
698
|
# Autonomy Mode - control AI action approval
|
|
694
699
|
parser.add_argument("--autonomy", type=str, choices=["suggest", "auto_edit", "full_auto"], help="Set autonomy mode for AI actions")
|
|
695
700
|
|
|
701
|
+
# Tool Approval - control tool execution approval
|
|
702
|
+
parser.add_argument("--trust", action="store_true", help="Auto-approve all tool executions (skip approval prompts)")
|
|
703
|
+
parser.add_argument("--approve-level", type=str, choices=["low", "medium", "high", "critical"],
|
|
704
|
+
help="Auto-approve tools up to this risk level (e.g., --approve-level high approves low/medium/high but prompts for critical)")
|
|
705
|
+
|
|
696
706
|
# Sandbox Execution - secure command execution
|
|
697
707
|
parser.add_argument("--sandbox", type=str, choices=["off", "basic", "strict"], help="Enable sandboxed command execution")
|
|
698
708
|
|
|
709
|
+
# External Agent - use external AI CLI tools
|
|
710
|
+
parser.add_argument("--external-agent", type=str, choices=["claude", "gemini", "codex", "cursor"],
|
|
711
|
+
help="Use external AI CLI tool (claude, gemini, codex, cursor)")
|
|
712
|
+
|
|
699
713
|
# If we're in a test environment, parse with empty args to avoid pytest interference
|
|
700
714
|
if in_test_env:
|
|
701
715
|
args, unknown_args = parser.parse_known_args([])
|
|
@@ -2922,7 +2936,8 @@ Provide ONLY the commit message, no explanations."""
|
|
|
2922
2936
|
if max_tokens:
|
|
2923
2937
|
# Pass llm as dict with model and max_tokens
|
|
2924
2938
|
agent_config["llm"] = {"model": self.args.llm, "max_tokens": max_tokens}
|
|
2925
|
-
|
|
2939
|
+
if os.environ.get('LOGLEVEL', '').upper() == 'DEBUG':
|
|
2940
|
+
print(f"[bold cyan]Max tokens set to: {max_tokens}[/bold cyan]")
|
|
2926
2941
|
else:
|
|
2927
2942
|
agent_config["llm"] = self.args.llm
|
|
2928
2943
|
|
|
@@ -2995,6 +3010,35 @@ Provide ONLY the commit message, no explanations."""
|
|
|
2995
3010
|
|
|
2996
3011
|
# ===== NEW CLI FEATURES INTEGRATION =====
|
|
2997
3012
|
|
|
3013
|
+
# Tool Approval - Auto-approve tools based on --trust or --approve-level
|
|
3014
|
+
if getattr(self.args, 'trust', False) or getattr(self.args, 'approve_level', None):
|
|
3015
|
+
from praisonaiagents.approval import set_approval_callback, ApprovalDecision
|
|
3016
|
+
|
|
3017
|
+
if getattr(self.args, 'trust', False):
|
|
3018
|
+
# Auto-approve all tools
|
|
3019
|
+
def auto_approve_all(function_name, arguments, risk_level):
|
|
3020
|
+
return ApprovalDecision(approved=True, reason="Auto-approved via --trust flag")
|
|
3021
|
+
set_approval_callback(auto_approve_all)
|
|
3022
|
+
print("[bold yellow]⚠️ Trust mode enabled - all tool executions will be auto-approved[/bold yellow]")
|
|
3023
|
+
elif getattr(self.args, 'approve_level', None):
|
|
3024
|
+
# Auto-approve up to specified risk level
|
|
3025
|
+
max_level = self.args.approve_level
|
|
3026
|
+
risk_order = {"low": 1, "medium": 2, "high": 3, "critical": 4}
|
|
3027
|
+
max_level_value = risk_order.get(max_level, 3)
|
|
3028
|
+
|
|
3029
|
+
def level_based_approve(function_name, arguments, risk_level):
|
|
3030
|
+
tool_level_value = risk_order.get(risk_level, 4)
|
|
3031
|
+
if tool_level_value <= max_level_value:
|
|
3032
|
+
return ApprovalDecision(approved=True, reason=f"Auto-approved (level {risk_level} <= {max_level})")
|
|
3033
|
+
else:
|
|
3034
|
+
# Signal to pause Live display before showing approval prompt
|
|
3035
|
+
# This is handled by the approval_pending flag in status_info
|
|
3036
|
+
from praisonaiagents.approval import console_approval_callback
|
|
3037
|
+
return console_approval_callback(function_name, arguments, risk_level)
|
|
3038
|
+
|
|
3039
|
+
set_approval_callback(level_based_approve)
|
|
3040
|
+
print(f"[bold cyan]Auto-approve enabled for tools up to '{max_level}' risk level[/bold cyan]")
|
|
3041
|
+
|
|
2998
3042
|
# Router - Smart model selection (must be before agent creation)
|
|
2999
3043
|
if getattr(self.args, 'router', False):
|
|
3000
3044
|
from .features.router import RouterHandler
|
|
@@ -3035,6 +3079,43 @@ Provide ONLY the commit message, no explanations."""
|
|
|
3035
3079
|
existing_tools = list(mcp_tools)
|
|
3036
3080
|
agent_config['tools'] = existing_tools
|
|
3037
3081
|
|
|
3082
|
+
# External Agent - Use external AI CLI tools directly
|
|
3083
|
+
if getattr(self.args, 'external_agent', None):
|
|
3084
|
+
from rich.console import Console
|
|
3085
|
+
ext_console = Console()
|
|
3086
|
+
external_agent_name = self.args.external_agent
|
|
3087
|
+
try:
|
|
3088
|
+
from .features.external_agents import ExternalAgentsHandler
|
|
3089
|
+
handler = ExternalAgentsHandler(verbose=getattr(self.args, 'verbose', False))
|
|
3090
|
+
|
|
3091
|
+
# Get workspace from current directory
|
|
3092
|
+
import os
|
|
3093
|
+
workspace = os.getcwd()
|
|
3094
|
+
|
|
3095
|
+
integration = handler.get_integration(external_agent_name, workspace=workspace)
|
|
3096
|
+
|
|
3097
|
+
if integration.is_available:
|
|
3098
|
+
ext_console.print(f"[bold cyan]🔌 Using external agent: {external_agent_name}[/bold cyan]")
|
|
3099
|
+
|
|
3100
|
+
# Run the external agent directly instead of PraisonAI agent
|
|
3101
|
+
import asyncio
|
|
3102
|
+
try:
|
|
3103
|
+
result = asyncio.run(integration.execute(prompt))
|
|
3104
|
+
ext_console.print(f"\n[bold green]Result from {external_agent_name}:[/bold green]")
|
|
3105
|
+
ext_console.print(result)
|
|
3106
|
+
# Return empty string to avoid duplicate printing by caller
|
|
3107
|
+
return ""
|
|
3108
|
+
except Exception as e:
|
|
3109
|
+
ext_console.print(f"[red]Error executing {external_agent_name}: {e}[/red]")
|
|
3110
|
+
return None
|
|
3111
|
+
else:
|
|
3112
|
+
ext_console.print(f"[yellow]⚠️ External agent '{external_agent_name}' is not installed[/yellow]")
|
|
3113
|
+
ext_console.print(f"[dim]Install with: {handler._get_install_instructions(external_agent_name)}[/dim]")
|
|
3114
|
+
return None
|
|
3115
|
+
except Exception as e:
|
|
3116
|
+
ext_console.print(f"[red]Error setting up external agent: {e}[/red]")
|
|
3117
|
+
return None
|
|
3118
|
+
|
|
3038
3119
|
# Fast Context - Codebase search
|
|
3039
3120
|
if getattr(self.args, 'fast_context', None):
|
|
3040
3121
|
from .features.fast_context import FastContextHandler
|
|
@@ -3264,7 +3345,9 @@ Now, {final_instruction.lower()}:"""
|
|
|
3264
3345
|
'result': None,
|
|
3265
3346
|
'error': None,
|
|
3266
3347
|
'start_time': time.time(),
|
|
3267
|
-
'available_tools': tool_names
|
|
3348
|
+
'available_tools': tool_names,
|
|
3349
|
+
'approval_pending': False, # Flag to pause Live display during approval
|
|
3350
|
+
'live_instance': None # Reference to Live instance for stopping
|
|
3268
3351
|
}
|
|
3269
3352
|
|
|
3270
3353
|
def tool_call_callback(message):
|
|
@@ -3327,6 +3410,53 @@ Now, {final_instruction.lower()}:"""
|
|
|
3327
3410
|
finally:
|
|
3328
3411
|
status_info['done'] = True
|
|
3329
3412
|
|
|
3413
|
+
# Set up approval callback that stops Live display before prompting
|
|
3414
|
+
# Only if not using --trust (which auto-approves everything)
|
|
3415
|
+
if not getattr(self.args, 'trust', False):
|
|
3416
|
+
from praisonaiagents.approval import set_approval_callback, ApprovalDecision
|
|
3417
|
+
from rich.prompt import Confirm
|
|
3418
|
+
from rich.panel import Panel
|
|
3419
|
+
|
|
3420
|
+
def cli_approval_with_live_pause(function_name, arguments, risk_level):
|
|
3421
|
+
"""Approval callback that stops Live display before prompting."""
|
|
3422
|
+
# Signal to stop Live display
|
|
3423
|
+
status_info['approval_pending'] = True
|
|
3424
|
+
|
|
3425
|
+
# Wait a moment for Live to stop
|
|
3426
|
+
time.sleep(0.2)
|
|
3427
|
+
|
|
3428
|
+
# Now show the approval prompt
|
|
3429
|
+
risk_colors = {"critical": "bold red", "high": "red", "medium": "yellow", "low": "blue"}
|
|
3430
|
+
risk_color = risk_colors.get(risk_level, "white")
|
|
3431
|
+
|
|
3432
|
+
tool_info = f"[bold]Function:[/] {function_name}\n"
|
|
3433
|
+
tool_info += f"[bold]Risk Level:[/] [{risk_color}]{risk_level.upper()}[/{risk_color}]\n"
|
|
3434
|
+
tool_info += "[bold]Arguments:[/]\n"
|
|
3435
|
+
for key, value in arguments.items():
|
|
3436
|
+
str_value = str(value)[:100] + "..." if len(str(value)) > 100 else str(value)
|
|
3437
|
+
tool_info += f" {key}: {str_value}\n"
|
|
3438
|
+
|
|
3439
|
+
console.print(Panel(tool_info.strip(), title="🔒 Tool Approval Required", border_style=risk_color))
|
|
3440
|
+
|
|
3441
|
+
try:
|
|
3442
|
+
approved = Confirm.ask(f"[{risk_color}]Execute this {risk_level} risk tool?[/{risk_color}]", default=False)
|
|
3443
|
+
status_info['approval_pending'] = False
|
|
3444
|
+
|
|
3445
|
+
if approved:
|
|
3446
|
+
console.print("[green]✅ Approved[/green]")
|
|
3447
|
+
return ApprovalDecision(approved=True, reason="User approved")
|
|
3448
|
+
else:
|
|
3449
|
+
console.print("[red]❌ Denied[/red]")
|
|
3450
|
+
return ApprovalDecision(approved=False, reason="User denied")
|
|
3451
|
+
except (KeyboardInterrupt, EOFError):
|
|
3452
|
+
status_info['approval_pending'] = False
|
|
3453
|
+
console.print("\n[red]❌ Cancelled[/red]")
|
|
3454
|
+
return ApprovalDecision(approved=False, reason="User cancelled")
|
|
3455
|
+
|
|
3456
|
+
# Only set if not already set by --approve-level
|
|
3457
|
+
if not getattr(self.args, 'approve_level', None):
|
|
3458
|
+
set_approval_callback(cli_approval_with_live_pause)
|
|
3459
|
+
|
|
3330
3460
|
# Start agent in background thread
|
|
3331
3461
|
thread = threading.Thread(target=run_agent, daemon=True)
|
|
3332
3462
|
thread.start()
|
|
@@ -3334,9 +3464,26 @@ Now, {final_instruction.lower()}:"""
|
|
|
3334
3464
|
# Show live status while processing
|
|
3335
3465
|
try:
|
|
3336
3466
|
with Live(build_status_display(), console=console, refresh_per_second=4, transient=True) as live:
|
|
3467
|
+
status_info['live_instance'] = live
|
|
3337
3468
|
while not status_info['done']:
|
|
3469
|
+
# Check if approval is pending - stop Live to show prompt
|
|
3470
|
+
if status_info['approval_pending']:
|
|
3471
|
+
break
|
|
3338
3472
|
live.update(build_status_display())
|
|
3339
3473
|
time.sleep(0.1)
|
|
3474
|
+
|
|
3475
|
+
# If approval was pending, wait for it to complete then restart Live
|
|
3476
|
+
while status_info['approval_pending']:
|
|
3477
|
+
time.sleep(0.1)
|
|
3478
|
+
|
|
3479
|
+
# Continue with Live display if not done
|
|
3480
|
+
if not status_info['done']:
|
|
3481
|
+
with Live(build_status_display(), console=console, refresh_per_second=4, transient=True) as live:
|
|
3482
|
+
while not status_info['done']:
|
|
3483
|
+
if status_info['approval_pending']:
|
|
3484
|
+
break
|
|
3485
|
+
live.update(build_status_display())
|
|
3486
|
+
time.sleep(0.1)
|
|
3340
3487
|
except KeyboardInterrupt:
|
|
3341
3488
|
console.print("\n[dim]Interrupted[/dim]")
|
|
3342
3489
|
# Unregister callback
|
|
@@ -57,7 +57,7 @@ class CloudDeployer:
|
|
|
57
57
|
file.write("FROM python:3.11-slim\n")
|
|
58
58
|
file.write("WORKDIR /app\n")
|
|
59
59
|
file.write("COPY . .\n")
|
|
60
|
-
file.write("RUN pip install flask praisonai==2.3.
|
|
60
|
+
file.write("RUN pip install flask praisonai==2.3.62 gunicorn markdown\n")
|
|
61
61
|
file.write("EXPOSE 8080\n")
|
|
62
62
|
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
|
63
63
|
|
|
@@ -135,7 +135,8 @@ class BaseCLIIntegration(ABC):
|
|
|
135
135
|
*cmd,
|
|
136
136
|
stdout=asyncio.subprocess.PIPE,
|
|
137
137
|
stderr=asyncio.subprocess.PIPE,
|
|
138
|
-
cwd=self.workspace
|
|
138
|
+
cwd=self.workspace,
|
|
139
|
+
env=self.get_env()
|
|
139
140
|
)
|
|
140
141
|
|
|
141
142
|
try:
|
|
@@ -173,7 +174,8 @@ class BaseCLIIntegration(ABC):
|
|
|
173
174
|
*cmd,
|
|
174
175
|
stdout=asyncio.subprocess.PIPE,
|
|
175
176
|
stderr=asyncio.subprocess.PIPE,
|
|
176
|
-
cwd=self.workspace
|
|
177
|
+
cwd=self.workspace,
|
|
178
|
+
env=self.get_env()
|
|
177
179
|
)
|
|
178
180
|
|
|
179
181
|
try:
|
|
@@ -208,7 +210,8 @@ class BaseCLIIntegration(ABC):
|
|
|
208
210
|
*cmd,
|
|
209
211
|
stdout=asyncio.subprocess.PIPE,
|
|
210
212
|
stderr=asyncio.subprocess.PIPE,
|
|
211
|
-
cwd=self.workspace
|
|
213
|
+
cwd=self.workspace,
|
|
214
|
+
env=self.get_env()
|
|
212
215
|
)
|
|
213
216
|
|
|
214
217
|
try:
|
|
@@ -127,23 +127,16 @@ class ClaudeCodeIntegration(BaseCLIIntegration):
|
|
|
127
127
|
"""
|
|
128
128
|
cmd = ["claude"]
|
|
129
129
|
|
|
130
|
-
# Add
|
|
131
|
-
if continue_session or self._session_active:
|
|
132
|
-
cmd.append("--continue")
|
|
133
|
-
|
|
134
|
-
# Add skip permissions flag
|
|
135
|
-
if self.skip_permissions:
|
|
136
|
-
cmd.append("--dangerously-skip-permissions")
|
|
137
|
-
|
|
138
|
-
# Add print mode flag
|
|
130
|
+
# Add print mode flag for non-interactive output
|
|
139
131
|
cmd.append("-p")
|
|
140
132
|
|
|
141
|
-
# Add prompt
|
|
142
|
-
cmd.append(prompt)
|
|
143
|
-
|
|
144
133
|
# Add output format
|
|
145
134
|
cmd.extend(["--output-format", self.output_format])
|
|
146
135
|
|
|
136
|
+
# Add continue flag if needed
|
|
137
|
+
if continue_session or self._session_active:
|
|
138
|
+
cmd.append("--continue")
|
|
139
|
+
|
|
147
140
|
# Add model if specified
|
|
148
141
|
if self.model:
|
|
149
142
|
cmd.extend(["--model", self.model])
|
|
@@ -160,6 +153,13 @@ class ClaudeCodeIntegration(BaseCLIIntegration):
|
|
|
160
153
|
if self.disallowed_tools:
|
|
161
154
|
cmd.extend(["--disallowedTools", ",".join(self.disallowed_tools)])
|
|
162
155
|
|
|
156
|
+
# Add verbose if needed
|
|
157
|
+
if options.get('verbose'):
|
|
158
|
+
cmd.append("--verbose")
|
|
159
|
+
|
|
160
|
+
# Add prompt last
|
|
161
|
+
cmd.append(prompt)
|
|
162
|
+
|
|
163
163
|
return cmd
|
|
164
164
|
|
|
165
165
|
async def execute(self, prompt: str, **options) -> str:
|
|
@@ -92,12 +92,6 @@ class GeminiCLIIntegration(BaseCLIIntegration):
|
|
|
92
92
|
"""
|
|
93
93
|
cmd = ["gemini"]
|
|
94
94
|
|
|
95
|
-
# Add print mode flag
|
|
96
|
-
cmd.append("-p")
|
|
97
|
-
|
|
98
|
-
# Add prompt
|
|
99
|
-
cmd.append(prompt)
|
|
100
|
-
|
|
101
95
|
# Add model
|
|
102
96
|
cmd.extend(["-m", self.model])
|
|
103
97
|
|
|
@@ -112,6 +106,12 @@ class GeminiCLIIntegration(BaseCLIIntegration):
|
|
|
112
106
|
if self.sandbox:
|
|
113
107
|
cmd.append("--sandbox")
|
|
114
108
|
|
|
109
|
+
# Add YOLO mode for non-interactive execution
|
|
110
|
+
cmd.append("--yolo")
|
|
111
|
+
|
|
112
|
+
# Add prompt as positional argument (must be last)
|
|
113
|
+
cmd.append(prompt)
|
|
114
|
+
|
|
115
115
|
return cmd
|
|
116
116
|
|
|
117
117
|
async def execute(self, prompt: str, **options) -> str:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.3.62"
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the CLI approval system.
|
|
3
|
+
|
|
4
|
+
Tests:
|
|
5
|
+
1. --trust flag auto-approves all tools
|
|
6
|
+
2. --approve-level flag with different risk levels
|
|
7
|
+
3. Approval callback integration
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
import sys
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
# Add the src directory to the path
|
|
15
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestApprovalFlags:
|
|
19
|
+
"""Test CLI approval flags."""
|
|
20
|
+
|
|
21
|
+
def test_trust_flag_exists(self):
|
|
22
|
+
"""Test that --trust flag is defined in argparse."""
|
|
23
|
+
import argparse
|
|
24
|
+
|
|
25
|
+
# Check that the args object has the trust attribute after parsing
|
|
26
|
+
# Parse with --help to avoid actually running anything
|
|
27
|
+
parser = argparse.ArgumentParser()
|
|
28
|
+
parser.add_argument("--trust", action="store_true")
|
|
29
|
+
parser.add_argument("--approve-level", type=str, choices=["low", "medium", "high", "critical"])
|
|
30
|
+
|
|
31
|
+
# Test that the arguments can be parsed
|
|
32
|
+
args = parser.parse_args(['--trust'])
|
|
33
|
+
assert args.trust is True
|
|
34
|
+
|
|
35
|
+
args = parser.parse_args([])
|
|
36
|
+
assert args.trust is False
|
|
37
|
+
|
|
38
|
+
def test_approve_level_flag_exists(self):
|
|
39
|
+
"""Test that --approve-level flag is defined in argparse."""
|
|
40
|
+
import argparse
|
|
41
|
+
|
|
42
|
+
parser = argparse.ArgumentParser()
|
|
43
|
+
parser.add_argument("--approve-level", type=str, choices=["low", "medium", "high", "critical"])
|
|
44
|
+
|
|
45
|
+
# Test that the arguments can be parsed
|
|
46
|
+
args = parser.parse_args(['--approve-level', 'high'])
|
|
47
|
+
assert args.approve_level == 'high'
|
|
48
|
+
|
|
49
|
+
args = parser.parse_args(['--approve-level', 'critical'])
|
|
50
|
+
assert args.approve_level == 'critical'
|
|
51
|
+
|
|
52
|
+
def test_trust_flag_in_praisonai_parser(self):
|
|
53
|
+
"""Test that --trust flag is in PraisonAI's actual parser."""
|
|
54
|
+
from praisonai.cli.main import PraisonAI
|
|
55
|
+
|
|
56
|
+
# Create instance - parse_args is called in __init__
|
|
57
|
+
praison = PraisonAI()
|
|
58
|
+
|
|
59
|
+
# Call parse_args to get the args
|
|
60
|
+
args = praison.parse_args()
|
|
61
|
+
|
|
62
|
+
# The args should have trust attribute after parse_args
|
|
63
|
+
assert hasattr(args, 'trust'), "PraisonAI.args should have 'trust' attribute"
|
|
64
|
+
# Cleanup
|
|
65
|
+
del praison
|
|
66
|
+
|
|
67
|
+
def test_approve_level_flag_in_praisonai_parser(self):
|
|
68
|
+
"""Test that --approve-level flag is in PraisonAI's actual parser."""
|
|
69
|
+
from praisonai.cli.main import PraisonAI
|
|
70
|
+
|
|
71
|
+
# Create instance - parse_args is called in __init__
|
|
72
|
+
praison = PraisonAI()
|
|
73
|
+
|
|
74
|
+
# Call parse_args to get the args
|
|
75
|
+
args = praison.parse_args()
|
|
76
|
+
|
|
77
|
+
# The args should have approve_level attribute after parse_args
|
|
78
|
+
assert hasattr(args, 'approve_level'), "PraisonAI.args should have 'approve_level' attribute"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TestApprovalCallback:
|
|
82
|
+
"""Test approval callback system."""
|
|
83
|
+
|
|
84
|
+
def test_set_approval_callback(self):
|
|
85
|
+
"""Test that we can set a custom approval callback."""
|
|
86
|
+
from praisonaiagents.approval import set_approval_callback, ApprovalDecision
|
|
87
|
+
|
|
88
|
+
callback_called = []
|
|
89
|
+
|
|
90
|
+
def custom_callback(function_name, arguments, risk_level):
|
|
91
|
+
callback_called.append((function_name, arguments, risk_level))
|
|
92
|
+
return ApprovalDecision(approved=True, reason="Test approved")
|
|
93
|
+
|
|
94
|
+
set_approval_callback(custom_callback)
|
|
95
|
+
|
|
96
|
+
# Verify callback was set
|
|
97
|
+
from praisonaiagents.approval import approval_callback
|
|
98
|
+
assert approval_callback == custom_callback
|
|
99
|
+
|
|
100
|
+
# Reset callback
|
|
101
|
+
set_approval_callback(None)
|
|
102
|
+
|
|
103
|
+
def test_auto_approve_callback(self):
|
|
104
|
+
"""Test auto-approve callback returns approved decision."""
|
|
105
|
+
from praisonaiagents.approval import ApprovalDecision
|
|
106
|
+
|
|
107
|
+
def auto_approve_callback(function_name, arguments, risk_level):
|
|
108
|
+
return ApprovalDecision(approved=True, reason="Auto-approved")
|
|
109
|
+
|
|
110
|
+
decision = auto_approve_callback("execute_command", {"command": "ls"}, "critical")
|
|
111
|
+
assert decision.approved is True
|
|
112
|
+
assert decision.reason == "Auto-approved"
|
|
113
|
+
|
|
114
|
+
def test_level_based_callback(self):
|
|
115
|
+
"""Test level-based approval callback."""
|
|
116
|
+
from praisonaiagents.approval import ApprovalDecision
|
|
117
|
+
|
|
118
|
+
def level_based_callback(function_name, arguments, risk_level, max_level="high"):
|
|
119
|
+
"""Approve based on risk level threshold."""
|
|
120
|
+
levels = {"low": 1, "medium": 2, "high": 3, "critical": 4}
|
|
121
|
+
tool_level = levels.get(risk_level, 4)
|
|
122
|
+
max_allowed = levels.get(max_level, 3)
|
|
123
|
+
|
|
124
|
+
if tool_level <= max_allowed:
|
|
125
|
+
return ApprovalDecision(approved=True, reason=f"Auto-approved (level {risk_level} <= {max_level})")
|
|
126
|
+
else:
|
|
127
|
+
return ApprovalDecision(approved=False, reason=f"Denied (level {risk_level} > {max_level})")
|
|
128
|
+
|
|
129
|
+
# Test with max_level="high"
|
|
130
|
+
# Should approve low, medium, high but deny critical
|
|
131
|
+
assert level_based_callback("test", {}, "low", "high").approved is True
|
|
132
|
+
assert level_based_callback("test", {}, "medium", "high").approved is True
|
|
133
|
+
assert level_based_callback("test", {}, "high", "high").approved is True
|
|
134
|
+
assert level_based_callback("test", {}, "critical", "high").approved is False
|
|
135
|
+
|
|
136
|
+
# Test with max_level="critical"
|
|
137
|
+
# Should approve everything
|
|
138
|
+
assert level_based_callback("test", {}, "critical", "critical").approved is True
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class TestApprovalRequirements:
|
|
142
|
+
"""Test approval requirement configuration."""
|
|
143
|
+
|
|
144
|
+
def test_default_dangerous_tools(self):
|
|
145
|
+
"""Test that default dangerous tools are configured."""
|
|
146
|
+
from praisonaiagents.approval import APPROVAL_REQUIRED_TOOLS, TOOL_RISK_LEVELS
|
|
147
|
+
|
|
148
|
+
# Check critical tools
|
|
149
|
+
assert "execute_command" in APPROVAL_REQUIRED_TOOLS
|
|
150
|
+
assert TOOL_RISK_LEVELS.get("execute_command") == "critical"
|
|
151
|
+
|
|
152
|
+
# Check high risk tools
|
|
153
|
+
assert "write_file" in APPROVAL_REQUIRED_TOOLS
|
|
154
|
+
assert TOOL_RISK_LEVELS.get("write_file") == "high"
|
|
155
|
+
|
|
156
|
+
def test_remove_approval_requirement(self):
|
|
157
|
+
"""Test removing approval requirement."""
|
|
158
|
+
from praisonaiagents.approval import (
|
|
159
|
+
add_approval_requirement,
|
|
160
|
+
remove_approval_requirement,
|
|
161
|
+
is_approval_required,
|
|
162
|
+
APPROVAL_REQUIRED_TOOLS
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Add a test tool
|
|
166
|
+
add_approval_requirement("test_tool", "medium")
|
|
167
|
+
assert is_approval_required("test_tool") is True
|
|
168
|
+
|
|
169
|
+
# Remove it
|
|
170
|
+
remove_approval_requirement("test_tool")
|
|
171
|
+
assert is_approval_required("test_tool") is False
|
|
172
|
+
|
|
173
|
+
def test_is_approval_required(self):
|
|
174
|
+
"""Test checking if approval is required."""
|
|
175
|
+
from praisonaiagents.approval import is_approval_required
|
|
176
|
+
|
|
177
|
+
# execute_command should require approval
|
|
178
|
+
assert is_approval_required("execute_command") is True
|
|
179
|
+
|
|
180
|
+
# read_file should not require approval
|
|
181
|
+
assert is_approval_required("read_file") is False
|
|
182
|
+
|
|
183
|
+
# list_files should not require approval
|
|
184
|
+
assert is_approval_required("list_files") is False
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class TestApprovalDecision:
|
|
188
|
+
"""Test ApprovalDecision class."""
|
|
189
|
+
|
|
190
|
+
def test_approval_decision_approved(self):
|
|
191
|
+
"""Test approved decision."""
|
|
192
|
+
from praisonaiagents.approval import ApprovalDecision
|
|
193
|
+
|
|
194
|
+
decision = ApprovalDecision(approved=True, reason="User approved")
|
|
195
|
+
assert decision.approved is True
|
|
196
|
+
assert decision.reason == "User approved"
|
|
197
|
+
assert decision.modified_args == {}
|
|
198
|
+
|
|
199
|
+
def test_approval_decision_denied(self):
|
|
200
|
+
"""Test denied decision."""
|
|
201
|
+
from praisonaiagents.approval import ApprovalDecision
|
|
202
|
+
|
|
203
|
+
decision = ApprovalDecision(approved=False, reason="User denied")
|
|
204
|
+
assert decision.approved is False
|
|
205
|
+
assert decision.reason == "User denied"
|
|
206
|
+
|
|
207
|
+
def test_approval_decision_with_modified_args(self):
|
|
208
|
+
"""Test decision with modified arguments."""
|
|
209
|
+
from praisonaiagents.approval import ApprovalDecision
|
|
210
|
+
|
|
211
|
+
decision = ApprovalDecision(
|
|
212
|
+
approved=True,
|
|
213
|
+
reason="Approved with modifications",
|
|
214
|
+
modified_args={"command": "ls -la"}
|
|
215
|
+
)
|
|
216
|
+
assert decision.approved is True
|
|
217
|
+
assert decision.modified_args == {"command": "ls -la"}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == "__main__":
|
|
221
|
+
pytest.main([__file__, "-v"])
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.3.57"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|