Saltar al contenido principal
Para despliegues de OpenClaw iMessage, usa imsg en un host de Mensajes de macOS con sesión iniciada. Si tu Gateway se ejecuta en Linux o Windows, apunta channels.imessage.cliPath a un contenedor SSH que ejecute imsg en el Mac.La recuperación entrante es automática. Después de reiniciar un puente o gateway, iMessage reproduce los mensajes perdidos mientras estaba inactivo y suprime la “bomba de backlog” obsoleta que Apple puede volcar tras una recuperación Push, deduplicando para que nada se despache dos veces. No hay configuración que habilitar; consulta Recuperación entrante después de reiniciar un puente o gateway.
Se eliminó la compatibilidad con BlueBubbles. Migra las configuraciones channels.bluebubbles a channels.imessage; OpenClaw admite iMessage solo mediante imsg. Empieza con Eliminación de BlueBubbles y la ruta iMessage de imsg para el anuncio breve, o Migrar desde BlueBubbles para la tabla de migración completa.
Estado: integración nativa con CLI externa. El Gateway inicia imsg rpc y habla JSON-RPC por stdio, sin daemon ni puerto separados. Las acciones avanzadas requieren imsg launch y una comprobación correcta de API privada.

Acciones de API privada

Respuestas, tapbacks, efectos, encuestas, adjuntos y gestión de grupos.

Emparejamiento

Los MD de iMessage usan por defecto el modo de emparejamiento.

Mac remoto

Usa un contenedor SSH cuando el Gateway no se ejecuta en el Mac de Mensajes.

Referencia de configuración

Referencia completa de campos de iMessage.

Configuración rápida

1

Instalar y verificar imsg

brew install steipete/tap/imsg
imsg rpc --help
imsg launch
openclaw channels status --probe
2

Configurar OpenClaw

{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "/usr/local/bin/imsg",
      dbPath: "/Users/user/Library/Messages/chat.db",
    },
  },
}
3

Iniciar gateway

openclaw gateway
4

Aprobar el primer emparejamiento por MD (dmPolicy predeterminada)

openclaw pairing list imessage
openclaw pairing approve imessage <CODE>
Las solicitudes de emparejamiento caducan después de 1 hora.

Requisitos y permisos (macOS)

  • Mensajes debe tener la sesión iniciada en el Mac que ejecuta imsg.
  • Se requiere Acceso total al disco para el contexto de proceso que ejecuta OpenClaw/imsg (acceso a la base de datos de Mensajes).
  • Se requiere permiso de Automatización para enviar mensajes a través de Messages.app.
  • Para acciones avanzadas (reaccionar / editar / anular envío / respuesta en hilo / efectos / encuestas / operaciones de grupo), System Integrity Protection debe estar desactivado; consulta Habilitar la API privada de imsg. El envío y la recepción básicos de texto y medios funcionan sin ello.
Los permisos se conceden por contexto de proceso. Si el gateway se ejecuta sin interfaz (LaunchAgent/SSH), ejecuta una vez un comando interactivo en ese mismo contexto para activar los avisos:
imsg chats --limit 1
# or
imsg send <handle> "test"
Una configuración por SSH remoto puede leer chats, pasar channels status --probe y procesar mensajes entrantes mientras los envíos salientes siguen fallando con un error de autorización de AppleEvents:
Not authorized to send Apple events to Messages. (-1743)
Comprueba la base de datos TCC del usuario del Mac con sesión iniciada o Ajustes del Sistema > Privacidad y seguridad > Automatización. Si la entrada de Automatización se registra para /usr/libexec/sshd-keygen-wrapper en lugar del proceso imsg o shell local, es posible que macOS no exponga un conmutador de Mensajes usable para ese cliente del lado del servidor SSH:
kTCCServiceAppleEvents | /usr/libexec/sshd-keygen-wrapper | auth_value=0 | com.apple.MobileSMS
En ese estado, repetir tccutil reset AppleEvents o volver a ejecutar imsg send mediante el mismo contenedor SSH puede seguir fallando porque el contexto de proceso que necesita Automatización de Mensajes es el contenedor SSH, no una app a la que la UI pueda concederlo.Usa en su lugar uno de los contextos de proceso de imsg compatibles:
  • Ejecuta el Gateway, o al menos el puente imsg, en la sesión local del usuario de Mensajes con sesión iniciada.
  • Inicia el Gateway con un LaunchAgent para ese usuario después de conceder Acceso total al disco y Automatización desde la misma sesión.
  • Si mantienes la topología SSH de dos usuarios, verifica que un imsg send saliente real funcione mediante el contenedor exacto antes de habilitar el canal. Si no se le puede conceder Automatización, reconfigura a una configuración imsg de un solo usuario en lugar de depender del contenedor SSH para los envíos.

