exaai-agent 2.1.2__tar.gz → 2.2.1__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.1}/PKG-INFO +30 -3
  2. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/README.md +28 -2
  3. exaai_agent-2.2.1/exaaiagnt/dashboard/server.py +99 -0
  4. exaai_agent-2.2.1/exaaiagnt/dashboard/templates/index.html +232 -0
  5. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/cli.py +11 -0
  6. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/main.py +12 -2
  7. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tui.py +2 -2
  8. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/llm.py +43 -8
  9. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/runtime/docker_runtime.py +9 -0
  10. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/telemetry/tracer.py +17 -1
  11. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/executor.py +15 -3
  12. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/k8s_scanner/k8s_actions.py +7 -1
  13. exaai_agent-2.2.1/exaaiagnt/tools/k8s_scanner/k8s_actions_schema.xml +36 -0
  14. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/prompt_injection/prompt_injection_actions.py +74 -0
  15. exaai_agent-2.2.1/exaaiagnt/tools/prompt_injection/prompt_injection_actions_schema.xml +28 -0
  16. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/python/python_instance.py +5 -0
  17. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/reporting/reporting_actions.py +34 -5
  18. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/web_search/web_search_actions.py +4 -2
  19. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/pyproject.toml +2 -1
  20. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/LICENSE +0 -0
  21. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/__init__.py +0 -0
  22. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/ExaaiAgent/__init__.py +0 -0
  23. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/ExaaiAgent/exaai_agent.py +0 -0
  24. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/ExaaiAgent/system_prompt.jinja +0 -0
  25. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/__init__.py +0 -0
  26. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/agent_supervisor.py +0 -0
  27. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/base_agent.py +0 -0
  28. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/scan_modes.py +0 -0
  29. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/shared_memory.py +0 -0
  30. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/agents/state.py +0 -0
  31. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/__init__.py +0 -0
  32. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/assets/tui_styles.tcss +0 -0
  33. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/__init__.py +0 -0
  34. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/agents_graph_renderer.py +0 -0
  35. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/base_renderer.py +0 -0
  36. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/browser_renderer.py +0 -0
  37. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/file_edit_renderer.py +0 -0
  38. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/finish_renderer.py +0 -0
  39. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/notes_renderer.py +0 -0
  40. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/proxy_renderer.py +0 -0
  41. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/python_renderer.py +0 -0
  42. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/registry.py +0 -0
  43. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/reporting_renderer.py +0 -0
  44. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/scan_info_renderer.py +0 -0
  45. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/terminal_renderer.py +0 -0
  46. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/thinking_renderer.py +0 -0
  47. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/user_message_renderer.py +0 -0
  48. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/tool_components/web_search_renderer.py +0 -0
  49. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/interface/utils.py +0 -0
  50. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/__init__.py +0 -0
  51. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/config.py +0 -0
  52. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/fallback.py +0 -0
  53. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/llm_traffic_controller.py +0 -0
  54. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/memory_compressor.py +0 -0
  55. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/output_processor.py +0 -0
  56. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/request_queue.py +0 -0
  57. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/llm/utils.py +0 -0
  58. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/README.md +0 -0
  59. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/__init__.py +0 -0
  60. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/auto_loader.py +0 -0
  61. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/cloud/.gitkeep +0 -0
  62. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/cloud/aws_cloud_security.jinja +0 -0
  63. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/cloud/azure_cloud_security.jinja +0 -0
  64. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/cloud/gcp_cloud_security.jinja +0 -0
  65. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/cloud/kubernetes_security.jinja +0 -0
  66. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/coordination/root_agent.jinja +0 -0
  67. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/custom/.gitkeep +0 -0
  68. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/frameworks/fastapi.jinja +0 -0
  69. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/frameworks/modern_js_frameworks.jinja +0 -0
  70. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/frameworks/nextjs.jinja +0 -0
  71. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/protocols/graphql.jinja +0 -0
  72. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/reconnaissance/.gitkeep +0 -0
  73. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/technologies/firebase_firestore.jinja +0 -0
  74. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/technologies/supabase.jinja +0 -0
  75. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/advanced_recon.jinja +0 -0
  76. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/api_security.jinja +0 -0
  77. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/authentication_jwt.jinja +0 -0
  78. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/broken_function_level_authorization.jinja +0 -0
  79. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/business_logic.jinja +0 -0
  80. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/cache_poisoning.jinja +0 -0
  81. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/cloud_security.jinja +0 -0
  82. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/csrf.jinja +0 -0
  83. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/deserialization.jinja +0 -0
  84. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/graphql_security.jinja +0 -0
  85. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/high_impact_bugs.jinja +0 -0
  86. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/http_smuggling.jinja +0 -0
  87. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/idor.jinja +0 -0
  88. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/information_disclosure.jinja +0 -0
  89. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/insecure_file_uploads.jinja +0 -0
  90. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/mass_assignment.jinja +0 -0
  91. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/oauth_oidc.jinja +0 -0
  92. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/open_redirect.jinja +0 -0
  93. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/path_traversal_lfi_rfi.jinja +0 -0
  94. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/post_exploitation.jinja +0 -0
  95. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/privilege_escalation.jinja +0 -0
  96. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/prompt_injection.jinja +0 -0
  97. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/prototype_pollution.jinja +0 -0
  98. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/race_conditions.jinja +0 -0
  99. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/rce.jinja +0 -0
  100. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/react2shell.jinja +0 -0
  101. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/reconnaissance_osint.jinja +0 -0
  102. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/sql_injection.jinja +0 -0
  103. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/ssrf.jinja +0 -0
  104. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/ssti.jinja +0 -0
  105. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/subdomain_takeover.jinja +0 -0
  106. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/waf_bypass.jinja +0 -0
  107. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/websocket_security.jinja +0 -0
  108. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/xss.jinja +0 -0
  109. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/prompts/vulnerabilities/xxe.jinja +0 -0
  110. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/runtime/__init__.py +0 -0
  111. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/runtime/runtime.py +0 -0
  112. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/runtime/tool_manager.py +0 -0
  113. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/runtime/tool_server.py +0 -0
  114. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/telemetry/__init__.py +0 -0
  115. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/__init__.py +0 -0
  116. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/agents_graph/__init__.py +0 -0
  117. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/agents_graph/agents_graph_actions.py +0 -0
  118. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/agents_graph/agents_graph_actions_schema.xml +0 -0
  119. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/argument_parser.py +0 -0
  120. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/browser/__init__.py +0 -0
  121. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/browser/browser_actions.py +0 -0
  122. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/browser/browser_actions_schema.xml +0 -0
  123. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/browser/browser_instance.py +0 -0
  124. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/browser/tab_manager.py +0 -0
  125. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/file_edit/__init__.py +0 -0
  126. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/file_edit/file_edit_actions.py +0 -0
  127. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/file_edit/file_edit_actions_schema.xml +0 -0
  128. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/finish/__init__.py +0 -0
  129. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/finish/finish_actions.py +0 -0
  130. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/finish/finish_actions_schema.xml +0 -0
  131. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/k8s_scanner/__init__.py +0 -0
  132. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/notes/__init__.py +0 -0
  133. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/notes/notes_actions.py +0 -0
  134. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/notes/notes_actions_schema.xml +0 -0
  135. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/prompt_injection/__init__.py +0 -0
  136. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/proxy/__init__.py +0 -0
  137. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/proxy/proxy_actions.py +0 -0
  138. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/proxy/proxy_actions_schema.xml +0 -0
  139. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/proxy/proxy_manager.py +0 -0
  140. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/python/__init__.py +0 -0
  141. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/python/python_actions.py +0 -0
  142. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/python/python_actions_schema.xml +0 -0
  143. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/python/python_manager.py +0 -0
  144. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/registry.py +0 -0
  145. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/reporting/__init__.py +0 -0
  146. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/reporting/reporting_actions_schema.xml +0 -0
  147. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/response_analyzer.py +0 -0
  148. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/smart_fuzzer.py +0 -0
  149. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/terminal/__init__.py +0 -0
  150. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/terminal/terminal_actions.py +0 -0
  151. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/terminal/terminal_actions_schema.xml +0 -0
  152. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/terminal/terminal_manager.py +0 -0
  153. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/terminal/terminal_session.py +0 -0
  154. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/thinking/__init__.py +0 -0
  155. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/thinking/thinking_actions.py +0 -0
  156. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/thinking/thinking_actions_schema.xml +0 -0
  157. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/tool_prompts.py +0 -0
  158. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/vuln_validator.py +0 -0
  159. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/waf_bypass.py +0 -0
  160. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/exaaiagnt/tools/web_search/__init__.py +0 -0
  161. {exaai_agent-2.1.2 → exaai_agent-2.2.1}/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.1
