exaai-agent 2.1.2__tar.gz → 2.2.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 (161) hide show
  1. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/PKG-INFO +29 -3
  2. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/README.md +28 -2
  3. exaai_agent-2.2.0/exaaiagnt/dashboard/server.py +99 -0
  4. exaai_agent-2.2.0/exaaiagnt/dashboard/templates/index.html +232 -0
  5. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/cli.py +11 -0
  6. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/llm.py +43 -8
  7. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/telemetry/tracer.py +17 -1
  8. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/executor.py +15 -3
  9. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/k8s_scanner/k8s_actions.py +6 -0
  10. exaai_agent-2.2.0/exaaiagnt/tools/k8s_scanner/k8s_actions_schema.xml +36 -0
  11. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/prompt_injection/prompt_injection_actions.py +74 -0
  12. exaai_agent-2.2.0/exaaiagnt/tools/prompt_injection/prompt_injection_actions_schema.xml +28 -0
  13. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/python/python_instance.py +5 -0
  14. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/reporting/reporting_actions.py +34 -5
  15. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/web_search/web_search_actions.py +4 -2
  16. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/pyproject.toml +1 -1
  17. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/LICENSE +0 -0
  18. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/__init__.py +0 -0
  19. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/ExaaiAgent/__init__.py +0 -0
  20. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/ExaaiAgent/exaai_agent.py +0 -0
  21. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/ExaaiAgent/system_prompt.jinja +0 -0
  22. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/__init__.py +0 -0
  23. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/agent_supervisor.py +0 -0
  24. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/base_agent.py +0 -0
  25. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/scan_modes.py +0 -0
  26. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/shared_memory.py +0 -0
  27. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/agents/state.py +0 -0
  28. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/__init__.py +0 -0
  29. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/assets/tui_styles.tcss +0 -0
  30. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/main.py +0 -0
  31. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/__init__.py +0 -0
  32. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/agents_graph_renderer.py +0 -0
  33. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/base_renderer.py +0 -0
  34. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/browser_renderer.py +0 -0
  35. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/file_edit_renderer.py +0 -0
  36. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/finish_renderer.py +0 -0
  37. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/notes_renderer.py +0 -0
  38. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/proxy_renderer.py +0 -0
  39. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/python_renderer.py +0 -0
  40. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/registry.py +0 -0
  41. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/reporting_renderer.py +0 -0
  42. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/scan_info_renderer.py +0 -0
  43. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/terminal_renderer.py +0 -0
  44. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/thinking_renderer.py +0 -0
  45. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/user_message_renderer.py +0 -0
  46. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tool_components/web_search_renderer.py +0 -0
  47. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/tui.py +0 -0
  48. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/interface/utils.py +0 -0
  49. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/__init__.py +0 -0
  50. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/config.py +0 -0
  51. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/fallback.py +0 -0
  52. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/llm_traffic_controller.py +0 -0
  53. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/memory_compressor.py +0 -0
  54. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/output_processor.py +0 -0
  55. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/request_queue.py +0 -0
  56. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/llm/utils.py +0 -0
  57. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/README.md +0 -0
  58. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/__init__.py +0 -0
  59. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/auto_loader.py +0 -0
  60. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/cloud/.gitkeep +0 -0
  61. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/cloud/aws_cloud_security.jinja +0 -0
  62. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/cloud/azure_cloud_security.jinja +0 -0
  63. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/cloud/gcp_cloud_security.jinja +0 -0
  64. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/cloud/kubernetes_security.jinja +0 -0
  65. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/coordination/root_agent.jinja +0 -0
  66. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/custom/.gitkeep +0 -0
  67. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/frameworks/fastapi.jinja +0 -0
  68. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/frameworks/modern_js_frameworks.jinja +0 -0
  69. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/frameworks/nextjs.jinja +0 -0
  70. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/protocols/graphql.jinja +0 -0
  71. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/reconnaissance/.gitkeep +0 -0
  72. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/technologies/firebase_firestore.jinja +0 -0
  73. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/technologies/supabase.jinja +0 -0
  74. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/advanced_recon.jinja +0 -0
  75. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/api_security.jinja +0 -0
  76. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/authentication_jwt.jinja +0 -0
  77. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/broken_function_level_authorization.jinja +0 -0
  78. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/business_logic.jinja +0 -0
  79. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/cache_poisoning.jinja +0 -0
  80. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/cloud_security.jinja +0 -0
  81. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/csrf.jinja +0 -0
  82. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/deserialization.jinja +0 -0
  83. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/graphql_security.jinja +0 -0
  84. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/high_impact_bugs.jinja +0 -0
  85. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/http_smuggling.jinja +0 -0
  86. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/idor.jinja +0 -0
  87. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/information_disclosure.jinja +0 -0
  88. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/insecure_file_uploads.jinja +0 -0
  89. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/mass_assignment.jinja +0 -0
  90. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/oauth_oidc.jinja +0 -0
  91. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/open_redirect.jinja +0 -0
  92. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/path_traversal_lfi_rfi.jinja +0 -0
  93. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/post_exploitation.jinja +0 -0
  94. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/privilege_escalation.jinja +0 -0
  95. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/prompt_injection.jinja +0 -0
  96. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/prototype_pollution.jinja +0 -0
  97. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/race_conditions.jinja +0 -0
  98. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/rce.jinja +0 -0
  99. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/react2shell.jinja +0 -0
  100. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/reconnaissance_osint.jinja +0 -0
  101. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/sql_injection.jinja +0 -0
  102. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/ssrf.jinja +0 -0
  103. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/ssti.jinja +0 -0
  104. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/subdomain_takeover.jinja +0 -0
  105. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/waf_bypass.jinja +0 -0
  106. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/websocket_security.jinja +0 -0
  107. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/xss.jinja +0 -0
  108. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/prompts/vulnerabilities/xxe.jinja +0 -0
  109. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/runtime/__init__.py +0 -0
  110. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/runtime/docker_runtime.py +0 -0
  111. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/runtime/runtime.py +0 -0
  112. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/runtime/tool_manager.py +0 -0
  113. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/runtime/tool_server.py +0 -0
  114. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/telemetry/__init__.py +0 -0
  115. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/__init__.py +0 -0
  116. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/agents_graph/__init__.py +0 -0
  117. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/agents_graph/agents_graph_actions.py +0 -0
  118. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/agents_graph/agents_graph_actions_schema.xml +0 -0
  119. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/argument_parser.py +0 -0
  120. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/browser/__init__.py +0 -0
  121. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/browser/browser_actions.py +0 -0
  122. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/browser/browser_actions_schema.xml +0 -0
  123. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/browser/browser_instance.py +0 -0
  124. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/browser/tab_manager.py +0 -0
  125. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/file_edit/__init__.py +0 -0
  126. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/file_edit/file_edit_actions.py +0 -0
  127. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/file_edit/file_edit_actions_schema.xml +0 -0
  128. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/finish/__init__.py +0 -0
  129. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/finish/finish_actions.py +0 -0
  130. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/finish/finish_actions_schema.xml +0 -0
  131. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/k8s_scanner/__init__.py +0 -0
  132. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/notes/__init__.py +0 -0
  133. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/notes/notes_actions.py +0 -0
  134. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/notes/notes_actions_schema.xml +0 -0
  135. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/prompt_injection/__init__.py +0 -0
  136. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/proxy/__init__.py +0 -0
  137. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/proxy/proxy_actions.py +0 -0
  138. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/proxy/proxy_actions_schema.xml +0 -0
  139. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/proxy/proxy_manager.py +0 -0
  140. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/python/__init__.py +0 -0
  141. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/python/python_actions.py +0 -0
  142. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/python/python_actions_schema.xml +0 -0
  143. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/python/python_manager.py +0 -0
  144. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/registry.py +0 -0
  145. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/reporting/__init__.py +0 -0
  146. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/reporting/reporting_actions_schema.xml +0 -0
  147. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/response_analyzer.py +0 -0
  148. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/smart_fuzzer.py +0 -0
  149. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/terminal/__init__.py +0 -0
  150. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/terminal/terminal_actions.py +0 -0
  151. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/terminal/terminal_actions_schema.xml +0 -0
  152. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/terminal/terminal_manager.py +0 -0
  153. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/terminal/terminal_session.py +0 -0
  154. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/thinking/__init__.py +0 -0
  155. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/thinking/thinking_actions.py +0 -0
  156. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/thinking/thinking_actions_schema.xml +0 -0
  157. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/tool_prompts.py +0 -0
  158. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/vuln_validator.py +0 -0
  159. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/waf_bypass.py +0 -0
  160. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/web_search/__init__.py +0 -0
  161. {exaai_agent-2.1.2 → exaai_agent-2.2.0}/exaaiagnt/tools/web_search/web_search_actions_schema.xml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: exaai-agent
