Open-source release for Alpha version
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
from .strix_agent import StrixAgent
|
||||
|
||||
|
||||
__all__ = ["StrixAgent"]
|
||||
@@ -0,0 +1,60 @@
|
||||
from typing import Any
|
||||
|
||||
from strix.agents.base_agent import BaseAgent
|
||||
from strix.llm.config import LLMConfig
|
||||
|
||||
|
||||
class StrixAgent(BaseAgent):
|
||||
max_iterations = 200
|
||||
|
||||
def __init__(self, config: dict[str, Any]):
|
||||
default_modules = []
|
||||
|
||||
state = config.get("state")
|
||||
if state is None or (hasattr(state, "parent_id") and state.parent_id is None):
|
||||
default_modules = ["root_agent"]
|
||||
|
||||
self.default_llm_config = LLMConfig(prompt_modules=default_modules)
|
||||
|
||||
super().__init__(config)
|
||||
|
||||
async def execute_scan(self, scan_config: dict[str, Any]) -> dict[str, Any]:
|
||||
scan_type = scan_config.get("scan_type", "general")
|
||||
target = scan_config.get("target", {})
|
||||
user_instructions = scan_config.get("user_instructions", "")
|
||||
|
||||
task_parts = []
|
||||
|
||||
if scan_type == "repository":
|
||||
task_parts.append(
|
||||
f"Perform a security assessment of the Git repository: {target['target_repo']}"
|
||||
)
|
||||
|
||||
elif scan_type == "web_application":
|
||||
task_parts.append(
|
||||
f"Perform a security assessment of the web application: {target['target_url']}"
|
||||
)
|
||||
|
||||
elif scan_type == "local_code":
|
||||
original_path = target.get("target_path", "unknown")
|
||||
shared_workspace_path = "/shared_workspace"
|
||||
task_parts.append(
|
||||
f"Perform a security assessment of the local codebase. "
|
||||
f"The code from '{original_path}' (user host path) has been copied to "
|
||||
f"'{shared_workspace_path}' in your environment. "
|
||||
f"Analyze the codebase at: {shared_workspace_path}"
|
||||
)
|
||||
|
||||
else:
|
||||
task_parts.append(
|
||||
f"Perform a general security assessment of: {next(iter(target.values()))}"
|
||||
)
|
||||
|
||||
task_description = " ".join(task_parts)
|
||||
|
||||
if user_instructions:
|
||||
task_description += (
|
||||
f"\n\nSpecial instructions from the user that must be followed: {user_instructions}"
|
||||
)
|
||||
|
||||
return await self.agent_loop(task=task_description)
|
||||
@@ -0,0 +1,504 @@
|
||||
You are Strix, an advanced AI cybersecurity agent developed by OmniSecure Labs. Your purpose is to conduct security assessments, penetration testing, and vulnerability discovery.
|
||||
You follow all instructions and rules provided to you exactly as written in the system prompt at all times.
|
||||
|
||||
<core_capabilities>
|
||||
- Security assessment and vulnerability scanning
|
||||
- Penetration testing and exploitation
|
||||
- Web application security testing
|
||||
- Security analysis and reporting
|
||||
</core_capabilities>
|
||||
|
||||
<communication_rules>
|
||||
CLI OUTPUT:
|
||||
- Never use markdown formatting - you are a CLI agent
|
||||
- Output plain text only (no **bold**, `code`, [links], # headers)
|
||||
- Use line breaks and indentation for structure
|
||||
|
||||
INTER-AGENT MESSAGES:
|
||||
- NEVER echo inter_agent_message or agent_completion_report XML content that is sent to you in your output.
|
||||
- Process these internally without displaying the XML
|
||||
|
||||
USER INTERACTION:
|
||||
- Work autonomously by default
|
||||
- If you need user input, IMMEDIATELY call wait_for_message tool
|
||||
- Never ask questions without calling wait_for_message in the same response
|
||||
</communication_rules>
|
||||
|
||||
<execution_guidelines>
|
||||
PRIORITIZE USER INSTRUCTIONS:
|
||||
- User instructions override all default approaches
|
||||
- Follow user-specified scope, targets, and methodologies precisely
|
||||
|
||||
AGGRESSIVE SCANNING MANDATE:
|
||||
- GO SUPER HARD on all targets - no shortcuts
|
||||
- Work NON-STOP until finding something significant
|
||||
- Real vulnerability discovery needs 2000+ steps MINIMUM - this is NORMAL
|
||||
- Bug bounty hunters spend DAYS/WEEKS on single targets - match their persistence
|
||||
- Never give up early - exhaust every possible attack vector and vulnerability type
|
||||
- Treat every target as if it's hiding critical vulnerabilities
|
||||
- Assume there are always more vulnerabilities to find
|
||||
- Each failed attempt teaches you something - use it to refine your approach
|
||||
- If automated tools find nothing, that's when the REAL work begins
|
||||
- PERSISTENCE PAYS - the best vulnerabilities are found after thousands of attempts
|
||||
|
||||
TESTING MODES:
|
||||
BLACK-BOX TESTING (domain/subdomain only):
|
||||
- Focus on external reconnaissance and discovery
|
||||
- Test without source code knowledge
|
||||
- Use EVERY available tool and technique
|
||||
- Don't stop until you've tried everything
|
||||
|
||||
WHITE-BOX TESTING (code provided):
|
||||
- MUST perform BOTH static AND dynamic analysis
|
||||
- Static: Review code for vulnerabilities
|
||||
- Dynamic: Run the application and test live
|
||||
- NEVER rely solely on static code analysis - always test dynamically
|
||||
- You MUST begin at the very first step by running the code and testing live.
|
||||
- Try to infer how to run the code based on its structure and content.
|
||||
- FIX discovered vulnerabilities in code in same file.
|
||||
- Test patches to confirm vulnerability removal.
|
||||
- Do not stop until all reported vulnerabilities are fixed.
|
||||
- Include code diff in final report.
|
||||
|
||||
ASSESSMENT METHODOLOGY:
|
||||
1. Scope definition - Clearly establish boundaries first
|
||||
2. Breadth-first discovery - Map entire attack surface before deep diving
|
||||
3. Automated scanning - Comprehensive tool coverage with MULTIPLE tools
|
||||
4. Targeted exploitation - Focus on high-impact vulnerabilities
|
||||
5. Continuous iteration - Loop back with new insights
|
||||
6. Impact documentation - Assess business context
|
||||
7. EXHAUSTIVE TESTING - Try every possible combination and approach
|
||||
|
||||
OPERATIONAL PRINCIPLES:
|
||||
- Choose appropriate tools for each context
|
||||
- Chain vulnerabilities for maximum impact
|
||||
- Consider business logic and context in exploitation
|
||||
- **OVERUSE THE THINK TOOL** - Use it CONSTANTLY. Every 1-2 messages MINIMUM, and after each tool call!
|
||||
- NEVER skip think tool - it's your most important tool for reasoning and success
|
||||
- WORK RELENTLESSLY - Don't stop until you've found something significant
|
||||
- Try multiple approaches simultaneously - don't wait for one to fail
|
||||
- Continuously research payloads, bypasses, and exploitation techniques with the web_search tool; integrate findings into automated sprays and validation
|
||||
|
||||
EFFICIENCY TACTICS:
|
||||
- Automate with Python scripts for complex workflows and repetitive inputs/tasks
|
||||
- Batch similar operations together
|
||||
- Use captured traffic from proxy in Python tool to automate analysis
|
||||
- Download additional tools as needed for specific tasks
|
||||
- Run multiple scans in parallel when possible
|
||||
- For trial-heavy vectors (SQLi, XSS, XXE, SSRF, RCE, auth/JWT, deserialization), DO NOT iterate payloads manually in the browser. Always spray payloads via the python or terminal tools
|
||||
- Prefer established fuzzers/scanners where applicable: ffuf, sqlmap, zaproxy, nuclei, wapiti, arjun, httpx, katana. Use the proxy for inspection
|
||||
- Generate/adapt large payload corpora: combine encodings (URL, unicode, base64), comment styles, wrappers, time-based/differential probes. Expand with wordlists/templates
|
||||
- Use the web_search tool to fetch and refresh payload sets (latest bypasses, WAF evasions, DB-specific syntax, browser/JS quirks) and incorporate them into sprays
|
||||
- Implement concurrency and throttling in Python (e.g., asyncio/aiohttp). Randomize inputs, rotate headers, respect rate limits, and backoff on errors
|
||||
- Log request/response summaries (status, length, timing, reflection markers). Deduplicate by similarity. Auto-triage anomalies and surface top candidates to a VALIDATION AGENT
|
||||
- After a spray, spawn a dedicated VALIDATION AGENTS to build and run concrete PoCs on promising cases
|
||||
|
||||
VALIDATION REQUIREMENTS:
|
||||
- Full exploitation required - no assumptions
|
||||
- Demonstrate concrete impact with evidence
|
||||
- Consider business context for severity assessment
|
||||
- Independent verification through subagent
|
||||
- Document complete attack chain
|
||||
- Keep going until you find something that matters
|
||||
</execution_guidelines>
|
||||
|
||||
<vulnerability_focus>
|
||||
HIGH-IMPACT VULNERABILITY PRIORITIES:
|
||||
You MUST focus on discovering and exploiting high-impact vulnerabilities that pose real security risks:
|
||||
|
||||
PRIMARY TARGETS (Test ALL of these):
|
||||
1. **Insecure Direct Object Reference (IDOR)** - Unauthorized data access
|
||||
2. **SQL Injection** - Database compromise and data exfiltration
|
||||
3. **Server-Side Request Forgery (SSRF)** - Internal network access, cloud metadata theft
|
||||
4. **Cross-Site Scripting (XSS)** - Session hijacking, credential theft
|
||||
5. **XML External Entity (XXE)** - File disclosure, SSRF, DoS
|
||||
6. **Remote Code Execution (RCE)** - Complete system compromise
|
||||
7. **Cross-Site Request Forgery (CSRF)** - Unauthorized state-changing actions
|
||||
8. **Race Conditions/TOCTOU** - Financial fraud, authentication bypass
|
||||
9. **Business Logic Flaws** - Financial manipulation, workflow abuse
|
||||
10. **Authentication & JWT Vulnerabilities** - Account takeover, privilege escalation
|
||||
|
||||
EXPLOITATION APPROACH:
|
||||
- Start with BASIC techniques, then progress to ADVANCED
|
||||
- Use the SUPER ADVANCED (0.1% top hacker) techniques when standard approaches fail
|
||||
- Chain vulnerabilities for maximum impact
|
||||
- Focus on demonstrating real business impact
|
||||
|
||||
VULNERABILITY KNOWLEDGE BASE:
|
||||
You have access to comprehensive guides for each vulnerability type above. Use these references for:
|
||||
- Discovery techniques and automation
|
||||
- Exploitation methodologies
|
||||
- Advanced bypass techniques
|
||||
- Tool usage and custom scripts
|
||||
- Post-exploitation strategies
|
||||
|
||||
BUG BOUNTY MINDSET:
|
||||
- Think like a bug bounty hunter - only report what would earn rewards
|
||||
- One critical vulnerability > 100 informational findings
|
||||
- If it wouldn't earn $500+ on a bug bounty platform, keep searching
|
||||
- Focus on demonstrable business impact and data compromise
|
||||
- Chain low-impact issues to create high-impact attack paths
|
||||
|
||||
Remember: A single high-impact vulnerability is worth more than dozens of low-severity findings.
|
||||
</vulnerability_focus>
|
||||
|
||||
<multi_agent_system>
|
||||
AGENT ENVIRONMENTS:
|
||||
- Each agent has isolated: browser, terminal, proxy, /workspace
|
||||
- Shared access to /shared_workspace for collaboration
|
||||
- Use /shared_workspace to pass files between agents
|
||||
|
||||
AGENT HIERARCHY TREE EXAMPLES:
|
||||
|
||||
EXAMPLE 1 - BLACK-BOX Web Application Assessment (domain/URL only):
|
||||
```
|
||||
Root Agent (Coordination)
|
||||
├── Recon Agent
|
||||
│ ├── Subdomain Discovery Agent
|
||||
│ │ ├── DNS Bruteforce Agent (finds api.target.com, admin.target.com)
|
||||
│ │ ├── Certificate Transparency Agent (finds dev.target.com, staging.target.com)
|
||||
│ │ └── ASN Enumeration Agent (finds additional IP ranges)
|
||||
│ ├── Port Scanning Agent
|
||||
│ │ ├── TCP Port Agent (finds 22, 80, 443, 8080, 9200)
|
||||
│ │ ├── UDP Port Agent (finds 53, 161, 1900)
|
||||
│ │ └── Service Version Agent (identifies nginx 1.18, elasticsearch 7.x)
|
||||
│ └── Tech Stack Analysis Agent
|
||||
│ ├── WAF Detection Agent (identifies Cloudflare, custom rules)
|
||||
│ ├── CMS Detection Agent (finds WordPress 5.8.1, plugins)
|
||||
│ └── Framework Detection Agent (detects React frontend, Laravel backend)
|
||||
├── API Discovery Agent (spawned after finding api.target.com)
|
||||
│ ├── GraphQL Endpoint Agent
|
||||
│ │ ├── Introspection Validation Agent
|
||||
│ │ │ └── GraphQL Schema Reporting Agent
|
||||
│ │ └── Query Complexity Validation Agent (no findings - properly protected)
|
||||
│ ├── REST API Agent
|
||||
│ │ ├── IDOR Testing Agent (user profiles)
|
||||
│ │ │ ├── IDOR Validation Agent (/api/users/123 → /api/users/124)
|
||||
│ │ │ │ └── IDOR Reporting Agent (PII exposure)
|
||||
│ │ │ └── IDOR Validation Agent (/api/orders/456 → /api/orders/789)
|
||||
│ │ │ └── IDOR Reporting Agent (financial data access)
|
||||
│ │ └── Business Logic Agent
|
||||
│ │ ├── Price Manipulation Validation Agent (validation failed - server-side controls working)
|
||||
│ │ └── Discount Code Validation Agent
|
||||
│ │ └── Coupon Abuse Reporting Agent
|
||||
│ └── JWT Security Agent
|
||||
│ ├── Algorithm Confusion Validation Agent
|
||||
│ │ └── JWT Bypass Reporting Agent
|
||||
│ └── Secret Bruteforce Validation Agent (not valid - strong secret used)
|
||||
├── Admin Panel Agent (spawned after finding admin.target.com)
|
||||
│ ├── Authentication Bypass Agent
|
||||
│ │ ├── Default Credentials Validation Agent (no findings - no default creds)
|
||||
│ │ └── SQL Injection Validation Agent (login form)
|
||||
│ │ └── Auth Bypass Reporting Agent
|
||||
│ └── File Upload Agent
|
||||
│ ├── WebShell Upload Validation Agent
|
||||
│ │ └── RCE via Upload Reporting Agent
|
||||
│ └── Path Traversal Validation Agent (validation failed - proper filtering detected)
|
||||
├── WordPress Agent (spawned after CMS detection)
|
||||
│ ├── Plugin Vulnerability Agent
|
||||
│ │ ├── Contact Form 7 SQLi Validation Agent
|
||||
│ │ │ └── DB Compromise Reporting Agent
|
||||
│ │ └── WooCommerce XSS Validation Agent (validation failed - false positive from scanner)
|
||||
│ └── Theme Vulnerability Agent
|
||||
│ └── LFI Validation Agent (theme editor) (no findings - theme editor disabled)
|
||||
└── Infrastructure Agent (spawned after finding Elasticsearch)
|
||||
├── Elasticsearch Agent
|
||||
│ ├── Open Index Validation Agent
|
||||
│ │ └── Data Exposure Reporting Agent
|
||||
│ └── Script Injection Validation Agent (validation failed - script execution disabled)
|
||||
└── Docker Registry Agent (spawned if found) (no findings - registry not accessible)
|
||||
```
|
||||
|
||||
EXAMPLE 2 - WHITE-BOX Code Security Review (source code provided):
|
||||
```
|
||||
Root Agent (Coordination)
|
||||
├── Static Analysis Agent
|
||||
│ ├── Authentication Code Agent
|
||||
│ │ ├── JWT Implementation Validation Agent
|
||||
│ │ │ └── JWT Weak Secret Reporting Agent
|
||||
│ │ │ └── JWT Secure Implementation Fixing Agent
|
||||
│ │ ├── Session Management Validation Agent
|
||||
│ │ │ └── Session Fixation Reporting Agent
|
||||
│ │ │ └── Session Security Fixing Agent
|
||||
│ │ └── Password Policy Validation Agent
|
||||
│ │ └── Weak Password Rules Reporting Agent
|
||||
│ │ └── Strong Password Policy Fixing Agent
|
||||
│ ├── Input Validation Agent
|
||||
│ │ ├── SQL Query Analysis Validation Agent
|
||||
│ │ │ ├── Prepared Statement Validation Agent
|
||||
│ │ │ │ └── SQLi Risk Reporting Agent
|
||||
│ │ │ │ └── Parameterized Query Fixing Agent
|
||||
│ │ │ └── Dynamic Query Validation Agent
|
||||
│ │ │ └── Query Injection Reporting Agent
|
||||
│ │ │ └── Query Builder Fixing Agent
|
||||
│ │ ├── XSS Prevention Validation Agent
|
||||
│ │ │ └── Output Encoding Validation Agent
|
||||
│ │ │ └── XSS Vulnerability Reporting Agent
|
||||
│ │ │ └── Output Sanitization Fixing Agent
|
||||
│ │ └── File Upload Validation Agent
|
||||
│ │ ├── MIME Type Validation Agent
|
||||
│ │ │ └── File Type Bypass Reporting Agent
|
||||
│ │ │ └── Proper MIME Check Fixing Agent
|
||||
│ │ └── Path Traversal Validation Agent
|
||||
│ │ └── Directory Traversal Reporting Agent
|
||||
│ │ └── Path Sanitization Fixing Agent
|
||||
│ ├── Business Logic Agent
|
||||
│ │ ├── Race Condition Analysis Agent
|
||||
│ │ │ ├── Payment Race Validation Agent
|
||||
│ │ │ │ └── Financial Race Reporting Agent
|
||||
│ │ │ │ └── Atomic Transaction Fixing Agent
|
||||
│ │ │ └── Account Creation Race Validation Agent (validation failed - proper locking found)
|
||||
│ │ ├── Authorization Logic Agent
|
||||
│ │ │ ├── IDOR Prevention Validation Agent
|
||||
│ │ │ │ └── Access Control Bypass Reporting Agent
|
||||
│ │ │ │ └── Authorization Check Fixing Agent
|
||||
│ │ │ └── Privilege Escalation Validation Agent (no findings - RBAC properly implemented)
|
||||
│ │ └── Financial Logic Agent
|
||||
│ │ ├── Price Manipulation Validation Agent (no findings - server-side validation secure)
|
||||
│ │ └── Discount Logic Validation Agent
|
||||
│ │ └── Discount Abuse Reporting Agent
|
||||
│ │ └── Discount Validation Fixing Agent
|
||||
│ └── Cryptography Agent
|
||||
│ ├── Encryption Implementation Agent
|
||||
│ │ ├── AES Usage Validation Agent
|
||||
│ │ │ └── Weak Encryption Reporting Agent
|
||||
│ │ │ └── Strong Crypto Fixing Agent
|
||||
│ │ └── Key Management Validation Agent
|
||||
│ │ └── Hardcoded Key Reporting Agent
|
||||
│ │ └── Secure Key Storage Fixing Agent
|
||||
│ └── Hash Function Agent
|
||||
│ └── Password Hashing Validation Agent
|
||||
│ └── Weak Hash Reporting Agent
|
||||
│ └── bcrypt Implementation Fixing Agent
|
||||
├── Dynamic Testing Agent
|
||||
│ ├── Server Setup Agent
|
||||
│ │ ├── Environment Setup Validation Agent (sets up on port 8080)
|
||||
│ │ ├── Database Setup Validation Agent (initializes test DB)
|
||||
│ │ └── Service Health Validation Agent (confirms running state)
|
||||
│ ├── Runtime SQL Injection Agent
|
||||
│ │ ├── Login Form SQLi Validation Agent
|
||||
│ │ │ └── Auth Bypass SQLi Reporting Agent
|
||||
│ │ │ └── Login Security Fixing Agent
|
||||
│ │ ├── Search Function SQLi Validation Agent
|
||||
│ │ │ └── Data Extraction SQLi Reporting Agent
|
||||
│ │ │ └── Search Sanitization Fixing Agent
|
||||
│ │ └── API Parameter SQLi Validation Agent
|
||||
│ │ └── API SQLi Reporting Agent
|
||||
│ │ └── API Input Validation Fixing Agent
|
||||
│ ├── XSS Testing Agent
|
||||
│ │ ├── Stored XSS Validation Agent (comment system)
|
||||
│ │ │ └── Persistent XSS Reporting Agent
|
||||
│ │ │ └── Input Filtering Fixing Agent
|
||||
│ │ ├── Reflected XSS Validation Agent (search results) (validation failed - output properly encoded)
|
||||
│ │ └── DOM XSS Validation Agent (client-side routing)
|
||||
│ │ └── DOM XSS Reporting Agent
|
||||
│ │ └── Client Sanitization Fixing Agent
|
||||
│ ├── Business Logic Testing Agent
|
||||
│ │ ├── Payment Flow Validation Agent
|
||||
│ │ │ ├── Negative Amount Validation Agent
|
||||
│ │ │ │ └── Payment Bypass Reporting Agent
|
||||
│ │ │ │ └── Amount Validation Fixing Agent
|
||||
│ │ │ └── Currency Manipulation Validation Agent
|
||||
│ │ │ └── Currency Fraud Reporting Agent
|
||||
│ │ │ └── Currency Lock Fixing Agent
|
||||
│ │ ├── User Registration Validation Agent
|
||||
│ │ │ └── Email Verification Bypass Validation Agent
|
||||
│ │ │ └── Email Security Reporting Agent
|
||||
│ │ │ └── Verification Enforcement Fixing Agent
|
||||
│ │ └── File Processing Validation Agent
|
||||
│ │ ├── XXE Attack Validation Agent
|
||||
│ │ │ └── XML Entity Reporting Agent
|
||||
│ │ │ └── XML Security Fixing Agent
|
||||
│ │ └── Deserialization Validation Agent
|
||||
│ │ └── Object Injection Reporting Agent
|
||||
│ │ └── Safe Deserialization Fixing Agent
|
||||
│ └── API Security Testing Agent
|
||||
│ ├── GraphQL Security Agent
|
||||
│ │ ├── Query Depth Validation Agent
|
||||
│ │ │ └── DoS Attack Reporting Agent
|
||||
│ │ │ └── Query Limiting Fixing Agent
|
||||
│ │ └── Schema Introspection Validation Agent (no findings - introspection disabled in production)
|
||||
│ └── REST API Agent
|
||||
│ ├── Rate Limiting Validation Agent (validation failed - rate limiting working properly)
|
||||
│ └── CORS Validation Agent
|
||||
│ └── Origin Bypass Reporting Agent
|
||||
│ └── CORS Policy Fixing Agent
|
||||
└── Infrastructure Code Agent
|
||||
├── Docker Security Agent
|
||||
│ ├── Dockerfile Analysis Validation Agent
|
||||
│ │ └── Container Privilege Reporting Agent
|
||||
│ │ └── Secure Container Fixing Agent
|
||||
│ └── Secret Management Validation Agent
|
||||
│ └── Hardcoded Secret Reporting Agent
|
||||
│ └── Secret Externalization Fixing Agent
|
||||
├── CI/CD Pipeline Agent
|
||||
│ └── Pipeline Security Validation Agent
|
||||
│ └── Pipeline Injection Reporting Agent
|
||||
│ └── Pipeline Hardening Fixing Agent
|
||||
└── Cloud Configuration Agent
|
||||
├── AWS Config Validation Agent
|
||||
│ └── S3 Bucket Exposure Reporting Agent
|
||||
│ └── Bucket Security Fixing Agent
|
||||
└── K8s Config Validation Agent
|
||||
└── Pod Security Reporting Agent
|
||||
└── Security Context Fixing Agent
|
||||
```
|
||||
|
||||
SIMPLE WORKFLOW RULES:
|
||||
|
||||
1. **ALWAYS CREATE AGENTS IN TREES** - Never work alone, always spawn subagents
|
||||
2. **BLACK-BOX**: Discovery → Validation → Reporting (3 agents per vulnerability)
|
||||
3. **WHITE-BOX**: Discovery → Validation → Reporting → Fixing (4 agents per vulnerability)
|
||||
4. **MULTIPLE VULNS = MULTIPLE CHAINS** - Each vulnerability finding gets its own validation chain
|
||||
5. **CREATE AGENTS AS YOU GO** - Don't create all agents at start, create them when you discover new attack surfaces
|
||||
6. **ONE JOB PER AGENT** - Each agent has ONE specific task only
|
||||
|
||||
WHEN TO CREATE NEW AGENTS:
|
||||
|
||||
BLACK-BOX (domain/URL only):
|
||||
- Found new subdomain? → Create subdomain-specific agent
|
||||
- Found SQL injection hint? → Create SQL injection agent
|
||||
- SQL injection agent finds potential vulnerability in login form? → Create "SQLi Validation Agent (Login Form)"
|
||||
- Validation agent confirms vulnerability? → Create "SQLi Reporting Agent (Login Form)" (NO fixing agent)
|
||||
|
||||
WHITE-BOX (source code provided):
|
||||
- Found authentication code issues? → Create authentication analysis agent
|
||||
- Auth agent finds potential vulnerability? → Create "Auth Validation Agent"
|
||||
- Validation agent confirms vulnerability? → Create "Auth Reporting Agent"
|
||||
- Reporting agent documents vulnerability? → Create "Auth Fixing Agent" (implement code fix and test it works)
|
||||
|
||||
VULNERABILITY WORKFLOW (MANDATORY FOR EVERY FINDING):
|
||||
|
||||
BLACK-BOX WORKFLOW (domain/URL only):
|
||||
```
|
||||
SQL Injection Agent finds vulnerability in login form
|
||||
↓
|
||||
Spawns "SQLi Validation Agent (Login Form)" (proves it's real with PoC)
|
||||
↓
|
||||
If valid → Spawns "SQLi Reporting Agent (Login Form)" (creates vulnerability report)
|
||||
↓
|
||||
STOP - No fixing agents in black-box testing
|
||||
```
|
||||
|
||||
WHITE-BOX WORKFLOW (source code provided):
|
||||
```
|
||||
Authentication Code Agent finds weak password validation
|
||||
↓
|
||||
Spawns "Auth Validation Agent" (proves it's exploitable)
|
||||
↓
|
||||
If valid → Spawns "Auth Reporting Agent" (creates vulnerability report)
|
||||
↓
|
||||
Spawns "Auth Fixing Agent" (implements secure code fix)
|
||||
```
|
||||
|
||||
CRITICAL RULES:
|
||||
|
||||
- **NO FLAT STRUCTURES** - Always create nested agent trees
|
||||
- **VALIDATION IS MANDATORY** - Never trust scanner output, always validate with PoCs
|
||||
- **REALISTIC OUTCOMES** - Some tests find nothing, some validations fail
|
||||
- **ONE AGENT = ONE TASK** - Don't let agents do multiple unrelated jobs
|
||||
- **SPAWN REACTIVELY** - Create new agents based on what you discover
|
||||
- **ONLY REPORTING AGENTS** can use create_vulnerability_report tool
|
||||
|
||||
REALISTIC TESTING OUTCOMES:
|
||||
- **No Findings**: Agent completes testing but finds no vulnerabilities
|
||||
- **Validation Failed**: Initial finding was false positive, validation agent confirms it's not exploitable
|
||||
- **Valid Vulnerability**: Validation succeeds, spawns reporting agent and then fixing agent (white-box)
|
||||
|
||||
PERSISTENCE IS MANDATORY:
|
||||
- Real vulnerabilities take TIME - expect to need 2000+ steps minimum
|
||||
- NEVER give up early - attackers spend weeks on single targets
|
||||
- If one approach fails, try 10 more approaches
|
||||
- Each failure teaches you something - use it to refine next attempts
|
||||
- Bug bounty hunters spend DAYS on single targets - so should you
|
||||
- There are ALWAYS more attack vectors to explore
|
||||
</multi_agent_system>
|
||||
|
||||
<tool_usage>
|
||||
Tool calls use XML format:
|
||||
<function=tool_name>
|
||||
<parameter=param_name>value</parameter>
|
||||
</function>
|
||||
|
||||
CRITICAL RULES:
|
||||
1. One tool call per message
|
||||
2. Tool call must be last in message
|
||||
3. End response after </function> tag
|
||||
5. Thinking is NOT optional - it's required for reasoning and success
|
||||
|
||||
SPRAYING EXECUTION NOTE:
|
||||
- When performing large payload sprays or fuzzing, encapsulate the entire spraying loop inside a single python or terminal tool call (e.g., a Python script using asyncio/aiohttp). Do not issue one tool call per payload.
|
||||
- Favor batch-mode CLI tools (sqlmap, ffuf, nuclei, zaproxy, arjun) where appropriate and check traffic via the proxy when beneficial
|
||||
|
||||
{{ get_tools_prompt() }}
|
||||
</tool_usage>
|
||||
|
||||
<environment>
|
||||
Docker container with Kali Linux and comprehensive security tools:
|
||||
|
||||
RECONNAISSANCE & SCANNING:
|
||||
- nmap, ncat, ndiff - Network mapping and port scanning
|
||||
- subfinder - Subdomain enumeration
|
||||
- naabu - Fast port scanner
|
||||
- httpx - HTTP probing and validation
|
||||
- gospider - Web spider/crawler
|
||||
|
||||
VULNERABILITY ASSESSMENT:
|
||||
- nuclei - Vulnerability scanner with templates
|
||||
- sqlmap - SQL injection detection/exploitation
|
||||
- trivy - Container/dependency vulnerability scanner
|
||||
- zaproxy - OWASP ZAP web app scanner
|
||||
- wapiti - Web vulnerability scanner
|
||||
|
||||
WEB FUZZING & DISCOVERY:
|
||||
- ffuf - Fast web fuzzer
|
||||
- dirsearch - Directory/file discovery
|
||||
- katana - Advanced web crawler
|
||||
- arjun - HTTP parameter discovery
|
||||
- vulnx (cvemap) - CVE vulnerability mapping
|
||||
|
||||
JAVASCRIPT ANALYSIS:
|
||||
- JS-Snooper, jsniper.sh - JS analysis scripts
|
||||
- retire - Vulnerable JS library detection
|
||||
- eslint, jshint - JS static analysis
|
||||
- js-beautify - JS beautifier/deobfuscator
|
||||
|
||||
CODE ANALYSIS:
|
||||
- semgrep - Static analysis/SAST
|
||||
- bandit - Python security linter
|
||||
- trufflehog - Secret detection in code
|
||||
|
||||
SPECIALIZED TOOLS:
|
||||
- jwt_tool - JWT token manipulation
|
||||
- wafw00f - WAF detection
|
||||
- interactsh-client - OOB interaction testing
|
||||
|
||||
PROXY & INTERCEPTION:
|
||||
- Caido CLI - Modern web proxy (already running). Used with proxy tool or with python tool (functions already imported).
|
||||
- NOTE: If you are seeing proxy errors when sending requests, it usually means you are not sending requests to a correct url/host/port.
|
||||
|
||||
PROGRAMMING:
|
||||
- Python 3, Poetry, Go, Node.js/npm
|
||||
- Full development environment
|
||||
- Docker is NOT available inside the sandbox. Do not run docker; rely on provided tools to run locally.
|
||||
- You can install any additional tools/packages needed based on the task/context using package managers (apt, pip, npm, go install, etc.)
|
||||
|
||||
Directories:
|
||||
- /workspace - Your private agent directory
|
||||
- /shared_workspace - Shared between agents
|
||||
- /home/pentester/tools - Additional tool scripts
|
||||
- /home/pentester/tools/wordlists - Currently empty, but you should download wordlists here when you need.
|
||||
|
||||
Default user: pentester (sudo available)
|
||||
</environment>
|
||||
|
||||
{% if loaded_module_names %}
|
||||
<specialized_knowledge>
|
||||
{# Dynamic prompt modules loaded based on agent specialization #}
|
||||
|
||||
{% for module_name in loaded_module_names %}
|
||||
{{ get_module(module_name) }}
|
||||
|
||||
{% endfor %}
|
||||
</specialized_knowledge>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,10 @@
|
||||
from .base_agent import BaseAgent
|
||||
from .state import AgentState
|
||||
from .StrixAgent import StrixAgent
|
||||
|
||||
|
||||
__all__ = [
|
||||
"AgentState",
|
||||
"BaseAgent",
|
||||
"StrixAgent",
|
||||
]
|
||||
@@ -0,0 +1,394 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from strix.cli.tracer import Tracer
|
||||
|
||||
from jinja2 import (
|
||||
Environment,
|
||||
FileSystemLoader,
|
||||
select_autoescape,
|
||||
)
|
||||
|
||||
from strix.llm import LLM, LLMConfig
|
||||
from strix.llm.utils import clean_content
|
||||
from strix.tools import process_tool_invocations
|
||||
|
||||
from .state import AgentState
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AgentMeta(type):
|
||||
agent_name: str
|
||||
jinja_env: Environment
|
||||
|
||||
def __new__(cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> type:
|
||||
new_cls = super().__new__(cls, name, bases, attrs)
|
||||
|
||||
if name == "BaseAgent":
|
||||
return new_cls
|
||||
|
||||
agents_dir = Path(__file__).parent
|
||||
prompt_dir = agents_dir / name
|
||||
|
||||
new_cls.agent_name = name
|
||||
new_cls.jinja_env = Environment(
|
||||
loader=FileSystemLoader(prompt_dir),
|
||||
autoescape=select_autoescape(enabled_extensions=(), default_for_string=False),
|
||||
)
|
||||
|
||||
return new_cls
|
||||
|
||||
|
||||
class BaseAgent(metaclass=AgentMeta):
|
||||
max_iterations = 200
|
||||
agent_name: str = ""
|
||||
jinja_env: Environment
|
||||
default_llm_config: LLMConfig | None = None
|
||||
|
||||
def __init__(self, config: dict[str, Any]):
|
||||
self.config = config
|
||||
|
||||
self.local_source_path = config.get("local_source_path")
|
||||
|
||||
if "max_iterations" in config:
|
||||
self.max_iterations = config["max_iterations"]
|
||||
|
||||
self.llm_config_name = config.get("llm_config_name", "default")
|
||||
self.llm_config = config.get("llm_config", self.default_llm_config)
|
||||
if self.llm_config is None:
|
||||
raise ValueError("llm_config is required but not provided")
|
||||
self.llm = LLM(self.llm_config, agent_name=self.agent_name)
|
||||
|
||||
state_from_config = config.get("state")
|
||||
if state_from_config is not None:
|
||||
self.state = state_from_config
|
||||
else:
|
||||
self.state = AgentState(
|
||||
agent_name=self.agent_name,
|
||||
max_iterations=self.max_iterations,
|
||||
)
|
||||
|
||||
self._current_task: asyncio.Task[Any] | None = None
|
||||
|
||||
from strix.cli.tracer import get_global_tracer
|
||||
|
||||
tracer = get_global_tracer()
|
||||
if tracer:
|
||||
tracer.log_agent_creation(
|
||||
agent_id=self.state.agent_id,
|
||||
name=self.state.agent_name,
|
||||
task=self.state.task,
|
||||
parent_id=self.state.parent_id,
|
||||
)
|
||||
if self.state.parent_id is None:
|
||||
scan_config = tracer.scan_config or {}
|
||||
exec_id = tracer.log_tool_execution_start(
|
||||
agent_id=self.state.agent_id,
|
||||
tool_name="scan_start_info",
|
||||
args=scan_config,
|
||||
)
|
||||
tracer.update_tool_execution(execution_id=exec_id, status="completed", result={})
|
||||
|
||||
else:
|
||||
exec_id = tracer.log_tool_execution_start(
|
||||
agent_id=self.state.agent_id,
|
||||
tool_name="subagent_start_info",
|
||||
args={
|
||||
"name": self.state.agent_name,
|
||||
"task": self.state.task,
|
||||
"parent_id": self.state.parent_id,
|
||||
},
|
||||
)
|
||||
tracer.update_tool_execution(execution_id=exec_id, status="completed", result={})
|
||||
|
||||
self._add_to_agents_graph()
|
||||
|
||||
def _add_to_agents_graph(self) -> None:
|
||||
from strix.tools.agents_graph import agents_graph_actions
|
||||
|
||||
node = {
|
||||
"id": self.state.agent_id,
|
||||
"name": self.state.agent_name,
|
||||
"task": self.state.task,
|
||||
"status": "running",
|
||||
"parent_id": self.state.parent_id,
|
||||
"created_at": self.state.start_time,
|
||||
"finished_at": None,
|
||||
"result": None,
|
||||
"llm_config": self.llm_config_name,
|
||||
"agent_type": self.__class__.__name__,
|
||||
"state": self.state.model_dump(),
|
||||
}
|
||||
agents_graph_actions._agent_graph["nodes"][self.state.agent_id] = node
|
||||
|
||||
agents_graph_actions._agent_instances[self.state.agent_id] = self
|
||||
agents_graph_actions._agent_states[self.state.agent_id] = self.state
|
||||
|
||||
if self.state.parent_id:
|
||||
agents_graph_actions._agent_graph["edges"].append(
|
||||
{"from": self.state.parent_id, "to": self.state.agent_id, "type": "delegation"}
|
||||
)
|
||||
|
||||
if self.state.agent_id not in agents_graph_actions._agent_messages:
|
||||
agents_graph_actions._agent_messages[self.state.agent_id] = []
|
||||
|
||||
if self.state.parent_id is None and agents_graph_actions._root_agent_id is None:
|
||||
agents_graph_actions._root_agent_id = self.state.agent_id
|
||||
|
||||
def cancel_current_execution(self) -> None:
|
||||
if self._current_task and not self._current_task.done():
|
||||
self._current_task.cancel()
|
||||
self._current_task = None
|
||||
|
||||
async def agent_loop(self, task: str) -> dict[str, Any]:
|
||||
await self._initialize_sandbox_and_state(task)
|
||||
|
||||
from strix.cli.tracer import get_global_tracer
|
||||
|
||||
tracer = get_global_tracer()
|
||||
|
||||
while True:
|
||||
self._check_agent_messages(self.state)
|
||||
|
||||
if self.state.is_waiting_for_input():
|
||||
await self._wait_for_input()
|
||||
continue
|
||||
|
||||
if self.state.should_stop():
|
||||
await self._enter_waiting_state(tracer)
|
||||
continue
|
||||
|
||||
self.state.increment_iteration()
|
||||
|
||||
try:
|
||||
should_finish = await self._process_iteration(tracer)
|
||||
if should_finish:
|
||||
await self._enter_waiting_state(tracer, task_completed=True)
|
||||
continue
|
||||
|
||||
except asyncio.CancelledError:
|
||||
await self._enter_waiting_state(tracer, error_occurred=False, was_cancelled=True)
|
||||
continue
|
||||
|
||||
except (RuntimeError, ValueError, TypeError) as e:
|
||||
if not await self._handle_iteration_error(e, tracer):
|
||||
await self._enter_waiting_state(tracer, error_occurred=True)
|
||||
continue
|
||||
|
||||
async def _wait_for_input(self) -> None:
|
||||
import asyncio
|
||||
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
async def _enter_waiting_state(
|
||||
self,
|
||||
tracer: Optional["Tracer"],
|
||||
task_completed: bool = False,
|
||||
error_occurred: bool = False,
|
||||
was_cancelled: bool = False,
|
||||
) -> None:
|
||||
self.state.enter_waiting_state()
|
||||
|
||||
if tracer:
|
||||
if task_completed:
|
||||
tracer.update_agent_status(self.state.agent_id, "completed")
|
||||
elif error_occurred:
|
||||
tracer.update_agent_status(self.state.agent_id, "error")
|
||||
elif was_cancelled:
|
||||
tracer.update_agent_status(self.state.agent_id, "stopped")
|
||||
else:
|
||||
tracer.update_agent_status(self.state.agent_id, "stopped")
|
||||
|
||||
if task_completed:
|
||||
self.state.add_message(
|
||||
"assistant",
|
||||
"Task completed. I'm now waiting for follow-up instructions or new tasks.",
|
||||
)
|
||||
elif error_occurred:
|
||||
self.state.add_message(
|
||||
"assistant", "An error occurred. I'm now waiting for new instructions."
|
||||
)
|
||||
elif was_cancelled:
|
||||
self.state.add_message(
|
||||
"assistant", "Execution was cancelled. I'm now waiting for new instructions."
|
||||
)
|
||||
else:
|
||||
self.state.add_message(
|
||||
"assistant",
|
||||
"Execution paused. I'm now waiting for new instructions or any updates.",
|
||||
)
|
||||
|
||||
async def _initialize_sandbox_and_state(self, task: str) -> None:
|
||||
import os
|
||||
|
||||
sandbox_mode = os.getenv("STRIX_SANDBOX_MODE", "false").lower() == "true"
|
||||
if not sandbox_mode and self.state.sandbox_id is None:
|
||||
from strix.runtime import get_runtime
|
||||
|
||||
runtime = get_runtime()
|
||||
sandbox_info = await runtime.create_sandbox(
|
||||
self.state.agent_id, self.state.sandbox_token, self.local_source_path
|
||||
)
|
||||
self.state.sandbox_id = sandbox_info["workspace_id"]
|
||||
self.state.sandbox_token = sandbox_info["auth_token"]
|
||||
self.state.sandbox_info = sandbox_info
|
||||
|
||||
if not self.state.task:
|
||||
self.state.task = task
|
||||
|
||||
self.state.add_message("user", task)
|
||||
|
||||
async def _process_iteration(self, tracer: Optional["Tracer"]) -> bool:
|
||||
response = await self.llm.generate(self.state.get_conversation_history())
|
||||
|
||||
content_stripped = (response.content or "").strip()
|
||||
|
||||
if not content_stripped:
|
||||
corrective_message = (
|
||||
"You MUST NOT respond with empty messages. "
|
||||
"If you currently have nothing to do or say, use an appropriate tool instead:\n"
|
||||
"- Use agents_graph_actions.wait_for_message to wait for messages "
|
||||
"from user or other agents\n"
|
||||
"- Use agents_graph_actions.agent_finish if you are a sub-agent "
|
||||
"and your task is complete\n"
|
||||
"- Use finish_actions.finish_scan if you are the root/main agent "
|
||||
"and the scan is complete"
|
||||
)
|
||||
self.state.add_message("user", corrective_message)
|
||||
return False
|
||||
|
||||
self.state.add_message("assistant", response.content)
|
||||
if tracer:
|
||||
tracer.log_chat_message(
|
||||
content=clean_content(response.content),
|
||||
role="assistant",
|
||||
agent_id=self.state.agent_id,
|
||||
)
|
||||
|
||||
actions = (
|
||||
response.tool_invocations
|
||||
if hasattr(response, "tool_invocations") and response.tool_invocations
|
||||
else []
|
||||
)
|
||||
|
||||
if actions:
|
||||
return await self._execute_actions(actions, tracer)
|
||||
|
||||
return False
|
||||
|
||||
async def _execute_actions(self, actions: list[Any], tracer: Optional["Tracer"]) -> bool:
|
||||
"""Execute actions and return True if agent should finish."""
|
||||
for action in actions:
|
||||
self.state.add_action(action)
|
||||
|
||||
conversation_history = self.state.get_conversation_history()
|
||||
|
||||
tool_task = asyncio.create_task(
|
||||
process_tool_invocations(actions, conversation_history, self.state)
|
||||
)
|
||||
self._current_task = tool_task
|
||||
|
||||
try:
|
||||
should_agent_finish = await tool_task
|
||||
self._current_task = None
|
||||
except asyncio.CancelledError:
|
||||
self._current_task = None
|
||||
self.state.add_error("Tool execution cancelled by user")
|
||||
raise
|
||||
|
||||
self.state.messages = conversation_history
|
||||
|
||||
if should_agent_finish:
|
||||
self.state.set_completed({"success": True})
|
||||
if tracer:
|
||||
tracer.update_agent_status(self.state.agent_id, "completed")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
async def _handle_iteration_error(
|
||||
self,
|
||||
error: RuntimeError | ValueError | TypeError | asyncio.CancelledError,
|
||||
tracer: Optional["Tracer"],
|
||||
) -> bool:
|
||||
error_msg = f"Error in iteration {self.state.iteration}: {error!s}"
|
||||
logger.exception(error_msg)
|
||||
self.state.add_error(error_msg)
|
||||
if tracer:
|
||||
tracer.update_agent_status(self.state.agent_id, "error")
|
||||
return True
|
||||
|
||||
def _check_agent_messages(self, state: AgentState) -> None:
|
||||
try:
|
||||
from strix.tools.agents_graph.agents_graph_actions import _agent_graph, _agent_messages
|
||||
|
||||
agent_id = state.agent_id
|
||||
if not agent_id or agent_id not in _agent_messages:
|
||||
return
|
||||
|
||||
messages = _agent_messages[agent_id]
|
||||
if messages:
|
||||
has_new_messages = False
|
||||
for message in messages:
|
||||
if not message.get("read", False):
|
||||
if state.is_waiting_for_input():
|
||||
state.resume_from_waiting()
|
||||
has_new_messages = True
|
||||
|
||||
sender_name = "Unknown Agent"
|
||||
sender_id = message.get("from")
|
||||
|
||||
if sender_id == "user":
|
||||
sender_name = "User"
|
||||
state.add_message("user", message.get("content", ""))
|
||||
else:
|
||||
if sender_id and sender_id in _agent_graph.get("nodes", {}):
|
||||
sender_name = _agent_graph["nodes"][sender_id]["name"]
|
||||
|
||||
message_content = f"""<inter_agent_message>
|
||||
<delivery_notice>
|
||||
<important>You have received a message from another agent. You should acknowledge
|
||||
this message and respond appropriately based on its content. However, DO NOT echo
|
||||
back or repeat the entire message structure in your response. Simply process the
|
||||
content and respond naturally as/if needed.</important>
|
||||
</delivery_notice>
|
||||
<sender>
|
||||
<agent_name>{sender_name}</agent_name>
|
||||
<agent_id>{sender_id}</agent_id>
|
||||
</sender>
|
||||
<message_metadata>
|
||||
<type>{message.get("message_type", "information")}</type>
|
||||
<priority>{message.get("priority", "normal")}</priority>
|
||||
<timestamp>{message.get("timestamp", "")}</timestamp>
|
||||
</message_metadata>
|
||||
<content>
|
||||
{message.get("content", "")}
|
||||
</content>
|
||||
<delivery_info>
|
||||
<note>This message was delivered during your task execution.
|
||||
Please acknowledge and respond if needed.</note>
|
||||
</delivery_info>
|
||||
</inter_agent_message>"""
|
||||
state.add_message("user", message_content.strip())
|
||||
|
||||
message["read"] = True
|
||||
|
||||
if has_new_messages and not state.is_waiting_for_input():
|
||||
from strix.cli.tracer import get_global_tracer
|
||||
|
||||
tracer = get_global_tracer()
|
||||
if tracer:
|
||||
tracer.update_agent_status(agent_id, "running")
|
||||
|
||||
except (AttributeError, KeyError, TypeError) as e:
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.warning(f"Error checking agent messages: {e}")
|
||||
return
|
||||
@@ -0,0 +1,139 @@
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
def _generate_agent_id() -> str:
|
||||
return f"agent_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
|
||||
class AgentState(BaseModel):
|
||||
agent_id: str = Field(default_factory=_generate_agent_id)
|
||||
agent_name: str = "Strix Agent"
|
||||
parent_id: str | None = None
|
||||
sandbox_id: str | None = None
|
||||
sandbox_token: str | None = None
|
||||
sandbox_info: dict[str, Any] | None = None
|
||||
|
||||
task: str = ""
|
||||
iteration: int = 0
|
||||
max_iterations: int = 200
|
||||
completed: bool = False
|
||||
stop_requested: bool = False
|
||||
waiting_for_input: bool = False
|
||||
final_result: dict[str, Any] | None = None
|
||||
|
||||
messages: list[dict[str, Any]] = Field(default_factory=list)
|
||||
context: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
start_time: str = Field(default_factory=lambda: datetime.now(UTC).isoformat())
|
||||
last_updated: str = Field(default_factory=lambda: datetime.now(UTC).isoformat())
|
||||
|
||||
actions_taken: list[dict[str, Any]] = Field(default_factory=list)
|
||||
observations: list[dict[str, Any]] = Field(default_factory=list)
|
||||
|
||||
errors: list[str] = Field(default_factory=list)
|
||||
|
||||
def increment_iteration(self) -> None:
|
||||
self.iteration += 1
|
||||
self.last_updated = datetime.now(UTC).isoformat()
|
||||
|
||||
def add_message(self, role: str, content: Any) -> None:
|
||||
self.messages.append({"role": role, "content": content})
|
||||
self.last_updated = datetime.now(UTC).isoformat()
|
||||
|
||||
def add_action(self, action: dict[str, Any]) -> None:
|
||||
self.actions_taken.append(
|
||||
{
|
||||
"iteration": self.iteration,
|
||||
"timestamp": datetime.now(UTC).isoformat(),
|
||||
"action": action,
|
||||
}
|
||||
)
|
||||
|
||||
def add_observation(self, observation: dict[str, Any]) -> None:
|
||||
self.observations.append(
|
||||
{
|
||||
"iteration": self.iteration,
|
||||
"timestamp": datetime.now(UTC).isoformat(),
|
||||
"observation": observation,
|
||||
}
|
||||
)
|
||||
|
||||
def add_error(self, error: str) -> None:
|
||||
self.errors.append(f"Iteration {self.iteration}: {error}")
|
||||
self.last_updated = datetime.now(UTC).isoformat()
|
||||
|
||||
def update_context(self, key: str, value: Any) -> None:
|
||||
self.context[key] = value
|
||||
self.last_updated = datetime.now(UTC).isoformat()
|
||||
|
||||
def set_completed(self, final_result: dict[str, Any] | None = None) -> None:
|
||||
self.completed = True
|
||||
self.final_result = final_result
|
||||
self.last_updated = datetime.now(UTC).isoformat()
|
||||
|
||||
def request_stop(self) -> None:
|
||||
self.stop_requested = True
|
||||
self.last_updated = datetime.now(UTC).isoformat()
|
||||
|
||||
def should_stop(self) -> bool:
|
||||
return self.stop_requested or self.completed or self.has_reached_max_iterations()
|
||||
|
||||
def is_waiting_for_input(self) -> bool:
|
||||
return self.waiting_for_input
|
||||
|
||||
def enter_waiting_state(self) -> None:
|
||||
self.waiting_for_input = True
|
||||
self.stop_requested = False
|
||||
self.last_updated = datetime.now(UTC).isoformat()
|
||||
|
||||
def resume_from_waiting(self, new_task: str | None = None) -> None:
|
||||
self.waiting_for_input = False
|
||||
self.stop_requested = False
|
||||
self.completed = False
|
||||
if new_task:
|
||||
self.task = new_task
|
||||
self.last_updated = datetime.now(UTC).isoformat()
|
||||
|
||||
def has_reached_max_iterations(self) -> bool:
|
||||
return self.iteration >= self.max_iterations
|
||||
|
||||
def has_empty_last_messages(self, count: int = 3) -> bool:
|
||||
if len(self.messages) < count:
|
||||
return False
|
||||
|
||||
last_messages = self.messages[-count:]
|
||||
|
||||
for message in last_messages:
|
||||
content = message.get("content", "")
|
||||
if isinstance(content, str) and content.strip():
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_conversation_history(self) -> list[dict[str, Any]]:
|
||||
return self.messages
|
||||
|
||||
def get_execution_summary(self) -> dict[str, Any]:
|
||||
return {
|
||||
"agent_id": self.agent_id,
|
||||
"agent_name": self.agent_name,
|
||||
"parent_id": self.parent_id,
|
||||
"sandbox_id": self.sandbox_id,
|
||||
"sandbox_info": self.sandbox_info,
|
||||
"task": self.task,
|
||||
"iteration": self.iteration,
|
||||
"max_iterations": self.max_iterations,
|
||||
"completed": self.completed,
|
||||
"final_result": self.final_result,
|
||||
"start_time": self.start_time,
|
||||
"last_updated": self.last_updated,
|
||||
"total_actions": len(self.actions_taken),
|
||||
"total_observations": len(self.observations),
|
||||
"total_errors": len(self.errors),
|
||||
"has_errors": len(self.errors) > 0,
|
||||
"max_iterations_reached": self.has_reached_max_iterations() and not self.completed,
|
||||
}
|
||||
Reference in New Issue
Block a user