Saltar al contenido principal

Paquetes públicos

@botmesh/baileys

toJid() lanza con una entrada que no puede convertir en JID. Devolvía '@s.whatsapp.net' para una entrada sin dígitos, y un texto inválido …@g.us:<usuario> para { groupId, userId }. Las dos lanzan ahora un TypeError.
Un participante de un grupo no tiene JID propio: usa el JID del grupo para el chat y toJid(userId) para la persona. Las ediciones de texto llegan al bot. Un mensaje editado se descartaba. Ahora llega como mensaje con el texto nuevo y el ref.id original, como en Telegram. isEditedMessage(ctx) lo distingue. El dedupe del core deja pasar la edición y sigue descartando un reintento idéntico. Solo se entrega la edición de un mensaje que el proceso ha visto, enviado en el mismo chat por quien manda la edición: la que nombra el mensaje de otra persona, uno del propio bot o uno de otro chat se descarta, igual que la de un original que ya no está en la caché de mensajes del provider (p. ej. tras un reinicio). Para volver al comportamiento anterior:

@botmesh/ai

provider: 'openai' con baseUrl envía max_tokens. Con cualquier baseUrl que no sea api.openai.com ni Azure OpenAI (*.openai.azure.com, *.cognitiveservices.azure.com o un endpoint de Azure AI Foundry *.services.ai.azure.com bajo /openai), el tope de tokens de salida va ahora en max_tokens, que es lo que esperan los servidores compatibles con OpenAI (vLLM, LM Studio, gateways). Si tu baseUrl es un proxy delante de OpenAI y usas un modelo o-series o gpt-5, vuelve a fijar el campo:
Las opciones de memoria fallan pronto. Un memory.maxFactLength, memory.maxFacts o memory.ttl inválido lanza ahora un RangeError desde aiPlugin() en vez de fallar en plug() (o, con maxFactLength, descartar en silencio todos los hechos). onStreamEnd tiene un contrato fijo. GenerateInput.onStreamEnd(info) (un AIStreamEndInfo: stopReason, más usage en Anthropic y Gemini) se llama exactamente una vez por llamada a stream(), tras el último fragmento y antes de que termine la iteración, y solo si el stream termina con normalidad: nunca si falla, se aborta o el consumidor sale antes con break. Una excepción lanzada por el callback hace fallar la iteración del consumidor. Todos los proveedores integrados y MockAIProvider lo cumplen, también la regla del aborto cuando el cliente ignora signal (un stream abortado no llama al callback y rechaza con el motivo del aborto); un AIProvider propio que llame a onStreamEnd también debería (comprueba signal.aborted antes de llamarlo). Entradas y datos congelados. MockAIProvider pasa a @botmesh/ai/testing, los helpers del plugin y los nombres de los tipos de los SDK a @botmesh/ai/internal, y los vocabularios, presets y textos exportados quedan congelados. Ver Migrar a la API estable.

@botmesh/meta

  • Los límites interactivos se comprueban antes de enviar. Un buttons o card sin botones, ids o títulos de botón repetidos (comparados tras el corte a 20 caracteres), una lista sin filas, con más de 10 filas o con ids de fila repetidos rechazan ahora con UnsupportedContentError. Graph también los rechazaba, pero con un error opaco tras la petición.
  • Una respuesta interactiva que no es una pulsación (por ejemplo call_permission_reply) llega como mensaje unknown con el payload en ctx.ref.native, no como interacción button con datos vacíos. Un handler que dependía de eso debe leer el mensaje.
  • Las subidas de media tienen su propio timeout, uploadTimeoutMs (5 minutos por defecto).
  • Nueva opción lang para las etiquetas que publishForm() fija en un Flow (por defecto 'en'): ponle el BotConfig.lang de tu bot. publishForm(def, { lang }) la sobrescribe.
  • Deprecados (se quitan en v3): toProviderError (usa toGraphError de @botmesh/graph-core), GraphClient (internos del transporte), los verifyChallenge/verifySignature reexportados (usa provider.verifyChallenge(query) y provider.ingest(), o impórtalos de @botmesh/graph-core) y debug (pasa un logger). phoneNumberId admite string | number en el tipo.

@botmesh/telegram

  • En modo webhook, ingest() tras stop() responde 503 y no emite nada (Telegram reenvía el Update tras el reinicio). Los hosts que vuelven a arrancar el provider no tienen que cambiar nada.
  • Las subidas de archivos tienen su propio timeout, uploadTimeoutMs (5 minutos por defecto).
  • Las encuestas se envían no anónimas y sus votos llegan al bot. Cada poll_answer se convierte en un evento 'poll' con la misma forma que en WhatsApp (vote.ref, vote.senderId, vote.options con voters). Los miembros del chat ven quién votó. Los canales no admiten votantes públicos: una encuesta a un canal se reintenta una vez como anónima (sin votos) y el canal se recuerda. Para mantener encuestas anónimas (sin recibir votos):
  • pollTimeoutMs sustituye a pollTimeout (segundos, deprecado): pollTimeout: 30 equivale a pollTimeoutMs: 30_000. toProviderError pasa a ser toTelegramError (el nombre antiguo es un alias deprecado), debug queda deprecado (pasa un logger) y los helpers de cable parseUpdate, parsePollAnswer, toOutbound y MEDIA_UPLOAD son @experimental.