3
- Version: 2.1.2
3
+ Version: 2.2.0
4
4
  Summary: ExaAi - Advanced AI Security Agent for Comprehensive Penetration Testing
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -151,8 +151,7 @@ ExaAiAgent is an elite AI-powered cybersecurity agent that acts like a real pene
151
151
  # Install ExaAiAgent
152
152
 
153
153
  # Method 1: Automated Script (Recommended)
154
- curl -sSL https://raw.githubusercontent.com/hleliofficiel/ExaAiAgent/main/install.sh | bash
155
-
154
+ pip install exaai-agent
156
155
  # Method 2: pipx
157
156
  pipx install exaai-agent
158
157
 
@@ -366,6 +365,33 @@ export PERPLEXITY_API_KEY="key" # For search capabilities
366
365
 
367
366
  ---
368
367
 
368
+ ## 🛠️ Troubleshooting
369
+
370
+ ### 🔧 Troubleshooting
371
+
372
+ #### Problem: "LLM Connection Failed" or Model Not Found
373
+ Modern models (like `gemini-3-pro-preview`) require the latest version of `litellm` to be recognized correctly.
374
+
375
+ **Solution: Update LiteLLM**
376
+ ```bash
377
+ pip install -U litellm
378
+ ```
379
+
380
+ **Linux/Debian Users (Externally Managed Environment):**
381
+ If you encounter permission errors or "externally-managed-environment", you may need to use a virtual environment (`venv`) or force a user install:
382
+
383
+ ```bash
384
+ # Option 1: Virtual Environment (Recommended for Servers)
385
+ python3 -m venv venv
386
+ source venv/bin/activate
387
+ pip install exaai-agent
388
+
389
+ # Option 2: Force User Install
390
+ pip install -U litellm --user --break-system-packages
391
+ ```
392
+
393
+ ---
394
+
369
395
  ## 🤝 Contributing
