Plugins

Plugin Types

Plugins come in two flavors:

Service plugins expose named services that apps access explicitly via self.require("name") or self.service("name").

Enhancer plugins dynamically inject providers into capabilities at startup. If the plugin is absent or offline, the capability falls back to the next provider. No app code changes needed.

This is the Graceful Enhancement pattern: capabilities degrade silently when a plugin goes offline. Your search still works without the search enhancer — it just loses graph-aware features.

Active Plugins

Plugin Type What It Does
agent-runtime Service Generic ephemeral CLI subprocess driver for agent-style tools (claude-cli, codex, gemini, etc.). Spawns a CLI per turn, drains stdout/stderr with line callbacks, supports periodic tick callbacks for live UI updates, kills on timeout. Lifted from dogfood-agent's run loop so any app needing 'spawn a coding-agent CLI for one turn' can share the same plumbing. No persistent daemon — each call is a one-shot subprocess
applio Service AI voice conversion via Applio (port 6969)
blender Service 3D modeling, rendering, and animation via Blender
cadquery Service Articulated 3D object compile + URDF export via CadQuery, run inside a user-home Python 3.12 venv
comfyui Enhancer (draw) GPU image generation via ComfyUI
command-launcher Service Spawns a borderless Chrome/Edge app-mode window pointing at the command palette when the global hotkey fires
dogfood-demo Service Sidecar EmptyOS daemon on :9001 with a curated sample vault. Lets ppt embed slides, screenshots, and any other share-shaped surface render against demo content instead of the user's personal vault — without flipping the personal daemon's demo mode
edge-tts Service Free in-process TTS via Microsoft Edge voices (edge-tts pip package)
email-smtp Service Injects an SMTP provider into the send capability — outbound email via your own mail server
global-hotkey Service Listens for global OS keyboard shortcuts and emits them to the EventBus
headroom Service Context compression middleware — shrinks think capability payloads before they reach cloud providers, using Headroom's three deterministic compressors (JSON SmartCrusher, AST CodeCompressor, ModernBERT-distilled Kompress-small for prose). Runs on CPU (~13ms via ONNX). Lazy-loaded — boot is unchanged when not installed
health Service System health checks — capabilities, connectors, apps
notifications Service Proactive notifications — vault file by default, Telegram when configured
obsidian Service Declares Obsidian as EmptyOS's vault viewer — owns URI templates (obsidian://open, obsidian://new) used by every note link. Swap this plugin (or override uri_templates in config) to route note links through a different viewer
ollama Enhancer (think) Local LLM inference via Ollama
openai-image Service Cloud image-gen fallback via OpenAI gpt-image-1. Cost-bearing — opt-in per call by domain routing, not auto-selected for free local generations
playwright Service Headless browser automation — provides the browse capability via Chromium
pronounce Service Phoneme-level pronunciation scoring via a local wav2vec2 service
sandbox-pool Service Supervises a small pool of throwaway EmptyOS daemons on :9002+ that Claude (and other operators) can lease, restart, and release through a daemon-owned API. The plugin spawns each python.exe and tracks its PID itself, so the auto-classifier doesn't need to allow raw Bash taskkill — kills go through POST /sandbox/api/lease//restart which knows which PID it owns. Mirrors plugins/dogfood-demo/ for subprocess shape but supervises N members instead of one
system-tray Service Adds EmptyOS to the native Windows/macOS system tray
telegram Service Push notifications + two-way bot via Telegram
voice-api Enhancer (speak+listen) Text-to-speech and speech-to-text via local Voice API
webcam Service Local camera capture via OpenCV — provides the see capability

How Enhancers Work

When the Ollama plugin starts, it registers itself as a provider for the think capability at priority 0 (highest). The capability's provider chain becomes:

think: ollama → openai → claude-cli → human

If Ollama crashes mid-request, the capability automatically falls back to OpenAI. Apps never see the failure — they just get a response.

# This code never changes regardless of which providers are available
result = await self.think("Analyze this code", domain="code")

Plugin Configuration

Plugins are configured in emptyos.toml:

[plugins.ollama]
url = "http://localhost:11434"

[plugins.comfyui]
url = "http://127.0.0.1:8188"
launcher = "D:/ComfyUI/run.bat"

[plugins.telegram]
token = "bot-token-here"
allowed_users = [123456789]

Plugins with a launcher config can auto-start their external service when EmptyOS boots.

Auto-Start Pattern

External services use embedded runtimes with headless launch:

# Plugin auto_start() — no visible windows
subprocess.Popen(
    [python_exe, "-s", main_py, "--flags"],
    cwd=launcher_dir,
    creationflags=subprocess.CREATE_NO_WINDOW,
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL,
)

The health plugin polls each service endpoint until ready, with a 60-second timeout.

Writing a Plugin

class MyPlugin(BasePlugin):
    async def on_start(self):
        # Register as a service
        self.register_service("my_service", self)

        # Or inject into a capability (enhancer pattern)
        self.inject_provider("think", MyProvider(), priority=0)

    async def on_stop(self):
        pass

Place in plugins/my-plugin/ with a plugin.py and manifest. The plugin loader discovers it automatically.