praisonAI 2.3.87__tar.gz → 2.4.0__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.
Files changed (128) hide show
  1. {praisonai-2.3.87 → praisonai-2.4.0}/PKG-INFO +64 -10
  2. {praisonai-2.3.87 → praisonai-2.4.0}/PraisonAI.egg-info/PKG-INFO +64 -10
  3. {praisonai-2.3.87 → praisonai-2.4.0}/PraisonAI.egg-info/SOURCES.txt +3 -0
  4. {praisonai-2.3.87 → praisonai-2.4.0}/PraisonAI.egg-info/requires.txt +1 -1
  5. {praisonai-2.3.87 → praisonai-2.4.0}/README.md +62 -8
  6. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/__init__.py +18 -0
  7. praisonai-2.4.0/praisonai/cli/features/background.py +296 -0
  8. praisonai-2.4.0/praisonai/cli/features/checkpoints.py +338 -0
  9. praisonai-2.4.0/praisonai/cli/features/hooks.py +583 -0
  10. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/deploy.py +1 -1
  11. praisonai-2.4.0/praisonai/version.py +1 -0
  12. {praisonai-2.3.87 → praisonai-2.4.0}/pyproject.toml +1 -1
  13. praisonai-2.3.87/praisonai/version.py +0 -1
  14. {praisonai-2.3.87 → praisonai-2.4.0}/PraisonAI.egg-info/dependency_links.txt +0 -0
  15. {praisonai-2.3.87 → praisonai-2.4.0}/PraisonAI.egg-info/entry_points.txt +0 -0
  16. {praisonai-2.3.87 → praisonai-2.4.0}/PraisonAI.egg-info/top_level.txt +0 -0
  17. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/__init__.py +0 -0
  18. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/__main__.py +0 -0
  19. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/agent_scheduler.py +0 -0
  20. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/agents_generator.py +0 -0
  21. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/api/call.py +0 -0
  22. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/auto.py +0 -0
  23. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/chainlit_ui.py +0 -0
  24. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/__init__.py +0 -0
  25. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/agent_scheduler.py +0 -0
  26. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/agents.py +0 -0
  27. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/at_mentions.py +0 -0
  28. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/auto_memory.py +0 -0
  29. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/autonomy_mode.py +0 -0
  30. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/base.py +0 -0
  31. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/compare.py +0 -0
  32. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/cost_tracker.py +0 -0
  33. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/eval.py +0 -0
  34. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/external_agents.py +0 -0
  35. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/fast_context.py +0 -0
  36. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/flow_display.py +0 -0
  37. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/git_integration.py +0 -0
  38. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/guardrail.py +0 -0
  39. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/handoff.py +0 -0
  40. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/image.py +0 -0
  41. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/interactive_tui.py +0 -0
  42. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/knowledge.py +0 -0
  43. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/mcp.py +0 -0
  44. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/message_queue.py +0 -0
  45. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/metrics.py +0 -0
  46. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/n8n.py +0 -0
  47. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/profile.py +0 -0
  48. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/profiling.py +0 -0
  49. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/repo_map.py +0 -0
  50. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/router.py +0 -0
  51. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/sandbox_executor.py +0 -0
  52. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/session.py +0 -0
  53. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/skills.py +0 -0
  54. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/slash_commands.py +0 -0
  55. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/telemetry.py +0 -0
  56. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/todo.py +0 -0
  57. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/tools.py +0 -0
  58. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/features/workflow.py +0 -0
  59. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/main.py +0 -0
  60. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/cli/schedule_cli.py +0 -0
  61. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/__init__.py +0 -0
  62. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/agent_tools.py +0 -0
  63. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/diff/__init__.py +0 -0
  64. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/diff/diff_strategy.py +0 -0
  65. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/tools/__init__.py +0 -0
  66. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/tools/apply_diff.py +0 -0
  67. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/tools/execute_command.py +0 -0
  68. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/tools/list_files.py +0 -0
  69. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/tools/read_file.py +0 -0
  70. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/tools/search_replace.py +0 -0
  71. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/tools/write_file.py +0 -0
  72. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/utils/__init__.py +0 -0
  73. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/utils/file_utils.py +0 -0
  74. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/utils/ignore_utils.py +0 -0
  75. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/code/utils/text_utils.py +0 -0
  76. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/inbuilt_tools/__init__.py +0 -0
  77. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
  78. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/inc/__init__.py +0 -0
  79. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/inc/config.py +0 -0
  80. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/inc/models.py +0 -0
  81. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/integrations/__init__.py +0 -0
  82. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/integrations/base.py +0 -0
  83. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/integrations/claude_code.py +0 -0
  84. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/integrations/codex_cli.py +0 -0
  85. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/integrations/cursor_cli.py +0 -0
  86. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/integrations/gemini_cli.py +0 -0
  87. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/profiler.py +0 -0
  88. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/scheduler/__init__.py +0 -0
  89. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/scheduler/agent_scheduler.py +0 -0
  90. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/scheduler/base.py +0 -0
  91. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/scheduler/daemon_manager.py +0 -0
  92. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/scheduler/state_manager.py +0 -0
  93. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/scheduler/yaml_loader.py +0 -0
  94. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/scheduler.py +0 -0
  95. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/setup/__init__.py +0 -0
  96. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/setup/build.py +0 -0
  97. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/setup/post_install.py +0 -0
  98. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/setup/setup_conda_env.py +0 -0
  99. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/setup.py +0 -0
  100. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/test.py +0 -0
  101. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/train.py +0 -0
  102. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/train_vision.py +0 -0
  103. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/agents.py +0 -0
  104. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/callbacks.py +0 -0
  105. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/chat.py +0 -0
  106. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/code.py +0 -0
  107. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/colab.py +0 -0
  108. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/colab_chainlit.py +0 -0
  109. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/components/aicoder.py +0 -0
  110. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/context.py +0 -0
  111. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/database_config.py +0 -0
  112. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/db.py +0 -0
  113. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/realtime.py +0 -0
  114. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/realtimeclient/__init__.py +0 -0
  115. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/realtimeclient/tools.py +0 -0
  116. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/ui/sql_alchemy.py +0 -0
  117. {praisonai-2.3.87 → praisonai-2.4.0}/praisonai/upload_vision.py +0 -0
  118. {praisonai-2.3.87 → praisonai-2.4.0}/setup.cfg +0 -0
  119. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test.py +0 -0
  120. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_agents_playbook.py +0 -0
  121. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_approval_system.py +0 -0
  122. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_basic.py +0 -0
  123. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_cli_features.py +0 -0
  124. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_custom_tools_fix.py +0 -0
  125. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_double_api_fix.py +0 -0
  126. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_n8n_integration.py +0 -0
  127. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_phase2_refactor.py +0 -0
  128. {praisonai-2.3.87 → praisonai-2.4.0}/tests/test_runner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PraisonAI