370
396
 
371
397
  We welcome contributions! Check out our [Contributing Guide](CONTRIBUTING.md).
@@ -109,8 +109,7 @@ ExaAiAgent is an elite AI-powered cybersecurity agent that acts like a real pene
109
109
  # Install ExaAiAgent
110
110
 
111
111
  # Method 1: Automated Script (Recommended)
112
- curl -sSL https://raw.githubusercontent.com/hleliofficiel/ExaAiAgent/main/install.sh | bash
113
-
112
+ pip install exaai-agent
114
113
  # Method 2: pipx
115
114
  pipx install exaai-agent
116
115
 
@@ -324,6 +323,33 @@ export PERPLEXITY_API_KEY="key" # For search capabilities
324
323
 
325
324
  ---
326
325
 
326
+ ## 🛠️ Troubleshooting
327
+
328
+ ### 🔧 Troubleshooting
329
+
330
+ #### Problem: "LLM Connection Failed" or Model Not Found
331
+ Modern models (like `gemini-3-pro-preview`) require the latest version of `litellm` to be recognized correctly.
332
+
333
+ **Solution: Update LiteLLM**
334
+ ```bash
335
+ pip install -U litellm
336
+ ```
337
+
338
+ **Linux/Debian Users (Externally Managed Environment):**
339
+ If you encounter permission errors or "externally-managed-environment", you may need to use a virtual environment (`venv`) or force a user install:
340
+
341
+ ```bash
342
+ # Option 1: Virtual Environment (Recommended for Servers)
343
+ python3 -m venv venv
344
+ source venv/bin/activate
345
+ pip install exaai-agent
346
+
347
+ # Option 2: Force User Install
348
+ pip install -U litellm --user --break-system-packages
349
+ ```
350
+
351
+ ---
352
+
327
353
  ## 🤝 Contributing
328
354
 
329
355
  We welcome contributions! Check out our [Contributing Guide](CONTRIBUTING.md).
