# Plugin Architecture OpenClaw employs a multi-tiered plugin architecture designed for high extensibility across model providers, messaging channels, and agent capabilities. The system is built on a massive type system that allows plugins to hook into almost every stage of the AI lifecycle, from initial message receipt to final inference and tool execution. *** ## Plugin Type System The core of the architecture is defined in `src/plugins/types.ts`. It provides a unified contract for different plugin flavors: `ProviderPlugin`, `WebSearchProviderPlugin`, `SpeechProviderPlugin`, and the general `OpenClawPluginDefinition`. ### Provider Plugin Hooks The `ProviderPlugin` is the most complex type, featuring over 40 hooks for deep integration with LLM providers. These are categorized into distinct functional areas: * **Catalog & Discovery**: Hooks like `catalog` and `augmentModelCatalog` allow plugins to publish model definitions dynamically. * **Auth & Credentials**: `prepareRuntimeAuth` and `resolveSyntheticAuth` handle the exchange of source credentials for runtime tokens. * **Model Resolution**: `resolveDynamicModel` and `normalizeResolvedModel` provide last-mile control over how model IDs are mapped to API endpoints. * **Streaming & Transport**: `createStreamFn` and `wrapStreamFn` allow plugins to replace or wrap the default transport layer with provider-specific logic. * **Usage & Billing**: `resolveUsageAuth` and `fetchUsageSnapshot` enable integrated quota tracking. ```typescript export type ProviderPlugin = { id: string; label: string; auth: ProviderAuthMethod[]; catalog?: ProviderPluginCatalog; resolveDynamicModel?: (ctx: ProviderResolveDynamicModelContext) => ProviderRuntimeModel | null | undefined; prepareRuntimeAuth?: (ctx: ProviderPrepareRuntimeAuthContext) => Promise; createStreamFn?: (ctx: ProviderCreateStreamFnContext) => StreamFn | null | undefined; fetchUsageSnapshot?: (ctx: ProviderFetchUsageSnapshotContext) => Promise; // ... and 30+ more hooks }; ``` *** ## Tool Factories Plugins expose agent-level capabilities through `OpenClawPluginToolFactory`. This pattern allows tools to be instantiated with a trusted execution context that includes workspace paths, session identifiers, and security boundaries. ```typescript export type OpenClawPluginToolContext = { config?: OpenClawConfig; workspaceDir?: string; agentId?: string; sessionId?: string; deliveryContext?: DeliveryContext; senderIsOwner?: boolean; }; export type OpenClawPluginToolFactory = ( ctx: OpenClawPluginToolContext, ) => AnyAgentTool | AnyAgentTool[] | null | undefined; ``` *** ## Config Schema Plugins declare their configuration requirements using a Zod-like validation system. The `OpenClawPluginConfigSchema` allows the host to validate plugin settings, generate UI forms, and provide help text without loading the plugin's full implementation. ```typescript export type OpenClawPluginConfigSchema = { safeParse?: (value: unknown) => { success: boolean; data?: unknown; error?: { issues?: Array<{ path: Array; message: string }> }; }; uiHints?: Record; jsonSchema?: Record; }; ``` *** ## Channel Handlers Interactive platform support (Telegram, Discord, Slack) is handled via specialized interactive handlers. These allow plugins to respond to platform-specific events like button clicks or modal submissions through a unified context. ```typescript export type PluginInteractiveTelegramHandlerContext = { channel: "telegram"; callback: { data: string; namespace: string; payload: string }; respond: { reply: (params: { text: string; buttons?: PluginInteractiveButtons }) => Promise; editMessage: (params: { text: string; buttons?: PluginInteractiveButtons }) => Promise; }; }; ``` *** ## Conversation Bindings Plugins can request "Conversation Bindings" to take over the message flow for a specific thread or user. This is used for interactive wizards or stateful interactions that bypass the general LLM dispatcher. * `requestConversationBinding`: Attaches a plugin to the current conversation. * `detachConversationBinding`: Releases the conversation back to the general agent. * `getCurrentConversationBinding`: Checks if the conversation is currently owned by a plugin. *** ## Plugin Lifecycle The lifecycle is managed through several stages, primarily defined in the `OpenClawPluginApi` provided during registration: 1. **Discovery**: Plugins are found in `bundled`, `global`, or `workspace` directories. 2. **Registration**: `register(api)` is called. The plugin registers its tools, hooks, and services. 3. **Activation**: `activate(api)` is called when the plugin is enabled and ready for service. 4. **Runtime**: The plugin responds to lifecycle hooks such as `before_agent_start`, `llm_input`, and `agent_end`. 5. **Uninstallation**: Managed via the `ClawHub` registry or local file deletion. *** ## Specialized Plugin Types ### Web Search Providers `WebSearchProviderPlugin` defines how the agent interacts with search engines. It requires specific credential resolution logic and a `createTool` factory that returns a standardized search tool definition. ### Speech Providers `SpeechProviderPlugin` handles text-to-speech (TTS) capabilities. It includes hooks for voice listing, directive parsing (e.g., for custom SSML or tokens), and synthesis for both standard and telephony audio formats. *** ## Relevance to Luna While Luna does not require the full overhead of OpenClaw's provider ecosystem, it can adopt several key patterns: * **Manifest-Based Registration**: Luna could use a simplified version of `OpenClawPluginDefinition` to allow for [[Skills]] that declare their dependencies and config schemas upfront. * **Hook Interceptors**: Adopting the `before_prompt_build` and `llm_output` hook patterns would allow Luna's [[Core]] to support middleware for logging, safety filtering, or memory injection. * **Trusted Contexts**: Passing a `ToolContext` similar to OpenClaw's would ensure that Luna's tools have consistent access to session and workspace state without relying on global variables.