4
4
  Summary: ExaAi - Advanced AI Security Agent for Comprehensive Penetration Testing
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -37,6 +37,7 @@ Requires-Dist: rich
37
37
  Requires-Dist: tenacity (>=9.0.0,<10.0.0)
38
38
  Requires-Dist: textual (>=4.0.0,<5.0.0)
39
39
  Requires-Dist: uvicorn
40
+ Requires-Dist: websockets (>=12.0,<13.0)
40
41
  Requires-Dist: xmltodict (>=0.13.0,<0.14.0)
41
42
  Description-Content-Type: text/markdown
42
43
 
@@ -151,8 +152,7 @@ ExaAiAgent is an elite AI-powered cybersecurity agent that acts like a real pene
151
152
  # Install ExaAiAgent
152
153
 
153
154
  # Method 1: Automated Script (Recommended)
154
- curl -sSL https://raw.githubusercontent.com/hleliofficiel/ExaAiAgent/main/install.sh | bash
155
-
155
+ pip install exaai-agent
156
156
  # Method 2: pipx
157
157
  pipx install exaai-agent
158
158
 
@@ -366,6 +366,33 @@ export PERPLEXITY_API_KEY="key" # For search capabilities
366
366
 
367
367
  ---
368
368
 
369
+ ## 🛠️ Troubleshooting
370
+
371
+ ### 🔧 Troubleshooting
372
+
373
+ #### Problem: "LLM Connection Failed" or Model Not Found
374
+ Modern models (like `gemini-3-pro-preview`) require the latest version of `litellm` to be recognized correctly.
375
+
376
+ **Solution: Update LiteLLM**
377
+ ```bash
378
+ pip install -U litellm
379
+ ```
380
+
381
+ **Linux/Debian Users (Externally Managed Environment):**
382
+ If you encounter permission errors or "externally-managed-environment", you may need to use a virtual environment (`venv`) or force a user install:
383
+
384
+ ```bash
385
+ # Option 1: Virtual Environment (Recommended for Servers)
386
+ python3 -m venv venv
387
+ source venv/bin/activate
388
+ pip install exaai-agent
389
+
390
+ # Option 2: Force User Install
391
+ pip install -U litellm --user --break-system-packages
392
+ ```
393
+
394
+ ---
395
+
369
396
  ## 🤝 Contributing
