Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
510 changes: 510 additions & 0 deletions CAPABILITIES_REPORT.md

Large diffs are not rendered by default.

376 changes: 376 additions & 0 deletions CODE_ANALYSIS_REPORT.md

Large diffs are not rendered by default.

Binary file modified __pycache__/main.cpython-312.pyc
Binary file not shown.
7 changes: 7 additions & 0 deletions agents/autonomous/agent_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,14 @@ async def _run_autonomous_loop(self, task_id: str, goal: str) -> Dict[str, Any]:
timestamp=iteration_start,
duration=iteration_duration
)

# Add bounds checking to prevent unbounded growth
self.iterations.append(loop_iteration)
if len(self.iterations) > self.max_iterations + 100:
logger.warning(f"Iterations history exceeded limit, pruning oldest entries")
# Keep last 2x max_iterations to preserve recent history
keep_count = min(self.max_iterations * 2, len(self.iterations))
self.iterations = self.iterations[-keep_count:]

# Update progress
self.state_manager.update_progress(task_id, iteration + 1, self.max_iterations)
Expand Down
96 changes: 55 additions & 41 deletions agents/autonomous/state_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@
"""

import asyncio
from io import TextIOWrapper
from io import TextIOWrapper
from io import TextIOWrapper
from io import TextIOWrapper
from io import TextIOWrapper
from io import TextIOWrapper
from io import TextIOWrapper
import logging
import json
import time
from typing import Dict, Any, List, Optional, Union
from dataclasses import dataclass, field, asdict
from enum import Enum
from pathlib import Path
from collections import deque
import uuid

logger = logging.getLogger(__name__)
logger: logging.Logger = logging.getLogger(__name__)

class TaskStatus(Enum):
PENDING = "pending"
Expand Down Expand Up @@ -63,13 +71,13 @@ class TaskState:
completion_percentage: float = 0.0

class AutonomousStateManager:
def __init__(self, workspace_base: str = "workspace"):
self.workspace_base = workspace_base
def __init__(self, workspace_base: str = "workspace") -> None:
self.workspace_base: str = workspace_base
self.active_tasks: Dict[str, TaskState] = {}
self.task_history: List[TaskState] = []

# State persistence
self.state_file = Path(workspace_base) / "autonomous_state.json"
self.state_file: Path = Path(workspace_base) / "autonomous_state.json"
self.load_state()

def create_task(self, user_request: str, goal: str) -> str:
Expand All @@ -79,7 +87,7 @@ def create_task(self, user_request: str, goal: str) -> str:
task_id = str(uuid.uuid4())

# Create workspace for this task
task_workspace = Path(self.workspace_base) / f"task_{task_id[:8]}"
task_workspace: Path = Path(self.workspace_base) / f"task_{task_id[:8]}"
task_workspace.mkdir(parents=True, exist_ok=True)

# Create subdirectories
Expand Down Expand Up @@ -115,16 +123,16 @@ def create_task(self, user_request: str, goal: str) -> str:
logger.info(f"Created autonomous task {task_id}: {goal}")
return task_id

def update_task_status(self, task_id: str, status: TaskStatus, context: Dict[str, Any] = None):
def update_task_status(self, task_id: str, status: TaskStatus, context: Dict[str, Any] = None) -> None:
"""
Update task status
"""
if task_id not in self.active_tasks:
logger.warning(f"Task {task_id} not found")
return

task = self.active_tasks[task_id]
old_status = task.status
task: TaskState = self.active_tasks[task_id]
old_status: TaskStatus = task.status
task.status = status
task.updated_at = time.time()

Expand Down Expand Up @@ -154,7 +162,13 @@ def add_action(self, task_id: str, action_type: ActionType, description: str,
timestamp=time.time()
)

task = self.active_tasks[task_id]
task: TaskState = self.active_tasks[task_id]

# Add bounds checking to prevent unbounded growth
if len(task.actions) >= 1000:
logger.warning(f"Task {task_id} actions limit reached (1000), removing oldest 500")
task.actions = task.actions[-500:]

task.actions.append(action)
task.updated_at = time.time()

Expand All @@ -163,15 +177,15 @@ def add_action(self, task_id: str, action_type: ActionType, description: str,

return action_id

def complete_action(self, task_id: str, action_id: str, result: Any = None, error: str = None):
def complete_action(self, task_id: str, action_id: str, result: Any = None, error: str = None) -> None:
"""
Complete an action with result or error
"""
if task_id not in self.active_tasks:
logger.warning(f"Task {task_id} not found")
return

task = self.active_tasks[task_id]
task: TaskState = self.active_tasks[task_id]

# Find the action
for action in task.actions:
Expand All @@ -192,15 +206,15 @@ def complete_action(self, task_id: str, action_id: str, result: Any = None, erro
else:
logger.info(f"Action {action_id} completed successfully")

def add_intermediate_result(self, task_id: str, result: Dict[str, Any]):
def add_intermediate_result(self, task_id: str, result: Dict[str, Any]) -> None:
"""
Add intermediate result to task
"""
if task_id not in self.active_tasks:
logger.warning(f"Task {task_id} not found")
return

task = self.active_tasks[task_id]
task: TaskState = self.active_tasks[task_id]
result_entry = {
"timestamp": time.time(),
"step": task.current_step,
Expand All @@ -212,36 +226,36 @@ def add_intermediate_result(self, task_id: str, result: Dict[str, Any]):
# Save result to file
self._save_intermediate_result(task_id, result_entry)

def add_generated_tool(self, task_id: str, tool_name: str, tool_code: str):
def add_generated_tool(self, task_id: str, tool_name: str, tool_code: str) -> None:
"""
Add a generated tool to the task
"""
if task_id not in self.active_tasks:
logger.warning(f"Task {task_id} not found")
return

task = self.active_tasks[task_id]
task: TaskState = self.active_tasks[task_id]
task.generated_tools.append(tool_name)
task.updated_at = time.time()

# Save tool to workspace
task_workspace = Path(task.workspace_path) / "tools"
tool_file = task_workspace / f"{tool_name}.py"
task_workspace: Path = Path(task.workspace_path) / "tools"
tool_file: Path = task_workspace / f"{tool_name}.py"

with open(tool_file, 'w', encoding='utf-8') as f:
f.write(tool_code)

logger.info(f"Generated tool {tool_name} for task {task_id}")

def update_progress(self, task_id: str, current_step: int, total_steps: int):
def update_progress(self, task_id: str, current_step: int, total_steps: int) -> None:
"""
Update task progress
"""
if task_id not in self.active_tasks:
logger.warning(f"Task {task_id} not found")
return

task = self.active_tasks[task_id]
task: TaskState = self.active_tasks[task_id]
task.current_step = current_step
task.total_steps = total_steps
task.completion_percentage = (current_step / total_steps * 100) if total_steps > 0 else 0
Expand All @@ -266,15 +280,15 @@ def get_all_active_tasks(self) -> Dict[str, TaskState]:
"""
return self.active_tasks.copy()

