@botmesh/core 2.0.0, the other public packages 1.0.0)
freezes the public API. The beta (@botmesh/core 2.0.0-beta, the other public
packages 1.0.0-beta) already ships it, so these pages also cover upgrading from
an alpha to the beta. Coming from a 2.0 alpha, most code keeps running:
renamed APIs keep a @deprecated alias (your editor strikes them through),
and the few hard breaks are marked Breaking on these pages.
Upgrade checklist
Work through it in order; each step links to the details.- Upgrade every
@botmesh/*package together, to the same release line. Mixing an old core with newer channel packages is not supported. - Dependencies (details): install ffmpeg if your Baileys
bot sends voice notes, move Gemini bots to
@google/genai, check theredispeer range and the Graph API version, and update dashboards that readbotwa_*metrics. - Run the type checker (
tsc --noEmit) and fix what it reports (details below): imports that moved to@botmesh/core/spi,/testingor/internal,@botmesh/ai/testing,@botmesh/baileys/internaland the removedRedisStoreAPIs. - Replace the deprecated names (table):
ctx.replyinstead ofctx.say,flowinstead ofaddKeyword,bot.send,bot.plugfor plugins,bot.trigger,onType… They still work, but they are removed in v3. - Review the behaviour changes (details). The two most
bots need:
lang: 'es'if your bot speaks Spanish (built-in texts are now English), andmaxAttempts: Nwhere a wait setretries: N. - Read the notes for your channels and plugins (details).
- Run your tests and start the bot once with a
bot.on('error', …)listener. Watch for the one-timeretrieswarning and forConfigValidationError, which now names any invalid duration option.
Package entries
The main entry (@botmesh/core) now exports only what an application needs
(372 names, down from 512). Everything else moved to a subpath:
Breaking: the names below are no longer exported by
@botmesh/core.
Change the import path; the values are the same.
MessagingFeature and MessagingSupport (ctx.supports()),
ResolvedWaitOptions (ConversationControlsBase.waitAny()) and JsonValue (the
shape of SerializedMediaRef.data and of the journal records; @botmesh/core/spi
exports it too). Import them from @botmesh/core, not from /internal or /testing.
The golden-transcript helpers (recordTranscript, RecordingProvider,
redactTranscript, transcriptFromStore, parseTranscript…) are still
re-exported by @botmesh/core, but deprecated: import them from
@botmesh/core/testing.
One name per concept
These aliases keep working and are marked@deprecated:
bot.enterConversation(handler, ctx) with an inline handler is not
deprecated: it is the way to start a conversation that is not registered.
BotConfig.storeConfig now has a type (file, maxMessagesPerChat,
autoSaveInterval), and the bot logs a warning when it is set. Setting
durability.backend and durability.journal to two different backends
throws a ConfigValidationError.
Plugins: one instance per name
Breaking:bot.plug() throws when a plugin with the same name is already
plugged. Before, the second instance was installed too and both ran their
handlers. To plug a plugin again, call the unplug function first. A plugin
built to run several times sets multiple: true in its spec:
Durations
Every duration option ofBotConfig takes a duration string or a number of
milliseconds, including all *Ms options. typing.minMs, typing.maxMs and
the durability backoff.baseMs / backoff.capMs accepted only numbers before;
they now accept '800ms', '1s' and so on.
Breaking (stricter validation): new Bot() now parses every duration
option and throws a ConfigValidationError naming it
(err.field === 'coordination.turnWaitMs') when a value cannot be read. Before,
some invalid values only failed later, with a message that did not name the
option, and null was accepted for a few of them.
Logger
TheLoggerLike contract is unchanged, and its calling convention is now
documented: the packages call logger.warn('message') or
logger.warn({ err, ...context }, 'message'), as pino does. See
Logging.
Channel and store packages
The public channel and store packages follow the same rule: one name per concept, the old one kept as a@deprecated alias until v3.
Every provider’s low-level error mapper is now named
to<Channel>Error; the channels
that were private during the alphas got the same renames (toDiscordError,
toEmailError, and toTikTokError on the still-private TikTok channel), and
@botmesh/messenger and @botmesh/instagram, public from the beta, ship only
toMessengerError and toInstagramError. The behaviour changes of these
packages (Telegram poll votes, Discord edits, SQLite schema versioning) are in
Upgrade notes: channels & plugins.
@botmesh/baileys: entries and native types
The main entry keeps what an application needs (BaileysProvider, its options,
toJid, isEditedMessage, RateLimiter, the PollStore types). The rest moved
to @botmesh/baileys/internal, which has no semver guarantee; the old names stay on
the main entry as @deprecated aliases until v3.
-
Breaking:
toInboundandtoOutboundare no longer exported by@botmesh/baileys: their signatures use Baileys’ own types. Import them from@botmesh/baileys/internal. -
Breaking (types only): Baileys’ native values are opaque.
provider.socketisBaileysSocket | null,provider.authisBaileysAuthState, and theraw/nativeof messages, refs and send results areBaileysNativeMessage; the three areunknownaliases. Nothing changes at runtime. UsesocketOf(provider)andauthStateOf(provider)from@botmesh/baileys/internalfor typed handles, or cast with your own@whiskeysockets/baileysimport: -
The JID predicates (
isGroup,isPn,isLid,isBroadcast,isNewsletter) returnbooleaninstead ofboolean | undefined(undefinedis nowfalse). -
pairingCodeis renamedpairingPhoneNumber(the value was always the phone number); passing both with different numbers throws aTypeError.
@botmesh/redis-store
Breaking: the cluster key-layout internals moved to a new subpath without a semver
promise, @botmesh/redis-store/internal. The layout option, the RedisKeyLayout
type, migrateToClusterLayout(), inspectKeyspace(), isRedisClusterClient() and
withClusterRetry() stay on the main entry.
Breaking: two deprecated APIs were removed.
@botmesh/ai entries
@botmesh/ai got the same split as core: 135 names on the main entry before, 98 after.
MockAIProvider, MockOptions and MockTurn are still re-exported by @botmesh/ai, but
deprecated: import them from @botmesh/ai/testing.
Breaking: the names below are no longer exported by @botmesh/ai. Change the import
path; the values are the same.
The client contracts did not change shape: their request and response types are now written
inline, so a fake client typed
AnthropicLike (or OpenAILike, GeminiLike…) compiles as
before. To name a response shape, derive it from the contract or import it from /internal:
PRESETS, DEFAULT_YES, DEFAULT_NO, AI_MESSAGES and
AI_LOCALES are deep-frozen, and typed readonly. Changing them used to change the defaults
of every bot in the process; now it throws a TypeError in strict mode. Extend a copy:
toolConfirm.yes/.no accept a readonly string[]).
Provider constructors now take an AIProviderConfig (model, maxTokens, effort,
thinking, baseUrl), the part of AIConfig a provider reads. Passing a whole AIConfig
held in a variable still compiles, but an object literal with plugin fields now fails
TypeScript’s excess-property check (TS2353), because providers never read them:
GenerateInput.onStreamEnd receives a named AIStreamEndInfo; its contract
(once, after the last chunk, only on a normal end) is in
Upgrade notes: channels & plugins.
HTTP, Graph and scaffolder packages
@botmesh/http: closed SecureFetchOptions. The options of secureFetch and
fetchBuffer no longer extend RequestInit. They hold the request fields the
client honours (method, headers, body, signal) and its policy
(allowHttp, allowPrivateHosts, timeoutMs, maxBytes, maxRedirects,
resolver, fetch).
- Breaking (types only):
redirectis gone. It was always overridden with'manual', because the client follows redirects itself and validates each hop; usemaxRedirects(0refuses any redirect). - The other
RequestInitfields (cache,keepalive,integrity…) still type-check through a@deprecatedoverload (LegacySecureFetchOptions) and are still forwarded tofetch. They stop being accepted at stable. - Streaming uploads need no extra field: with a
ReadableStream(or async iterable)body, the client sets theduplex: 'half'that fetch requires. Drop an explicitduplexalong with the otherRequestInitfields. - A variable typed
SecureFetchOptionsno longer accepts browser fields (mode,credentials,window…). Remove them. resolvePublicHostis nowresolveHost: it resolves any host and leaves the verdict toassertSafeTarget. The old name stays as a@deprecatedalias.
@botmesh/graph-core: one options object. checkGraphSignature takes
{ body, headers, appSecret, logger }. The positional form keeps working as a
@deprecated overload with the same results. graph-core is a public package and
is covered by semver like the others: a breaking change in it is a major bump.
@botmesh/meta: session-window plumbing is experimental. What an app
configures and queries stays public and covered by semver:
SessionWindowManager (constructor, forScope, state, isOpen, expiresAt,
pending, clearPending), SessionWindowOptions, the SessionWindowStore
contract and its MemorySessionWindowStore, the report and reopen types,
SessionWindowReopenedError and the constants. The hooks the provider drives
(send, noteInbound, noteFailedStatus) and their RawSend type are now
@experimental: they may change in a minor release. Nothing changes at runtime.
See API stability.
create-botmesh: internals in /internal. The main entry is the
scaffolder: scaffold, sanitizeName, getTemplate and TEMPLATE_NAMES.
TEMPLATES, agentsMd, findBotMeshMonorepo and AgentsMdInfo moved to
create-botmesh/internal (no semver); the main entry re-exports them as
@deprecated until stable. The CLI does not change.
Sandbox API token
@botmesh/sandbox is private, but scripts that drive its web API change:
- Breaking:
POST /api/<channel>/send|fault|status|resetnow needs the per-run token in thex-sandbox-tokenheader. Read it fromsandbox.token(or the CLI output), or fix it withstartSandbox({ token })/--token. - Requests whose
Hostis not loopback, or whoseOriginis another site, get a 403. PassallowedHosts/--allow-hostwhen the hub is reached through another name on purpose. - Bad input is now a 4xx: unknown fields, wrong types and a body that is not a
JSON object are refused, and
toUserInput()throwsSandboxInputError.