Habilitar la API privada de imsg

imsg se distribuye en dos modos operativos:
  • Modo básico (predeterminado, no requiere cambios en SIP): texto y medios salientes mediante send, vigilancia/historial entrante, lista de chats. Esto es lo que obtienes de inmediato con una instalación nueva de brew install steipete/tap/imsg más los permisos estándar de macOS anteriores.
  • Modo de API privada: imsg inyecta una dylib auxiliar en Messages.app para llamar a funciones internas de IMCore. Esto habilita react, edit, unsend, reply (en hilo), sendWithEffect, poll y poll-vote (encuestas nativas de Mensajes), renameGroup, setGroupIcon, addParticipant, removeParticipant, leaveGroup, además de indicadores de escritura y confirmaciones de lectura.
La superficie de acciones avanzadas de esta página requiere el modo de API privada. El README de imsg es explícito sobre el requisito:
Las funciones avanzadas como read, typing, launch, envío enriquecido respaldado por puente, mutación de mensajes y gestión de chats son optativas. Requieren que SIP esté desactivado y que se inyecte una dylib auxiliar en Messages.app. imsg launch se niega a inyectar cuando SIP está activado.
La técnica de inyección de auxiliar usa la propia dylib de imsg para llegar a las API privadas de Mensajes. No hay servidor de terceros ni runtime de BlueBubbles en la ruta OpenClaw iMessage.
Desactivar SIP es una concesión real de seguridad. SIP es una de las protecciones centrales de macOS contra la ejecución de código de sistema modificado; desactivarlo en todo el sistema abre superficie de ataque y efectos secundarios adicionales. En particular, desactivar SIP en Macs con Apple Silicon también desactiva la capacidad de instalar y ejecutar apps de iOS en tu Mac.Trata esto como una decisión operativa deliberada, no como un valor predeterminado. Si tu modelo de amenazas no puede tolerar que SIP esté desactivado, iMessage incluido se limita al modo básico: solo envío/recepción de texto y medios, sin reacciones / edición / anulación de envío / efectos / operaciones de grupo.

Configuración

  1. Instala (o actualiza) imsg en el Mac que ejecuta Messages.app:
    brew install steipete/tap/imsg
    imsg --version
    imsg status --json
    
    La salida de imsg status --json informa de bridge_version, rpc_methods y selectors por método para que puedas ver qué admite la compilación actual antes de empezar.
  2. Desactiva System Integrity Protection y (en macOS moderno) Library Validation. Inyectar una dylib auxiliar no Apple en la Messages.app firmada por Apple requiere SIP desactivado y validación de bibliotecas relajada. El paso de SIP en modo Recuperación es específico de la versión de macOS:
    • macOS 10.13-10.15 (Sierra-Catalina): desactiva Library Validation mediante Terminal, reinicia en modo Recuperación, ejecuta csrutil disable, reinicia.
    • macOS 11+ (Big Sur y posterior), Intel: modo Recuperación (o Recuperación por Internet), csrutil disable, reinicia.
    • macOS 11+, Apple Silicon: secuencia de inicio con botón de encendido para entrar en Recuperación; en versiones recientes de macOS, mantén pulsada la tecla Mayús izquierda al hacer clic en Continuar y luego csrutil disable. Las configuraciones de máquina virtual siguen un flujo separado, así que toma primero una instantánea de la VM.
    En macOS 11 y posterior, csrutil disable por sí solo normalmente no basta. Apple sigue imponiendo la validación de bibliotecas contra Messages.app como binario de plataforma, por lo que se rechaza un auxiliar firmado ad hoc (Library Validation failed: ... platform binary, but mapped file is not) incluso con SIP desactivado. Después de desactivar SIP, desactiva también la validación de bibliotecas y reinicia:
    sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist DisableLibraryValidation -bool true
    
    macOS 26 (Tahoe), verificado en 26.5.1: SIP desactivado más el comando DisableLibraryValidation anterior basta para inyectar el auxiliar desde 26.0 hasta 26.5.x. No se requieren boot-args. El plist es el factor decisivo y el paso que falta con más frecuencia cuando la inyección falla en Tahoe:
    • Con el plist: imsg launch inyecta y imsg status informa advanced_features: true.
    • Sin el plist (incluso con SIP desactivado): imsg launch falla con Failed to launch: Timeout waiting for Messages.app to initialize. AMFI rechaza el auxiliar ad hoc al cargarlo, por lo que el puente nunca queda listo y el lanzamiento agota el tiempo de espera. Ese tiempo de espera es el síntoma más común en Tahoe; la solución es el plist anterior, no nada más drástico.
    Si la inyección de imsg launch o selectors específicos empiezan a devolver false después de una actualización de macOS, esta barrera suele ser la causa. Comprueba el estado de SIP y de validación de bibliotecas antes de asumir que el propio paso de SIP falló. Si esos ajustes son correctos y el puente aún no puede inyectarse, recopila imsg status --json junto con la salida de imsg launch y repórtalo al proyecto imsg en lugar de debilitar controles de seguridad adicionales en todo el sistema.
  3. Inyecta el asistente. Con SIP desactivado y Messages.app con la sesión iniciada:
    imsg launch
    
    imsg launch se niega a inyectar cuando SIP sigue activado, por lo que esto también sirve como confirmación de que el paso 2 surtió efecto.
  4. Verifica el puente desde OpenClaw:
    openclaw channels status --probe
    
    La entrada de iMessage debería informar works, y imsg status --json | jq '{rpc_methods, selectors}' debería mostrar las capacidades expuestas por tu compilación de macOS. La creación de encuestas requiere selectors.pollPayloadMessage; votar requiere tanto selectors.pollVoteMessage como el método RPC poll.vote. El Plugin de OpenClaw anuncia solo las acciones admitidas por la prueba almacenada en caché, mientras que una caché vacía se mantiene optimista y prueba en el primer envío.