def complete_task(self, task_id: str, final_result: Dict[str, Any]):
def complete_task(self, task_id: str, final_result: Dict[str, Any]) -> None:
"""
Mark task as completed
"""
if task_id not in self.active_tasks:
logger.warning(f"Task {task_id} not found")
return

task = self.active_tasks[task_id]
task: TaskState = self.active_tasks[task_id]
task.status = TaskStatus.COMPLETED
task.updated_at = time.time()
task.completion_percentage = 100.0
Expand All @@ -294,15 +308,15 @@ def complete_task(self, task_id: str, final_result: Dict[str, Any]):

logger.info(f"Task {task_id} completed successfully")

def fail_task(self, task_id: str, error: str):
def fail_task(self, task_id: str, error: str) -> None:
"""
Mark task as failed
"""
if task_id not in self.active_tasks:
logger.warning(f"Task {task_id} not found")
return

task = self.active_tasks[task_id]
task: TaskState = self.active_tasks[task_id]
task.status = TaskStatus.FAILED
task.updated_at = time.time()

Expand All @@ -322,7 +336,7 @@ def get_task_context(self, task_id: str) -> Dict[str, Any]:
"""
Get comprehensive task context
"""
task = self.get_task_state(task_id)
task: TaskState | None = self.get_task_state(task_id)
if not task:
return {}

Expand Down Expand Up @@ -354,7 +368,7 @@ def get_task_context(self, task_id: str) -> Dict[str, Any]:
"context": task.context
}