@botmesh/sqlite-store

saveMessage y setCursor lanzan TypeError con un id de mensaje, id de chat, clave o valor de cursor que lleve un surrogate UTF-16 suelto (antes SQLite lo guardaba como U+FFFD y dos ids podían compartir fila). Escapa o quita esos caracteres antes de guardarlos. Versionado del esquema. La base de datos guarda su layout en PRAGMA user_version (SQLITE_SCHEMA_VERSION, ahora 2). Abrir un archivo escrito por una versión anterior (versión 0 o 1) lo migra en el sitio y conserva todas las filas; no hay que hacer nada. La versión 2 incorpora a las migraciones versionadas las tablas del scheduler durable (schedules, schedule_fences) y la de durabilidad (durable): antes se creaban sobre la marcha la primera vez que se llamaba a schedules() o durability(), y ahora se crean al abrir el archivo. Un archivo escrito por una versión más nueva se rechaza con SQLiteSchemaVersionError (code: 'SQLITE_SCHEMA_TOO_NEW') y no se toca: no se admite volver atrás, así que haz una copia del archivo antes de actualizar si puedes necesitar revertir. Volver a la alpha anterior. La alpha anterior escribe y lee la versión de esquema 1. En cuanto esta versión abre un archivo, este pasa a la versión 2 y la alpha anterior lo rechaza con SQLITE_SCHEMA_TOO_NEW en su siguiente arranque, aunque la app nunca llame a schedules() ni a durability(). Pasa lo mismo en un despliegue escalonado: un proceso que sigue en la alpha anterior y comparte el archivo falla al reiniciarse, así que para todos los procesos de la versión vieja antes de que el primero de la nueva abra el archivo. Para volver atrás, restaura la copia hecha antes de actualizar. La migración no cambia ninguna fila (solo añade tablas con CREATE TABLE IF NOT EXISTS), así que, si no tienes copia, puedes fijar a mano PRAGMA user_version = 1 con la CLI sqlite3 mientras ningún proceso tenga el archivo abierto, y la alpha anterior vuelve a abrirlo. El salto de versión existe para que las migraciones futuras puedan dar por hecho que esas tablas existen; sin él seguirían fuera de la versión. Opciones nuevas: busyTimeoutMs (por defecto 5000, PRAGMA busy_timeout) y un logger que acepta cualquier LoggerLike. Las cadenas DDL exportadas SCHEDULES_SCHEMA y DURABLE_SCHEMA quedan deprecadas.

@botmesh/messenger y @botmesh/instagram

Los dos canales son públicos desde la beta (1.0.0-beta), así que su primera versión en npm ya trae la entrada congelada. Dentro del monorepo o de un fork, importar un nombre quitado deja de compilar: La opción debug queda deprecada en los dos: pasa un logger. Más de 3 botones (o 10 filas de lista) en Messenger, o más de 13 quick replies en Instagram, rechazan con UnsupportedContentError en vez de cortarse.

@botmesh/evolution, @botmesh/wppconnect y @botmesh/wa-gateway-kit

Nuevos en npm en la beta (antes eran privados). Si los usabas desde el monorepo:
  • Los parsers de webhooks, los mapeadores de salida y los tokens de media (parseWebhook, parseMessage, toOutbound, numberedMenu, keyFromRef, target, jidOf…) y los helpers JWT de Evolution (verifyHs256Jwt, signHs256Jwt) pasaron a @botmesh/evolution/internal y @botmesh/wppconnect/internal, sin garantía semver.
  • Los providers ya no reexportan WaIdentityStore ni normalizeJid: impórtalos de @botmesh/wa-gateway-kit. IdentityPersistence y RestHttpOptions siguen en la entrada principal de los providers.
  • Se quitó la opción debug: pasa un logger (pino({ level: 'debug' })).
  • Las opciones inválidas lanzan TypeError (antes un Error genérico; los mensajes no cambian). Además, baseUrl ya no admite credenciales (user:pass@), query string ni fragmento, identityCacheSize debe ser un entero positivo (o Infinity) y http.timeoutMs / http.maxBytes se validan al construir el provider, no en la primera petición.
  • Solo una URL http(s) se entrega al gateway para que la descargue. { url } es siempre un enlace, como en Meta y Telegram: admite http(s) o un data: URI en base64 (que va inline), y una ruta local o una URL file: ahí rechaza con UnsupportedContentError en vez de leerse (antes se pasaba tal cual y el gateway intentaba abrir esa ruta en su propio host). Envía un archivo local como { path } o como string con la ruta; el bot lo lee y lo manda en base64.