Si openclaw channels status --probe informa que el canal está en works, pero acciones específicas lanzan “iMessage <action> requires the imsg private API bridge” en el momento del envío, ejecuta imsg launch de nuevo: el asistente puede desconectarse (reinicio de Messages.app, actualización del SO, etc.) y el estado available: true almacenado en caché seguirá anunciando acciones hasta que la próxima prueba lo actualice.

Cuando SIP permanece activado

Si desactivar SIP no es aceptable para tu modelo de amenazas:
  • imsg vuelve al modo básico: solo texto + medios + recepción.
  • El Plugin de OpenClaw sigue anunciando envío de texto/medios y supervisión entrante; oculta react, edit, unsend, reply, sendWithEffect y las operaciones de grupo de la superficie de acciones (según la puerta de capacidad por método).
  • Puedes ejecutar un Mac independiente que no sea Apple Silicon (o un Mac dedicado para bot) con SIP desactivado para la carga de trabajo de iMessage, mientras mantienes SIP activado en tus dispositivos principales. Consulta Usuario macOS dedicado para bot (identidad de iMessage separada) a continuación.

Control de acceso y enrutamiento

channels.imessage.dmPolicy controla los mensajes directos:
  • pairing (predeterminado)
  • allowlist (requiere al menos una entrada allowFrom)
  • open (requiere que allowFrom incluya "*")
  • disabled
Campo de lista de permitidos: channels.imessage.allowFrom.Las entradas de la lista de permitidos deben identificar remitentes: identificadores o grupos estáticos de acceso de remitente (accessGroup:<name>). Usa channels.imessage.groupAllowFrom para destinos de chat como chat_id:*, chat_guid:* o chat_identifier:*; usa channels.imessage.groups para claves de registro numéricas chat_id.

Vinculaciones de conversaciones ACP

Los chats de iMessage pueden vincularse a sesiones ACP. Flujo rápido para operador:
  • Ejecuta /acp spawn codex --bind here dentro del MD o chat de grupo permitido.
  • Los mensajes futuros en esa misma conversación de iMessage se enrutan a la sesión ACP generada.
  • /new y /reset restablecen la misma sesión ACP vinculada en el lugar.
  • /acp close cierra la sesión ACP y elimina la vinculación.
Las vinculaciones persistentes configuradas usan entradas bindings[] de nivel superior con type: "acp" y match.channel: "imessage". match.peer.id puede usar:
  • identificador de MD normalizado como +15555550123 o user@example.com
  • chat_id:<id> (recomendado para vinculaciones de grupo estables)
  • chat_guid:<guid>
  • chat_identifier:<identifier>
Ejemplo:
{
  agents: {
    list: [
      {
        id: "codex",
        runtime: {
          type: "acp",
          acp: { agent: "codex", backend: "acpx", mode: "persistent" },
        },
      },
    ],
  },
  bindings: [
    {
      type: "acp",
      agentId: "codex",
      match: {
        channel: "imessage",
        accountId: "default",
        peer: { kind: "group", id: "chat_id:123" },
      },
      acp: { label: "codex-group" },
    },
  ],
}
Consulta Agentes ACP para el comportamiento compartido de vinculaciones ACP.