3
- Version: 2.3.87
3
+ Version: 2.4.0
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.27
14
+ Requires-Dist: praisonaiagents>=0.2.0
15
15
  Requires-Dist: python-dotenv>=0.19.0
16
16
  Requires-Dist: instructor>=1.3.3
17
17
  Requires-Dist: PyYAML>=6.0
@@ -960,21 +960,75 @@ callbacks:
960
960
 
961
961
  ### 9. Hooks
962
962
 
963
- Configure in `.praison/hooks.json`:
963
+ Intercept and modify agent behavior at various lifecycle points:
964
964
 
965
965
  ```python
966
- from praisonaiagents.memory import HooksManager
966
+ from praisonaiagents.hooks import (
967
+ HookRegistry, HookRunner, HookEvent, HookResult,
968
+ BeforeToolInput
969
+ )
970
+
971
+ # Create a hook registry
972
+ registry = HookRegistry()
967
973
 
968
- hooks = HooksManager()
974
+ # Log all tool calls
975
+ @registry.on(HookEvent.BEFORE_TOOL)
976
+ def log_tools(event_data: BeforeToolInput) -> HookResult:
977
+ print(f"Tool: {event_data.tool_name}")
978
+ return HookResult.allow()
969
979
 
970
- # Register Python hooks
971
- hooks.register("pre_write_code", lambda ctx: print(f"Writing {ctx['file']}"))
980
+ # Block dangerous operations
981
+ @registry.on(HookEvent.BEFORE_TOOL)
982
+ def security_check(event_data: BeforeToolInput) -> HookResult:
983
+ if "delete" in event_data.tool_name.lower():
984
+ return HookResult.deny("Delete operations blocked")
985
+ return HookResult.allow()
972
986
 
973
987
  # Execute hooks
974
- result = hooks.execute("pre_write_code", {"file": "main.py"})
988
+ runner = HookRunner(registry)
989
+ ```
990
+
991
+ **CLI Commands:**
992
+ ```bash
993
+ praisonai hooks list # List registered hooks
994
+ praisonai hooks test before_tool # Test hooks for an event
995
+ praisonai hooks run "echo test" # Run a command hook
996
+ praisonai hooks validate hooks.json # Validate configuration
997
+ ```
998
+
999
+
1000
+ ### 10. Shadow Git Checkpoints
1001
+
1002
+ File-level undo/restore using shadow git:
1003
+
1004
+ ```python
1005
+ from praisonaiagents.checkpoints import CheckpointService
1006
+
1007
+ service = CheckpointService(workspace_dir="./my_project")
1008
+ await service.initialize()
1009
+
1010
+ # Save checkpoint before changes
1011
+ result = await service.save("Before refactoring")
1012
+
1013
+ # Make changes...
1014
+
1015
+ # Restore if needed
1016
+ await service.restore(result.checkpoint.id)
1017
+
1018
+ # View diff
1019
+ diff = await service.diff()
975
1020
  ```