370
397
 
371
398
  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)
@@ -224,7 +224,17 @@ async def warm_up_llm() -> None:
224
224
  error_text.append("\n\n", style="white")
225
225
  error_text.append("Could not establish connection to the language model.\n", style="white")
226
226
  error_text.append("Please check your configuration and try again.\n", style="white")
227
- error_text.append(f"\nError: {e}", style="dim white")
227
+
228
+ # Enhanced error diagnosis
229
+ error_str = str(e)
230
+ if "AuthenticationError" in error_str:
231
+ error_text.append("🔍 Hint: Your API key seems invalid or expired.\n", style="bold yellow")
232
+ elif "NotFoundError" in error_str:
233
+ error_text.append(f"🔍 Hint: The model '{model_name}' was not found. Check if the model name is correct.\n", style="bold yellow")
234
+ elif "ConnectionError" in error_str:
235
+ error_text.append("🔍 Hint: Network connection failed. Check your internet or proxy settings.\n", style="bold yellow")
236
+
237
+ error_text.append(f"\nError Details: {e}", style="dim white")
228
238
 
229
239
  panel = Panel(
230
240
  error_text,
@@ -242,7 +252,7 @@ async def warm_up_llm() -> None:
242
252
 
243
253
  def get_version() -> str:
244
254
  """Get the current ExaAi version."""
245
- return "2.1.2"
255
+ return "2.2.1"
246
256
 
247
257
 
248
258
  def parse_arguments() -> argparse.Namespace:
@@ -45,7 +45,7 @@ def get_package_version() -> str:
45
45
  return pkg_version("exaai-agent")
46
46
  except PackageNotFoundError:
47
47
  # Fallback version if package not installed
48
- return "2.1.2"
48
+ return "2.2.1"
49
49
 
50
50
 
51
51
  class ChatTextArea(TextArea): # type: ignore[misc]
@@ -80,7 +80,7 @@ class SplashScreen(Static): # type: ignore[misc]
80
80
  NEON_ORANGE = "#ff8800"
81
81
  SOFT_WHITE = "#e0e0e0"
82
82
 
83
- # Enhanced ASCII Logo - ExaAi v2.1.2
83
+ # Enhanced ASCII Logo - ExaAi v2.2.1
84
84
  BANNER = r"""
85
85
  ███████╗██╗ ██╗ █████╗ █████╗ ██╗
86
86
  ██╔════╝╚██╗██╔╝██╔══██╗ ██╔══██╗██║
@@ -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)
@@ -104,6 +104,14 @@ class DockerRuntime(AbstractRuntime):
104
104
  self._tool_server_port = tool_server_port
105
105
  self._tool_server_token = tool_server_token
106
106
 
107
+ # Mount kubeconfig if available
108
+ volumes = {}
109
+ kube_config = os.path.expanduser("~/.kube")
110
+ if os.path.exists(kube_config):
111
+ volumes[kube_config] = {'bind': '/root/.kube', 'mode': 'ro'}
112
+ # Also mount for pentester user
113
+ volumes[kube_config] = {'bind': '/home/pentester/.kube', 'mode': 'ro'}
114
+
107
115
  container = self.client.containers.run(
108
116
  EXAAI_IMAGE,
109
117
  command="sleep infinity",
@@ -114,6 +122,7 @@ class DockerRuntime(AbstractRuntime):
114
122
  f"{caido_port}/tcp": caido_port,
115
123
  f"{tool_server_port}/tcp": tool_server_port,
116
124
  },
125
+ volumes=volumes,
117
126
  cap_add=["NET_ADMIN", "NET_RAW"],
118
127
  labels={"exaai-scan-id": scan_id},
119
128
  environment={
@@ -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]: