Use a plugin
config is typed by the plugin (definePlugin<MyConfig>()): an unknown option
is a compile error, and a config declared with the plugin’s config interface can
be passed as is.
bot.plug(plugin, config?) returns an unplug function (not the bot, so
plug() calls do not chain). Calling it first runs the plugin’s
uninstall(ctx), if it has one, and then reverts, newest first, what the
plugin registered through ctx.bot:
- commands (
command()), matchers (when(),onMessage(),onType()), conversations and flow triggers (conversation(),use(flow)); - hooks (
before(),after(),onError(),hook()); - nested plugins (
ctx.bot.plug()); - flows registered at runtime with
ctx.bot.registerFlow(), or created (not replaced) withctx.bot.replaceFlow(); - every
ctx.onDispose(fn)callback.
ctx.bot.on() /
once()), injected tools (ctx.bot.provide() / ctx.tools), locale messages
(ctx.bot.locale()), scheduled jobs (schedule(), every(), delay()),
idle handlers (onIdle()), a flow that replaceFlow() overwrote (it is not
restored), handoff state, store writes, or timers and connections of your own.
Undo those in ctx.onDispose() (or uninstall()). Calling unplug twice is a
no-op. If install() throws synchronously, whatever it registered so far is
rolled back before the error reaches you; if it returns a promise that
rejects, the rejection goes to the 'error' event and nothing is rolled back.
Dependencies between plugins (requires)
A plugin can list the plugins it needs by name. bot.plug() checks them
before running install() and throws a descriptive error when one is not
plugged:
inbox later does not unplug
crm. A plugin nested inside another one (ctx.bot.plug() from install())
may require its parent.
One instance per name (multiple)
bot.plug() refuses a plugin whose name is already plugged on the bot, so a
plugin registered twice by mistake never runs its handlers twice:
multiple: true in its spec (every instance must set it). Each instance is
then unplugged on its own, and any of them satisfies requires.
Built-in: slashCommands
Config:{ greeting?: string; unknownMessage?: string }. The command
prefix is a bot option (new Bot({ commandPrefix })), not a plugin option.
The replies are built-in texts in the bot’s
language.
Unknown
/commands get an automatic “unknown command” reply
(unknownMessage).
Create a plugin
Simple plugin
Configurable plugin with cleanup
Plugin structure
definePlugin(spec) returns the spec unchanged, typed. A plugin can also be a
function (config) => spec.
The
ctx that install receives:
Share tools (dependency injection)
Share tools between handlers, hooks and plugins without globals.The problem it solves
How it works
1. Register a tool
bot.provide(name, value) returns this, so calls can be chained:
2. Type it
ctx.tools is typed by the DefaultTools interface, which is empty until you
augment it (declaration merging). Declare each tool once, next to the code that
provides it, and every handler sees it typed:
@botmesh/ai types
ctx.tools.ai, and @botmesh/nlu types ctx.tools.nlu. In JavaScript there is
nothing to declare.
3. Use it in any handler
4. Available in hooks and plugins too
install(ctx) also receives ctx.tools, so plugins can consume
tools registered elsewhere (or register their own with ctx.bot.provide).
Outside handlers (a scheduled task, a webhook), read them from
bot.injectTools.
Remove a tool
bot.unprovide(name) removes a tool and returns true if it existed. Unplugging
a plugin does not undo its provide() calls, so do it in ctx.onDispose():
Multiple tools
DefaultTools as shown above.
Plugin packages
BesidesslashCommandsPlugin, these packages ship ready-made plugins:
- AI —
@botmesh/ai: LLM replies (aiPlugin), with tools, memory and RAG and streaming. - Voice —
@botmesh/voice(voicePlugin): speech-to-text for inbound voice notes and text-to-speech replies. - Integrations —
@botmesh/chatwoot,@botmesh/tickets,@botmesh/moderation,@botmesh/dialogflowand@botmesh/nlu(local intent matching). - Tracing —
@botmesh/otel(otelMetricsPlugin):bot.metrics()and the bot’s spans in OpenTelemetry.
@botmesh/rag (retrieval for aiPlugin) is
described with memory and RAG.
Next steps
- Scheduler — scheduled tasks, which read tools from
bot.injectTools - Example: rate limiter — full plugin