Plugin System 🟡

Plugins are how OpenClaw extends its capabilities. This chapter explains the 6-phase plugin lifecycle, the jiti loader mechanism, and the Hook system.

Learning Objectives

After reading this chapter, you'll be able to:


I. Three Plugin Types

TypeExamplePurpose
Channel Plugintelegram, discord, slackConnect to a messaging platform
Provider Pluginanthropic, openai, ollamaConnect to an LLM service
Capability Pluginmemory-core, mcporterAdd tools or capabilities

II. 6-Phase Plugin Lifecycle

flowchart TB
    P1["Phase 1: Discovery\nScan extensions/ directories\nFind openclaw.plugin.json files"]
    P2["Phase 2: Manifest Parsing\nParse plugin metadata\n(id, channels, providers, etc.)"]
    P3["Phase 3: Activation Decision\nShould this plugin load?\n(enabledByDefault, user config)"]
    P4["Phase 4: Loading via jiti\nImport TypeScript plugin code\n(SDK alias resolution applied)"]
    P5["Phase 5: Registry\nRegister channels/providers/tools\ninto PluginRegistry"]
    P6["Phase 6: Activation\nStart channels\nConnect to platforms"]

    P1 --> P2 --> P3 --> P4 --> P5 --> P6

Phase 1: Discovery

src/plugins/discovery.ts (924 lines) scans:

Phase 4: Loading via jiti

OpenClaw uses jiti to load TypeScript plugin files at runtime without a build step:

// src/plugins/loader.ts

const jitiRequire = jiti(import.meta.url, {
  interopDefault: true,
  alias: buildPluginLoaderAliasMap(),  // SDK Alias!
});

const pluginModule = jitiRequire(pluginEntryPath);

The SDK Alias Mechanism

When a plugin imports 'openclaw/plugin-sdk/core', jiti intercepts it and redirects to the actual src/plugin-sdk/core.ts file — ensuring the plugin always uses the same runtime instance as the core.

// buildPluginLoaderAliasMap() builds:
{
  'openclaw/plugin-sdk/core': '/path/to/src/plugin-sdk/core.ts',
  'openclaw/plugin-sdk/provider-entry': '/path/to/src/plugin-sdk/provider-entry.ts',
  // ... 20+ entries
}

This prevents "duplicate module" bugs where a plugin's copy of a dependency is different from core's copy.


III. PluginRegistry: The Runtime Core

src/plugins/registry.ts (1212 lines) maintains runtime maps of all registered capabilities:

// PluginRegistry internals (conceptual)
class PluginRegistry {
  channels: Map<string, ChannelPlugin>;
  providers: Map<string, ProviderPlugin>;
  tools: Map<string, ToolFactory>;
  hooks: Map<HookName, HookHandler[]>;
}

When a channel plugin registers itself via api.channel.register(plugin), the registry stores it and the channel becomes available for routing.


IV. Hook System

Plugins can register hooks to intercept the agent lifecycle:

Hook NameWhenUse Case
before-agent-replyBefore AI generates responseContent filtering, adding context
before-tool-callBefore a tool executesSecurity checks, logging
after-agent-replyAfter AI response is sentAnalytics, post-processing
after-tool-callAfter tool executionResult transformation
// Registering a hook in a Capability plugin
api.hooks.on('before-agent-reply', async (context) => {
  // Inject retrieved memories into the context
  const memories = await memoryStore.search(context.lastMessage);
  context.systemPromptAppend(formatMemories(memories));
});

V. Memory Slot Singleton Constraint

An important architectural detail: certain "singleton" plugins (like memory) use a special registry slot that can only have one implementation active at a time. If two memory plugins are both enabled, only the first loaded wins.

This is by design — to prevent conflicting memory implementations from interfering with each other.


Key Source Files

FileSizeRole
src/plugins/discovery.ts924 linesPlugin discovery logic
src/plugins/loader.ts1946 linesPlugin loading via jiti
src/plugins/registry.ts1212 linesRuntime plugin registry
src/plugins/types.ts2739 linesAll plugin type definitions
src/plugin-sdk/core.ts21KBSDK public API entry

Summary

  1. 6-phase lifecycle: discovery → manifest → activation decision → jiti load → registry → activation.
  2. jiti enables TypeScript at runtime: no build step required for plugins.
  3. SDK Alias ensures plugins and core share the same module instances — preventing subtle bugs.
  4. PluginRegistry is the runtime map of all capabilities.
  5. Hooks allow plugins to intercept the agent lifecycle at 4 points.

← Gateway Core | → Module Boundaries & SDK Contract