976
1021
 
977
- ### 10. Field Names Reference (A-I-G-S)
1022
+ **CLI Commands:**
1023
+ ```bash
1024
+ praisonai checkpoint save "Before changes" # Save checkpoint
1025
+ praisonai checkpoint list # List checkpoints
1026
+ praisonai checkpoint diff # Show changes
1027
+ praisonai checkpoint restore abc123 # Restore to checkpoint
1028
+ ```
1029
+
1030
+
1031
+ ### 11. Field Names Reference (A-I-G-S)
978
1032
 
979
1033
  PraisonAI accepts both old (agents.yaml) and new (workflow.yaml) field names. Use the **canonical names** for new projects:
980
1034
 
@@ -1008,7 +1062,7 @@ steps: # Define steps (not 'tasks')
1008
1062
 
1009
1063
  > **Note:** The parser accepts both old and new names. Run `praisonai workflow validate <file.yaml>` to see suggestions for canonical names.
1010
1064
 
1011
- ### 11. Extended agents.yaml with Workflow Patterns
1065
+ ### 12. Extended agents.yaml with Workflow Patterns
1012
1066
 
1013
1067
  **Feature Parity:** Both `agents.yaml` and `workflow.yaml` now support the same features:
1014
1068
  - All workflow patterns (route, parallel, loop, repeat)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PraisonAI
3
- Version: 2.3.87
3
+ Version: 2.4.0
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.27
14
+ Requires-Dist: praisonaiagents>=0.2.0
15
15
  Requires-Dist: python-dotenv>=0.19.0
16
16
  Requires-Dist: instructor>=1.3.3
17
17
  Requires-Dist: PyYAML>=6.0
@@ -960,21 +960,75 @@ callbacks:
960
960
 
961
961
  ### 9. Hooks
962
962
 
963
- Configure in `.praison/hooks.json`:
963
+ Intercept and modify agent behavior at various lifecycle points:
964
964
 
