Plugin SDK
The OpenBot Plugin SDK (@meetopenbot/plugin-sdk) is the supported way to define plugins: event handlers, typed payloads, storage access, tool metadata, and client UI widgets. This page tracks the package API; for walk-throughs, see Your First Channel, Your First Agent, OpenBot.one client, Plugin Development, and Your First Plugin.
Installation
Section titled “Installation”npm install @meetopenbot/plugin-sdkThe SDK declares peer dependencies on melony and zod. Ensure compatible versions are installed in your plugin project so TypeScript resolves types correctly (your package manager usually surfaces this when peers are missing).
The package root re-exports z from Zod alongside SDK modules, so you can import { z, definePlugin } from '@meetopenbot/plugin-sdk' when you want schema helpers without a separate Zod import.
How plugins fit together
Section titled “How plugins fit together”Plugins run on OpenBot’s event bus Melony exposes to the host. You only need SDK types — you typically do not import Melony types directly in plugin code.
- Registration — The host loads your module and invokes
factory(context)with aPluginContext. - Subscription —
factoryreturns a Melony plugin that uses the builder (PluginBuilder) to callbuilder.on(eventType, handler). - Processing — Handlers run as async generators; they inspect the event and may call APIs or
context.storage. - Publication — Handlers
yieldfurther bus events (for exampleagent:output,client:ui:widget).
Define a plugin
Section titled “Define a plugin”Use definePlugin for inference-friendly typing over a plain Plugin or PluginModule object.
Plugin, PluginModule, and PluginRef
Section titled “Plugin, PluginModule, and PluginRef”Plugin is the shape the OpenBot host expects:
| Field | Purpose |
|---|---|
id | Stable plugin identifier. |
name / description | Human-readable metadata; image is optional branding. |
configSchema? | JSON-Schema-shaped description of plugin config validated from AGENT.md. See ConfigSchema. |
toolDefinitions? | Named tools merged for runtime plugins — see ToolDefinition. |
factory | (context: PluginContext) => PluginFactory — wires handlers onto the bus. |
PluginModule is the same contract without id. Publish community plugins as npm packages: the host assigns id from the package name. Local / first-party plugins in AGENT.md typically use Plugin including id.
PluginRef (used in agent definitions) ties a plugin id to optional config:
interface PluginRef { id: string; config?: Record<string, unknown>;}Plugin context
Section titled “Plugin context”When the host calls factory, it passes PluginContext:
| Field | Purpose |
|---|---|
agentId | Agent this plugin instance is wired for. |
agentDetails | Full AgentDetails (Agent plus instructions and pluginRefs). |
config | Effective plugin configuration (from refs / host). |
storage | Storage facade for channels, threads, agents, variables, files, memories, specs, and persisted events. |
tools | Tool surface exposed to handlers (merged tool implementations; shape is host-dependent). |
Runtime wiring (PluginFactory, PluginBuilder, PluginHandlerContext)
Section titled “Runtime wiring (PluginFactory, PluginBuilder, PluginHandlerContext)”From the SDK’s perspective:
PluginFactoryaliasesMelonyPlugin— whatever you return fromfactoryregisters with Melony’s runtime.PluginBuilderaliasesMelonyBuilder— fluent API forbuilder.on(...).PluginHandlerContextaliasesMelony’sRuntimeContext— available inside handlers when the runtime provides it.
The host defines which Melony scheduler and state extensions are attached; handlers often receive OpenBot-aware state — see OpenBotState.
Config schema and tools
Section titled “Config schema and tools”ConfigSchema describes AGENT.md plugin configuration: an object schema with typed properties (string | number | boolean | integer), optional enum, numeric bounds, descriptions, defaults, and format: 'password' \| 'url' \| 'email'.
ToolDefinition advertises tools for other plugins (for example LLM runtimes):
interface ToolDefinition { description: string; inputSchema: unknown;}toolDefinitions is a record of ToolDefinition keyed by tool name.
Events
Section titled “Events”Plugins communicate by raising and handling events. All SDK events extend a BaseEvent: optional id, type string, optional meta, plus type-specific data.
EventMeta
Section titled “EventMeta”Common correlation fields carried on meta (additional keys allowed):
| Field | Role |
|---|---|
agentId? | Target or source agent where applicable. |
threadId? | Conversation thread. |
toolCallId? | Correlate tool calls and results. |
In OpenBot, user-visible messaging is thread-scoped only: HTTP integrations must provide channelId and threadId together (Your First Channel). Types often keep threadId optional for portability — when handling agent:invoke for a normal chat turn, forward threadId into agentOutput / uiWidget so replies stay on the same thread.
Core plugin-facing event types
Section titled “Core plugin-facing event types”| Type string | TypeScript name | Notes |
|---|---|---|
agent:invoke | AgentInvokeEvent | User / agent message ingress. data.content is the payload; optional role (user, assistant, system). |
agent:output | AgentOutputEvent | Assistant-style reply to the client. meta.agentId is required. |
client:ui:widget | UIWidgetEvent | Render a widget; data is RenderUIWidgetData (UI widgets). meta.agentId required. |
client:ui:widget:response | UIWidgetResponseEvent | User acted on a widget (widgetId, actionId, optional values / metadata). |
Tool action events
Section titled “Tool action events”Tool invocations use dynamic event types:
action:${toolName}—ToolActionEvent: runtime requests a tool;datais tool-specific.action:${toolName}:result—ToolResultEvent: handler finished;datais the result payload.
Use toolResult() to construct a result event with consistent typing.
Unions you will see in types
Section titled “Unions you will see in types”PluginEvent— Narrow union of invoke, output, widget, widget-response, and tool events above. The production bus may carry additional shapes;OpenBotEventisPluginEventor anyBaseEventwith arbitrarydata.- Handlers written against
PluginEventcover the documented authoring paths; fallback toOpenBotEventwhen integrating experimental host events.
OpenBot runtime state
Section titled “OpenBot runtime state”OpenBotState captures host-provided correlation on Melony handler contexts:
- Identifiers:
agentId,runId,channelId,threadId(conversation slices on OpenBot includethreadId) - Hydrated snapshots: optional
agentDetails,channelDetails,threadDetails triggerEvent?— the bus event that started the slice of work (typed asOpenBotEvent)shortTermMessages?— conversational scratch space asShortTermMessage[](system|user|assistant|toolvariants; assistant entries may attachtoolCalls, tool turns includetoolCallIdandtoolName)
Exact population is host/runtime-dependent; guard optional fields before use.
Helpers
Section titled “Helpers”| Helper | Purpose |
|---|---|
shouldHandleInvoke(event, agentId) | Returns whether this plugin instance should react to agent:invoke for the given agentId, respecting optional routing in event.data.agentId. |
agentOutput({ agentId, content, threadId?, meta? }) | Builds a typed AgentOutputEvent. |
toolResult(toolName, request, data) | Builds action:${toolName}:result, forwarding meta from the originating request-like object. |
uiWidget({ agentId, widget, threadId?, meta? }) | Builds client:ui:widget wrapping RenderUIWidgetData (see UI widgets). |
withMeta(source, event) | Merges source.meta into another event clone for correlation propagation. |
Storage
Section titled “Storage”Access persistence through PluginContext.storage.
Agents, channels, and threads
Section titled “Agents, channels, and threads”Typed records include Agent, AgentDetails (extends Agent with instructions + pluginRefs), Channel, Thread, plus ChannelDetails and ThreadDetails (include spec and state blobs the host interprets).
Storage methods (all async):
Channels & threads
getChannels,createChannel,getChannelDetailscreateThread,getThreads,getThreadDetailspatchChannelState,patchThreadState,patchChannelSpec,patchThreadSpec
Agents
getAgents,getAgentDetails,createAgent,updateAgent,deleteAgent
Events
getEvents— persisted bus history for scope (channelId;threadIdrequired for thread transcripts in OpenBot)
Variables & files
getVariables,createVariable,deleteVariablelistFiles,readFile— workspace paths scoped bychannelId
Memory
appendMemory,listMemories,deleteMemory,updateMemoryMemoryRecord:id,scope,content, optionaltags[], timestampsListMemoriesArgs: filter byscopes,query,tag,limit
Refer to the SDK source storage.ts for exact argument and return types.
UI widgets and uiWidget
Section titled “UI widgets and uiWidget”Widgets are described by UIWidgetSpec union members; when emitting you usually pass RenderUIWidgetData, which is the spec types with widgetId optional (the client may assign one).
Shared shape (UIWidgetBase)
Section titled “Shared shape (UIWidgetBase)”All widgets extend UIWidgetBase:
widgetId, optionaltitle/descriptionmedia?—UIMediaItem(image|video|audio|file) with URLs and optional thumbnailsactions?— globalUIWidgetActionbuttonsstate:open|submitted|cancelled|errordisplay:expanded|collapsedsize:small|medium|large|fullmetadatabag for arbitrary client hints
Widget kinds
Section titled “Widget kinds”kind | Extra fields |
|---|---|
message | body? optional primary text alongside title/description |
choice | actions required (at least one) |
form | fields: UIWidgetField[], optional submitLabel |
list | items: UIWidgetListItem[] |
media | items: UIMediaItem[]; layout?: single | grid | carousel |
Actions, fields, and list rows
Section titled “Actions, fields, and list rows”UIWidgetAction: id, label, optional value, variant?: 'primary' | 'secondary' | 'danger', disabled?
UIWidgetField (forms): id, label, type:
text | textarea | number | boolean | select | multiselect | date
plus optional description, placeholder, required, options[], defaultValue.
UIWidgetListItem: id, label, optional description, image, badge, actions[], status (pending | in_progress | done | error | cancelled), metadata.
Example: minimal reply plugin
Section titled “Example: minimal reply plugin”import { definePlugin, shouldHandleInvoke, agentOutput } from '@meetopenbot/plugin-sdk';
export default definePlugin({ id: 'hello-world', name: 'Hello World', description: 'Responds with Hello World', factory: (context) => (builder) => { builder.on('agent:invoke', async function* (event) { if (shouldHandleInvoke(event, context.agentId)) { yield agentOutput({ agentId: context.agentId, content: 'Hello, World!', threadId: event.meta?.threadId, }); } }); },});Further reading
Section titled “Further reading”- Source & issues:
meetopenbot/plugin-sdkon GitHub - Architecture: Architecture — how OpenBot relates to Melony and the host
- Plugin overview: Plugin Development