Domain Plugin System#
EurekaClaw uses a three-tier plugin architecture to support domain-specific research:
EurekaClaw (general pipeline) ← domain-agnostic: survey / theory / experiment / writer
└── DomainPlugin (e.g. MAB) ← domain sub-interface: tools + skills + workflow + benchmark
└── Workflow ← per-domain research guidance injected into agent prompts
Plugins add domain-specific tools, skills, and prompt guidance without modifying core code.
DomainPlugin Base Class#
File: eurekaclaw/domains/base.py
class DomainPlugin(ABC):
name: str = "" # machine identifier, e.g. "mab"
display_name: str = "" # human-readable, e.g. "Stochastic Multi-Armed Bandits"
keywords: list[str] = [] # strings that trigger auto-detection
description: str = ""
Abstract Methods#
@abstractmethod
def register_tools(self, registry: ToolRegistry) -> None:
"""Inject domain-specific tools into the shared ToolRegistry."""
...
@abstractmethod
def get_workflow_hint(self) -> str:
"""Return domain-specific research guidance (injected into agent system prompts)."""
...
Optional Methods#
def get_skills_dirs(self) -> list[Path]:
"""Return extra skill directories. Default: []."""
return []
def get_benchmark_problems(self, level: str) -> list[dict]:
"""Return benchmark problems for 'level1', 'level2', or 'level3'. Default: []."""
return []
Plugin Registry#
File: eurekaclaw/domains/__init__.py
Registration#
@register_domain
class MyPlugin(DomainPlugin):
name = "my_domain"
...
The @register_domain decorator registers a plugin class by its name.
Resolution#
def resolve_domain(domain: str) -> DomainPlugin | None
Auto-detect the right plugin from a domain string or conjecture keywords. Matching order:
Exact key match against registered plugin names
Keyword scan — returns the first plugin whose
keywordslist contains any word fromdomain
Returns None if no plugin matches (runs in general mode).
MAB Domain Plugin#
Package: eurekaclaw/domains/mab/
The built-in example plugin for stochastic multi-armed bandit theory.
@register_domain
class MABDomainPlugin(DomainPlugin):
name = "mab"
display_name = "Stochastic Multi-Armed Bandits"
description = "Regret bounds, concentration, lower bounds for K-armed bandits"
keywords = [
"bandit", "multi-armed", "mab", "ucb", "thompson", "regret",
"exploration", "exploitation", "stochastic bandit",
]
Auto-detected when domain contains: bandit, UCB, thompson, regret, exploration, multi-armed, etc.
Package Structure#
domains/mab/
├── __init__.py MABDomainPlugin
├── workflow.py WORKFLOW_HINT (research guidance text)
├── envs/
│ ├── stochastic.py GaussianBandit, BernoulliBandit environments
│ └── runner.py run_experiment(), sweep_T() — UCB1 & Thompson Sampling
├── tools/
│ ├── concentration.py Hoeffding, Bernstein, sub-Gaussian, UCB radius formulas
│ ├── regret.py Regret decomposition, Lai-Robbins lower bound
│ ├── information.py KL(Bernoulli), KL(Gaussian), Fano's inequality
│ └── bandit_tool.py BanditExperimentTool (LLM-callable tool)
├── skills/
│ ├── ucb_regret_analysis.md
│ ├── thompson_sampling_analysis.md
│ ├── lower_bound_construction.md
│ └── bandit_simulation.md
└── benchmark/
├── level1.json Reproduce known bounds (UCB1, Lai-Robbins)
├── level2.json Refine existing results (Bernstein-UCB, MOSS, KL-UCB)
└── level3.json Open problems (heavy tails, infinite-arm, batched bandits)
How to Add a New Domain#
Create the plugin package:
# eurekaclaw/domains/my_domain/__init__.py
from eurekaclaw.domains.base import DomainPlugin
from eurekaclaw.domains import register_domain
@register_domain
class MyDomainPlugin(DomainPlugin):
name = "my_domain"
display_name = "My Research Domain"
description = "Short description for display"
keywords = ["keyword1", "keyword2", "related term"]
def register_tools(self, registry: ToolRegistry) -> None:
registry.register(MySpecialTool())
def get_workflow_hint(self) -> str:
return """
When researching my_domain:
- Always start by checking known results X and Y
- Use technique Z for the main proof step
...
"""
def get_skills_dirs(self) -> list[Path]:
return [Path(__file__).parent / "skills"]
def get_benchmark_problems(self, level: str) -> list[dict]:
bm_file = Path(__file__).parent / "benchmark" / f"{level}.json"
return json.loads(bm_file.read_text()) if bm_file.exists() else []
Register the import in
eurekaclaw/domains/__init__.py:
_DOMAIN_PACKAGES = [
"eurekaclaw.domains.mab",
"eurekaclaw.domains.my_domain", # add this line
]
That’s it.
resolve_domain("keyword1 problem")will auto-select your plugin.
Domain Plugin Integration#
When MetaOrchestrator runs with a detected domain plugin, it:
Calls
plugin.register_tools(tool_registry)— adds domain tools to the registryCalls
plugin.get_skills_dirs()— loads domain skills intoSkillRegistryInjects
plugin.get_workflow_hint()into agent system prompts
No changes to any core agent or orchestrator code are needed.