965
965
  ```python
966
- from praisonaiagents.memory import HooksManager
966
+ from praisonaiagents.hooks import (
967
+ HookRegistry, HookRunner, HookEvent, HookResult,
968
+ BeforeToolInput
969
+ )
970
+
971
+ # Create a hook registry
972
+ registry = HookRegistry()
967
973
 
968
- hooks = HooksManager()
974
+ # Log all tool calls
975
+ @registry.on(HookEvent.BEFORE_TOOL)
976
+ def log_tools(event_data: BeforeToolInput) -> HookResult:
977
+ print(f"Tool: {event_data.tool_name}")
978
+ return HookResult.allow()
969
979
 
970
- # Register Python hooks
971
- hooks.register("pre_write_code", lambda ctx: print(f"Writing {ctx['file']}"))
980
+ # Block dangerous operations
981
+ @registry.on(HookEvent.BEFORE_TOOL)
982
+ def security_check(event_data: BeforeToolInput) -> HookResult:
983
+ if "delete" in event_data.tool_name.lower():
984
+ return HookResult.deny("Delete operations blocked")
985
+ return HookResult.allow()
972
986
 
973
987
  # Execute hooks
974
- result = hooks.execute("pre_write_code", {"file": "main.py"})
988
+ runner = HookRunner(registry)
989
+ ```
990
+
991
+ **CLI Commands:**
992
+ ```bash
993
+ praisonai hooks list # List registered hooks
994
+ praisonai hooks test before_tool # Test hooks for an event
995
+ praisonai hooks run "echo test" # Run a command hook
996
+ praisonai hooks validate hooks.json # Validate configuration
997
+ ```
998
+
999
+
1000
+ ### 10. Shadow Git Checkpoints
1001
+
1002
+ File-level undo/restore using shadow git:
1003
+
1004
+ ```python
1005
+ from praisonaiagents.checkpoints import CheckpointService
1006
+
1007
+ service = CheckpointService(workspace_dir="./my_project")
1008
+ await service.initialize()
1009
+
1010
+ # Save checkpoint before changes
1011
+ result = await service.save("Before refactoring")
1012
+
1013
+ # Make changes...
1014
+
1015
+ # Restore if needed
1016
+ await service.restore(result.checkpoint.id)
1017
+
1018
+ # View diff
1019
+ diff = await service.diff()
975
1020
  ```
976
1021
 
977
- ### 10. Field Names Reference (A-I-G-S)
1022
+ **CLI Commands:**
1023
+ ```bash
1024
+ praisonai checkpoint save "Before changes" # Save checkpoint
1025
+ praisonai checkpoint list # List checkpoints
1026
+ praisonai checkpoint diff # Show changes
1027
+ praisonai checkpoint restore abc123 # Restore to checkpoint
1028
+ ```
1029
+
1030
+
1031
+ ### 11. Field Names Reference (A-I-G-S)
978
1032
 
979
1033
  PraisonAI accepts both old (agents.yaml) and new (workflow.yaml) field names. Use the **canonical names** for new projects:
980
1034
 
@@ -1008,7 +1062,7 @@ steps: # Define steps (not 'tasks')
1008
1062
 
1009
1063
  > **Note:** The parser accepts both old and new names. Run `praisonai workflow validate <file.yaml>` to see suggestions for canonical names.
1010
1064
 
1011
- ### 11. Extended agents.yaml with Workflow Patterns
1065
+ ### 12. Extended agents.yaml with Workflow Patterns
1012
1066
 
1013
1067
  **Feature Parity:** Both `agents.yaml` and `workflow.yaml` now support the same features:
1014
1068
  - All workflow patterns (route, parallel, loop, repeat)
@@ -31,7 +31,9 @@ praisonai/cli/features/agents.py
31
31
  praisonai/cli/features/at_mentions.py
32
32
  praisonai/cli/features/auto_memory.py
33
33
  praisonai/cli/features/autonomy_mode.py
34
+ praisonai/cli/features/background.py
34
35
  praisonai/cli/features/base.py
36
+ praisonai/cli/features/checkpoints.py
35
37
  praisonai/cli/features/compare.py
36
38
  praisonai/cli/features/cost_tracker.py
37
39
  praisonai/cli/features/eval.py
@@ -41,6 +43,7 @@ praisonai/cli/features/flow_display.py
41
43
  praisonai/cli/features/git_integration.py
42
44
  praisonai/cli/features/guardrail.py
