clawft

Plugin System

Plugin system architecture, six extension points, WASM sandbox, built-in plugins, and how to write custom plugins.

Overview

The clawft plugin system provides six core extension points through the clawft-plugin crate, plus runtime WASM sandboxing, skill hot-reload, autonomous skill creation, slash-command integration, and MCP tool exposure.

Extension PointTraitPurpose
ToolsToolAgent tool execution (web search, file I/O, etc.)
ChannelsChannelAdapterExternal platform message handling
Pipeline StagesPipelineStageCustom processing stages in the agent pipeline
SkillsSkillHigh-level agent capabilities with tools and instructions
Memory BackendsMemoryBackendPluggable memory storage (vector, KV, graph)
Voice HandlersVoiceHandlerVoice/audio processing

Plugin Manifest

Every plugin declares its capabilities, permissions, and resource limits through a clawft.plugin.json manifest file:

{
  "name": "my-plugin",
  "version": "0.1.0",
  "capabilities": ["tool"],
  "permissions": {
    "network": false,
    "filesystem": false,
    "env_vars": []
  },
  "resources": {
    "max_memory_mb": 64,
    "max_cpu_seconds": 10
  }
}

The PluginManifest struct in clawft-plugin validates and parses this file.

Plugin Traits

Tool

#[async_trait]
pub trait Tool: Send + Sync {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn parameters_schema(&self) -> serde_json::Value;
    async fn execute(
        &self,
        args: serde_json::Value,
        ctx: &dyn ToolContext,
    ) -> Result<serde_json::Value, PluginError>;
}

ChannelAdapter

#[async_trait]
pub trait ChannelAdapter: Send + Sync {
    fn name(&self) -> &str;
    async fn start(&mut self, host: Arc<dyn ChannelAdapterHost>) -> Result<(), PluginError>;
    async fn stop(&mut self) -> Result<(), PluginError>;
    async fn send(&self, payload: MessagePayload) -> Result<(), PluginError>;
}

PipelineStage

#[async_trait]
pub trait PipelineStage: Send + Sync {
    fn stage_type(&self) -> PipelineStageType;
    fn name(&self) -> &str;
    async fn process(
        &self,
        input: serde_json::Value,
    ) -> Result<serde_json::Value, PluginError>;
}

Skill

#[async_trait]
pub trait Skill: Send + Sync {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn tools(&self) -> Vec<Arc<dyn Tool>>;
    fn system_prompt(&self) -> Option<String>;
}

MemoryBackend

#[async_trait]
pub trait MemoryBackend: Send + Sync {
    async fn store(&self, key: &str, value: &[u8]) -> Result<(), PluginError>;
    async fn retrieve(&self, key: &str) -> Result<Option<Vec<u8>>, PluginError>;
    async fn delete(&self, key: &str) -> Result<(), PluginError>;
    async fn search(&self, query: &str, limit: usize) -> Result<Vec<String>, PluginError>;
}

Built-in Plugin Crates

Nine plugin crates extend the framework:

CratePurposeKey Dependencies
clawft-plugin-git7 git tools (status, diff, log, commit, branch, checkout, blame)git2
clawft-plugin-cargo5 cargo subcommands (build, test, check, clippy, fmt)(subprocess)
clawft-plugin-oauth2OAuth2 auth flows + authenticated REST toolsreqwest
clawft-plugin-treesitterAST analysis (parse, query, symbols, patterns)tree-sitter
clawft-plugin-browserCDP browser automation (navigate, screenshot, eval, interact)tokio
clawft-plugin-calendarCalendar CRUD (create, read, update, delete, list)chrono
clawft-plugin-containersDocker/Podman lifecycle (build, run, stop, logs, list)tokio
clawft-security57 audit checks across 10 categories; weft security scanclawft-types

All plugin crates depend on clawft-plugin for the Tool trait.

WASM Sandbox

Feature gate: wasm-plugins

The WASM plugin host uses wasmtime 29 with the WIT component model to execute untrusted plugin code in a sandboxed environment.

Resource Limits

ResourceDefaultConfigurable
Fuel (CPU budget)1,000,000,000 instructionsYes
Memory16 MBYes
Binary size300 KB maxYes
Wall-clock timeoutEpoch interruptionYes

Host Functions

Five host functions are exposed to WASM plugins, each gated by the plugin's declared permissions:

FunctionPermission GateBehavior
http-requestpermissions.networkValidates URL against allowlist; rejects private IPs (SSRF check). Rate-limited.
read-filepermissions.filesystemCanonicalizes path; rejects symlinks outside allowed directories.
write-filepermissions.filesystemSame canonicalization and symlink rejection.
get-envpermissions.env_varsReturns only explicitly listed environment variables.
logAlways availableRate-limited to prevent log flooding.

All host function calls are audit-logged. Fuel metering tracks instruction count (traps on exhaustion), and epoch interruption provides wall-clock timeout as a secondary safeguard.

Plugin Permission System

PluginPermissions

FieldTypeDescription
networkVec<String>URL allowlist for http-request
filesystemVec<PathBuf>Allowed directory paths for file access
env_varsVec<String>Permitted environment variable names
shellboolWhether shell execution is allowed

Permission Diff

When a plugin version upgrade requests new permissions, PermissionDiff computes exactly which permissions are new. Only new permissions require user approval. The PermissionStore persists approved permissions per plugin.

PermissionApprover Trait

Abstracts the user consent flow. Implementations can prompt interactively (CLI) or through a UI. The approver receives only the diff of new permissions.

Skill Loader

Skills are discovered in priority order (first match wins):

  1. Workspace -- ./skills/ in the current project
  2. User (managed) -- ~/.clawft/skills/
  3. Builtin -- bundled skills shipped with the binary

WASM-based skills are automatically registered when discovered. The loader parses the skill manifest, validates permissions, and registers declared tools.

Skill Hot-Reload

The hot-reload system uses the notify crate to watch skill directories:

  1. File watcher detects a change
  2. Debounce timer prevents rapid successive reloads
  3. New skill version is loaded and validated alongside the old version
  4. Atomic swap: the new version replaces the old one; in-flight calls complete on the old version
  5. Skill precedence is re-evaluated

Slash-Command Framework

The SlashCommandRegistry provides a unified command system:

  • Skills with user-invocable: true contribute slash commands to /help output
  • Collision detection prevents two skills from registering the same command name
  • Commands are dispatched to the owning skill's handler

MCP Skill Exposure

Skills are automatically exposed as MCP tools via SkillToolProvider:

  • Skills appear in tools/list responses with auto-generated JSON Schema
  • tools/call requests are routed through skill.execute_tool()
  • Hot-reload updates the MCP tool listing automatically

PluginHost Unification

The unified PluginHost manages all plugin types through a single lifecycle controller:

  • ChannelAdapterShim wraps existing Channel implementations for backward compatibility
  • start_all() and stop_all() operate concurrently across all registered plugins
  • SoulConfig injects SOUL.md content into plugin contexts for personality propagation

Autonomous Skill Creation

The system can detect repeated task patterns and auto-generate skills. When a pattern repeats beyond a configurable threshold (default 3), the agent generates a SKILL.md, installs it in pending state, and prompts for user approval. This feature is disabled by default:

{
  "skills": {
    "autonomous": {
      "enabled": true,
      "pattern_threshold": 3
    }
  }
}

On this page