Patrones de despliegue

Usa un Apple ID dedicado y un usuario de macOS dedicado para que el tráfico del bot quede aislado de tu perfil personal de Messages.Flujo típico:
  1. Crea/inicia sesión con un usuario de macOS dedicado.
  2. Inicia sesión en Messages con el Apple ID del bot en ese usuario.
  3. Instala imsg en ese usuario.
  4. Crea un contenedor SSH para que OpenClaw pueda ejecutar imsg en el contexto de ese usuario.
  5. Apunta channels.imessage.accounts.<id>.cliPath y .dbPath a ese perfil de usuario.
La primera ejecución puede requerir aprobaciones de GUI (Automatización + Acceso completo al disco) en esa sesión de usuario del bot.
Topología común:
  • Gateway se ejecuta en Linux/VM
  • iMessage + imsg se ejecuta en un Mac de tu tailnet
  • el contenedor cliPath usa SSH para ejecutar imsg
  • remoteHost habilita recuperaciones de adjuntos por SCP
Ejemplo:
{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "~/.openclaw/scripts/imsg-ssh",
      remoteHost: "bot@mac-mini.tailnet-1234.ts.net",
      includeAttachments: true,
      dbPath: "/Users/bot/Library/Messages/chat.db",
    },
  },
}
#!/usr/bin/env bash
exec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@"
Usa claves SSH para que tanto SSH como SCP sean no interactivos. Asegúrate primero de que la clave del host sea de confianza (por ejemplo, ssh bot@mac-mini.tailnet-1234.ts.net) para que known_hosts quede poblado.
iMessage admite configuración por cuenta bajo channels.imessage.accounts.Cada cuenta puede sobrescribir campos como cliPath, dbPath, allowFrom, groupPolicy, mediaMaxMb, opciones de historial y listas de permitidos de raíz de adjuntos.
Define channels.imessage.dmHistoryLimit para inicializar nuevas sesiones de mensajes directos con historial reciente decodificado de imsg para esa conversación. Usa channels.imessage.dms["<sender>"].historyLimit para sobrescrituras por remitente, incluido 0 para desactivar el historial para un remitente.El historial de MD de iMessage se recupera bajo demanda desde imsg. Dejar dmHistoryLimit sin definir desactiva la inicialización global de historial de MD, pero un channels.imessage.dms["<sender>"].historyLimit positivo por remitente sigue habilitando la inicialización para ese remitente.