43
45
  praisonai/cli/features/handoff.py
46
+ praisonai/cli/features/hooks.py
44
47
  praisonai/cli/features/image.py
45
48
  praisonai/cli/features/interactive_tui.py
46
49
  praisonai/cli/features/knowledge.py
@@ -1,7 +1,7 @@
1
1
  rich>=13.7
2
2
  markdown>=3.5
3
3
  pyparsing>=3.0.0
4
- praisonaiagents>=0.1.27
4
+ praisonaiagents>=0.2.0
5
5
  python-dotenv>=0.19.0
6
6
  instructor>=1.3.3
7
7
  PyYAML>=6.0
@@ -863,21 +863,75 @@ callbacks:
863
863
 
864
864
  ### 9. Hooks
865
865
 
866
- Configure in `.praison/hooks.json`:
866
+ Intercept and modify agent behavior at various lifecycle points:
867
867
 
868
868
  ```python
869
- from praisonaiagents.memory import HooksManager
869
+ from praisonaiagents.hooks import (
870
+ HookRegistry, HookRunner, HookEvent, HookResult,
871
+ BeforeToolInput
872
+ )
873
+
874
+ # Create a hook registry
875
+ registry = HookRegistry()
870
876
 
871
- hooks = HooksManager()
877
+ # Log all tool calls
878
+ @registry.on(HookEvent.BEFORE_TOOL)
879
+ def log_tools(event_data: BeforeToolInput) -> HookResult:
880
+ print(f"Tool: {event_data.tool_name}")
881
+ return HookResult.allow()
872
882
 
873
- # Register Python hooks
874
- hooks.register("pre_write_code", lambda ctx: print(f"Writing {ctx['file']}"))
883
+ # Block dangerous operations
884
+ @registry.on(HookEvent.BEFORE_TOOL)
885
+ def security_check(event_data: BeforeToolInput) -> HookResult:
886
+ if "delete" in event_data.tool_name.lower():
887
+ return HookResult.deny("Delete operations blocked")
888
+ return HookResult.allow()
875
889
 
876
890
  # Execute hooks
877
- result = hooks.execute("pre_write_code", {"file": "main.py"})
891
+ runner = HookRunner(registry)
892
+ ```
893
+
894
+ **CLI Commands:**
895
+ ```bash
896
+ praisonai hooks list # List registered hooks
897
+ praisonai hooks test before_tool # Test hooks for an event
898
+ praisonai hooks run "echo test" # Run a command hook
899
+ praisonai hooks validate hooks.json # Validate configuration
900
+ ```
901
+
902
+
903
+ ### 10. Shadow Git Checkpoints
904
+
905
+ File-level undo/restore using shadow git:
906
+
907
+ ```python
908
+ from praisonaiagents.checkpoints import CheckpointService
909
+
910
+ service = CheckpointService(workspace_dir="./my_project")
911
+ await service.initialize()
912
+
913
+ # Save checkpoint before changes
914
+ result = await service.save("Before refactoring")
915
+
916
+ # Make changes...
917
+
918
+ # Restore if needed
919
+ await service.restore(result.checkpoint.id)
920
+
921
+ # View diff
922
+ diff = await service.diff()
878
923
  ```
879
924
 
880
- ### 10. Field Names Reference (A-I-G-S)
925
+ **CLI Commands:**
926
+ ```bash
927
+ praisonai checkpoint save "Before changes" # Save checkpoint
928
+ praisonai checkpoint list # List checkpoints
929
+ praisonai checkpoint diff # Show changes
930
+ praisonai checkpoint restore abc123 # Restore to checkpoint
931
+ ```
932
+
933
+
934
+ ### 11. Field Names Reference (A-I-G-S)
881
935
 
882
936
  PraisonAI accepts both old (agents.yaml) and new (workflow.yaml) field names. Use the **canonical names** for new projects:
883
937
 
@@ -911,7 +965,7 @@ steps: # Define steps (not 'tasks')
911
965
 
912
966
  > **Note:** The parser accepts both old and new names. Run `praisonai workflow validate <file.yaml>` to see suggestions for canonical names.
