Skip to content

Trait System

AgentZero’s core principle is that every subsystem is a trait. This means you can swap implementations without touching the orchestration layer.

SubsystemTraitShips WithExtension
AI ModelsProviderOpenAI-compatible (OpenRouter, OpenAI, Anthropic, Ollama)Implement Provider trait
MemoryMemoryStoreSQLite, Turso/libsqlImplement MemoryStore trait
ToolsTool50+ built-in (file I/O, shell, networking, browser, delegation, memory, git, SOP, cron, hardware)Implement Tool trait, WASM plugin, or process plugin
ChannelsChannelTelegram, Discord, Slack, MattermostImplement Channel trait
SecurityPolicy configAllowlists, OTP, audit, estop, leak guard, syscall anomalyConfig-driven ([security.*])
ObservabilityConfig-drivenRuntime traces, OpenTelemetry export[observability] config
RuntimeOrchestratorNative single-process[runtime] config (native/docker)
PluginsWASM sandboxExtism-based plugin host.wasm modules with manifest.json
SkillsSkill registryBuilt-in skillforge + SOP engineInstall from local/remote/git
IdentityConfig-drivenOpenClaw markdown, AIEOS JSON[identity] config
GatewayHTTP serviceAxum-based REST APIEndpoint handlers
CostTrackerToken + USD tracking with limits[cost] config

The Provider trait abstracts AI model access:

#[async_trait]
pub trait Provider: Send + Sync {
async fn complete(&self, request: CompletionRequest) -> Result<CompletionResponse>;
fn name(&self) -> &str;
}

Ships with an OpenAI-compatible implementation that works with OpenRouter, OpenAI, Anthropic, Ollama, and any /v1/chat/completions endpoint.

The MemoryStore trait abstracts conversation persistence:

#[async_trait]
pub trait MemoryStore: Send + Sync {
async fn append(&self, entry: MemoryEntry) -> Result<()>;
async fn recent(&self, limit: usize) -> Result<Vec<MemoryEntry>>;
async fn search(&self, query: &str, limit: usize) -> Result<Vec<MemoryEntry>>;
}

Ships with SQLite (default) and Turso/libsql (feature-gated).

The Tool trait abstracts agent capabilities:

#[async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> &'static str;
fn description(&self) -> &'static str { "" }
fn input_schema(&self) -> Option<serde_json::Value> { None }
async fn execute(&self, input: &str, ctx: &ToolContext) -> anyhow::Result<ToolResult>;
}

All 50+ built-in tools implement this trait with input_schema() for structured tool-use APIs. WASM plugins, process plugins, and MCP servers are wrapped in Tool adapters.

Each trait lives in agentzero-core. Implementations live in their own crates:

agentzero-core # Traits, types, security, delegation, routing
├── agentzero-providers # Provider implementations
├── agentzero-storage # MemoryStore + encrypted KV (absorbed crypto, memory)
├── agentzero-tools # 50+ tool implementations (absorbed autonomy, hardware, cron, skills)
├── agentzero-channels # Channel implementations (absorbed leak-guard)
├── agentzero-plugins # WASM plugin host (wasmi default, wasmtime optional)
└── agentzero-infra # Orchestration + runtime (absorbed runtime)

This ensures the core never depends on infrastructure — only the reverse.

Security is not a trait — it’s a policy layer enforced at construction time:

  1. Tool instances are created with their security config baked in
  2. Tools validate every call against allowlists, path restrictions, and size limits
  3. The agent orchestrator doesn’t need to know about security — it’s already enforced
User message → Agent → Tool::execute() → [security policy check] → actual execution

If a policy check fails, the tool returns an error. The agent sees the error and can adjust.