PraisonAI 3.0.0__py3-none-any.whl
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/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Autonomy Modes for PraisonAI CLI.
|
|
3
|
+
|
|
4
|
+
Inspired by Codex CLI's approval modes and Gemini CLI's approval settings.
|
|
5
|
+
Provides configurable autonomy levels: suggest, auto-edit, full-auto.
|
|
6
|
+
|
|
7
|
+
Architecture:
|
|
8
|
+
- AutonomyMode: Enum for different autonomy levels
|
|
9
|
+
- AutonomyPolicy: Policy configuration for each mode
|
|
10
|
+
- AutonomyManager: Manages mode transitions and approvals
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any, Callable, Dict, Optional, Set
|
|
15
|
+
from enum import Enum
|
|
16
|
+
import logging
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AutonomyMode(Enum):
|
|
22
|
+
"""
|
|
23
|
+
Autonomy levels for agent execution.
|
|
24
|
+
|
|
25
|
+
Inspired by Codex CLI's approval modes:
|
|
26
|
+
- SUGGEST: Read-only, requires approval for all changes
|
|
27
|
+
- AUTO_EDIT: Auto-approve file edits, require approval for commands
|
|
28
|
+
- FULL_AUTO: Auto-approve everything (YOLO mode)
|
|
29
|
+
"""
|
|
30
|
+
SUGGEST = "suggest" # Default: ask for approval on everything
|
|
31
|
+
AUTO_EDIT = "auto_edit" # Auto-approve file edits only
|
|
32
|
+
FULL_AUTO = "full_auto" # Auto-approve everything (dangerous)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def from_string(cls, value: str) -> "AutonomyMode":
|
|
36
|
+
"""Parse mode from string."""
|
|
37
|
+
value = value.lower().strip().replace("-", "_")
|
|
38
|
+
for mode in cls:
|
|
39
|
+
if mode.value == value:
|
|
40
|
+
return mode
|
|
41
|
+
raise ValueError(f"Unknown autonomy mode: {value}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ActionType(Enum):
|
|
45
|
+
"""Types of actions that require approval."""
|
|
46
|
+
FILE_READ = "file_read"
|
|
47
|
+
FILE_WRITE = "file_write"
|
|
48
|
+
FILE_DELETE = "file_delete"
|
|
49
|
+
SHELL_COMMAND = "shell_command"
|
|
50
|
+
NETWORK_REQUEST = "network_request"
|
|
51
|
+
CODE_EXECUTION = "code_execution"
|
|
52
|
+
GIT_OPERATION = "git_operation"
|
|
53
|
+
TOOL_CALL = "tool_call"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class ActionRequest:
|
|
58
|
+
"""
|
|
59
|
+
Request for an action that may require approval.
|
|
60
|
+
|
|
61
|
+
Attributes:
|
|
62
|
+
action_type: Type of action
|
|
63
|
+
description: Human-readable description
|
|
64
|
+
details: Additional details (file path, command, etc.)
|
|
65
|
+
risk_level: Estimated risk (low, medium, high)
|
|
66
|
+
reversible: Whether the action can be undone
|
|
67
|
+
"""
|
|
68
|
+
action_type: ActionType
|
|
69
|
+
description: str
|
|
70
|
+
details: Dict[str, Any] = field(default_factory=dict)
|
|
71
|
+
risk_level: str = "medium"
|
|
72
|
+
reversible: bool = True
|
|
73
|
+
|
|
74
|
+
def __str__(self) -> str:
|
|
75
|
+
return f"{self.action_type.value}: {self.description}"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class ApprovalResult:
|
|
80
|
+
"""Result of an approval request."""
|
|
81
|
+
approved: bool
|
|
82
|
+
reason: Optional[str] = None
|
|
83
|
+
modified_action: Optional[ActionRequest] = None
|
|
84
|
+
remember_choice: bool = False
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass
|
|
88
|
+
class AutonomyPolicy:
|
|
89
|
+
"""
|
|
90
|
+
Policy configuration for autonomy mode.
|
|
91
|
+
|
|
92
|
+
Defines which actions are auto-approved, require approval,
|
|
93
|
+
or are blocked entirely.
|
|
94
|
+
"""
|
|
95
|
+
mode: AutonomyMode
|
|
96
|
+
|
|
97
|
+
# Actions that are auto-approved
|
|
98
|
+
auto_approve: Set[ActionType] = field(default_factory=set)
|
|
99
|
+
|
|
100
|
+
# Actions that always require approval
|
|
101
|
+
require_approval: Set[ActionType] = field(default_factory=set)
|
|
102
|
+
|
|
103
|
+
# Actions that are blocked entirely
|
|
104
|
+
blocked: Set[ActionType] = field(default_factory=set)
|
|
105
|
+
|
|
106
|
+
# Trusted paths for file operations
|
|
107
|
+
trusted_paths: Set[str] = field(default_factory=set)
|
|
108
|
+
|
|
109
|
+
# Trusted commands (patterns)
|
|
110
|
+
trusted_commands: Set[str] = field(default_factory=set)
|
|
111
|
+
|
|
112
|
+
# Maximum auto-approved actions before requiring confirmation
|
|
113
|
+
max_auto_actions: int = 10
|
|
114
|
+
|
|
115
|
+
# Whether to show what would be done in suggest mode
|
|
116
|
+
show_preview: bool = True
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def for_mode(cls, mode: AutonomyMode) -> "AutonomyPolicy":
|
|
120
|
+
"""Create default policy for a given mode."""
|
|
121
|
+
if mode == AutonomyMode.SUGGEST:
|
|
122
|
+
return cls(
|
|
123
|
+
mode=mode,
|
|
124
|
+
auto_approve={ActionType.FILE_READ},
|
|
125
|
+
require_approval={
|
|
126
|
+
ActionType.FILE_WRITE,
|
|
127
|
+
ActionType.FILE_DELETE,
|
|
128
|
+
ActionType.SHELL_COMMAND,
|
|
129
|
+
ActionType.CODE_EXECUTION,
|
|
130
|
+
ActionType.GIT_OPERATION,
|
|
131
|
+
ActionType.NETWORK_REQUEST,
|
|
132
|
+
ActionType.TOOL_CALL,
|
|
133
|
+
},
|
|
134
|
+
blocked=set(),
|
|
135
|
+
show_preview=True
|
|
136
|
+
)
|
|
137
|
+
elif mode == AutonomyMode.AUTO_EDIT:
|
|
138
|
+
return cls(
|
|
139
|
+
mode=mode,
|
|
140
|
+
auto_approve={
|
|
141
|
+
ActionType.FILE_READ,
|
|
142
|
+
ActionType.FILE_WRITE,
|
|
143
|
+
ActionType.TOOL_CALL,
|
|
144
|
+
},
|
|
145
|
+
require_approval={
|
|
146
|
+
ActionType.FILE_DELETE,
|
|
147
|
+
ActionType.SHELL_COMMAND,
|
|
148
|
+
ActionType.CODE_EXECUTION,
|
|
149
|
+
ActionType.GIT_OPERATION,
|
|
150
|
+
ActionType.NETWORK_REQUEST,
|
|
151
|
+
},
|
|
152
|
+
blocked=set(),
|
|
153
|
+
show_preview=True
|
|
154
|
+
)
|
|
155
|
+
elif mode == AutonomyMode.FULL_AUTO:
|
|
156
|
+
return cls(
|
|
157
|
+
mode=mode,
|
|
158
|
+
auto_approve={
|
|
159
|
+
ActionType.FILE_READ,
|
|
160
|
+
ActionType.FILE_WRITE,
|
|
161
|
+
ActionType.FILE_DELETE,
|
|
162
|
+
ActionType.SHELL_COMMAND,
|
|
163
|
+
ActionType.CODE_EXECUTION,
|
|
164
|
+
ActionType.GIT_OPERATION,
|
|
165
|
+
ActionType.NETWORK_REQUEST,
|
|
166
|
+
ActionType.TOOL_CALL,
|
|
167
|
+
},
|
|
168
|
+
require_approval=set(),
|
|
169
|
+
blocked=set(),
|
|
170
|
+
show_preview=False,
|
|
171
|
+
max_auto_actions=100
|
|
172
|
+
)
|
|
173
|
+
else:
|
|
174
|
+
raise ValueError(f"Unknown mode: {mode}")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class AutonomyManager:
|
|
178
|
+
"""
|
|
179
|
+
Manages autonomy mode and approval flow.
|
|
180
|
+
|
|
181
|
+
Handles:
|
|
182
|
+
- Mode transitions
|
|
183
|
+
- Approval requests
|
|
184
|
+
- Action tracking
|
|
185
|
+
- Policy enforcement
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
def __init__(
|
|
189
|
+
self,
|
|
190
|
+
mode: AutonomyMode = AutonomyMode.SUGGEST,
|
|
191
|
+
policy: Optional[AutonomyPolicy] = None,
|
|
192
|
+
approval_callback: Optional[Callable[[ActionRequest], ApprovalResult]] = None,
|
|
193
|
+
verbose: bool = False
|
|
194
|
+
):
|
|
195
|
+
self.mode = mode
|
|
196
|
+
self.policy = policy or AutonomyPolicy.for_mode(mode)
|
|
197
|
+
self.approval_callback = approval_callback or self._default_approval
|
|
198
|
+
self.verbose = verbose
|
|
199
|
+
|
|
200
|
+
# Tracking
|
|
201
|
+
self._action_count = 0
|
|
202
|
+
self._auto_approved_count = 0
|
|
203
|
+
self._denied_count = 0
|
|
204
|
+
self._remembered_approvals: Dict[str, bool] = {}
|
|
205
|
+
|
|
206
|
+
def set_mode(self, mode: AutonomyMode) -> None:
|
|
207
|
+
"""Change the autonomy mode."""
|
|
208
|
+
old_mode = self.mode
|
|
209
|
+
self.mode = mode
|
|
210
|
+
self.policy = AutonomyPolicy.for_mode(mode)
|
|
211
|
+
|
|
212
|
+
if self.verbose:
|
|
213
|
+
logger.info(f"Autonomy mode changed: {old_mode.value} -> {mode.value}")
|
|
214
|
+
|
|
215
|
+
def request_approval(self, action: ActionRequest) -> ApprovalResult:
|
|
216
|
+
"""
|
|
217
|
+
Request approval for an action.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
ApprovalResult with approval decision
|
|
221
|
+
"""
|
|
222
|
+
self._action_count += 1
|
|
223
|
+
|
|
224
|
+
# Check if blocked
|
|
225
|
+
if action.action_type in self.policy.blocked:
|
|
226
|
+
self._denied_count += 1
|
|
227
|
+
return ApprovalResult(
|
|
228
|
+
approved=False,
|
|
229
|
+
reason=f"Action type '{action.action_type.value}' is blocked by policy"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Check remembered approvals
|
|
233
|
+
action_key = self._get_action_key(action)
|
|
234
|
+
if action_key in self._remembered_approvals:
|
|
235
|
+
approved = self._remembered_approvals[action_key]
|
|
236
|
+
if approved:
|
|
237
|
+
self._auto_approved_count += 1
|
|
238
|
+
else:
|
|
239
|
+
self._denied_count += 1
|
|
240
|
+
return ApprovalResult(approved=approved, reason="Remembered choice")
|
|
241
|
+
|
|
242
|
+
# Check if auto-approved
|
|
243
|
+
if action.action_type in self.policy.auto_approve:
|
|
244
|
+
# Check max auto actions
|
|
245
|
+
if self._auto_approved_count >= self.policy.max_auto_actions:
|
|
246
|
+
# Force approval after max auto actions
|
|
247
|
+
return self._request_user_approval(action)
|
|
248
|
+
|
|
249
|
+
self._auto_approved_count += 1
|
|
250
|
+
return ApprovalResult(approved=True, reason="Auto-approved by policy")
|
|
251
|
+
|
|
252
|
+
# Check if requires approval
|
|
253
|
+
if action.action_type in self.policy.require_approval:
|
|
254
|
+
return self._request_user_approval(action)
|
|
255
|
+
|
|
256
|
+
# Default: require approval for unknown action types
|
|
257
|
+
return self._request_user_approval(action)
|
|
258
|
+
|
|
259
|
+
def _request_user_approval(self, action: ActionRequest) -> ApprovalResult:
|
|
260
|
+
"""Request approval from user via callback."""
|
|
261
|
+
result = self.approval_callback(action)
|
|
262
|
+
|
|
263
|
+
if result.remember_choice:
|
|
264
|
+
action_key = self._get_action_key(action)
|
|
265
|
+
self._remembered_approvals[action_key] = result.approved
|
|
266
|
+
|
|
267
|
+
if result.approved:
|
|
268
|
+
self._auto_approved_count += 1
|
|
269
|
+
else:
|
|
270
|
+
self._denied_count += 1
|
|
271
|
+
|
|
272
|
+
return result
|
|
273
|
+
|
|
274
|
+
def _get_action_key(self, action: ActionRequest) -> str:
|
|
275
|
+
"""Generate a key for remembering approval decisions."""
|
|
276
|
+
# Use action type and relevant details
|
|
277
|
+
key_parts = [action.action_type.value]
|
|
278
|
+
|
|
279
|
+
if "path" in action.details:
|
|
280
|
+
key_parts.append(action.details["path"])
|
|
281
|
+
if "command" in action.details:
|
|
282
|
+
key_parts.append(action.details["command"])
|
|
283
|
+
|
|
284
|
+
return ":".join(key_parts)
|
|
285
|
+
|
|
286
|
+
def _default_approval(self, action: ActionRequest) -> ApprovalResult:
|
|
287
|
+
"""Default approval callback using console input."""
|
|
288
|
+
from rich.console import Console
|
|
289
|
+
from rich.panel import Panel
|
|
290
|
+
from rich.prompt import Confirm
|
|
291
|
+
|
|
292
|
+
console = Console()
|
|
293
|
+
|
|
294
|
+
# Build action description
|
|
295
|
+
risk_color = {
|
|
296
|
+
"low": "green",
|
|
297
|
+
"medium": "yellow",
|
|
298
|
+
"high": "red"
|
|
299
|
+
}.get(action.risk_level, "white")
|
|
300
|
+
|
|
301
|
+
details_str = "\n".join(
|
|
302
|
+
f" {k}: {v}" for k, v in action.details.items()
|
|
303
|
+
) if action.details else " (no details)"
|
|
304
|
+
|
|
305
|
+
panel_content = f"""
|
|
306
|
+
[bold]{action.description}[/bold]
|
|
307
|
+
|
|
308
|
+
Type: {action.action_type.value}
|
|
309
|
+
Risk: [{risk_color}]{action.risk_level}[/{risk_color}]
|
|
310
|
+
Reversible: {"Yes" if action.reversible else "No"}
|
|
311
|
+
|
|
312
|
+
Details:
|
|
313
|
+
{details_str}
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
console.print(Panel(
|
|
317
|
+
panel_content,
|
|
318
|
+
title="🔒 Approval Required",
|
|
319
|
+
border_style="yellow"
|
|
320
|
+
))
|
|
321
|
+
|
|
322
|
+
approved = Confirm.ask("Approve this action?", default=False)
|
|
323
|
+
remember = False
|
|
324
|
+
|
|
325
|
+
if approved:
|
|
326
|
+
remember = Confirm.ask("Remember this choice for similar actions?", default=False)
|
|
327
|
+
|
|
328
|
+
return ApprovalResult(
|
|
329
|
+
approved=approved,
|
|
330
|
+
remember_choice=remember
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
def get_stats(self) -> Dict[str, int]:
|
|
334
|
+
"""Get approval statistics."""
|
|
335
|
+
return {
|
|
336
|
+
"total_actions": self._action_count,
|
|
337
|
+
"auto_approved": self._auto_approved_count,
|
|
338
|
+
"denied": self._denied_count,
|
|
339
|
+
"remembered": len(self._remembered_approvals)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
def reset_stats(self) -> None:
|
|
343
|
+
"""Reset approval statistics."""
|
|
344
|
+
self._action_count = 0
|
|
345
|
+
self._auto_approved_count = 0
|
|
346
|
+
self._denied_count = 0
|
|
347
|
+
|
|
348
|
+
def clear_remembered(self) -> None:
|
|
349
|
+
"""Clear remembered approval decisions."""
|
|
350
|
+
self._remembered_approvals.clear()
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
# ============================================================================
|
|
354
|
+
# CLI Integration Handler
|
|
355
|
+
# ============================================================================
|
|
356
|
+
|
|
357
|
+
class AutonomyModeHandler:
|
|
358
|
+
"""
|
|
359
|
+
Handler for integrating autonomy modes with PraisonAI CLI.
|
|
360
|
+
"""
|
|
361
|
+
|
|
362
|
+
def __init__(self, verbose: bool = False):
|
|
363
|
+
self.verbose = verbose
|
|
364
|
+
self._manager: Optional[AutonomyManager] = None
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
def feature_name(self) -> str:
|
|
368
|
+
return "autonomy_mode"
|
|
369
|
+
|
|
370
|
+
def initialize(
|
|
371
|
+
self,
|
|
372
|
+
mode: str = "suggest",
|
|
373
|
+
approval_callback: Optional[Callable[[ActionRequest], ApprovalResult]] = None
|
|
374
|
+
) -> AutonomyManager:
|
|
375
|
+
"""
|
|
376
|
+
Initialize the autonomy manager.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
mode: Autonomy mode string (suggest, auto_edit, full_auto)
|
|
380
|
+
approval_callback: Optional custom approval callback
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
Configured AutonomyManager
|
|
384
|
+
"""
|
|
385
|
+
try:
|
|
386
|
+
autonomy_mode = AutonomyMode.from_string(mode)
|
|
387
|
+
except ValueError:
|
|
388
|
+
logger.warning(f"Unknown mode '{mode}', defaulting to 'suggest'")
|
|
389
|
+
autonomy_mode = AutonomyMode.SUGGEST
|
|
390
|
+
|
|
391
|
+
self._manager = AutonomyManager(
|
|
392
|
+
mode=autonomy_mode,
|
|
393
|
+
approval_callback=approval_callback,
|
|
394
|
+
verbose=self.verbose
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
if self.verbose:
|
|
398
|
+
from rich import print as rprint
|
|
399
|
+
rprint(f"[cyan]Autonomy mode: {autonomy_mode.value}[/cyan]")
|
|
400
|
+
|
|
401
|
+
return self._manager
|
|
402
|
+
|
|
403
|
+
def get_manager(self) -> Optional[AutonomyManager]:
|
|
404
|
+
"""Get the current autonomy manager."""
|
|
405
|
+
return self._manager
|
|
406
|
+
|
|
407
|
+
def request_approval(self, action: ActionRequest) -> ApprovalResult:
|
|
408
|
+
"""Request approval for an action."""
|
|
409
|
+
if not self._manager:
|
|
410
|
+
self._manager = self.initialize()
|
|
411
|
+
|
|
412
|
+
return self._manager.request_approval(action)
|
|
413
|
+
|
|
414
|
+
def set_mode(self, mode: str) -> None:
|
|
415
|
+
"""Change the autonomy mode."""
|
|
416
|
+
if not self._manager:
|
|
417
|
+
self.initialize(mode)
|
|
418
|
+
else:
|
|
419
|
+
try:
|
|
420
|
+
autonomy_mode = AutonomyMode.from_string(mode)
|
|
421
|
+
self._manager.set_mode(autonomy_mode)
|
|
422
|
+
except ValueError as e:
|
|
423
|
+
logger.error(f"Failed to set mode: {e}")
|
|
424
|
+
|
|
425
|
+
def get_mode(self) -> str:
|
|
426
|
+
"""Get the current autonomy mode."""
|
|
427
|
+
if self._manager:
|
|
428
|
+
return self._manager.mode.value
|
|
429
|
+
return AutonomyMode.SUGGEST.value
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
# ============================================================================
|
|
433
|
+
# Convenience Functions
|
|
434
|
+
# ============================================================================
|
|
435
|
+
|
|
436
|
+
def create_file_write_action(path: str, content_preview: str = "") -> ActionRequest:
|
|
437
|
+
"""Create an action request for file write."""
|
|
438
|
+
return ActionRequest(
|
|
439
|
+
action_type=ActionType.FILE_WRITE,
|
|
440
|
+
description=f"Write to file: {path}",
|
|
441
|
+
details={
|
|
442
|
+
"path": path,
|
|
443
|
+
"preview": content_preview[:200] if content_preview else "(no preview)"
|
|
444
|
+
},
|
|
445
|
+
risk_level="medium",
|
|
446
|
+
reversible=True
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def create_shell_command_action(command: str, cwd: str = ".") -> ActionRequest:
|
|
451
|
+
"""Create an action request for shell command."""
|
|
452
|
+
# Assess risk based on command
|
|
453
|
+
dangerous_patterns = ["rm ", "sudo ", "chmod ", "chown ", "> /", "| sh", "curl |"]
|
|
454
|
+
risk = "high" if any(p in command for p in dangerous_patterns) else "medium"
|
|
455
|
+
|
|
456
|
+
return ActionRequest(
|
|
457
|
+
action_type=ActionType.SHELL_COMMAND,
|
|
458
|
+
description=f"Execute command: {command}",
|
|
459
|
+
details={
|
|
460
|
+
"command": command,
|
|
461
|
+
"cwd": cwd
|
|
462
|
+
},
|
|
463
|
+
risk_level=risk,
|
|
464
|
+
reversible=False
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def create_git_action(operation: str, details: Dict[str, Any] = None) -> ActionRequest:
|
|
469
|
+
"""Create an action request for git operation."""
|
|
470
|
+
return ActionRequest(
|
|
471
|
+
action_type=ActionType.GIT_OPERATION,
|
|
472
|
+
description=f"Git {operation}",
|
|
473
|
+
details=details or {},
|
|
474
|
+
risk_level="low" if operation in ["status", "log", "diff"] else "medium",
|
|
475
|
+
reversible=operation not in ["push", "force-push"]
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def create_tool_call_action(tool_name: str, args: Dict[str, Any] = None) -> ActionRequest:
|
|
480
|
+
"""Create an action request for tool call."""
|
|
481
|
+
return ActionRequest(
|
|
482
|
+
action_type=ActionType.TOOL_CALL,
|
|
483
|
+
description=f"Call tool: {tool_name}",
|
|
484
|
+
details={
|
|
485
|
+
"tool": tool_name,
|
|
486
|
+
"args": args or {}
|
|
487
|
+
},
|
|
488
|
+
risk_level="low",
|
|
489
|
+
reversible=True
|
|
490
|
+
)
|