def _log_action(self, task_id: str, action_type: ActionType, description: str, parameters: Dict[str, Any]):
def _log_action(self, task_id: str, action_type: ActionType, description: str, parameters: Dict[str, Any]) -> None:
"""
Log action to console and file
"""
Expand All @@ -368,15 +382,15 @@ def _log_action(self, task_id: str, action_type: ActionType, description: str, p

logger.info(f"Task {task_id}: {description}")

def _log_action_to_file(self, task_id: str, action: AgentAction):
def _log_action_to_file(self, task_id: str, action: AgentAction) -> None:
"""
Log action to task-specific log file
"""
if task_id not in self.active_tasks:
return

task = self.active_tasks[task_id]
log_file = Path(task.workspace_path) / "logs" / "actions.log"
task: TaskState = self.active_tasks[task_id]
log_file: Path = Path(task.workspace_path) / "logs" / "actions.log"

log_entry = {
"timestamp": action.timestamp,
Expand All @@ -392,15 +406,15 @@ def _log_action_to_file(self, task_id: str, action: AgentAction):
with open(log_file, 'a', encoding='utf-8') as f:
f.write(json.dumps(log_entry, default=str) + '\n')

def _save_intermediate_result(self, task_id: str, result: Dict[str, Any]):
def _save_intermediate_result(self, task_id: str, result: Dict[str, Any]) -> None:
"""
Save intermediate result to file
"""
if task_id not in self.active_tasks:
return

task = self.active_tasks[task_id]
results_file = Path(task.workspace_path) / "outputs" / "intermediate_results.json"
task: TaskState = self.active_tasks[task_id]
results_file: Path = Path(task.workspace_path) / "outputs" / "intermediate_results.json"

# Load existing results
results = []
Expand All @@ -418,16 +432,16 @@ def _save_intermediate_result(self, task_id: str, result: Dict[str, Any]):
with open(results_file, 'w', encoding='utf-8') as f:
json.dump(results, f, indent=2, default=str)

def _save_task_state(self, task_id: str, task: TaskState):
def _save_task_state(self, task_id: str, task: TaskState) -> None:
"""
Save task state to file
"""
state_file = Path(task.workspace_path) / "task_state.json"
state_file: Path = Path(task.workspace_path) / "task_state.json"

with open(state_file, 'w', encoding='utf-8') as f:
json.dump(asdict(task), f, indent=2, default=str)

def save_state(self):
def save_state(self) -> None:
"""
Save all active states to file
"""
Expand All @@ -445,7 +459,7 @@ def save_state(self):
except Exception as e:
logger.error(f"Failed to save state: {e}")

def load_state(self):
def load_state(self) -> None:
"""
Load state from file
"""
Expand All @@ -471,12 +485,12 @@ def load_state(self):
except Exception as e:
logger.error(f"Failed to load state: {e}")

def cleanup_old_tasks(self, max_age_hours: int = 24):
def cleanup_old_tasks(self, max_age_hours: int = 24) -> None:
"""
Clean up old completed tasks
"""
current_time = time.time()
cutoff_time = current_time - (max_age_hours * 3600)
current_time: float = time.time()
cutoff_time: float = current_time - (max_age_hours * 3600)

# Clean up history
self.task_history = [
Expand All @@ -489,7 +503,7 @@ def cleanup_old_tasks(self, max_age_hours: int = 24):
for task_dir in workspace_base.glob("task_*"):
if task_dir.is_dir():
# Check modification time
mod_time = task_dir.stat().st_mtime
mod_time: float = task_dir.stat().st_mtime
if mod_time < cutoff_time:
try:
import shutil
Expand All @@ -514,10 +528,10 @@ def _calculate_average_completion_time(self) -> float:
"""
Calculate average completion time for completed tasks
"""
completed_tasks = [t for t in self.task_history if t.status == TaskStatus.COMPLETED]
completed_tasks: List[TaskState] = [t for t in self.task_history if t.status == TaskStatus.COMPLETED]

if not completed_tasks:
return 0.0

total_time = sum(task.updated_at - task.created_at for task in completed_tasks)
total_time: float | int = sum(task.updated_at - task.created_at for task in completed_tasks)
return total_time / len(completed_tasks)
Loading
Loading