Mainstream messaging

iMessage

Status: integração nativa com CLI externa. O Gateway inicia `imsg rpc` e se comunica por JSON-RPC via stdio — sem daemon ou porta separada. O modo de API privada é fortemente recomendado para um canal iMessage completo; respostas, tapbacks, efeitos, enquetes, respostas a anexos e ações de grupo exigem `imsg launch` e uma sondagem bem-sucedida da API privada.

Status: integração nativa com CLI externa. O Gateway inicia imsg rpc e se comunica por JSON-RPC via stdio — sem daemon ou porta separada. O modo de API privada é fortemente recomendado para um canal iMessage completo; respostas, tapbacks, efeitos, enquetes, respostas a anexos e ações de grupo exigem imsg launch e uma sondagem bem-sucedida da API privada.

Na configuração local comum, a configuração do OpenClaw pode oferecer, mediante confirmação do usuário, a instalação ou atualização do imsg pelo Homebrew no Mac com sessão iniciada no Mensagens. A configuração manual e as topologias com wrapper SSH continuam sendo gerenciadas pelo operador: instale ou atualize o imsg no mesmo contexto de usuário que executará o Gateway ou o wrapper.

Configuração rápida

Mac local (caminho rápido)

  • Instalar e verificar o imsg

    bash
    brew install steipete/tap/imsgbrew update && brew upgrade imsgimsg rpc --helpimsg launchopenclaw channels status --probe

    Quando o assistente de configuração local detecta que o comando padrão imsg está ausente, ele pode solicitar a instalação de steipete/tap/imsg pelo Homebrew. Se detectar um imsg gerenciado pelo Homebrew, ele poderá solicitar sua reinstalação ou atualização. Wrappers personalizados de cliPath não são modificados.

  • Configurar o OpenClaw

    json5
    {channels: {imessage: {enabled: true,cliPath: "/usr/local/bin/imsg",dbPath: "/Users/user/Library/Messages/chat.db",},},}
  • Iniciar o Gateway

    bash
    openclaw gateway
  • Aprovar o pareamento da primeira mensagem direta (dmPolicy padrão)

    bash
    openclaw pairing list imessageopenclaw pairing approve imessage <CODE>

    As solicitações de pareamento expiram após 1 hora.

  • Mac remoto por SSH

    A maioria das configurações não precisa de SSH. Use esta topologia somente quando o Gateway não puder ser executado no Mac com sessão iniciada no Mensagens. O OpenClaw exige apenas um cliPath compatível com stdio; portanto, você pode apontar cliPath para um script wrapper que se conecte por SSH a um Mac remoto e execute o imsg. Instale e atualize o imsg nesse Mac remoto, não no host do Gateway:

    bash
    ssh messages-mac 'brew install steipete/tap/imsg && brew update && brew upgrade imsg'
    bash
    #!/usr/bin/env bashexec ssh -T messages-mac imsg "$@"

    Configuração recomendada quando os anexos estão habilitados:

    json5
    {channels: {imessage: {  enabled: true,  cliPath: "~/.openclaw/scripts/imsg-ssh",  remoteHost: "user@gateway-host", // usado para buscar anexos via SCP  includeAttachments: true,  // Opcional: raízes adicionais permitidas para anexos (combinadas com o padrão  // /Users/*/Library/Messages/Attachments).  attachmentRoots: ["/Users/*/Library/Messages/Attachments"],  remoteAttachmentRoots: ["/Users/*/Library/Messages/Attachments"],},},}

    Se remoteHost não estiver definido, o OpenClaw tentará detectá-lo automaticamente analisando o script wrapper SSH. remoteHost deve ser host ou user@host (sem espaços nem opções SSH); valores inseguros são ignorados. O OpenClaw usa verificação estrita da chave do host para SCP, portanto a chave do host de retransmissão já deve existir em ~/.ssh/known_hosts. Os caminhos dos anexos são validados em relação às raízes permitidas (attachmentRoots / remoteAttachmentRoots).

    Requisitos e permissões (macOS)

    • Deve haver uma sessão iniciada no Mensagens no Mac que executa o imsg.
    • O Acesso Total ao Disco é obrigatório para o contexto do processo que executa o OpenClaw/imsg (acesso ao banco de dados do Mensagens).
    • A permissão de Automação é obrigatória para enviar mensagens pelo Messages.app.
    • Para ações avançadas (reagir / editar / cancelar envio / resposta em thread / efeitos / enquetes / operações de grupo), a Proteção de Integridade do Sistema deve ser desabilitada — consulte Habilitação da API privada do imsg. O envio e o recebimento básicos de texto e mídia funcionam sem isso.
    Falha em envios pelo wrapper SSH com AppleEvents -1743

    Uma configuração com SSH remoto pode ler conversas, passar por channels status --probe e processar mensagens de entrada, enquanto os envios ainda falham com um erro de autorização do AppleEvents:

    text
    Não autorizado a enviar eventos Apple para o Mensagens. (-1743)

    Verifique o banco de dados TCC do usuário com sessão iniciada no Mac ou System Settings > Privacy & Security > Automation. Se a entrada de Automação estiver registrada para /usr/libexec/sshd-keygen-wrapper em vez do processo imsg ou do shell local, o macOS poderá não disponibilizar um controle utilizável do Mensagens para esse cliente SSH no lado do servidor:

    text
    kTCCServiceAppleEvents | /usr/libexec/sshd-keygen-wrapper | auth_value=0 | com.apple.MobileSMS

    Nesse estado, repetir tccutil reset AppleEvents ou executar novamente imsg send pelo mesmo wrapper SSH pode continuar falhando, pois o contexto do processo que precisa da Automação do Mensagens é o wrapper SSH, não um aplicativo ao qual a interface possa conceder a permissão.

    Use um dos contextos de processo compatíveis com o imsg:

    • Execute o Gateway, ou pelo menos a ponte do imsg, na sessão local do usuário com login no Mensagens.
    • Inicie o Gateway com um LaunchAgent para esse usuário após conceder Acesso Total ao Disco e Automação na mesma sessão.
    • Se mantiver a topologia SSH com dois usuários, verifique se um envio real com imsg send é bem-sucedido pelo wrapper exato antes de habilitar o canal. Se não for possível conceder Automação a ele, reconfigure para uma instalação do imsg com um único usuário, em vez de depender do wrapper SSH para envios.

    Habilitação da API privada do imsg

    O imsg é fornecido em dois modos operacionais. Para o OpenClaw, o modo de API privada é a configuração recomendada, pois fornece ao canal as ações nativas do iMessage esperadas pelos usuários. O modo básico continua sendo útil para instalações de baixo risco, verificação inicial ou hosts nos quais o SIP não pode ser desabilitado.

    • Modo básico (padrão, nenhuma alteração no SIP é necessária): envio de texto e mídia por meio de send, monitoramento/histórico de entrada e lista de conversas. Isso é o que se obtém imediatamente após uma nova execução de brew install steipete/tap/imsg, além das permissões padrão do macOS descritas acima.
    • Modo de API privada: o imsg injeta uma dylib auxiliar no Messages.app para chamar funções internas do IMCore. Isso libera react, edit, unsend, reply (em thread), sendWithEffect, poll e poll-vote (enquetes nativas do Mensagens), renameGroup, setGroupIcon, addParticipant, removeParticipant, leaveGroup, além de indicadores de digitação e confirmações de leitura.

    A superfície de ações recomendada nesta página exige o modo de API privada. O README do imsg é explícito quanto ao requisito:

    Recursos avançados como read, typing, launch, envio avançado apoiado pela ponte, alteração de mensagens e gerenciamento de conversas são opcionais. Eles exigem que o SIP esteja desabilitado e que uma dylib auxiliar seja injetada no Messages.app. imsg launch se recusa a realizar a injeção quando o SIP está habilitado.

    A técnica de injeção do auxiliar usa a própria dylib do imsg para acessar as APIs privadas do Mensagens. Não há servidor de terceiros nem runtime do BlueBubbles no caminho do iMessage no OpenClaw.

    Configuração

    1. Instale (ou atualize) o imsg no Mac que executa o Messages.app:

      bash
      brew install steipete/tap/imsgbrew update && brew upgrade imsgimsg --versionimsg status --json

      A saída de imsg status --json informa bridge_version, rpc_methods e os selectors de cada método, para que você possa ver o que a compilação atual oferece antes de começar.

    2. Desabilite a Proteção de Integridade do Sistema e, no macOS moderno, a Validação de Biblioteca. A injeção de uma dylib auxiliar que não seja da Apple no Messages.app, assinado pela Apple, exige que o SIP esteja desativado e que a validação de biblioteca esteja flexibilizada. A etapa do SIP no modo de Recuperação é específica de cada versão do macOS:

      • macOS 10.13-10.15 (Sierra-Catalina): desabilite a Validação de Biblioteca pelo Terminal, reinicie no modo de Recuperação, execute csrutil disable e reinicie.
      • macOS 11+ (Big Sur e posteriores), Intel: entre no modo de Recuperação (ou Recuperação pela Internet), execute csrutil disable e reinicie.
      • macOS 11+, Apple Silicon: use a sequência de inicialização pelo botão liga/desliga para entrar na Recuperação; nas versões recentes do macOS, mantenha pressionada a tecla Left Shift ao clicar em Continue e, em seguida, execute csrutil disable. Configurações de máquina virtual seguem um fluxo separado; portanto, primeiro crie um snapshot da VM.

      No macOS 11 e posterior, apenas csrutil disable geralmente não é suficiente. A Apple ainda impõe a validação de bibliotecas para o Messages.app como um binário da plataforma; por isso, um auxiliar assinado de forma ad hoc é rejeitado (Library Validation failed: ... platform binary, but mapped file is not) mesmo com o SIP desativado. Após desativar o SIP, desative também a validação de bibliotecas e reinicie:

      bash
      sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist DisableLibraryValidation -bool true

      macOS 26 (Tahoe), verificado na versão 26.5.1: o SIP desativado mais o comando DisableLibraryValidation acima são suficientes para injetar o auxiliar nas versões 26.0 a 26.5.x. Nenhum boot-arg é necessário. O plist é o fator decisivo e a etapa ausente mais comum quando a injeção falha no Tahoe:

      • Com o plist: imsg launch faz a injeção e imsg status informa advanced_features: true.
      • Sem o plist (mesmo com o SIP desativado): imsg launch falha com Failed to launch: Timeout waiting for Messages.app to initialize. O AMFI rejeita o auxiliar ad hoc durante o carregamento, portanto a ponte nunca fica pronta e a inicialização atinge o tempo limite. Esse tempo limite é o sintoma que a maioria das pessoas encontra no Tahoe; a correção é o plist acima, não algo mais drástico.

      Se a injeção por imsg launch ou selectors específicos começarem a retornar falso após uma atualização do macOS, essa barreira geralmente é a causa. Verifique o estado do SIP e da validação de bibliotecas antes de presumir que a própria etapa do SIP falhou. Se essas configurações estiverem corretas e a ponte ainda não conseguir fazer a injeção, colete imsg status --json junto com a saída de imsg launch e reporte ao projeto imsg, em vez de enfraquecer outros controles de segurança de todo o sistema.

    3. Injete o auxiliar. Com o SIP desativado e a sessão iniciada no Messages.app:

      bash
      imsg launch

      imsg launch se recusa a fazer a injeção quando o SIP ainda está ativado; portanto, isso também serve como confirmação de que a etapa 2 foi efetivada.

    4. Verifique a ponte pelo OpenClaw:

      bash
      openclaw channels status --probe

      A entrada do iMessage deve informar works, e imsg status --json | jq '{rpc_methods, selectors}' deve mostrar os recursos expostos pela sua compilação do macOS. A criação de enquetes exige selectors.pollPayloadMessage; a votação exige tanto selectors.pollVoteMessage quanto o método RPC poll.vote. O Plugin do OpenClaw anuncia somente as ações compatíveis com a sondagem armazenada em cache, enquanto um cache vazio permanece otimista e faz a sondagem no primeiro envio.

    Se openclaw channels status --probe informar o canal como works, mas ações específicas gerarem "iMessage <action> requires the imsg private API bridge" no momento do envio, execute imsg launch novamente — o auxiliar pode ser desconectado (reinicialização do Messages.app, atualização do sistema operacional etc.), e o estado available: true armazenado em cache continuará anunciando ações até que a próxima sondagem o atualize.

    Quando o SIP permanece ativado

    Se desativar o SIP não for aceitável para o seu modelo de ameaças:

    • O imsg volta ao modo básico — somente texto + mídia + recebimento.
    • O Plugin do OpenClaw ainda anuncia o envio de texto/mídia e o monitoramento de mensagens recebidas; ele oculta react, edit, unsend, reply, sendWithEffect e operações de grupo da superfície de ações (de acordo com a barreira de recursos por método).
    • Você pode executar um Mac separado sem Apple Silicon (ou um Mac dedicado ao bot) com o SIP desativado para a carga de trabalho do iMessage, mantendo o SIP ativado nos seus dispositivos principais. Consulte Usuário dedicado do macOS para o bot (identidade separada do iMessage) abaixo.

    Controle de acesso e roteamento

    Política de mensagens diretas

    channels.imessage.dmPolicy controla as mensagens diretas:

    • pairing (padrão)
    • allowlist (exige pelo menos uma entrada em allowFrom)
    • open (exige que allowFrom inclua "*")
    • disabled

    Campo da lista de permissões: channels.imessage.allowFrom.

    As entradas da lista de permissões devem identificar remetentes: identificadores ou grupos estáticos de acesso de remetentes (accessGroup:<name>). Use channels.imessage.groupAllowFrom para destinos de conversa como chat_id:*, chat_guid:* ou chat_identifier:*; use channels.imessage.groups para chaves numéricas chat_id do registro.

    Política de grupos + menções

    channels.imessage.groupPolicy controla o tratamento de grupos:

    • allowlist (padrão)
    • open
    • disabled

    Lista de permissões de remetentes de grupos: channels.imessage.groupAllowFrom.

    As entradas de groupAllowFrom também podem referenciar grupos estáticos de acesso de remetentes (accessGroup:<name>).

    Comportamento alternativo em tempo de execução: se groupAllowFrom não estiver definido, as verificações de remetentes de grupos do iMessage usarão allowFrom; defina groupAllowFrom quando a admissão de mensagens diretas e de grupos precisar ser diferente. Um groupAllowFrom: [] explicitamente vazio não usa o comportamento alternativo — ele bloqueia todos os remetentes de grupos em allowlist. Observação sobre o tempo de execução: se channels.imessage estiver completamente ausente, o tempo de execução usará groupPolicy="allowlist" e registrará um aviso (mesmo que channels.defaults.groupPolicy esteja definido).

    Barreira de menções para grupos:

    • o iMessage não possui metadados nativos de menções
    • a detecção de menções usa padrões de expressões regulares (agents.list[].groupChat.mentionPatterns, com comportamento alternativo por messages.groupChat.mentionPatterns)
    • sem padrões configurados, a barreira de menções não pode ser aplicada
    • comandos de controle enviados por remetentes autorizados ignoram a barreira de menções

    systemPrompt por grupo:

    Cada entrada em channels.imessage.groups.* aceita uma string opcional systemPrompt, injetada no prompt de sistema do agente em cada turno que processa uma mensagem desse grupo. A resolução espelha channels.whatsapp.groups:

    1. Prompt de sistema específico do grupo (groups["<chat_id>"].systemPrompt): usado quando a entrada específica do grupo existe no mapa e sua chave systemPrompt está definida. Se systemPrompt for uma string vazia (""), o curinga será suprimido e nenhum prompt de sistema será aplicado a esse grupo.
    2. Prompt de sistema curinga do grupo (groups["*"].systemPrompt): usado quando a entrada específica do grupo está totalmente ausente do mapa ou quando existe, mas não define nenhuma chave systemPrompt.
    json5
    {  channels: {    imessage: {      groupPolicy: "allowlist",      groupAllowFrom: ["+15555550123"],      groups: {        "*": { systemPrompt: "Use a ortografia britânica." },        "8421": {          requireMention: true,          systemPrompt: "Esta é a conversa da escala de plantão. Mantenha as respostas com menos de 3 frases.",        },        "9907": {          // supressão explícita: o curinga "Use a ortografia britânica." não se aplica aqui          systemPrompt: "",        },      },    },  },}

    Os prompts por grupo se aplicam somente a mensagens de grupos — as mensagens diretas não são afetadas.

    Sessões e respostas determinísticas

    • As mensagens diretas usam roteamento direto; os grupos usam roteamento de grupo.
    • Com o padrão session.dmScope=main, as mensagens diretas do iMessage são consolidadas na sessão principal do agente.
    • As sessões de grupos são isoladas (agent:<agentId>:imessage:group:<chat_id>).
    • As respostas são encaminhadas de volta ao iMessage usando os metadados do canal/destino de origem.

    Comportamento de threads semelhantes a grupos:

    Algumas threads do iMessage com vários participantes podem chegar com is_group=false. Se esse chat_id estiver configurado explicitamente em channels.imessage.groups, o OpenClaw o tratará como tráfego de grupo (barreira de grupo + isolamento da sessão de grupo).

    Vínculos de conversas ACP

    As conversas do iMessage podem ser vinculadas a sessões ACP.

    Fluxo rápido para o operador:

    • Execute /acp spawn codex --bind here dentro da mensagem direta ou da conversa de grupo permitida.
    • As mensagens futuras nessa mesma conversa do iMessage serão encaminhadas para a sessão ACP criada.
    • /new e /reset redefinem no local a mesma sessão ACP vinculada.
    • /acp close fecha a sessão ACP e remove o vínculo.

    Os vínculos persistentes configurados usam entradas bindings[] de nível superior com type: "acp" e match.channel: "imessage".

    match.peer.id pode usar:

    • identificador normalizado de mensagem direta, como +15555550123 ou user@example.com
    • chat_id:<id> (recomendado para vínculos estáveis de grupos)
    • chat_guid:<guid>
    • chat_identifier:<identifier>

    Exemplo:

    json5
    {  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" },    },  ],}

    Consulte Agentes ACP para saber mais sobre o comportamento compartilhado dos vínculos ACP.

    Padrões de implantação

    Usuário dedicado do macOS para o bot (identidade separada do iMessage)

    Use um Apple ID e um usuário do macOS dedicados para que o tráfego do bot fique isolado do seu perfil pessoal do Messages.

    Fluxo típico:

    1. Crie um usuário dedicado do macOS e inicie a sessão nele.
    2. Nesse usuário, inicie uma sessão no Messages com o Apple ID do bot.
    3. Instale o imsg nesse usuário.
    4. Crie um wrapper SSH para que o OpenClaw possa executar o imsg no contexto desse usuário.
    5. Aponte channels.imessage.accounts.<id>.cliPath e .dbPath para o perfil desse usuário.

    A primeira execução pode exigir aprovações pela interface gráfica (Automation + Full Disk Access) na sessão de usuário do bot.

    Mac remoto por Tailscale (exemplo)

    Topologia comum:

    • o Gateway é executado no Linux/VM
    • o iMessage + imsg são executados em um Mac na sua tailnet
    • o wrapper cliPath usa SSH para executar o imsg
    • remoteHost permite buscar anexos por SCP

    Exemplo:

    json5
    {  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",    },  },}
    bash
    #!/usr/bin/env bashexec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@"

    Use chaves SSH para que tanto o SSH quanto o SCP sejam não interativos. Primeiro, certifique-se de que a chave do host seja confiável (por exemplo, ssh bot@mac-mini.tailnet-1234.ts.net) para que known_hosts seja preenchido.

    Padrão de múltiplas contas

    O iMessage oferece suporte à configuração por conta em channels.imessage.accounts.

    Cada conta pode substituir campos como cliPath, dbPath, allowFrom, groupPolicy, mediaMaxMb, configurações de histórico e listas de permissões de raízes de anexos.

    Histórico de mensagens diretas

    Defina channels.imessage.dmHistoryLimit para preencher novas sessões de mensagens diretas com o histórico recente de imsg decodificado dessa conversa. Use channels.imessage.dms["<sender>"].historyLimit para substituições por remetente, incluindo 0 para desativar o histórico de um remetente.

    O histórico de MDs do iMessage é obtido sob demanda do imsg. Deixar dmHistoryLimit sem definição desativa o preenchimento global do histórico de MDs, mas um valor positivo de channels.imessage.dms["<sender>"].historyLimit por remetente ainda ativa o preenchimento para esse remetente.

    Mídia, divisão em blocos e destinos de entrega

    Anexos e mídia
    • a ingestão de anexos recebidos fica desativada por padrão — defina channels.imessage.includeAttachments: true para encaminhar fotos, mensagens de voz, vídeos e outros anexos ao agente. Com essa opção desativada, iMessages que contêm apenas anexos são descartadas antes de chegar ao agente e podem não produzir nenhuma linha de log Inbound message.
    • caminhos de anexos remotos podem ser obtidos via SCP quando remoteHost está definido
    • os caminhos de anexos devem corresponder às raízes permitidas:
      • channels.imessage.attachmentRoots (local)
      • channels.imessage.remoteAttachmentRoots (modo SCP remoto)
      • as raízes configuradas estendem o padrão de raiz predefinido /Users/*/Library/Messages/Attachments (são mescladas, não substituídas)
    • o SCP usa verificação estrita da chave do host (StrictHostKeyChecking=yes)
    • o tamanho da mídia de saída usa channels.imessage.mediaMaxMb (padrão de 16 MB)
    Texto de saída e divisão em blocos
    • limite do bloco de texto: channels.imessage.textChunkLimit (padrão de 4000)
    • modo de divisão em blocos: channels.imessage.streaming.chunkMode
      • length (padrão)
      • newline (divisão priorizando parágrafos)
    • negrito/itálico/sublinhado/tachado em Markdown na saída é convertido em texto estilizado nativo (destinatários no macOS 15+ renderizam a estilização; destinatários em versões anteriores veem texto simples sem os marcadores); tabelas Markdown são convertidas de acordo com o modo de tabelas Markdown do canal
    • channels.imessage.sendTransport (auto por padrão, bridge, applescript) seleciona como o imsg realiza os envios
    Formatos de endereçamento

    Destinos explícitos preferenciais:

    • chat_id:123 (recomendado para roteamento estável)
    • chat_guid:...
    • chat_identifier:...

    Destinos por identificador também são compatíveis:

    • imessage:+1555...
    • sms:+1555...
    • user@example.com
    bash
    imsg chats --limit 20

    Ações da API privada

    Quando imsg launch está em execução e openclaw channels status --probe informa privateApi.available: true, a ferramenta de mensagens pode usar ações nativas do iMessage além dos envios normais de texto.

    Todas as ações ficam ativadas por padrão; use channels.imessage.actions para desativar ações individuais:

    json5
    {  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,      },    },  },}
    Ações disponíveis
    • react: Adiciona/remove tapbacks do iMessage (messageId, emoji, remove). Os tapbacks compatíveis correspondem a amar, curtir, não curtir, rir, enfatizar e questionar. A remoção sem um emoji limpa qualquer tapback que esteja definido.
    • reply: Envia uma resposta encadeada a uma mensagem existente (messageId, text ou message, mais chatGuid, chatId, chatIdentifier ou to). A resposta com anexo também exige uma compilação do imsg cujo send-rich seja compatível com --file.
    • sendWithEffect: Envia texto com um efeito do iMessage (text ou message, effect ou effectId). Nomes curtos: slam, loud, gentle, invisibleink, confetti, lasers, fireworks, balloon, heart, echo, happybirthday, shootingstar, sparkles, spotlight.
    • edit: Edita uma mensagem enviada em versões compatíveis do macOS/API privada (messageId, text ou newText). Somente mensagens enviadas pelo próprio gateway podem ser editadas.
    • unsend: Retira uma mensagem enviada em versões compatíveis do macOS/API privada (messageId). Somente mensagens enviadas pelo próprio gateway podem ter o envio desfeito.
    • upload-file: Envia mídia/arquivos (buffer como base64 ou um media/path/filePath carregado, filename, asVoice opcional). Alias legado: sendAttachment.
    • renameGroup, setGroupIcon, addParticipant, removeParticipant, leaveGroup: Gerenciam conversas em grupo quando o destino atual é uma conversa em grupo. Essas ações alteram a identidade do app Mensagens no host, portanto exigem um remetente proprietário ou um cliente Gateway operator.admin.
    • poll: Cria uma enquete nativa do app Mensagens da Apple (pollQuestion, pollOption repetido de 2 a 12 vezes, mais chatGuid, chatId, chatIdentifier ou to). Destinatários no iOS/iPadOS/macOS 26+ a veem e votam nela de forma nativa; versões anteriores do sistema operacional recebem um texto alternativo "Enquete enviada". Exige selectors.pollPayloadMessage.
    • poll-vote: Vota em uma enquete existente (pollId ou messageId, mais exatamente um entre pollOptionIndex, pollOptionId ou pollOptionText). Exige selectors.pollVoteMessage e o método RPC poll.vote.

    As enquetes recebidas e aceitas são renderizadas para o agente com a pergunta, os rótulos numerados das opções, as contagens de votos e o ID da mensagem da enquete necessário para poll-vote.

    IDs de mensagens

    O contexto de entrada do iMessage inclui tanto valores curtos de MessageSid quanto GUIDs completos de mensagens (MessageSidFull), quando disponíveis. IDs curtos têm o escopo limitado ao cache recente de respostas baseado em SQLite e são verificados em relação à conversa atual antes do uso. Se um ID curto tiver expirado ou pertencer a outra conversa, tente novamente com o MessageSidFull completo.

    Detecção de recursos

    O OpenClaw oculta as ações da API privada somente quando o status da sondagem em cache indica que a ponte está indisponível. Se o status for desconhecido, as ações permanecem visíveis e o envio executa sondagens de forma tardia, permitindo que a primeira ação tenha êxito após imsg launch sem uma atualização manual separada do status.

    Confirmações de leitura e digitação

    Quando a ponte da API privada está ativa, as conversas recebidas e aceitas são marcadas como lidas, e as conversas diretas exibem um balão de digitação assim que o turno é aceito, enquanto o agente prepara o contexto e gera a resposta. Desative a marcação como lida com:

    json5
    {  channels: {    imessage: {      sendReadReceipts: false,    },  },}

    Compilações antigas do imsg, anteriores à lista de recursos por método, desativam silenciosamente a digitação/leitura; o OpenClaw registra um aviso uma vez a cada reinicialização para que seja possível atribuir a ausência da confirmação.

    Tapbacks recebidos

    O OpenClaw assina os tapbacks do iMessage e encaminha as reações aceitas como eventos do sistema, em vez de texto de mensagem normal, para que o tapback de um usuário não acione um ciclo comum de respostas.

    O modo de notificação é controlado por channels.imessage.reactionNotifications:

    • "own" (padrão): notifica somente quando usuários reagem a mensagens criadas pelo bot.
    • "all": notifica sobre todos os tapbacks recebidos de remetentes autorizados.
    • "off": ignora tapbacks recebidos.

    Substituições por conta usam channels.imessage.accounts.<id>.reactionNotifications.

    Reações de aprovação (👍 / 👎)

    Quando approvals.exec.enabled ou approvals.plugin.enabled é verdadeiro e a solicitação é encaminhada ao iMessage, o gateway entrega uma solicitação de aprovação de forma nativa e aceita um tapback para resolvê-la:

    • 👍 (tapback Like) → allow-once
    • 👎 (tapback Dislike) → deny
    • allow-always permanece como alternativa manual: envie /approve <id> allow-always como uma resposta comum.

    O processamento da reação exige que o identificador do usuário que reagiu seja de um aprovador explícito. A lista de aprovadores é lida de channels.imessage.allowFrom (ou channels.imessage.accounts.<id>.allowFrom); adicione o número de telefone do usuário no formato E.164 ou o e-mail do ID Apple dele (destinos de conversa como chat_id:* não são entradas válidas de aprovadores). A entrada curinga "*" é respeitada, mas permite que qualquer remetente aprove; uma lista de aprovadores vazia desativa completamente o atalho de reação. O atalho de reação ignora intencionalmente reactionNotifications, dmPolicy e groupAllowFrom, pois a lista de permissões de aprovadores explícitos é a única verificação relevante para resolver a aprovação.

    A autorização do comando de texto /approve segue a mesma lista: quando channels.imessage.allowFrom não está vazio, /approve <id> <decision> é autorizado com base nessa lista de aprovadores (não na lista de permissões mais ampla de MDs), e remetentes permitidos na lista de permissões de MDs, mas ausentes de allowFrom, recebem uma recusa explícita. Quando allowFrom está vazio, a alternativa da mesma conversa permanece em vigor, e /approve autoriza qualquer pessoa permitida pela lista de permissões de MDs. Adicione a allowFrom todos os operadores que devam aprovar — via /approve ou por reações.

    Observações para operadores:

    • A associação da reação é armazenada tanto na memória quanto no armazenamento persistente com chaves do gateway (com TTL correspondente à expiração da aprovação), e o gateway também consulta periodicamente as solicitações pendentes em busca de tapbacks; portanto, um tapback recebido pouco depois da reinicialização do gateway ainda resolve a aprovação.
    • O tapback is_from_me=true do próprio operador (por exemplo, de um dispositivo Apple emparelhado) resolve a aprovação quando esse identificador pertence a um aprovador explícito.
    • As solicitações de aprovação só são encaminhadas a uma conversa em grupo quando aprovadores explícitos estão configurados; caso contrário, qualquer membro do grupo poderia aprovar.
    • Tapbacks legados em formato de texto (Liked "…" como texto simples de clientes Apple muito antigos) não podem resolver aprovações porque não contêm GUID de mensagem; a resolução por reação exige os metadados estruturados de tapback emitidos pelos clientes atuais do macOS/iOS.

    Gravações de configuração

    O iMessage permite por padrão gravações de configuração iniciadas pelo canal (para /config set|unset quando commands.config: true).

    Para desativar:

    json5
    {  channels: {    imessage: {      configWrites: false,    },  },}

    Agregação de MDs divididas no envio (comando + URL em uma única composição)

    Quando um usuário digita um comando e uma URL juntos — por exemplo, Dump https://example.com/article — o app Mensagens da Apple divide o envio em duas linhas separadas de chat.db:

    1. Uma mensagem de texto ("Dump").
    2. Um balão de pré-visualização de URL ("https://...") com imagens de pré-visualização OG como anexos.

    As duas linhas chegam ao OpenClaw com um intervalo de aproximadamente 0.8-2.0 s na maioria das configurações. Sem a agregação, o agente recebe apenas o comando no turno 1 (e frequentemente responde "envie-me a URL") antes de a URL chegar no turno 2. Isso faz parte do pipeline de envio da Apple, não é algo introduzido pelo OpenClaw ou pelo imsg.

    channels.imessage.coalesceSameSenderDms habilita o armazenamento em buffer de linhas consecutivas do mesmo remetente em uma DM. Quando o imsg expõe o marcador estrutural de prévia de URL balloon_bundle_id: "com.apple.messages.URLBalloonProvider" em uma das linhas de origem, o OpenClaw mescla apenas esse envio realmente dividido e mantém quaisquer outras linhas armazenadas em buffer como turnos separados. Em versões mais antigas do imsg que não emitem nenhum metadado de balão, o OpenClaw não consegue distinguir um envio dividido de envios separados e, portanto, recorre à mesclagem do agrupamento. Isso preserva o comportamento anterior aos metadados, em vez de regredir envios divididos de Dump <url> para dois turnos. Os chats em grupo continuam sendo despachados por mensagem para preservar a estrutura de turnos com vários usuários.

    Quando habilitar

    Habilite quando:

    • Você distribui Skills que esperam command + payload em uma única mensagem (despejar, colar, salvar, enfileirar etc.).
    • Seus usuários colam URLs junto com comandos.
    • Você pode aceitar a latência adicional nos turnos de DM (veja abaixo).

    Deixe desabilitado quando:

    • Você precisa de latência mínima para comandos acionados por uma única palavra em DMs.
    • Todos os seus fluxos são comandos de execução única, sem cargas adicionais posteriores.

    Habilitação

    json5
    {  channels: {    imessage: {      coalesceSameSenderDms: true, // habilitação opcional (padrão: false)    },  },}

    Com o sinalizador ativado e sem um messages.inbound.byChannel.imessage explícito nem um messages.inbound.debounceMs global, a janela de debounce aumenta para 7000 ms (o padrão legado é 0 ms — sem debounce). A janela mais ampla é necessária porque o intervalo entre envios divididos da prévia de URL da Apple pode chegar a vários segundos enquanto o Messages.app emite a linha da prévia.

    Para ajustar a janela por conta própria:

    json5
    {  messages: {    inbound: {      byChannel: {        // 7000 ms abrangem os atrasos observados nas prévias de URL do Messages.app.        imessage: 7000,      },    },  },}

    Implicações

    • A mesclagem precisa exige metadados atuais da carga do imsg. Com balloon_bundle_id presente, apenas o envio realmente dividido é mesclado; a mesclagem de contingência sem metadados descrita acima é uma compatibilidade retroativa temporária, removida quando o imsg passar a aglutinar os envios divididos na origem.
    • Latência adicional para mensagens de DM. Com o sinalizador ativado, toda DM (incluindo comandos de controle independentes e mensagens posteriores com um único texto) aguarda até o limite da janela de debounce antes de ser despachada, caso uma linha de prévia de URL esteja a caminho. As mensagens de chats em grupo mantêm o despacho imediato.
    • A saída mesclada é limitada. O texto mesclado é limitado a 4000 caracteres, com um marcador explícito …[truncated]; os anexos são limitados a 20; as entradas de origem são limitadas a 10 (além disso, a primeira e as mais recentes são mantidas). Cada GUID de origem é registrado em coalescedMessageGuids para telemetria posterior.
    • Somente DMs. Os chats em grupo seguem para o despacho por mensagem, para que o bot permaneça responsivo quando várias pessoas estiverem digitando.
    • Opcional, por canal. Outros canais (Discord, Slack, Telegram, WhatsApp, …) não são afetados. Configurações legadas do BlueBubbles que definem channels.bluebubbles.coalesceSameSenderDms devem migrar esse valor para channels.imessage.coalesceSameSenderDms.

    Cenários e o que o agente vê

    A coluna "Sinalizador ativado" mostra o comportamento em uma versão do imsg que emite balloon_bundle_id. Em versões mais antigas do imsg que não emitem nenhum metadado de balão, as linhas abaixo marcadas como "Dois turnos"/"N turnos" recorrem a uma mesclagem legada (um turno): o OpenClaw não consegue distinguir estruturalmente um envio dividido de envios separados e, portanto, preserva a mesclagem anterior aos metadados. A separação precisa é ativada assim que a versão passa a emitir metadados de balão.

    O usuário redige chat.db produz Sinalizador desativado (padrão) Sinalizador ativado + janela (imsg emite metadados de balão)
    Dump https://example.com (um envio) 2 linhas com intervalo de ~1 s Dois turnos do agente: apenas "Dump", depois URL Um turno: texto mesclado Dump https://example.com
    Save this 📎image.jpg caption (anexo + texto) 2 linhas sem metadados de balão de URL Dois turnos Dois turnos após a observação dos metadados; um turno mesclado em sessões antigas/pré-travamento sem metadados
    /status (comando independente) 1 linha Despacho imediato Aguarda até o limite da janela e então despacha
    URL colada sozinha 1 linha Despacho imediato Aguarda até o limite da janela e então despacha
    Texto + URL enviados deliberadamente como duas mensagens separadas, com minutos de intervalo 2 linhas fora da janela Dois turnos Dois turnos (a janela expira entre eles)
    Rajada rápida (>10 DMs pequenas dentro da janela) N linhas sem metadados de balão de URL N turnos N turnos após a observação dos metadados; um turno mesclado e limitado em sessões antigas/pré-travamento sem metadados
    Duas pessoas digitando em um chat em grupo N linhas de M remetentes M+ turnos (um por agrupamento de remetente) M+ turnos — chats em grupo não são aglutinados

    Recuperação de entrada após a reinicialização de uma ponte ou do Gateway

    O iMessage recupera mensagens perdidas enquanto o Gateway estava inativo e, ao mesmo tempo, suprime a "bomba de mensagens pendentes" obsoletas que a Apple pode descarregar após uma recuperação de Push. O comportamento padrão está sempre ativado e é baseado na desduplicação de entrada.

    • Desduplicação de repetição. Cada mensagem de entrada despachada é registrada por seu GUID da Apple no estado persistente do Plugin (imessage.inbound-dedupe), reivindicada durante a ingestão e confirmada após o processamento (liberada em caso de falha transitória para permitir uma nova tentativa). Tudo o que já foi processado é descartado, em vez de ser despachado duas vezes. Isso permite que a recuperação repita as mensagens de forma agressiva sem manter registros individuais por mensagem.
    • Recuperação do período de inatividade. Na inicialização, o monitor memoriza o último rowid despachado do chat.db (um cursor persistente por conta) e o passa para imsg watch.subscribe como since_rowid, para que o imsg repita as linhas recebidas enquanto o Gateway estava inativo e, em seguida, acompanhe as novas linhas em tempo real. A repetição é limitada às 500 linhas mais recentes e a mensagens com até ~2 horas, enquanto a desduplicação descarta tudo o que já tiver sido processado.
    • Limite de idade para mensagens pendentes obsoletas. As linhas acima do limite de inicialização são realmente novas; uma linha cuja data de envio seja mais de ~15 minutos anterior à chegada pertence às mensagens pendentes descarregadas pelo Push e é suprimida. As linhas repetidas (no limite ou abaixo dele) usam a janela de recuperação mais ampla, para que uma mensagem perdida recentemente seja entregue sem incluir o histórico antigo.

    A recuperação funciona tanto em configurações locais quanto remotas de cliPath, porque a repetição de since_rowid ocorre pela mesma conexão RPC do imsg. A diferença é a janela: quando o Gateway consegue ler o chat.db (localmente), ele ancora o limite do rowid de inicialização, restringe o intervalo da repetição e entrega mensagens perdidas com até algumas horas. Por meio de um cliPath SSH remoto, ele não consegue ler o banco de dados; portanto, a repetição não é limitada e todas as linhas usam o limite de idade de mensagens em tempo real — ainda recupera mensagens perdidas recentemente e suprime mensagens pendentes antigas, mas com a janela mais estreita de mensagens em tempo real. Execute o Gateway no Mac com Messages para obter a janela de recuperação mais ampla.

    Sinal visível para o operador

    As mensagens pendentes suprimidas são registradas no nível padrão, nunca descartadas silenciosamente (o sinalizador recovery mostra qual janela foi aplicada):

    text
    imessage: mensagens pendentes de entrada obsoletas suprimidas account=<id> sent=<iso> recovery=<bool> (&lt;N&gt; suprimidas desde a inicialização)

    Migração

    channels.imessage.catchup.* está obsoleto — a recuperação do período de inatividade é automática e não exige configuração em novas instalações. Configurações existentes com catchup.enabled: true continuam sendo respeitadas como um perfil de compatibilidade para a janela de repetição da recuperação. Blocos de recuperação desabilitados (enabled: false ou sem enabled: true) foram descontinuados; openclaw doctor --fix os remove.

    Solução de problemas

    imsg não encontrado ou RPC incompatível

    Valide o binário e a compatibilidade com RPC:

    bash
    imsg rpc --helpimsg status --jsonopenclaw channels status --probe

    Se a sondagem indicar incompatibilidade com RPC, atualize o imsg. Se as ações de API privada estiverem indisponíveis, execute imsg launch na sessão do usuário do macOS com login ativo e faça a sondagem novamente. Se o Gateway não estiver sendo executado no macOS, use a configuração de Mac remoto por SSH acima, em vez do caminho local padrão do imsg.

    As mensagens são enviadas, mas as iMessages recebidas não chegam

    Primeiro, confirme se a mensagem chegou ao Mac local. Se o chat.db não mudar, o OpenClaw não poderá receber a mensagem, mesmo que imsg status --json indique uma ponte íntegra.

    bash
    imsg chats --limit 10 --jsonimsg watch --chat-id <chat-id> --jsonsqlite3 ~/Library/Messages/chat.db \"select datetime(max(date)/1000000000 + 978307200, 'unixepoch', 'localtime'), max(ROWID) from message;"

    Se as mensagens enviadas pelo telefone não criarem novas linhas, corrija a camada do Messages do macOS e do Apple Push antes de alterar a configuração do OpenClaw. Uma única atualização dos serviços geralmente é suficiente:

    bash
    launchctl kickstart -k system/com.apple.apsdlaunchctl kickstart -k gui/$(id -u)/com.apple.CommCenterlaunchctl kickstart -k gui/$(id -u)/com.apple.identityservicesdlaunchctl kickstart -k gui/$(id -u)/com.apple.imagentimsg launchopenclaw gateway restart

    Envie uma nova iMessage pelo telefone e confirme uma nova linha no chat.db ou um evento em imsg watch antes de depurar as sessões do OpenClaw. Não execute isso como um ciclo periódico de reinicialização da ponte; repetir imsg launch e reinicializações do Gateway durante o trabalho ativo pode interromper entregas e deixar execuções do canal em andamento sem conclusão.

    O Gateway não está sendo executado no macOS

    O cliPath: "imsg" padrão deve ser executado no Mac conectado ao Messages. No Linux ou Windows, defina channels.imessage.cliPath como um script wrapper que se conecta via SSH a esse Mac e executa imsg "$@".

    bash
    #!/usr/bin/env bashexec ssh -T messages-mac imsg "$@"

    Em seguida, execute:

    bash
    openclaw channels status --probe --channel imessage
    As DMs são ignoradas

    Verifique:

    • channels.imessage.dmPolicy
    • channels.imessage.allowFrom
    • aprovações de pareamento (openclaw pairing list imessage)
    As mensagens em grupo são ignoradas

    Verifique:

    • channels.imessage.groupPolicy
    • channels.imessage.groupAllowFrom
    • comportamento da lista de permissões de channels.imessage.groups
    • configuração de padrões de menção (agents.list[].groupChat.mentionPatterns)
    Anexos remotos falham

    Verifique:

    • channels.imessage.remoteHost
    • channels.imessage.remoteAttachmentRoots
    • autenticação por chave SSH/SCP no host do Gateway
    • a chave do host existe em ~/.ssh/known_hosts no host do Gateway
    • legibilidade do caminho remoto no Mac que executa o Messages
    As solicitações de permissão do macOS foram ignoradas

    Execute novamente em um terminal gráfico interativo, no mesmo contexto de usuário/sessão, e aprove as solicitações:

    bash
    imsg chats --limit 1imsg send <handle> "test"

    Confirme que Full Disk Access + Automation foram concedidos ao contexto de processo que executa o OpenClaw/imsg.

    Referências de configuração

    Relacionados

    Was this useful?
    On this page

    On this page