@@ -0,0 +1,99 @@
1
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
2
+ from fastapi.staticfiles import StaticFiles
3
+ from fastapi.responses import HTMLResponse
4
+ import uvicorn
5
+ import json
6
+ import asyncio
7
+ import logging
8
+ from typing import List, Dict, Any
9
+ import os
10
+
11
+ from exaaiagnt.telemetry.tracer import get_global_tracer
12
+
13
+ app = FastAPI(title="ExaAi Live Dashboard")
14
+
15
+ # Serve static files
16
+ static_dir = os.path.join(os.path.dirname(__file__), "static")
17
+ app.mount("/static", StaticFiles(directory=static_dir), name="static")
18
+
19
+ class ConnectionManager:
20
+ def __init__(self):
21
+ self.active_connections: List[WebSocket] = []
22
+
23
+ async def connect(self, websocket: WebSocket):
24
+ await websocket.accept()
25
+ self.active_connections.append(websocket)
26
+
27
+ def disconnect(self, websocket: WebSocket):
28
+ self.active_connections.remove(websocket)
29
+
30
+ async def broadcast(self, message: str):
31
+ for connection in self.active_connections:
32
+ try:
33
+ await connection.send_text(message)
34
+ except Exception:
35
+ pass
36
+
37
+ manager = ConnectionManager()
38
+
39
+ @app.get("/")
40
+ async def get_dashboard():
41
+ html_path = os.path.join(os.path.dirname(__file__), "templates", "index.html")
42
+ with open(html_path, "r") as f:
43
+ return HTMLResponse(content=f.read())
44
+
45
+ @app.get("/api/stats")
46
+ async def get_stats():
47
+ tracer = get_global_tracer()
48
+ if not tracer:
49
+ return {"status": "Waiting for agent..."}
50
+
51
+ return {
52
+ "agents_count": len(tracer.agents),
53
+ "vulnerabilities": len(tracer.vulnerability_reports),
54
+ "tool_calls": len(tracer.tool_executions),
55
+ "start_time": tracer.start_time,
56
+ "run_name": tracer.run_name
57
+ }
58
+
59
+ @app.get("/api/vulnerabilities")
60
+ async def get_vulns():
61
+ tracer = get_global_tracer()
62
+ if not tracer:
63
+ return []
64
+ return tracer.vulnerability_reports
65
+
66
+ @app.websocket("/ws")
67
+ async def websocket_endpoint(websocket: WebSocket):
68
+ await manager.connect(websocket)
69
+ try:
70
+ while True:
71
+ # Send updates every second
72
+ tracer = get_global_tracer()
73
+ if tracer:
74
+ data = {
75
+ "agents": tracer.agents,
76
+ "stats": {
77
+ "active": sum(1 for a in tracer.agents.values() if a.get("status") == "running"),
78
+ "completed": sum(1 for a in tracer.agents.values() if a.get("status") == "completed"),
79
+ "failed": sum(1 for a in tracer.agents.values() if a.get("status") == "failed"),
80
+ },
81
+ "recent_logs": tracer.chat_messages[-10:] if tracer.chat_messages else []
82
+ }
83
+ await websocket.send_json(data)
84
+ await asyncio.sleep(1)
85
+ except WebSocketDisconnect:
86
+ manager.disconnect(websocket)
87
+ except Exception as e:
88
+ logging.error(f"WebSocket error: {e}")
89
+ manager.disconnect(websocket)
90
+
91
+ def start_dashboard(host="0.0.0.0", port=8000):
92
+ """Start the dashboard server in a background thread or process."""
93
+ config = uvicorn.Config(app, host=host, port=port, log_level="error")
94
+ server = uvicorn.Server(config)
95
+ # We'll run this in a thread from the main agent
96
+ import threading
97
+ t = threading.Thread(target=server.run, daemon=True)
98
+ t.start()
99
+ return t
@@ -0,0 +1,232 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ExaAi Mission Control</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <style>
10
+ body { background-color: #0f172a; color: #e2e8f0; font-family: 'Courier New', Courier, monospace; }
11
+ .neon-text { text-shadow: 0 0 5px #3b82f6, 0 0 10px #3b82f6; }
12
+ .neon-border { box-shadow: 0 0 5px #3b82f6; border-color: #3b82f6; }
13
+ .agent-card { transition: all 0.3s ease; }
14
+ .agent-card:hover { transform: translateY(-2px); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); }
15
+ .vuln-critical { border-left: 4px solid #ef4444; background: rgba(239, 68, 68, 0.1); }
16
+ .vuln-high { border-left: 4px solid #f97316; background: rgba(249, 115, 22, 0.1); }
17
+ .vuln-medium { border-left: 4px solid #eab308; background: rgba(234, 179, 8, 0.1); }
18
+ ::-webkit-scrollbar { width: 8px; }
19
+ ::-webkit-scrollbar-track { background: #1e293b; }
20
+ ::-webkit-scrollbar-thumb { background: #475569; border-radius: 4px; }
21
+ ::-webkit-scrollbar-thumb:hover { background: #64748b; }
22
+ </style>
23
+ </head>
24
+ <body class="h-screen flex flex-col overflow-hidden">
25
+ <!-- Header -->
26
+ <header class="bg-slate-900 border-b border-slate-700 p-4 flex justify-between items-center shadow-lg z-10">
27
+ <div class="flex items-center gap-3">
28
+ <div class="w-3 h-3 rounded-full bg-green-500 animate-pulse"></div>
29
+ <h1 class="text-2xl font-bold tracking-wider text-blue-400 neon-text">EXAAI <span class="text-white">MISSION CONTROL</span></h1>
30
+ </div>
31
+ <div class="flex gap-6 text-sm">
32
+ <div class="flex flex-col items-end">
33
+ <span class="text-slate-400">STATUS</span>
34
+ <span class="text-green-400 font-bold">ONLINE</span>
35
+ </div>
36
+ <div class="flex flex-col items-end">
37
+ <span class="text-slate-400">AGENTS</span>
38
+ <span id="total-agents" class="text-blue-400 font-bold">0</span>
39
+ </div>
40
+ <div class="flex flex-col items-end">
41
+ <span class="text-slate-400">VULNERABILITIES</span>
42
+ <span id="total-vulns" class="text-red-500 font-bold neon-text">0</span>
43
+ </div>
44
+ </div>
45
+ </header>
46
+
47
+ <!-- Main Content -->
48
+ <main class="flex-1 flex overflow-hidden">
49
+ <!-- Sidebar: Agent Tree -->
50
+ <aside class="w-1/4 bg-slate-900 border-r border-slate-700 flex flex-col">
51
+ <div class="p-3 border-b border-slate-700 bg-slate-800">
52
+ <h2 class="font-bold text-slate-300 flex items-center gap-2">
53
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path></svg>
54
+ Active Operations
55
+ </h2>
56
+ </div>
57
+ <div id="agent-list" class="flex-1 overflow-y-auto p-3 space-y-2">
58
+ <!-- Agents injected here -->
59
+ </div>
60
+ </aside>
61
+
62
+ <!-- Center: Live Feed & Stats -->
63
+ <section class="flex-1 flex flex-col bg-slate-900/50 relative">
64
+ <!-- Matrix Rain Background Effect (Optional/Subtle) -->
65
+ <div class="absolute inset-0 pointer-events-none opacity-5 bg-[url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0IiBoZWlnaHQ9IjQiPgo8cmVjdCB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSIjMWUyOTNiIi8+CjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiMzMzQxNTUiLz4KPC9zdmc+')]"></div>
66
+
67
+ <div class="h-1/2 border-b border-slate-700 flex">
68
+ <!-- Live Terminal Log -->
69
+ <div class="w-2/3 border-r border-slate-700 flex flex-col">
70
+ <div class="p-2 bg-slate-800 border-b border-slate-700 flex justify-between">
71
+ <span class="text-xs font-mono text-slate-400">>_ SYSTEM LOGS</span>
72
+ <span class="text-xs text-green-500">● LIVE</span>
73
+ </div>
74
+ <div id="console-logs" class="flex-1 overflow-y-auto p-4 font-mono text-xs text-slate-300 space-y-1 bg-black/40">
75
+ <!-- Logs injected here -->
76
+ </div>
77
+ </div>
78
+
79
+ <!-- Quick Stats Chart -->
80
+ <div class="w-1/3 p-4 bg-slate-800/30 flex flex-col items-center justify-center">
81
+ <canvas id="statusChart"></canvas>
82
+ </div>
83
+ </div>
84
+
85
+ <!-- Vulnerabilities Panel -->
86
+ <div class="h-1/2 flex flex-col bg-slate-900">
87
+ <div class="p-3 border-b border-slate-700 bg-slate-800 flex justify-between items-center">
88
+ <h2 class="font-bold text-red-400 flex items-center gap-2">
89
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
90
+ Detected Vulnerabilities
91
+ </h2>
92
+ <span class="text-xs text-slate-500">Auto-refreshing...</span>
93
+ </div>
94
+ <div id="vuln-list" class="flex-1 overflow-y-auto p-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
95
+ <!-- Vulns injected here -->
96
+ </div>
97
+ </div>
98
+ </section>
99
+ </main>
100
+
101
+ <script>
102
+ const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
103
+ const socket = new WebSocket(`${wsProtocol}//${window.location.host}/ws`);
104
+
105
+ // Chart setup
106
+ const ctx = document.getElementById('statusChart').getContext('2d');
107
+ const statusChart = new Chart(ctx, {
108
+ type: 'doughnut',
109
+ data: {
110
+ labels: ['Active', 'Completed', 'Failed'],
111
+ datasets: [{
112
+ data: [0, 0, 0],
113
+ backgroundColor: ['#3b82f6', '#22c55e', '#ef4444'],
114
+ borderWidth: 0
115
+ }]
116
+ },
117
+ options: {
118
+ responsive: true,
119
+ plugins: {
120
+ legend: { position: 'bottom', labels: { color: '#94a3b8' } }
121
+ },
122
+ cutout: '70%'
123
+ }
124
+ });
125
+
126
+ socket.onmessage = function(event) {
127
+ const data = JSON.parse(event.data);
128
+ updateDashboard(data);
129
+ };
130
+
131
+ function updateDashboard(data) {
132
+ // Update Headers
133
+ document.getElementById('total-agents').textContent = Object.keys(data.agents).length;
134
+
135
+ // Update Agent List
136
+ const agentList = document.getElementById('agent-list');
137
+ agentList.innerHTML = '';
138
+
139
+ Object.values(data.agents).forEach(agent => {
140
+ const div = document.createElement('div');
141
+ div.className = `agent-card p-3 rounded bg-slate-800 border border-slate-700 ${agent.status === 'running' ? 'border-l-4 border-l-blue-500' : ''}`;
142
+ div.innerHTML = `
143
+ <div class="flex justify-between items-start mb-1">
144
+ <span class="font-bold text-xs text-slate-200 truncate" title="${agent.name}">${agent.name}</span>
145
+ <span class="text-[10px] px-1.5 py-0.5 rounded ${getStatusColor(agent.status)}">${agent.status}</span>
146
+ </div>
147
+ <div class="text-[10px] text-slate-500 truncate">${agent.id.substring(0, 8)}...</div>
148
+ <div class="text-[11px] text-slate-400 mt-1 line-clamp-2">${agent.task || 'No task description'}</div>
149
+ `;
150
+ agentList.appendChild(div);
151
+ });
152
+
153
+ // Update Logs
154
+ const logContainer = document.getElementById('console-logs');
155
+ logContainer.innerHTML = '';
156
+ data.recent_logs.forEach(log => {
157
+ const div = document.createElement('div');
158
+ const time = new Date(log.timestamp * 1000).toLocaleTimeString();
159
+ const color = log.role === 'assistant' ? 'text-blue-400' : 'text-slate-400';
160
+ div.innerHTML = `<span class="text-slate-600">[${time}]</span> <span class="${color}">${log.role.toUpperCase()}:</span> <span class="text-slate-300">${escapeHtml(log.content.substring(0, 150))}...</span>`;
161
+ logContainer.appendChild(div);
162
+ });
163
+
164
+ // Update Chart
165
+ statusChart.data.datasets[0].data = [
166
+ data.stats.active,
167
+ data.stats.completed,
168
+ data.stats.failed
169
+ ];
170
+ statusChart.update();
171
+
172
+ // Update Vulns (Fetching separately or from WS if included)
173
+ fetchVulns();
174
+ }
175
+
176
+ async function fetchVulns() {
177
+ try {
178
+ const response = await fetch('/api/vulnerabilities');
179
+ const vulns = await response.json();
180
+ document.getElementById('total-vulns').textContent = vulns.length;
181
+
182
+ const container = document.getElementById('vuln-list');
183
+ container.innerHTML = '';
184
+
185
+ if (vulns.length === 0) {
186
+ container.innerHTML = '<div class="col-span-full text-center text-slate-500 py-10">No vulnerabilities detected... yet.</div>';
187
+ return;
188
+ }
189
+
190
+ vulns.forEach(v => {
191
+ const div = document.createElement('div');
192
+ const severityClass = `vuln-${v.severity.toLowerCase()}`;
193
+ div.className = `p-4 rounded bg-slate-800 border border-slate-700 ${severityClass}`;
194
+ div.innerHTML = `
195
+ <div class="flex justify-between items-center mb-2">
196
+ <h3 class="font-bold text-sm text-white">${v.title}</h3>
197
+ <span class="text-[10px] font-bold uppercase tracking-wider px-2 py-1 bg-black/30 rounded text-white">${v.severity}</span>
198
+ </div>
199
+ <p class="text-xs text-slate-400 line-clamp-3 mb-2">${v.content}</p>
200
+ <div class="text-[10px] text-slate-500">ID: ${v.report_id}</div>
201
+ `;
202
+ container.appendChild(div);
203
+ });
204
+ } catch (e) {
205
+ console.error("Failed to fetch vulns", e);
206
+ }
207
+ }
208
+
209
+ function getStatusColor(status) {
210
+ switch(status) {
211
+ case 'running': return 'bg-blue-900 text-blue-300';
212
+ case 'completed': return 'bg-green-900 text-green-300';
213
+ case 'failed': return 'bg-red-900 text-red-300';
214
+ default: return 'bg-slate-700 text-slate-300';
215
+ }
216
+ }
217
+
218
+ function escapeHtml(text) {
219
+ if (!text) return "";
220
+ return text
221
+ .replace(/&/g, "&amp;")
222
+ .replace(/</g, "&lt;")
223
+ .replace(/>/g, "&gt;")
224
+ .replace(/"/g, "&quot;")
225
+ .replace(/'/g, "&#039;");
226
+ }
227
+
228
+ // Initial load
229
+ fetchVulns();
230
+ </script>
231
+ </body>
232
+ </html>
@@ -30,7 +30,18 @@ BANNER = r"""
30
30
  """
31
31
 
32
32
 
33
+ from exaaiagnt.dashboard.server import start_dashboard
34
+
33
35
  async def run_cli(args: Any) -> None: # noqa: PLR0915
36
+ # Start the live dashboard
37
+ try:
38
+ start_dashboard()
39
+ console_temp = Console()
40
+ console_temp.print("[bold green]🚀 Live Dashboard available at: http://localhost:8000[/]")
41
+ except Exception as e:
42
+ import logging
43
+ logging.error(f"Failed to start dashboard: {e}")
44
+
34
45
  # Detect if running in a real terminal or headless (pipe/background)
35
46
  is_tty = sys.stdout.isatty()
36
47
  console = Console(force_terminal=is_tty, no_color=not is_tty)
@@ -424,19 +424,54 @@ class LLM:
424
424
  else:
425
425
  completion_args["reasoning_effort"] = "high"
426
426
 
427
- # Use Adaptive Traffic Controller for intelligent rate limiting
427
+ # Use Adaptive Traffic Controller for intelligent rate limiting with retries
428
428
  controller = get_traffic_controller()
429
429
  agent_id = self.agent_id or "unknown_agent"
430
430
 
431
431
  async def do_request():
432
- from litellm import completion
433
- return completion(**completion_args, stream=False)
434
-
435
- response = await controller.queue_request(
436
- do_request,
437
- agent_id=agent_id,
438
- priority=RequestPriority.NORMAL
432
+ try:
433
+ from litellm import completion
434
+ return await completion(**completion_args, stream=False)
435
+ except litellm.RateLimitError:
436
+ # Let tenacity (in controller) handle retry
437
+ raise
438
+ except Exception as e:
439
+ # Log other transient errors for potential retry
440
+ logger.warning(f"Transient LLM error: {e}")
441
+ raise
442
+
443
+ # Wrap in retry logic
444
+ from tenacity import (
445
+ retry,
446
+ stop_after_attempt,
447
+ wait_exponential,
448
+ retry_if_exception_type,
449
+ )
450
+ import litellm
451
+
452
+ @retry(
453
+ retry=retry_if_exception_type((
454
+ litellm.RateLimitError,
455
+ litellm.ServiceUnavailableError,
456
+ litellm.APIConnectionError,
457
+ litellm.Timeout
458
+ )),
459
+ wait=wait_exponential(multiplier=1, min=4, max=60),
460
+ stop=stop_after_attempt(5),
461
+ reraise=True
439
462
  )
463
+ async def execute_with_retries():
464
+ return await controller.queue_request(
465
+ do_request,
466
+ agent_id=agent_id,
467
+ priority=RequestPriority.NORMAL
468
+ )
469
+
470
+ try:
471
+ response = await execute_with_retries()
472
+ except Exception:
473
+ self._total_stats.failed_requests += 1
474
+ raise
440
475
 
441
476
  self._total_stats.requests += 1
442
477
  self._last_request_stats = RequestStats(requests=1)
@@ -137,19 +137,35 @@ class Tracer:
137
137
  ) -> int:
138
138
  message_id = self._next_message_id
139
139
  self._next_message_id += 1
140
+
141
+ # Ensure imports if missing
142
+ import time
140
143
 
141
144
  message_data = {
142
145
  "message_id": message_id,
143
146
  "content": content,
144
147
  "role": role,
145
148
  "agent_id": agent_id,
146
- "timestamp": datetime.now(UTC).isoformat(),
149
+ "timestamp": time.time(), # Use epoch time for easier frontend handling
147
150
  "metadata": metadata or {},
148
151
  }
149
152
 
150
153
  self.chat_messages.append(message_data)
154
+ # Removed auto-save on every message to improve performance
151
155
  return message_id
152
156
 
157
+ def get_dashboard_data(self) -> dict[str, Any]:
158
+ """Return data formatted for the live dashboard."""
159
+ return {
160
+ "agents": self.agents,
161
+ "vulnerabilities": self.vulnerability_reports,
162
+ "stats": {
163
+ "active_agents": sum(1 for a in self.agents.values() if a.get("status") == "running"),
164
+ "total_requests": sum(1 for msg in self.chat_messages if msg["role"] == "assistant"),
165
+ "total_vulns": len(self.vulnerability_reports)
166
+ }
167
+ }
168
+
153
169
  def log_tool_execution_start(self, agent_id: str, tool_name: str, args: dict[str, Any]) -> int:
154
170
  execution_id = self._next_execution_id
155
171
  self._next_execution_id += 1
@@ -90,11 +90,23 @@ async def _execute_tool_locally(tool_name: str, agent_state: Any | None, **kwarg
90
90
  if needs_agent_state(tool_name):
91
91
  if agent_state is None:
92
92
  raise ValueError(f"Tool '{tool_name}' requires agent_state but none was provided.")
93
- result = tool_func(agent_state=agent_state, **converted_kwargs)
93
+ # Check if the tool function is a coroutine (async)
94
+ if inspect.iscoroutinefunction(tool_func):
95
+ result = await tool_func(agent_state=agent_state, **converted_kwargs)
96
+ else:
97
+ # Run synchronous blocking tools in a separate thread
98
+ import asyncio
99
+ result = await asyncio.to_thread(tool_func, agent_state=agent_state, **converted_kwargs)
94
100
  else:
95
- result = tool_func(**converted_kwargs)
101
+ # Check if the tool function is a coroutine (async)
102
+ if inspect.iscoroutinefunction(tool_func):
103
+ result = await tool_func(**converted_kwargs)
104
+ else:
105
+ # Run synchronous blocking tools in a separate thread
106
+ import asyncio
107
+ result = await asyncio.to_thread(tool_func, **converted_kwargs)
96
108
 
97
- return await result if inspect.isawaitable(result) else result
109
+ return result
98
110
 
99
111
 
100
112
  def validate_tool_availability(tool_name: str | None) -> tuple[bool, str]:
@@ -55,6 +55,9 @@ class SecurityFinding:
55
55
  status: CheckStatus = CheckStatus.FAIL
56
56
 
57
57
 
58
+ from exaaiagnt.tools.registry import register_tool
59
+
60
+ @register_tool
58
61
  class K8sScanner:
59
62
  """
60
63
  Kubernetes Security Scanner.
@@ -292,6 +295,7 @@ class K8sScanner:
292
295
 
293
296
  # === Convenience Functions ===
294
297
 
298
+ @register_tool
295
299
  def scan_cluster(context: Optional[str] = None) -> Dict[str, Any]:
296
300
  """Run a full cluster scan."""
297
301
  scanner = K8sScanner(context=context)
@@ -299,6 +303,7 @@ def scan_cluster(context: Optional[str] = None) -> Dict[str, Any]:
299
303
  return scanner.export_report()
300
304
 
301
305
 
306
+ @register_tool
302
307
  def check_rbac(namespace: str = "default") -> List[Dict[str, Any]]:
303
308
  """Run RBAC checks only."""
304
309
  scanner = K8sScanner()
@@ -306,6 +311,7 @@ def check_rbac(namespace: str = "default") -> List[Dict[str, Any]]:
306
311
  return scanner.export_report()["findings"]
307
312
 
308
313
 
314
+ @register_tool
309
315
  def check_pod_security(namespace: str = "default") -> List[Dict[str, Any]]:
310
316
  """Run Pod Security checks only."""
311
317
  scanner = K8sScanner()
@@ -0,0 +1,36 @@
1
+ <tool name="scan_cluster">
2
+ <description>
3
+ Run a full security scan on the connected Kubernetes cluster.
4
+ Checks for RBAC misconfigurations, Pod Security Standards violations,
5
+ missing NetworkPolicies, and insecure secret management.
6
+ </description>
7
+ <parameters>
8
+ <parameter name="context" type="string" optional="true">
9
+ The kubeconfig context to use. Defaults to current context.
10
+ </parameter>
11
+ </parameters>
12
+ </tool>
13
+
14
+ <tool name="check_rbac">
15
+ <description>
16
+ Check for dangerous RBAC permissions in a specific namespace.
17
+ Detects wildcard permissions, cluster-admin abuse, and risky role bindings.
18
+ </description>
19
+ <parameters>
20
+ <parameter name="namespace" type="string" optional="true">
21
+ Target namespace. Defaults to "default".
22
+ </parameter>
23
+ </parameters>
24
+ </tool>
25
+
26
+ <tool name="check_pod_security">
27
+ <description>
28
+ Audit pods against Pod Security Standards (PSS).
29
+ Finds privileged containers, host namespace usage, and capability issues.
30
+ </description>
31
+ <parameters>
32
+ <parameter name="namespace" type="string" optional="true">
33
+ Target namespace. Defaults to "default".
34
+ </parameter>
35
+ </parameters>
36
+ </tool>