Medios, fragmentación y destinos de entrega

  • la ingesta de archivos adjuntos entrantes está desactivada de forma predeterminada — establece channels.imessage.includeAttachments: true para reenviar fotos, notas de voz, video y otros archivos adjuntos al agente. Con esta opción desactivada, los iMessages que solo contienen adjuntos se descartan antes de llegar al agente y puede que no produzcan ninguna línea de registro Inbound message.
  • las rutas de archivos adjuntos remotos pueden obtenerse mediante SCP cuando remoteHost está establecido
  • las rutas de archivos adjuntos deben coincidir con raíces permitidas:
    • channels.imessage.attachmentRoots (local)
    • channels.imessage.remoteAttachmentRoots (modo SCP remoto)
    • las raíces configuradas amplían el patrón de raíz predeterminado /Users/*/Library/Messages/Attachments (se fusionan, no se reemplazan)
  • SCP usa comprobación estricta de clave de host (StrictHostKeyChecking=yes)
  • el tamaño de medios salientes usa channels.imessage.mediaMaxMb (predeterminado 16 MB)
  • límite de fragmento de texto: channels.imessage.textChunkLimit (predeterminado 4000)
  • modo de fragmentación: channels.imessage.chunkMode
    • length (predeterminado)
    • newline (división priorizando párrafos)
  • la negrita, cursiva, subrayado y tachado de Markdown salientes se convierten en texto con estilo nativo (los destinatarios en macOS 15+ muestran el estilo; los destinatarios más antiguos ven texto sin formato sin los marcadores); las tablas de Markdown se convierten según el modo de tablas Markdown del canal
  • channels.imessage.sendTransport (auto predeterminado, bridge, applescript) selecciona cómo imsg entrega los envíos
Destinos explícitos preferidos:
  • chat_id:123 (recomendado para enrutamiento estable)
  • chat_guid:...
  • chat_identifier:...
También se admiten destinos por identificador:
  • imessage:+1555...
  • sms:+1555...
  • user@example.com
imsg chats --limit 20

Acciones de API privada

Cuando imsg launch está en ejecución y openclaw channels status --probe informa privateApi.available: true, la herramienta de mensajes puede usar acciones nativas de iMessage además de los envíos de texto normales. Todas las acciones están activadas de forma predeterminada; usa channels.imessage.actions para desactivar acciones individuales:
{
  channels: {
    imessage: {
      actions: {
        reactions: true,
        edit: true,
        unsend: true,
        reply: true,
        sendWithEffect: true,
        sendAttachment: true,
        renameGroup: true,
        setGroupIcon: true,
        addParticipant: true,
        removeParticipant: true,
        leaveGroup: true,
        polls: true,
      },
    },
  },
}
  • react: Añade/elimina tapbacks de iMessage (messageId, emoji, remove). Los tapbacks admitidos se asignan a me encanta, me gusta, no me gusta, risa, énfasis y pregunta. Eliminar sin un emoji borra cualquier tapback que se haya establecido.
  • reply: Envía una respuesta en hilo a un mensaje existente (messageId, text o message, más chatGuid, chatId, chatIdentifier o to). Responder con adjunto además necesita una compilación de imsg cuyo send-rich admita --file.
  • sendWithEffect: Envía texto con un efecto de iMessage (text o message, effect o effectId). Nombres cortos: slam, loud, gentle, invisibleink, confetti, lasers, fireworks, balloon, heart, echo, happybirthday, shootingstar, sparkles, spotlight.
  • edit: Edita un mensaje enviado en versiones compatibles de macOS/API privada (messageId, text o newText). Solo se pueden editar los mensajes que el propio Gateway envió.
  • unsend: Retira un mensaje enviado en versiones compatibles de macOS/API privada (messageId). Solo se pueden anular los mensajes que el propio Gateway envió.
  • upload-file: Envía medios/archivos (buffer como base64 o un media/path/filePath hidratado, filename, asVoice opcional). Alias heredado: sendAttachment.
  • renameGroup, setGroupIcon, addParticipant, removeParticipant, leaveGroup: Gestiona chats grupales cuando el destino actual es una conversación grupal. Estas acciones modifican la identidad de Mensajes del host, por lo que requieren un remitente propietario o un cliente de Gateway operator.admin.
  • poll: Crea una encuesta nativa de Apple Messages (pollQuestion, pollOption repetido de 2 a 12 veces, más chatGuid, chatId, chatIdentifier o to). Los destinatarios en iOS/iPadOS/macOS 26+ la ven y votan de forma nativa; las versiones anteriores del sistema operativo reciben una alternativa de texto “Se envió una encuesta”. Requiere selectors.pollPayloadMessage.
  • poll-vote: Vota en una encuesta existente (pollId o messageId, más exactamente uno de pollOptionIndex, pollOptionId o pollOptionText). Requiere selectors.pollVoteMessage y el método RPC poll.vote.
Las encuestas entrantes aceptadas se muestran al agente con la pregunta, etiquetas de opciones numeradas, recuentos de votos y el ID del mensaje de encuesta que necesita poll-vote.
El contexto entrante de iMessage incluye tanto valores MessageSid cortos como GUIDs de mensaje completos (MessageSidFull) cuando están disponibles. Los ID cortos están limitados a la caché reciente de respuestas respaldada por SQLite y se comprueban contra el chat actual antes de usarse. Si un ID corto expiró o pertenece a otro chat, reintenta con el MessageSidFull completo.
OpenClaw oculta las acciones de API privada solo cuando el estado de sondeo en caché indica que el puente no está disponible. Si el estado es desconocido, las acciones permanecen visibles y los sondeos se despachan de forma diferida para que la primera acción pueda funcionar después de imsg launch sin una actualización manual de estado aparte.
Cuando el puente de API privada está activo, los chats entrantes aceptados se marcan como leídos y los chats directos muestran una burbuja de escritura en cuanto se acepta el turno, mientras el agente prepara el contexto y genera. Desactiva el marcado de lectura con:
{
  channels: {
    imessage: {
      sendReadReceipts: false,
    },
  },
}
Las compilaciones antiguas de imsg que son anteriores a la lista de capacidades por método desactivan silenciosamente escritura/lectura; OpenClaw registra una advertencia una sola vez por reinicio para que la confirmación faltante sea atribuible.
OpenClaw se suscribe a tapbacks de iMessage y enruta las reacciones aceptadas como eventos del sistema en lugar de texto de mensaje normal, por lo que un tapback de usuario no activa un bucle de respuesta ordinario.El modo de notificación se controla con channels.imessage.reactionNotifications:
  • "own" (predeterminado): notificar solo cuando los usuarios reaccionen a mensajes escritos por el bot.
  • "all": notificar todos los tapbacks entrantes de remitentes autorizados.
  • "off": ignorar tapbacks entrantes.
Las sobrescrituras por cuenta usan channels.imessage.accounts.<id>.reactionNotifications.
Cuando approvals.exec.enabled o approvals.plugin.enabled es true y la solicitud se enruta a iMessage, el Gateway entrega una solicitud de aprobación de forma nativa y acepta un tapback para resolverla:
  • 👍 (tapback Me gusta) → allow-once
  • 👎 (tapback No me gusta) → deny
  • allow-always sigue siendo una alternativa manual: envía /approve <id> allow-always como respuesta normal.
El manejo de reacciones requiere que el identificador del usuario que reacciona sea un aprobador explícito. La lista de aprobadores se lee de channels.imessage.allowFrom (o channels.imessage.accounts.<id>.allowFrom); añade el número de teléfono del usuario en formato E.164 o su correo de Apple ID (los destinos de chat como chat_id:* no son entradas de aprobador válidas). La entrada comodín "*" se respeta, pero permite aprobar a cualquier remitente; una lista de aprobadores vacía desactiva por completo el atajo de reacción. El atajo de reacción omite intencionalmente reactionNotifications, dmPolicy y groupAllowFrom porque la lista de permitidos de aprobadores explícitos es la única puerta que importa para resolver aprobaciones.La autorización del comando de texto /approve sigue la misma lista: cuando channels.imessage.allowFrom no está vacía, /approve <id> <decision> se autoriza contra esa lista de aprobadores (no contra la lista más amplia de permitidos para DM), y los remitentes permitidos en la lista de DM pero no en allowFrom reciben una denegación explícita. Cuando allowFrom está vacía, la alternativa de mismo chat permanece vigente y /approve autoriza a cualquiera que permita la lista de DM. Añade a allowFrom todos los operadores que deban aprobar, mediante /approve o mediante reacciones.Notas para operadores:
  • El enlace de reacción se almacena tanto en memoria como en el almacén persistente con claves del Gateway (TTL igual al vencimiento de la aprobación), y el Gateway también sondea las solicitudes pendientes en busca de tapbacks, por lo que un tapback que llega poco después de reiniciar el Gateway todavía resuelve la aprobación.
  • El tapback propio del operador con is_from_me=true (por ejemplo, desde un dispositivo Apple emparejado) resuelve la aprobación cuando ese identificador es un aprobador explícito.
  • Las solicitudes de aprobación se enrutan a una conversación grupal solo cuando hay aprobadores explícitos configurados; de lo contrario, cualquier miembro del grupo podría aprobar.
  • Los tapbacks heredados con estilo de texto (Liked "…" como texto sin formato desde clientes Apple muy antiguos) no pueden resolver aprobaciones porque no llevan GUID de mensaje; la resolución de reacciones requiere los metadatos estructurados de tapback que emiten los clientes actuales de macOS / iOS.

Escrituras de configuración

iMessage permite de forma predeterminada escrituras de configuración iniciadas por el canal (para /config set|unset cuando commands.config: true). Desactivar:
{
  channels: {
    imessage: {
      configWrites: false,
    },
  },
}

Fusionar DM de envío dividido (comando + URL en una composición)

Cuando un usuario escribe juntos un comando y una URL — por ejemplo, Dump https://example.com/article — la app Mensajes de Apple divide el envío en dos filas chat.db separadas:
  1. Un mensaje de texto ("Dump").
  2. Un globo de vista previa de URL ("https://...") con imágenes de vista previa OG como adjuntos.
Las dos filas llegan a OpenClaw con una separación de ~0,8-2,0 s en la mayoría de las configuraciones. Sin fusión, el agente recibe solo el comando en el turno 1 (y a menudo responde “envíame la URL”) antes de que llegue la URL en el turno 2. Esto es el pipeline de envío de Apple, no algo que introduzcan OpenClaw o imsg. channels.imessage.coalesceSameSenderDms opta un DM en el almacenamiento en búfer de filas consecutivas del mismo remitente. Cuando imsg expone el marcador estructural de vista previa de URL balloon_bundle_id: "com.apple.messages.URLBalloonProvider" en una de las filas de origen, OpenClaw fusiona solo ese envío dividido real y mantiene cualquier otra fila almacenada en búfer como turnos separados. En compilaciones antiguas de imsg que no emiten metadatos de globo en absoluto, OpenClaw no puede distinguir un envío dividido de envíos separados, por lo que recurre a fusionar el grupo. Eso preserva el comportamiento anterior a los metadatos en lugar de hacer que los envíos divididos Dump <url> regresen a dos turnos. Los chats grupales siguen despachándose por mensaje para preservar la estructura de turnos multiusuario.
Actívalo cuando:
  • Distribuyas Skills que esperan command + payload en un solo mensaje (dump, paste, save, queue, etc.).
  • Tus usuarios peguen URLs junto con comandos.
  • Puedas aceptar la latencia adicional del turno de DM (consulta abajo).
Déjalo desactivado cuando:
  • Necesites latencia mínima de comandos para disparadores de DM de una sola palabra.
  • Todos tus flujos sean comandos de una sola ejecución sin seguimientos de carga útil.

Escenarios y lo que ve el agente

La columna “Marca activada” muestra el comportamiento en una compilación de imsg que emite balloon_bundle_id. En compilaciones antiguas de imsg que no emiten metadatos de globo en absoluto, las filas siguientes marcadas como “Dos turnos” / “N turnos” recurren en su lugar a una fusión heredada (un turno): OpenClaw no puede distinguir estructuralmente un envío dividido de envíos separados, por lo que conserva la fusión previa a los metadatos. La separación precisa se activa una vez que la compilación emite metadatos de globo.
El usuario redactachat.db produceMarca desactivada (predeterminado)Marca activada + ventana (imsg emite metadatos de globo)
Dump https://example.com (un envío)2 filas con ~1 s de diferenciaDos turnos del agente: “Dump” solo, luego URLUn turno: texto fusionado Dump https://example.com
Save this 📎image.jpg caption (archivo adjunto + texto)2 filas sin metadatos de globo de URLDos turnosDos turnos después de observar metadatos; un turno fusionado en sesiones antiguas/previas al latch sin metadatos
/status (comando independiente)1 filaDespacho instantáneoEspera hasta la ventana y luego despacha
URL pegada sola1 filaDespacho instantáneoEspera hasta la ventana y luego despacha
Texto + URL enviados como dos mensajes separados deliberados, minutos aparte2 filas fuera de la ventanaDos turnosDos turnos (la ventana expira entre ellos)
Inundación rápida (>10 DM pequeños dentro de la ventana)N filas sin metadatos de globo de URLN turnosN turnos después de observar metadatos; un turno fusionado acotado en sesiones antiguas/previas al latch sin metadatos
Dos personas escribiendo en un chat grupalN filas de M remitentesM+ turnos (uno por bucket de remitente)M+ turnos — los chats grupales no se fusionan

Recuperación de entrada después de reiniciar un puente o Gateway

iMessage recupera los mensajes perdidos mientras el Gateway estaba inactivo y, al mismo tiempo, suprime la antigua “bomba de backlog” que Apple puede descargar después de una recuperación Push. El comportamiento predeterminado está siempre activado y se basa en la deduplicación de entrada.
  • Deduplicación de reproducción. Cada mensaje entrante despachado se registra por su GUID de Apple en el estado persistente del Plugin (imessage.inbound-dedupe), se reclama durante la ingesta y se confirma después de manejarlo (se libera ante un fallo transitorio para que pueda reintentarse). Todo lo ya manejado se descarta en lugar de despacharse dos veces. Esto permite que la recuperación reproduzca de forma agresiva sin contabilidad por mensaje.
  • Recuperación de tiempo de inactividad. Al iniciar, el monitor recuerda el último chat.db rowid despachado (un cursor persistido por cuenta) y lo pasa a imsg watch.subscribe como since_rowid, de modo que imsg reproduce las filas que llegaron mientras el Gateway estaba inactivo y luego sigue en vivo. La reproducción se limita a las 500 filas más recientes y a mensajes de hasta ~2 horas de antigüedad, y la deduplicación descarta todo lo ya manejado.
  • Valla de antigüedad para backlog obsoleto. Las filas por encima del límite de inicio son realmente en vivo; una cuya fecha de envío sea más de ~15 minutos anterior a su llegada es backlog de descarga Push y se suprime. Las filas reproducidas (en el límite o por debajo) usan en cambio la ventana de recuperación más amplia, de modo que se entrega un mensaje perdido recientemente mientras que el historial antiguo no.
La recuperación funciona tanto con configuraciones cliPath locales como remotas, porque la reproducción since_rowid se ejecuta sobre la misma conexión RPC de imsg. La diferencia es la ventana: cuando el Gateway puede leer chat.db (local), ancla el límite rowid de inicio, limita el intervalo de reproducción y entrega mensajes perdidos de hasta un par de horas de antigüedad. Con un cliPath SSH remoto no puede leer la base de datos, por lo que la reproducción no tiene límite y cada fila usa la valla de antigüedad en vivo: aun así recupera mensajes perdidos recientemente y sigue suprimiendo backlog antiguo, solo que con la ventana en vivo más estrecha. Ejecuta el Gateway en la Mac de Messages para obtener la ventana de recuperación más amplia.

Señal visible para el operador

El backlog suprimido se registra en el nivel predeterminado, nunca se descarta silenciosamente (la marca recovery muestra qué ventana se aplicó):
imessage: suppressed stale inbound backlog account=<id> sent=<iso> recovery=<bool> (<N> suppressed since start)

Migración

channels.imessage.catchup.* está obsoleto: la recuperación de tiempo de inactividad es automática y no necesita configuración para instalaciones nuevas. Las configuraciones existentes con catchup.enabled: true siguen respetándose como perfil de compatibilidad para la ventana de reproducción de recuperación. Los bloques catchup deshabilitados (enabled: false o sin enabled: true) se retiran; openclaw doctor --fix los elimina.

Solución de problemas

Valida el binario y la compatibilidad RPC:
imsg rpc --help
imsg status --json
openclaw channels status --probe
Si la sonda informa que RPC no es compatible, actualiza imsg. Si las acciones de API privada no están disponibles, ejecuta imsg launch en la sesión del usuario de macOS que inició sesión y vuelve a sondear. Si el Gateway no se está ejecutando en macOS, usa la configuración de Mac remota por SSH anterior en lugar de la ruta local predeterminada de imsg.
Primero demuestra si el mensaje llegó a la Mac local. Si chat.db no cambia, OpenClaw no puede recibir el mensaje incluso cuando imsg status --json informa de un puente saludable.
imsg chats --limit 10 --json
imsg watch --chat-id <chat-id> --json
sqlite3 ~/Library/Messages/chat.db \
  "select datetime(max(date)/1000000000 + 978307200, 'unixepoch', 'localtime'), max(ROWID) from message;"
Si los mensajes enviados desde el teléfono no crean filas nuevas, repara la capa de Messages de macOS y Apple Push antes de cambiar la configuración de OpenClaw. Una actualización puntual del servicio suele ser suficiente:
launchctl kickstart -k system/com.apple.apsd
launchctl kickstart -k gui/$(id -u)/com.apple.CommCenter
launchctl kickstart -k gui/$(id -u)/com.apple.identityservicesd
launchctl kickstart -k gui/$(id -u)/com.apple.imagent
imsg launch
openclaw gateway restart
Envía un iMessage nuevo desde el teléfono y confirma una fila nueva en chat.db o un evento de imsg watch antes de depurar sesiones de OpenClaw. No ejecutes esto como un bucle periódico de relanzamiento del puente; imsg launch repetido más reinicios del Gateway durante trabajo activo pueden interrumpir entregas y dejar ejecuciones de canal en curso varadas.
El cliPath: "imsg" predeterminado debe ejecutarse en la Mac con sesión iniciada en Messages. En Linux o Windows, establece channels.imessage.cliPath en un script contenedor que haga SSH a esa Mac y ejecute imsg "$@".
#!/usr/bin/env bash
exec ssh -T messages-mac imsg "$@"
Luego ejecuta:
openclaw channels status --probe --channel imessage
Revisa:
  • channels.imessage.dmPolicy
  • channels.imessage.allowFrom
  • aprobaciones de emparejamiento (openclaw pairing list imessage)
Revisa:
  • channels.imessage.groupPolicy
  • channels.imessage.groupAllowFrom
  • comportamiento de lista de permitidos de channels.imessage.groups
  • configuración de patrones de mención (agents.list[].groupChat.mentionPatterns)
Revisa:
  • channels.imessage.remoteHost
  • channels.imessage.remoteAttachmentRoots
  • autenticación con clave SSH/SCP desde el host del Gateway
  • que exista la clave de host en ~/.ssh/known_hosts en el host del Gateway
  • legibilidad de la ruta remota en la Mac que ejecuta Messages
Vuelve a ejecutar en una terminal GUI interactiva en el mismo contexto de usuario/sesión y aprueba los avisos:
imsg chats --limit 1
imsg send <handle> "test"
Confirma que Acceso total al disco + Automatización estén concedidos para el contexto de proceso que ejecuta OpenClaw/imsg.

Punteros de referencia de configuración

Relacionado