Files
ajarbot/agent_registry.py
Jordan Ramos e909cc0044 Add MCP delegation bridge and diagram tools
**Features Added**:

1. **Agent Registry (agent_registry.py)**
   - Thread-safe global singleton for MCP tool access to Agent instance
   - Enables MCP tools to call Agent.delegate() without circular imports
   - Registered at bot startup in bot_runner.py

2. **Sub-Agent Manager (sub_agent_manager.py)**
   - Watchdog system monitoring sub-agent lifecycle
   - Detects hung agents (5min timeout, 30s check interval)
   - Auto-cleanup and status tracking

3. **delegate_task MCP Tool (mcp_tools.py)**
   - Exposes Agent.delegate() to Claude via MCP protocol
   - Enables parallel sub-agent execution via tool calls
   - Supports specialist prompts and agent ID caching

4. **Memory Write Locks (memory_system.py)**
   - Thread-safe writes to prevent file corruption
   - Protects write_memory(), update_soul(), update_user()

5. **Diagram Tools**
   - Mermaid MCP server (flowcharts, sequence diagrams, etc.)
   - Excalidraw MCP server (hand-drawn style diagrams)
   - Config files in config/ directory

6. **Adapter Improvements**
   - Enhanced error handling across all adapters
   - Unified logging patterns

**Testing**: Ready for parallel sub-agent testing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-01 14:34:24 -07:00

77 lines
1.9 KiB
Python

"""Agent Registry - Thread-safe global singleton for MCP tool access.
MCP tools are module-level functions that cannot access the Agent instance
directly. This registry provides a thread-safe bridge so that tools like
delegate_task can call Agent.delegate() without circular imports.
Usage:
# At bot startup (bot_runner.py):
from agent_registry import register_agent
agent = Agent(...)
register_agent(agent)
# In MCP tools (mcp_tools.py):
from agent_registry import get_agent
agent = get_agent()
if agent:
result = agent.delegate(task, specialist_prompt)
"""
import threading
from typing import Optional, TYPE_CHECKING
if TYPE_CHECKING:
from agent import Agent
# Module-level singleton state
_agent: Optional['Agent'] = None
_lock = threading.Lock()
def register_agent(agent: 'Agent') -> None:
"""Register the main Agent instance for MCP tool access.
Must be called exactly once at bot startup, after Agent is initialized.
Thread-safe.
Args:
agent: The main Agent instance (not a sub-agent).
Raises:
ValueError: If agent is None or is a sub-agent.
"""
global _agent
if agent is None:
raise ValueError("Cannot register None as the main agent")
if getattr(agent, 'is_sub_agent', False):
raise ValueError("Cannot register a sub-agent as the main agent")
with _lock:
_agent = agent
print(f"[AgentRegistry] Main agent registered (provider={agent.llm.provider})")
def get_agent() -> Optional['Agent']:
"""Get the registered main Agent instance.
Thread-safe. Returns None if no agent has been registered yet.
Returns:
The main Agent instance, or None.
"""
with _lock:
return _agent
def clear_agent() -> None:
"""Clear the registered agent (for testing or shutdown).
Thread-safe.
"""
global _agent
with _lock:
_agent = None
print("[AgentRegistry] Agent registry cleared")