Saltar al contenido principal
La línea estable (@botmesh/core 2.0.0, los demás paquetes públicos 1.0.0) congela la API pública. La beta (@botmesh/core 2.0.0-beta, los demás paquetes públicos 1.0.0-beta) ya la incluye, así que estas páginas también sirven para pasar de una alpha a la beta. Si vienes de una alpha de 2.0, casi todo tu código sigue funcionando: las APIs renombradas conservan un alias @deprecated (tu editor las tacha), y las pocas rupturas reales están marcadas como Ruptura en estas páginas.

Checklist de actualización

Síguelo en orden; cada paso enlaza a los detalles.
  1. Actualiza todos los paquetes @botmesh/* a la vez, a la misma línea de versiones. No se soporta mezclar un core antiguo con paquetes de canal nuevos.
  2. Dependencias (detalles): instala ffmpeg si tu bot de Baileys envía notas de voz, pasa los bots de Gemini a @google/genai, revisa el rango del peer redis y la versión de la Graph API, y actualiza los dashboards que leen métricas botwa_*.
  3. Pasa el verificador de tipos (tsc --noEmit) y corrige lo que reporte (detalles abajo): imports que se movieron a @botmesh/core/spi, /testing o /internal, @botmesh/ai/testing, @botmesh/baileys/internal y las APIs eliminadas de RedisStore.
  4. Reemplaza los nombres obsoletos (tabla): ctx.reply en vez de ctx.say, flow en vez de addKeyword, bot.send, bot.plug para plugins, bot.trigger, onType… Siguen funcionando, pero se eliminan en la v3.
  5. Revisa los cambios de comportamiento (detalles). Los dos que necesitan casi todos los bots: lang: 'es' si tu bot habla español (los textos internos ahora están en inglés) y maxAttempts: N donde una espera usaba retries: N.
  6. Lee las notas de tus canales y plugins (detalles).
  7. Pasa tus tests y arranca el bot una vez con un listener bot.on('error', …). Atento al aviso único de retries y a ConfigValidationError, que ahora nombra cualquier opción de duración inválida.

Entradas del paquete

La entrada principal (@botmesh/core) ahora solo exporta lo que necesita una aplicación (372 nombres, antes 512). Todo lo demás pasó a un subpath: Ruptura: @botmesh/core ya no exporta los nombres de abajo. Cambia la ruta del import; los valores son los mismos.
Algunos tipos del motor que se movieron siguen apareciendo en firmas que usa una aplicación, así que la entrada principal los conserva: MessagingFeature y MessagingSupport (ctx.supports()), ResolvedWaitOptions (ConversationControlsBase.waitAny()) y JsonValue (la forma de SerializedMediaRef.data y de los registros de los journals; también lo exporta @botmesh/core/spi). Impórtalos de @botmesh/core, no de /internal ni de /testing. @botmesh/core sigue reexportando las funciones de transcripciones doradas (recordTranscript, RecordingProvider, redactTranscript, transcriptFromStore, parseTranscript…), pero están obsoletas: impórtalas de @botmesh/core/testing.

Un nombre por concepto

Estos alias siguen funcionando y están marcados como @deprecated: bot.enterConversation(handler, ctx) con un handler en línea no está obsoleto: es la forma de arrancar una conversación que no está registrada. BotConfig.storeConfig ahora tiene tipo (file, maxMessagesPerChat, autoSaveInterval) y el bot avisa en el log cuando se usa. Poner durability.backend y durability.journal con dos backends distintos lanza un ConfigValidationError.

Plugins: una instancia por nombre

Ruptura: bot.plug() lanza si ya hay enchufado un plugin con el mismo name. Antes se instalaba también la segunda instancia y las dos ejecutaban sus handlers. Para volver a enchufar un plugin, llama antes a su función unplug. Un plugin pensado para correr varias veces pone multiple: true en su spec:

Duraciones

Toda opción de duración de BotConfig acepta un texto de duración o un número de milisegundos, incluidas todas las opciones *Ms. typing.minMs, typing.maxMs y backoff.baseMs / backoff.capMs de la durabilidad solo aceptaban números; ahora aceptan '800ms', '1s', etc. Ruptura (validación más estricta): new Bot() ahora interpreta cada opción de duración y lanza un ConfigValidationError que la nombra (err.field === 'coordination.turnWaitMs') cuando un valor no se puede leer. Antes algunos valores inválidos fallaban más tarde, con un mensaje que no nombraba la opción, y unas pocas aceptaban null.

Logger

El contrato LoggerLike no cambia, y su convención de llamada queda documentada: los paquetes llaman logger.warn('mensaje') o logger.warn({ err, ...contexto }, 'mensaje'), como pino. Ver Logs.

Paquetes de canales y stores

Los paquetes públicos de canales y stores siguen la misma regla: un nombre por concepto, con el antiguo como alias @deprecated hasta v3. El mapeador de errores de bajo nivel de cada provider se llama ahora to<Canal>Error; los canales que eran privados durante las alphas tienen los mismos cambios de nombre (toDiscordError, toEmailError y toTikTokError en el canal de TikTok, que sigue siendo privado), y @botmesh/messenger y @botmesh/instagram, públicos desde la beta, solo traen toMessengerError y toInstagramError. Los cambios de comportamiento de estos paquetes (votos de encuestas en Telegram, ediciones en Discord, versionado del esquema de SQLite) están en Notas de actualización: canales y plugins.

@botmesh/baileys: entradas y tipos nativos

La entrada principal conserva lo que necesita una aplicación (BaileysProvider, sus opciones, toJid, isEditedMessage, RateLimiter, los tipos de PollStore). El resto pasó a @botmesh/baileys/internal, sin garantía semver; los nombres antiguos siguen en la entrada principal como alias @deprecated hasta v3.
  • Ruptura: @botmesh/baileys ya no exporta toInbound ni toOutbound: sus firmas usan los tipos propios de Baileys. Impórtalos desde @botmesh/baileys/internal.
  • Ruptura (solo tipos): los valores nativos de Baileys son opacos. provider.socket es BaileysSocket | null, provider.auth es BaileysAuthState, y el raw / native de mensajes, refs y resultados de envío es BaileysNativeMessage; los tres son alias de unknown. En ejecución no cambia nada. Usa socketOf(provider) y authStateOf(provider) de @botmesh/baileys/internal para tenerlos tipados, o haz el cast con tu propio import de @whiskeysockets/baileys:
  • Los predicados de JID (isGroup, isPn, isLid, isBroadcast, isNewsletter) devuelven boolean en lugar de boolean | undefined (undefined ahora es false).
  • pairingCode pasa a llamarse pairingPhoneNumber (el valor siempre fue el número de teléfono); pasar los dos con números distintos lanza un TypeError.

@botmesh/redis-store

Ruptura: los internos del layout de claves cluster pasan a un subpath nuevo sin garantía semver, @botmesh/redis-store/internal. La opción layout, el tipo RedisKeyLayout, migrateToClusterLayout(), inspectKeyspace(), isRedisClusterClient() y withClusterRetry() siguen en la entrada principal. Ruptura: se eliminan dos API deprecadas.

Entradas de @botmesh/ai

@botmesh/ai se divide igual que el core: 135 nombres en la entrada principal antes, 98 después. @botmesh/ai sigue reexportando MockAIProvider, MockOptions y MockTurn, pero obsoletos: impórtalos de @botmesh/ai/testing. Ruptura: @botmesh/ai ya no exporta estos nombres. Cambia la ruta del import; los valores son los mismos. Los contratos de cliente no cambian de forma: sus tipos de petición y respuesta ahora van escritos en línea, así que un cliente falso tipado AnthropicLike (u OpenAILike, GeminiLike…) compila igual. Para nombrar una respuesta, derívala del contrato o impórtala de /internal:
Ruptura (datos congelados): PRESETS, DEFAULT_YES, DEFAULT_NO, AI_MESSAGES y AI_LOCALES están congelados en profundidad y tipados readonly. Antes, modificarlos cambiaba los valores por defecto de todos los bots del proceso; ahora lanza un TypeError en modo estricto. Extiende una copia:
Pasarlos tal cual sigue compilando (toolConfirm.yes/.no aceptan un readonly string[]). Los constructores de proveedores reciben ahora un AIProviderConfig (model, maxTokens, effort, thinking, baseUrl), la parte de AIConfig que lee un proveedor. Pasar un AIConfig entero guardado en una variable sigue compilando, pero un objeto literal con campos del plugin falla ahora la comprobación de propiedades sobrantes de TypeScript (TS2353), porque los proveedores nunca los leen:
GenerateInput.onStreamEnd recibe un AIStreamEndInfo con nombre; su contrato (una vez, tras el último fragmento, solo si termina con normalidad) está en Notas de actualización: canales y plugins.

Paquetes HTTP, Graph y scaffolder

@botmesh/http: SecureFetchOptions cerrado. Las opciones de secureFetch y fetchBuffer ya no extienden RequestInit. Llevan los campos de la petición que el cliente respeta (method, headers, body, signal) y su política (allowHttp, allowPrivateHosts, timeoutMs, maxBytes, maxRedirects, resolver, fetch).
  • Ruptura (solo tipos): desaparece redirect. Siempre se sobrescribía con 'manual', porque el cliente sigue las redirecciones por su cuenta y valida cada salto; usa maxRedirects (0 rechaza cualquier redirección).
  • Los demás campos de RequestInit (cache, keepalive, integrity…) siguen compilando con una sobrecarga @deprecated (LegacySecureFetchOptions) y se siguen pasando a fetch. Dejan de aceptarse en la estable.
  • Las subidas en streaming no necesitan ningún campo extra: con un body ReadableStream (o iterable asíncrono), el cliente pone el duplex: 'half' que exige fetch. Quita el duplex explícito junto con los demás campos de RequestInit.
  • Una variable con tipo SecureFetchOptions ya no admite campos de navegador (mode, credentials, window…). Quítalos.
  • resolvePublicHost pasa a llamarse resolveHost: resuelve cualquier host y deja el veredicto a assertSafeTarget. El nombre antiguo queda como alias @deprecated.
@botmesh/graph-core: un objeto de opciones. checkGraphSignature recibe { body, headers, appSecret, logger }. La forma posicional sigue funcionando como sobrecarga @deprecated, con los mismos resultados. graph-core es un paquete público y lo cubre semver como a los demás: un cambio incompatible en él es una versión mayor.
@botmesh/meta: la fontanería de la ventana de sesión es experimental. Lo que una app configura y consulta sigue siendo público y lo cubre semver: SessionWindowManager (constructor, forScope, state, isOpen, expiresAt, pending, clearPending), SessionWindowOptions, el contrato SessionWindowStore y su MemorySessionWindowStore, los tipos de informe y de reapertura, SessionWindowReopenedError y las constantes. Los ganchos que mueve el provider (send, noteInbound, noteFailedStatus) y su tipo RawSend pasan a @experimental: pueden cambiar en una versión menor. En ejecución no cambia nada. Ver Estabilidad de la API. create-botmesh: internos en /internal. La entrada principal es el scaffolder: scaffold, sanitizeName, getTemplate y TEMPLATE_NAMES. TEMPLATES, agentsMd, findBotMeshMonorepo y AgentsMdInfo pasan a create-botmesh/internal (sin semver); la entrada principal los reexporta como @deprecated hasta la estable. La CLI no cambia.

Token de la API del sandbox

@botmesh/sandbox es privado, pero los scripts que usan su API web cambian:
  • Ruptura: POST /api/<canal>/send|fault|status|reset necesita ahora el token de la ejecución en la cabecera x-sandbox-token. Léelo de sandbox.token (o de la salida de la CLI), o fíjalo con startSandbox({ token }) / --token.
  • Las peticiones con un Host que no es de loopback, o con un Origin de otro sitio, reciben un 403. Pasa allowedHosts / --allow-host si llegas al hub con otro nombre a propósito.
  • Una entrada incorrecta es ahora un 4xx: se rechazan los campos desconocidos, los tipos erróneos y un cuerpo que no sea un objeto JSON, y toUserInput() lanza SandboxInputError.

Siguientes pasos