"""Agent registry — single point of registration for sub-agents (ADR-0014). Each agent module contributes: - a `BaseAgent` subclass instance - a module-level `MANIFEST: AgentManifest` The orchestrator, registry endpoint, and inference framework all read from here. Adding an agent is: add a module, register it once below. """ from __future__ import annotations from .base import BaseAgent from .manifest import AgentManifest from .overdue_task import OverdueTaskAgent, MANIFEST as OVERDUE_TASK_MANIFEST from .momentum import MomentumAgent, MANIFEST as MOMENTUM_MANIFEST from .time_of_day import TimeOfDayAgent, MANIFEST as TIME_OF_DAY_MANIFEST from .recent_patterns import RecentPatternsAgent, MANIFEST as RECENT_PATTERNS_MANIFEST from .focus_area import FocusAreaAgent, MANIFEST as FOCUS_AREA_MANIFEST _REGISTERED: list[tuple[BaseAgent, AgentManifest]] = [ (OverdueTaskAgent(), OVERDUE_TASK_MANIFEST), (MomentumAgent(), MOMENTUM_MANIFEST), (TimeOfDayAgent(), TIME_OF_DAY_MANIFEST), (RecentPatternsAgent(), RECENT_PATTERNS_MANIFEST), (FocusAreaAgent(), FOCUS_AREA_MANIFEST), ] # Sanity check — agent_id and manifest.id must agree, otherwise the registry # becomes inconsistent across endpoints. for _agent, _manifest in _REGISTERED: if _agent.agent_id != _manifest.id: raise RuntimeError( f"Manifest mismatch: {_agent.__class__.__name__}.agent_id={_agent.agent_id!r} " f"≠ MANIFEST.id={_manifest.id!r}" ) _AGENTS: dict[str, BaseAgent] = {a.agent_id: a for a, _ in _REGISTERED} _MANIFESTS: dict[str, AgentManifest] = {m.id: m for _, m in _REGISTERED} def get_agent(agent_id: str) -> BaseAgent: if agent_id not in _AGENTS: raise KeyError(f"Unknown agent: {agent_id!r}. Known: {sorted(_AGENTS)}") return _AGENTS[agent_id] def all_agents() -> list[BaseAgent]: return list(_AGENTS.values()) def get_manifest(agent_id: str) -> AgentManifest: if agent_id not in _MANIFESTS: raise KeyError(f"Unknown agent: {agent_id!r}. Known: {sorted(_MANIFESTS)}") return _MANIFESTS[agent_id] def all_manifests() -> list[AgentManifest]: return list(_MANIFESTS.values())