| """Agent Manager — orchestrates all 5 persistent AI agents. |
| |
| Manages the lifecycle of all agents: |
| - Starts/stops all agents |
| - Assigns goals to the best-suited agent |
| - Monitors agent progress |
| - Handles agent failures and reassignment |
| - Provides aggregated stats |
| |
| The agents run persistently in background threads, continuously |
| picking up goals from the goal memory and working on them. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import logging |
| import time |
| from typing import Any, Callable |
|
|
| from ..memory.goal_memory import GoalMemory, Goal |
| from ..memory.persistent import PersistentMemory |
| from .agent_base import BaseAgent |
| from .planner_agent import PlannerAgent |
| from .coder_agent import CoderAgent |
| from .researcher_agent import ResearcherAgent |
| from .reviewer_agent import ReviewerAgent |
| from .executor_agent import ExecutorAgent |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| class AgentManager: |
| """Manages 5 persistent AI agents that work on goals and projects. |
| |
| Agents: |
| 1. Planner — breaks goals into steps |
| 2. Coder — writes code, creates files |
| 3. Researcher — gathers information |
| 4. Reviewer — validates quality |
| 5. Executor — runs commands, executes tools |
| |
| All agents run in background threads and continuously process goals. |
| """ |
|
|
| def __init__(self, goal_memory: GoalMemory, |
| persistent_memory: PersistentMemory | None = None, |
| generate_fn: Callable[[str], str] | None = None, |
| tool_registry: Any = None) -> None: |
| self.goal_memory = goal_memory |
| self.persistent_memory = persistent_memory |
| self._generate_fn = generate_fn |
| self._tool_registry = tool_registry |
|
|
| |
| self.agents: dict[str, BaseAgent] = { |
| "planner": PlannerAgent(goal_memory, persistent_memory, generate_fn), |
| "coder": CoderAgent(goal_memory, persistent_memory, generate_fn), |
| "researcher": ResearcherAgent(goal_memory, persistent_memory, generate_fn), |
| "reviewer": ReviewerAgent(goal_memory, persistent_memory, generate_fn), |
| "executor": ExecutorAgent(goal_memory, persistent_memory, generate_fn, tool_registry), |
| } |
|
|
| self._running = False |
| self._start_time = 0.0 |
| self._stats = { |
| "total_goals_assigned": 0, |
| "total_goals_completed": 0, |
| "total_goals_failed": 0, |
| "reassignments": 0, |
| } |
|
|
| def set_generate_fn(self, fn: Callable[[str], str]) -> None: |
| """Set the LLM generation function for all agents.""" |
| self._generate_fn = fn |
| for agent in self.agents.values(): |
| agent.set_generate_fn(fn) |
|
|
| def start_all(self) -> None: |
| """Start all 5 agents.""" |
| self._running = True |
| self._start_time = time.time() |
| for agent in self.agents.values(): |
| agent.start() |
| logger.info("All 5 agents started") |
|
|
| def stop_all(self) -> None: |
| """Stop all agents.""" |
| self._running = False |
| for agent in self.agents.values(): |
| agent.stop() |
| logger.info("All agents stopped") |
|
|
| def pause_all(self) -> None: |
| """Pause all agents.""" |
| for agent in self.agents.values(): |
| agent.pause() |
|
|
| def resume_all(self) -> None: |
| """Resume all agents.""" |
| for agent in self.agents.values(): |
| agent.resume() |
|
|
| def create_project(self, title: str, description: str, priority: str = "high", |
| tags: list[str] | None = None) -> Goal: |
| """Create a new project goal and let the planner agent pick it up.""" |
| goal = self.goal_memory.create_goal( |
| title=title, description=description, priority=priority, |
| tags=tags or ["project"], |
| ) |
| self._stats["total_goals_assigned"] += 1 |
| logger.info("Created project: %s (will be picked up by planner agent)", title) |
| return goal |
|
|
| def assign_goal(self, goal_id: str, agent_name: str) -> bool: |
| """Manually assign a goal to a specific agent.""" |
| if agent_name not in self.agents: |
| logger.warning("Unknown agent: %s", agent_name) |
| return False |
| result = self.goal_memory.assign_agent(goal_id, agent_name) |
| if result: |
| self._stats["total_goals_assigned"] += 1 |
| return True |
| return False |
|
|
| def get_active_projects(self) -> list[Goal]: |
| """Get all active goals/projects.""" |
| return self.goal_memory.get_active_goals() |
|
|
| def get_agent_status(self) -> dict[str, Any]: |
| """Get status of all agents.""" |
| return {name: agent.get_status() for name, agent in self.agents.items()} |
|
|
| def get_project_progress(self) -> list[dict[str, Any]]: |
| """Get progress of all active projects.""" |
| active = self.goal_memory.get_active_goals() |
| return [g.as_dict() for g in active] |
|
|
| def get_stats(self) -> dict[str, Any]: |
| """Get aggregated stats.""" |
| agent_stats = {name: agent.get_status()["stats"] for name, agent in self.agents.items()} |
| goal_stats = self.goal_memory.get_stats() |
|
|
| total_completed = sum(s["goals_completed"] for s in agent_stats.values()) |
| total_failed = sum(s["goals_failed"] for s in agent_stats.values()) |
| total_steps = sum(s["steps_executed"] for s in agent_stats.values()) |
|
|
| return { |
| "manager": { |
| **self._stats, |
| "running": self._running, |
| "uptime_s": round(time.time() - self._start_time, 1) if self._start_time else 0, |
| "total_goals_completed": total_completed, |
| "total_goals_failed": total_failed, |
| "total_steps_executed": total_steps, |
| }, |
| "agents": agent_stats, |
| "goals": goal_stats, |
| } |
|
|