913
967
 
914
- ### 11. Extended agents.yaml with Workflow Patterns
968
+ ### 12. Extended agents.yaml with Workflow Patterns
915
969
 
916
970
  **Feature Parity:** Both `agents.yaml` and `workflow.yaml` now support the same features:
917
971
  - All workflow patterns (route, parallel, loop, repeat)
@@ -29,6 +29,9 @@ Features:
29
29
  - at_mentions: @ mention autocomplete for files and directories
30
30
  - compare: CLI command comparison feature
31
31
  - eval: Agent evaluation framework (accuracy, performance, reliability, criteria)
32
+ - hooks: Hook system for intercepting and modifying agent behavior
33
+ - checkpoints: Shadow git checkpointing for file-level undo/restore
34
+ - background: Background agent task execution and management
32
35
  """
33
36
 
34
37
  # Type hints available for IDE support
@@ -71,6 +74,12 @@ __all__ = [
71
74
  'CompareHandler',
72
75
  # Agent Skills
73
76
  'SkillsHandler',
77
+ # Hooks
78
+ 'HooksHandler',
79
+ # Checkpoints
80
+ 'CheckpointsHandler',
81
+ # Background
82
+ 'BackgroundHandler',
74
83
  ]
75
84
 
76
85
  def __getattr__(name):
@@ -170,4 +179,13 @@ def __getattr__(name):
170
179
  elif name == 'SkillsHandler':
171
180
  from .skills import SkillsHandler
172
181
  return SkillsHandler
182
+ elif name == 'HooksHandler':
183
+ from .hooks import HooksHandler
184
+ return HooksHandler
185
+ elif name == 'CheckpointsHandler':
186
+ from .checkpoints import CheckpointsHandler
187
+ return CheckpointsHandler
188
+ elif name == 'BackgroundHandler':
189
+ from .background import BackgroundHandler
190
+ return BackgroundHandler
173
191
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,296 @@
1
+ """
2
+ Background Agents CLI Feature for PraisonAI.
3
+
4
+ Provides CLI commands for managing background agent tasks.
5
+
6
+ Commands:
7
+ - praisonai background list # List background tasks
8
+ - praisonai background status <id> # Get task status
9
+ - praisonai background cancel <id> # Cancel a task
10
+ - praisonai background clear # Clear completed tasks
11
+ """
12
+
13
+ import os
14
+ import asyncio
15
+ from typing import Optional, List
16
+
17
+
18
+ class BackgroundHandler:
19
+ """
20
+ Handler for background CLI commands.
21
+
22
+ Provides functionality to:
23
+ - List running and completed background tasks
24
+ - Check task status
25
+ - Cancel running tasks
26
+ - Clear completed tasks
27
+ """
28
+
29
+ def __init__(self, verbose: bool = False):
30
+ self.verbose = verbose
31
+ self._runner = None
32
+
33
+ @property
34
+ def feature_name(self) -> str:
35
+ return "background"
36
+
37
+ async def _get_runner(self):
38
+ """Lazy load the background runner."""
39
+ if self._runner is None:
40
+ from praisonaiagents.background import BackgroundRunner
41
+ self._runner = BackgroundRunner()
42
+ return self._runner
43
+
44
+ async def list_tasks(self, status: Optional[str] = None) -> List[dict]:
45
+ """
46
+ List all background tasks.
47
+
48
+ Args:
49
+ status: Optional status filter (pending, running, completed, failed)
50
+
51
+ Returns:
52
+ List of task dictionaries
53
+ """
54
+ runner = await self._get_runner()
55
+
56
+ status_enum = None
57
+ if status:
58
+ from praisonaiagents.background import TaskStatus
59
+ status_map = {
60
+ "pending": TaskStatus.PENDING,
61
+ "running": TaskStatus.RUNNING,
62
+ "completed": TaskStatus.COMPLETED,
63
+ "failed": TaskStatus.FAILED,
64
+ "cancelled": TaskStatus.CANCELLED
65
+ }
66
+ status_enum = status_map.get(status.lower())
67
+
68
+ tasks = runner.list_tasks(status=status_enum)
69
+
70
+ self._print_tasks_table(tasks)
71
+ return tasks
72
+
73
+ def _print_tasks_table(self, tasks: List[dict]):
74
+ """Print tasks in table format."""
75
+ try:
76
+ from rich.console import Console
77
+ from rich.table import Table
78
+
79
+ console = Console()
80
+
81
+ if not tasks:
82
+ console.print("[yellow]No background tasks[/yellow]")
83
+ return
84
+
85
+ table = Table(title="Background Tasks")
86
+ table.add_column("ID", style="cyan")
87
+ table.add_column("Name", style="green")
88
+ table.add_column("Status", style="yellow")
89
+ table.add_column("Progress", style="blue")
90
+ table.add_column("Duration", style="magenta")
91
+
92
+ for task in tasks:
93
+ status = task.get("status", "unknown")
94
+ status_color = {
95
+ "pending": "dim",
96
+ "running": "yellow",
97
+ "completed": "green",
98
+ "failed": "red",
99
+ "cancelled": "dim"
100
+ }.get(status, "white")
101
+
102
+ progress = f"{task.get('progress', 0) * 100:.0f}%"
103
+ duration = task.get("duration_seconds")
104
+ duration_str = f"{duration:.1f}s" if duration else "-"
105
+
106
+ table.add_row(
107
+ task.get("id", "?"),
108
+ task.get("name", "unnamed"),
109
+ f"[{status_color}]{status}[/{status_color}]",
110
+ progress,
111
+ duration_str
112
+ )
113
+
114
+ console.print(table)
115
+ except ImportError:
116
+ print("Background Tasks:")
117
+ for task in tasks:
118
+ print(f" {task.get('id')} - {task.get('name')} ({task.get('status')})")
119
+
120
+ async def get_status(self, task_id: str) -> Optional[dict]:
121
+ """
122
+ Get status of a specific task.
123
+
124
+ Args:
125
+ task_id: Task ID
126
+
127
+ Returns:
128
+ Task dictionary or None
129
+ """
130
+ runner = await self._get_runner()
131
+ task = runner.get_task(task_id)
132
+
133
+ if task is None:
134
+ self._print_error(f"Task not found: {task_id}")
135
+ return None
136
+
137
+ task_dict = task.to_dict()
138
+ self._print_task_details(task_dict)
139
+ return task_dict
140
+
141
+ def _print_task_details(self, task: dict):
142
+ """Print detailed task information."""
143
+ try:
144
+ from rich.console import Console
145
+ from rich.panel import Panel
146
+
147
+ console = Console()
148
+
149
+ status = task.get("status", "unknown")
150
+ status_color = {
151
+ "pending": "dim",
152
+ "running": "yellow",
153
+ "completed": "green",
154
+ "failed": "red",
155
+ "cancelled": "dim"
156
+ }.get(status, "white")
157
+
158
+ content = f"""
159
+ [bold]ID:[/bold] {task.get('id')}
160
+ [bold]Name:[/bold] {task.get('name')}
161
+ [bold]Status:[/bold] [{status_color}]{status}[/{status_color}]
162
+ [bold]Progress:[/bold] {task.get('progress', 0) * 100:.0f}%
163
+ [bold]Created:[/bold] {task.get('created_at')}
164
+ [bold]Started:[/bold] {task.get('started_at') or '-'}
165
+ [bold]Completed:[/bold] {task.get('completed_at') or '-'}
166
+ [bold]Duration:[/bold] {task.get('duration_seconds', 0):.2f}s
167
+ """
168
+ if task.get("error"):
169
+ content += f"[bold]Error:[/bold] [red]{task.get('error')}[/red]\n"
170
+
171
+ if task.get("result"):
172
+ result_str = str(task.get("result"))[:200]
173
+ content += f"[bold]Result:[/bold] {result_str}\n"
174
+
175
+ console.print(Panel(content.strip(), title=f"Task: {task.get('name')}"))
176
+ except ImportError:
177
+ print(f"Task: {task.get('name')}")
178
+ print(f" ID: {task.get('id')}")
179
+ print(f" Status: {task.get('status')}")
180
+ print(f" Progress: {task.get('progress', 0) * 100:.0f}%")
181
+
182
+ async def cancel_task(self, task_id: str) -> bool:
183
+ """
184
+ Cancel a running task.
185
+
186
+ Args:
187
+ task_id: Task ID to cancel
188
+
189
+ Returns:
190
+ True if cancelled
191
+ """
192
+ runner = await self._get_runner()
193
+ result = await runner.cancel_task(task_id)
194
+
195
+ if result:
196
+ self._print_success(f"Task cancelled: {task_id}")
197
+ else:
198
+ self._print_error(f"Could not cancel task: {task_id}")
199
+
200
+ return result
201
+
202
+ async def clear_completed(self) -> int:
203
+ """
204
+ Clear all completed tasks.
205
+
206
+ Returns:
207
+ Number of tasks cleared
208
+ """
209
+ runner = await self._get_runner()
210
+ count = len([t for t in runner.tasks if t.is_completed])
211
+ runner.clear_completed()
212
+
213
+ self._print_success(f"Cleared {count} completed tasks")
214
+ return count
215
+
216
+ def _print_success(self, message: str):
217
+ """Print success message."""
218
+ try:
219
+ from rich.console import Console
220
+ Console().print(f"[green]✓[/green] {message}")
221
+ except ImportError:
222
+ print(f"✓ {message}")
223
+
224
+ def _print_error(self, message: str):
225
+ """Print error message."""
226
+ try:
227
+ from rich.console import Console
228
+ Console().print(f"[red]✗[/red] {message}")
229
+ except ImportError:
230
+ print(f"✗ {message}")
231
+
232
+
233
+ def handle_background_command(args: List[str], verbose: bool = False):
234
+ """
235
+ Handle background CLI commands.
236
+
237
+ Usage:
238
+ praisonai background list [--status <status>]
239
+ praisonai background status <task_id>
240
+ praisonai background cancel <task_id>
241
+ praisonai background clear
242
+ """
243
+ handler = BackgroundHandler(verbose=verbose)
244
+
245
+ if not args:
246
+ print("Usage: praisonai background <command> [options]")
247
+ print("\nCommands:")
248
+ print(" list [--status <status>] List background tasks")
249
+ print(" status <task_id> Get task status")
250
+ print(" cancel <task_id> Cancel a task")
251
+ print(" clear Clear completed tasks")
252
+ return
253
+
254
+ command = args[0]
255
+
256
+ if command == "list":
257
+ status = None
258
+ if "--status" in args:
259
+ idx = args.index("--status")
260
+ if idx + 1 < len(args):
261
+ status = args[idx + 1]
262
+
263
+ asyncio.run(handler.list_tasks(status=status))
264
+
265
+ elif command == "status":
266
+ if len(args) < 2:
267
+ print("Usage: praisonai background status <task_id>")
268
+ return
269
+
270
+ asyncio.run(handler.get_status(args[1]))
271
+
272
+ elif command == "cancel":
273
+ if len(args) < 2:
274
+ print("Usage: praisonai background cancel <task_id>")
275
+ return
276
+
277
+ asyncio.run(handler.cancel_task(args[1]))
278
+
279
+ elif command == "clear":
280
+ asyncio.run(handler.clear_completed())
281
+
282
+ elif command == "help" or command == "--help":
283
+ print("Background CLI Commands:")
284
+ print("\n praisonai background list [--status <status>]")
285
+ print(" List all background tasks")
286
+ print(" Status: pending, running, completed, failed, cancelled")
287
+ print("\n praisonai background status <task_id>")
288
+ print(" Get detailed status of a specific task")
289
+ print("\n praisonai background cancel <task_id>")
290
+ print(" Cancel a running task")
291
+ print("\n praisonai background clear")
292
+ print(" Clear all completed tasks from the list")
293
+
294
+ else:
295
+ print(f"Unknown command: {command}")
296
+ print("Use 'praisonai background help' for available commands")