Plugins que pasan a npm

@botmesh/rag, @botmesh/voice, @botmesh/nlu, @botmesh/dialogflow, @botmesh/chatwoot, @botmesh/tickets y @botmesh/moderation se publican en npm desde la beta (1.0.0-beta). El código que los usaba desde el monorepo (workspace:*) necesita estos cambios:
  • Helpers movidos a una entrada /internal (sin semver; una aplicación rara vez los necesita): @botmesh/rag/internal (splitMarkdownSections, resolveChunkOptions, cosine, hashText, tokenize, resolveEmbedder, createPdfParser), @botmesh/voice/internal (trimPunctuation, resolveStt, resolveTts, OPENAI_TTS_MIMETYPES, elevenLabsMimetype, estimateOggDurationSec, estimateDurationSec, sniffMimetype), @botmesh/dialogflow/internal (normalizeEsResult, normalizeCxResult, toSessionId, parseCredentials, createServiceAccountJwt, DIALOGFLOW_SCOPE, DEFAULT_TOKEN_URI), @botmesh/chatwoot/internal (ChatwootMapping, verifyWebhook, inboundText, outboundText), @botmesh/tickets/internal (renderTemplate, localClock) y @botmesh/moderation/internal (findLinks, findInviteLinks, compileTerms, foldText).
  • @botmesh/dialogflow: dialogflowPlugin es el nombre canónico y dialogflowContext un alias obsoleto. languageCode vale por defecto 'en' (antes 'es'): ponle el idioma de tu agente. En grupos, sessionScope vale por defecto 'participant' (una sesión de Dialogflow por miembro, como las conversaciones); usa sessionScope: 'chat' para mantener una sesión por grupo. Las opciones mal escritas (minConfidence: 80, un fulfillment desconocido…) lanzan ahora DialogflowError (INVALID_CONFIG) en bot.plug(), y los textos de más de 256 caracteres ya no se envían (maxTextLength).
  • Datos congelados: DEFAULT_VOICE_LIMITS, SUPPORTED_EXTENSIONS (rag), TICKET_PRIORITIES y TICKET_STATUSES ya no se pueden mutar; cópialos antes.
  • @botmesh/chatwoot: se rechaza una firma de webhook sin x-chatwoot-timestamp (no caducaba, así que una petición capturada se podía repetir). Activa webhookAllowUntimedSignature: true solo para un emisor que no pueda añadir el timestamp.
  • @botmesh/tickets: TicketKeyValue tiene un setIf opcional. Un almacenamiento propio que lo implemente pasa el desk a modo compartido (seguro con réplicas).

Paquetes que eran privados

Estas notas importan al código que usaba los paquetes desde el monorepo o un fork antes de la beta. @botmesh/discord, @botmesh/email y @botmesh/flow-schema están en npm desde la beta; @botmesh/tiktok y los paquetes de producto siguen siendo privados.
  • @botmesh/discord: ctx.interaction.id (interactionId) es el id de la interacción a secas; el token se queda en el provider, y la forma alfa "<id>:<token>" de acknowledgeInteraction() se quitó antes de la primera publicación (solo se conocen los ids que recibió el provider). Las interacciones que el bot no confirma en autoAcknowledgeMs (2 s; el alias alfa autoAckMs se quitó) se difieren por él; pon autoAcknowledgeMs: false para seguir haciéndolo tú. Los mensajes enviados llevan allowed_mentions: { parse: ['users'] } si no indican otro (allowedMentions lo cambia), así que @everyone, @here y las menciones de rol ya no notifican por defecto. Las ediciones de mensajes (MESSAGE_UPDATE) llegan ahora como mensajes con el ref.id original (isEditedMessage(ctx); editedMessages: 'ignore' las descarta).
  • Mapeadores de errores renombrados (los nombres antiguos son alias deprecados): toTikTokError, toEmailError. toDiscordError no tiene alias: toProviderError se quitó de @botmesh/discord antes de su primera publicación.
  • Stores de producto: InboxStore.erase, ContactStore.erase y DeploymentStore.prune son métodos nuevos obligatorios, y DeploymentStore.list / InboxStore.listNotes aceptan un cursor after. Un store propio debe añadirlos (las suites de conformidad compartidas de test-utils/ los comprueban). attachInbox() sin tenantId guarda ahora las conversaciones en el tenant del propio bot en vez de 'local'.
  • @botmesh/flow-schema: publishFlow() acepta cualquier registro con un publish() compatible (un DeploymentRegistry encaja) y ya no depende de @botmesh/deployments en tiempo de ejecución.