Capabilities

The Capability Model

Every app accesses the world through capabilities, never direct tools. A capability is a human verb — with a chain of providers that try in order until one succeeds.

Human is always the final fallback. If every tool is offline, the system asks you.

16 Capabilities

Capability Purpose Providers
think Generate text responses. ollama, openai, claude-cli, human
read Read content from a path. filesystem, markitdown, human
write Write content to a path. filesystem, human
search Search for content. grep, human
speak Generate speech from text. edge-tts → openai-tts → kokoro → xtts
listen Transcribe audio to text. openai-whisper → whisper (local)
draw Generate images from text. comfyui
animate Generate a video clip from a prompt + optional reference image. comfyui-ltx
model Generate an articulated 3D model from a prompt. robot-modeller (cadquery)
artifact Generate a single-file HTML artifact from a prompt. viz (standalone HTML)
see Capture an image from a camera. webcam → human
pronounce Score a learner's pronunciation against a reference text. wav2vec2 (local)
send Deliver an outbound message to an external recipient. email-smtp → human
browse Drive a headless browser. playwright (headless Chromium)
footage Fetch a royalty-free stock video clip matching a search term. pexels → pixabay → human
translate Translate strings from a source language into a target language. nllb-200 (local) → llm-translate

Provider Chains

Providers are tried in priority order. Plugins inject high-priority providers at startup (enhancer pattern).

think: ollama → openai → claude-cli → human
read:  filesystem → human
write: filesystem → human
search: grep → human
speak: voice-api (kokoro → edge-tts → xtts)
listen: voice-api (whisper)
draw:  comfyui

If a plugin is absent, its provider simply isn't in the chain. Apps never know which provider answered — they just call self.think() or self.search().

Domain Routing

The think capability supports domain-specific routing. Different domains can use different models:

# Uses the domain-specific provider chain for 'code'
result = await self.think("Analyze this", domain="code")

# Default chain
result = await self.think("Summarize this article")

Domains are configured in emptyos.toml under [capabilities.think.domains].