Agent Orchestration
WASP supports multiple concurrent autonomous agents, each with its own goal queue, context, and execution lifecycle. The AgentOrchestrator manages the full multi-agent system.
AgentOrchestrator (src/agent_manager/__init__.py)
The orchestrator:
- Creates and destroys agent instances
- Manages the global token budget (100,000 tokens/minute across all agents)
- Enforces CPU threshold (85% → pause all agent work)
- Routes messages between agents
- Provides the
agent_managerskill interface
Global Resource Limits
| Setting | Default | Description |
|---|---|---|
AGENTS_MAX_ACTIVE | 10 | Max concurrent active agents |
AGENTS_MAX_CONCURRENT_STEPS | 5 | Max steps across all agents per tick |
AGENTS_CPU_THRESHOLD | 85% | CPU pause threshold |
AGENTS_GLOBAL_TOKEN_BUDGET_PER_MINUTE | 100,000 | Rate limit across all agents |
Agent Structure
Each agent is a persistent entity with:
@dataclass
class Agent:
id: str # UUID
name: str # Human-readable name
objective: str # Current high-level objective
status: AgentStatus # IDLE, RUNNING, PAUSED, ERROR, ARCHIVED
priority: int # 1-10 (default: 6 for agent-created goals)
goal_queue: list[str] # Queue of goal IDs
current_goal_id: str # Active goal
chat_id: str # Notification target
created_at: datetime
last_active: datetime
Agents are stored in:
- Redis:
agentshash (fast reads) - PostgreSQL:
agentstable (durable, survives Redis flush)
Agent Lifecycle
create_agent("Monitor BTC", priority=6)
│
▼
Agent(status=IDLE)
│
▼ goal assigned via create_goal()
Agent(status=RUNNING, current_goal=...)
│
├── goal completes → Agent(status=IDLE)
│ │
│ ▼ next goal in queue, or IDLE
│
├── manual pause → Agent(status=PAUSED)
│ │
│ ▼ resume → Agent(status=RUNNING)
│
└── archive → Agent(status=ARCHIVED)
→ deleted from Redis + PostgreSQL
Creating Agents via Skill
agent_manager(
action="create",
name="crypto_watcher",
objective="Monitor BTC and ETH prices, alert if >5% change",
priority=7
)
The AgentManagerSkill is registered as a built-in skill and late-wired to the AgentOrchestrator at startup.
Agent Communication
Agents communicate via the event bus (Redis Streams):
- Agents can publish events via
EventType.AGENT_MESSAGE - The orchestrator routes agent messages to the correct handler
- Agents can trigger sub-goals in other agents
AgentTickJob (every 15 sec)
async def run(self):
# Check CPU backpressure
cpu = await asyncio.to_thread(psutil.cpu_percent, interval=0.1)
if cpu > threshold:
return
# Get all running agents, sorted by priority
running_agents = sorted(
[a for a in agents if a.status == RUNNING],
key=lambda a: a.priority,
reverse=True,
)
# Process up to MAX_CONCURRENT_STEPS across all agents
tasks = []
for agent in running_agents[:max_concurrent]:
tasks.append(orchestrator.tick_agent(agent))
await asyncio.gather(*tasks)
Recurring Agent Goals
When a reminder has an agent_id attached (set when an agent creates a reminder):
ReminderCheckerJobfires the reminder- Calls
agent_orchestrator.create_agent_goal()to restart the agent's cycle - The agent picks up the new goal on the next tick
This enables agents to create recurring self-sustaining loops.
Deleting Agents
agent_manager(action="archive", agent_id="...")
delete_agent() removes from both Redis AND PostgreSQL to prevent stale duplicates from reappearing after restarts.
Meta-Agent Supervisor (optional)
When META_AGENT_ENABLED=true, the MetaSupervisor:
- Coordinates teams of up to 5 agents
- Decomposes complex objectives across multiple specialized agents
- Manages inter-agent dependencies
- Triggered via
meta_orchestrateskill
Currently disabled by default — enable for advanced multi-agent scenarios.