Extension Loading
Extensions are OpenClaw's pluggable modules, including channel extensions (e.g., Microsoft Teams, Matrix) and feature extensions (e.g., LanceDB, OpenTelemetry). This chapter covers how they are discovered, loaded, and registered at runtime.
Loading Flow
Source: src/plugins/loader.ts
Step-by-Step
- Scan -- Traverse all subdirectories under
extensions/ - Parse manifest -- Read each extension's
package.jsonor dedicated manifest file - Schema validation -- Ensure the manifest contains required fields (id, name, version, entry)
- Dynamic import -- Use
import()to load the plugin's entry module - Initialize -- Call the plugin's exported init function, passing in
OpenClawPluginApi - Register -- The plugin registers tools, channels, hooks, etc. through the API
Manifest Parsing
Every extension must have a manifest declaring plugin metadata and entry point:
// Plugin manifest (simplified)
interface PluginManifest {
id: string; // Unique identifier
name: string; // Display name
version: string; // Semver version
entry: string; // Entry module path
kind: "extension"; // Plugin kind
dependencies?: string[]; // Required dependencies
}Dynamic Import
Source: src/plugins/loader.ts
Plugins are dynamically imported using the jiti resolver, which supports loading TypeScript source files directly:
// Dynamic import with jiti resolver (simplified)
// Supports loading:
// - Compiled JS from dist/
// - TypeScript source directly
// - ESM and CJS modulesTIP
jiti is a runtime TypeScript/ESM transformation tool that lets OpenClaw load TypeScript plugin source code directly, without requiring pre-compilation.
Extension Directory Structure
A typical extension structure:
extensions/msteams/
├── package.json # Manifest + dependencies
├── src/
│ ├── index.ts # Entry point (exports init function)
│ ├── plugin.ts # ChannelPlugin implementation
│ ├── send.ts # Message sending
│ ├── receive.ts # Message receiving
│ └── auth.ts # Authentication
└── tsconfig.jsonRegistry Recording
Source: src/plugins/registry.ts
Once loaded, each plugin is recorded as a PluginRecord:
interface PluginRecord {
id: string;
name: string;
version: string;
kind: "extension" | "skill" | "core";
origin: string; // Source path
manifest: PluginManifest;
}The registry manages all registrations by type:
Error Handling
Error handling strategy during the loading process:
| Error Type | Handling |
|---|---|
| Missing manifest | Skip, log warning |
| Schema validation failure | Skip, log detailed error |
| Module import failure | Skip, log error and stack trace |
| Init function throws | Mark as failed, do not register |
A single plugin's loading failure does not affect other plugins or Gateway startup.
Summary
- Extensions are loaded through a manifest + dynamic import pattern
- jiti enables direct loading of TypeScript source code
- Loading flow: scan -> parse -> validate -> import -> initialize -> register
- Error isolation -- a single plugin failure does not affect other plugins or system startup
- All registrations are tracked through
PluginRecordin the registry
Next: Hooks Mechanism