Mainstream messaging
iMessage
Status: integração nativa com CLI externa. O Gateway inicia imsg rpc e se comunica por JSON-RPC em stdio (sem daemon/porta separado). Ações avançadas exigem imsg launch e uma sondagem bem-sucedida da API privada.
Respostas, tapbacks, efeitos, enquetes, anexos e gerenciamento de grupos.
DMs do iMessage usam modo de pareamento por padrão.
Use um wrapper SSH quando o Gateway não estiver em execução no Mac do Messages.
Referência completa dos campos do iMessage.
Configuração rápida
Mac local (caminho rápido)
Instale e verifique imsg
brew install steipete/tap/imsgimsg rpc --helpimsg launchopenclaw channels status --probeConfigure o OpenClaw
{channels: {imessage: {enabled: true,cliPath: "/usr/local/bin/imsg",dbPath: "/Users/user/Library/Messages/chat.db",},},}Inicie o gateway
openclaw gatewayAprove o primeiro pareamento por DM (dmPolicy padrão)
openclaw pairing list imessageopenclaw pairing approve imessage <CODE>Solicitações de pareamento expiram após 1 hora.
Mac remoto via SSH
O OpenClaw exige apenas um cliPath compatível com stdio, então você pode apontar cliPath para um script wrapper que faz SSH para um Mac remoto e executa imsg.
#!/usr/bin/env bashexec ssh -T gateway-host imsg "$@"Configuração recomendada quando anexos estão habilitados:
{channels: {imessage: { enabled: true, cliPath: "~/.openclaw/scripts/imsg-ssh", remoteHost: "user@gateway-host", // usado para buscas de anexos por SCP includeAttachments: true, // Opcional: sobrescreve as raízes de anexos permitidas. // Os padrões incluem /Users/*/Library/Messages/Attachments attachmentRoots: ["/Users/*/Library/Messages/Attachments"], remoteAttachmentRoots: ["/Users/*/Library/Messages/Attachments"],},},}Se remoteHost não estiver definido, o OpenClaw tenta detectá-lo automaticamente analisando o script wrapper SSH.
remoteHost deve ser host ou user@host (sem espaços ou opções SSH).
O OpenClaw usa verificação estrita de chave de host para SCP, portanto a chave do host de retransmissão já deve existir em ~/.ssh/known_hosts.
Caminhos de anexos são validados contra raízes permitidas (attachmentRoots / remoteAttachmentRoots).
Requisitos e permissões (macOS)
- O Messages deve estar com sessão iniciada no Mac que executa
imsg. - Acesso Total ao Disco é obrigatório para o contexto de processo que executa OpenClaw/
imsg(acesso ao banco de dados do Messages). - Permissão de Automação é obrigatória para enviar mensagens por meio do Messages.app.
- Para ações avançadas (reagir / editar / desfazer envio / resposta em thread / efeitos / enquetes / operações de grupo), a Proteção de Integridade do Sistema deve ser desativada — consulte Habilitando a API privada do imsg abaixo. Envio/recebimento básico de texto e mídia funciona sem isso.
Envios por wrapper SSH falham com AppleEvents -1743
Uma configuração por SSH remoto pode ler chats, passar em channels status --probe e processar mensagens de entrada, enquanto envios de saída ainda falham com um erro de autorização do AppleEvents:
Not authorized to send Apple events to Messages. (-1743)Verifique o banco de dados TCC do usuário do Mac com sessão iniciada ou Ajustes do Sistema > Privacidade e Segurança > Automação. 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 pode não expor um seletor utilizável do Messages para esse cliente do lado do servidor SSH:
kTCCServiceAppleEvents | /usr/libexec/sshd-keygen-wrapper | auth_value=0 | com.apple.MobileSMSNesse estado, repetir tccutil reset AppleEvents ou executar novamente imsg send pelo mesmo wrapper SSH pode continuar falhando porque o contexto de processo que precisa da Automação do Messages é o wrapper SSH, não um app ao qual a UI possa conceder permissão.
Use um dos contextos de processo imsg compatíveis em vez disso:
- Execute o Gateway, ou pelo menos a ponte
imsg, na sessão local do usuário do Messages com login ativo. - Inicie o Gateway com um LaunchAgent para esse usuário após conceder Acesso Total ao Disco e Automação a partir da mesma sessão.
- Se você mantiver a topologia SSH de dois usuários, verifique se um
imsg sendreal de saída funciona pelo wrapper exato antes de habilitar o canal. Se a Automação não puder ser concedida, reconfigure para uma instalaçãoimsgde usuário único em vez de depender do wrapper SSH para envios.
Habilitando a API privada do imsg
imsg é distribuído em dois modos operacionais:
- Modo básico (padrão, sem necessidade de alterações no SIP): texto e mídia de saída via
send, observação/histórico de entrada, lista de chats. Isso é o que você obtém de imediato com uma instalação nova viabrew install steipete/tap/imsgmais as permissões padrão do macOS acima. - Modo de API privada:
imsginjeta uma dylib auxiliar noMessages.apppara chamar funções internas doIMCore. Isso desbloqueiareact,edit,unsend,reply(em thread),sendWithEffect,pollepoll-vote(enquetes nativas do Messages),renameGroup,setGroupIcon,addParticipant,removeParticipant,leaveGroup, além de indicadores de digitação e recibos de leitura.
Para acessar a superfície de ações avançadas que esta página do canal documenta, você precisa do modo de API privada. O README do imsg é explícito sobre o requisito:
Recursos avançados como
read,typing,launch, envio rico com suporte de ponte, mutação de mensagens e gerenciamento de chats são opcionais. Eles exigem que o SIP esteja desativado e que uma dylib auxiliar seja injetada noMessages.app.imsg launchse recusa a injetar quando o SIP está habilitado.
A técnica de injeção do auxiliar usa a própria dylib do imsg para acessar APIs privadas do Messages. Não há servidor de terceiros nem runtime BlueBubbles no caminho iMessage do OpenClaw.
Configuração
-
Instale (ou atualize)
imsgno Mac que executa Messages.app:bash brew install steipete/tap/imsgimsg --versionimsg status --jsonA saída de
imsg status --jsonrelatabridge_version,rpc_methodseselectorspor método, para que você veja o que a build atual oferece suporte antes de começar. -
Desative a Proteção de Integridade do Sistema e, (no macOS moderno) a Validação de Biblioteca. Injetar uma dylib auxiliar não Apple no
Messages.appassinado pela Apple exige SIP desativado e validação de biblioteca relaxada. A etapa de SIP no modo de Recuperação é específica da versão do macOS:- macOS 10.13-10.15 (Sierra-Catalina): desative a Validação de Biblioteca pelo Terminal, reinicie no Modo de Recuperação, execute
csrutil disable, reinicie. - macOS 11+ (Big Sur e posterior), Intel: Modo de Recuperação (ou Recuperação pela Internet),
csrutil disable, reinicie. - macOS 11+, Apple Silicon: sequência de inicialização pelo botão liga/desliga para entrar em Recuperação; em versões recentes do macOS, mantenha pressionada a tecla Shift Esquerdo ao clicar em Continuar, depois
csrutil disable. Configurações de máquina virtual seguem um fluxo separado, então faça primeiro um snapshot da VM.
No macOS 11 e posteriores, apenas
csrutil disablegeralmente não é suficiente. A Apple ainda aplica validação de biblioteca contra oMessages.appcomo binário de plataforma, então um auxiliar assinado ad hoc é rejeitado (Library Validation failed: ... platform binary, but mapped file is not) mesmo com SIP desativado. Após desativar o SIP, também desative a validação de biblioteca e reinicie:bash sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist DisableLibraryValidation -bool truemacOS 26 (Tahoe), verificado na 26.5.1: SIP desativado mais o comando
DisableLibraryValidationacima é suficiente para injetar o auxiliar da 26.0 até 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 launchinjeta eimsg statusrelataadvanced_features: true. - Sem o plist (mesmo com SIP desativado):
imsg launchfalha comFailed to launch: Timeout waiting for Messages.app to initialize. O AMFI rejeita o auxiliar ad hoc no carregamento, então a ponte nunca fica pronta e o lançamento expira. Esse timeout é o sintoma que a maioria das pessoas encontra no Tahoe, e a correção é o plist acima, não algo mais drástico.
Isso foi confirmado com um antes/depois controlado no macOS 26.5.1 (Apple Silicon): com o plist, a dylib é mapeada no
Messages.appe a ponte sobe; remova o plist e reinicie, eimsg launchproduz a falha de timeout acima, com a dylib não mapeada.Se a injeção de
imsg launchouselectorsespecíficos começarem a retornar falso após uma atualização do macOS, esse gate geralmente é a causa. Verifique o estado do SIP e da validação de biblioteca antes de presumir que a própria etapa do SIP falhou. Se essas configurações estiverem corretas e a ponte ainda não conseguir injetar, coleteimsg status --jsonmais a saída deimsg launche relate ao projetoimsgem vez de enfraquecer controles adicionais de segurança em todo o sistema.Siga o fluxo em modo de Recuperação da Apple para seu Mac para desabilitar o SIP antes de executar
imsg launch. - macOS 10.13-10.15 (Sierra-Catalina): desative a Validação de Biblioteca pelo Terminal, reinicie no Modo de Recuperação, execute
-
Injete o auxiliar. Com o SIP desabilitado e o Messages.app conectado:
bash imsg launchimsg launchse recusa a injetar quando o SIP ainda está habilitado, então isso também serve como confirmação de que a etapa 2 funcionou. -
Verifique a ponte a partir do OpenClaw:
bash openclaw channels status --probeA entrada do iMessage deve relatar
works, eimsg status --json | jq '{rpc_methods, selectors}'deve mostrar as capacidades expostas pela sua compilação do macOS. A criação de enquetes exigeselectors.pollPayloadMessage; a votação exige tantoselectors.pollVoteMessagequanto o método RPCpoll.vote. O Plugin do OpenClaw anuncia apenas ações compatíveis com a sondagem em cache, enquanto um cache vazio permanece otimista e sonda no primeiro envio.
Se openclaw channels status --probe relatar 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 sair de operação (reinicialização do Messages.app, atualização do SO etc.) e o status em cache available: true continuará anunciando ações até que a próxima sondagem atualize.
Quando você não pode desabilitar o SIP
Se SIP desabilitado não for aceitável para seu modelo de ameaças:
imsgvolta para o modo básico — texto + mídia + recebimento apenas.- O Plugin do OpenClaw ainda anuncia envio de texto/mídia e monitoramento de entrada; ele apenas oculta
react,edit,unsend,reply,sendWithEffecte operações de grupo da superfície de ações (conforme o gate de capacidade por método). - Você pode executar um Mac separado que não seja Apple Silicon (ou um Mac dedicado para bot) com SIP desativado para a carga de trabalho do iMessage, mantendo o SIP habilitado nos seus dispositivos principais. Veja Usuário macOS dedicado para bot (identidade iMessage separada) abaixo.
Controle de acesso e roteamento
Política de DM
channels.imessage.dmPolicy controla mensagens diretas:
pairing(padrão)allowlistopen(exige queallowFrominclua"*")disabled
Campo da lista de permissões: channels.imessage.allowFrom.
Entradas da lista de permissões devem identificar remetentes: identificadores ou grupos estáticos de acesso de remetente (accessGroup:<name>). Use channels.imessage.groupAllowFrom para alvos de chat como chat_id:*, chat_guid:* ou chat_identifier:*; use channels.imessage.groups para chaves numéricas de registro chat_id.
Política de grupo + menções
channels.imessage.groupPolicy controla o tratamento de grupos:
allowlist(padrão quando configurado)opendisabled
Lista de permissões de remetentes de grupo: channels.imessage.groupAllowFrom.
Entradas de groupAllowFrom também podem referenciar grupos estáticos de acesso de remetente (accessGroup:<name>).
Fallback em tempo de execução: se groupAllowFrom não estiver definido, as verificações de remetente de grupo do iMessage usam allowFrom; defina groupAllowFrom quando a admissão de DMs e grupos precisar ser diferente.
Observação de tempo de execução: se channels.imessage estiver completamente ausente, o runtime volta para groupPolicy="allowlist" e registra um aviso (mesmo que channels.defaults.groupPolicy esteja definido).
Gate de menções para grupos:
- iMessage não tem metadados nativos de menção
- a detecção de menções usa padrões regex (
agents.list[].groupChat.mentionPatterns, fallbackmessages.groupChat.mentionPatterns) - sem padrões configurados, o gate de menções não pode ser imposto
Comandos de controle de remetentes autorizados podem ignorar o gate de menções em grupos.
systemPrompt por grupo:
Cada entrada em channels.imessage.groups.* aceita uma string opcional systemPrompt. O valor é injetado no prompt de sistema do agente em cada turno que trata uma mensagem nesse grupo. A resolução espelha a resolução de prompt por grupo usada por channels.whatsapp.groups:
- Prompt de sistema específico do grupo (
groups["<chat_id>"].systemPrompt): usado quando a entrada específica do grupo existe no mapa e sua chavesystemPromptestá definida. SesystemPromptfor uma string vazia (""), o curinga é suprimido e nenhum prompt de sistema é aplicado a esse grupo. - Prompt de sistema curinga de grupo (
groups["*"].systemPrompt): usado quando a entrada específica do grupo está totalmente ausente do mapa, ou quando ela existe mas não define nenhuma chavesystemPrompt.
{ channels: { imessage: { groupPolicy: "allowlist", groupAllowFrom: ["+15555550123"], groups: { "*": { systemPrompt: "Use British spelling." }, "8421": { requireMention: true, systemPrompt: "This is the on-call rotation chat. Keep replies under 3 sentences.", }, "9907": { // explicit suppression: the wildcard "Use British spelling." does not apply here systemPrompt: "", }, }, }, },}Prompts por grupo se aplicam apenas a mensagens de grupo — mensagens diretas neste canal não são afetadas.
Sessões e respostas determinísticas
- DMs usam roteamento direto; grupos usam roteamento de grupo.
- Com
session.dmScope=mainpadrão, DMs do iMessage são agrupadas na sessão principal do agente. - Sessões de grupo são isoladas (
agent:<agentId>:imessage:group:<chat_id>). - Respostas são roteadas de volta para o iMessage usando metadados de canal/alvo de origem.
Comportamento de thread parecido com grupo:
Algumas threads do iMessage com vários participantes podem chegar com is_group=false.
Se esse chat_id estiver explicitamente configurado em channels.imessage.groups, o OpenClaw o trata como tráfego de grupo (gate de grupo + isolamento de sessão de grupo).
Associações de conversa ACP
Chats legados do iMessage também podem ser associados a sessões ACP.
Fluxo rápido do operador:
- Execute
/acp spawn codex --bind heredentro da DM ou do chat em grupo permitido. - Mensagens futuras nessa mesma conversa do iMessage são roteadas para a sessão ACP criada.
/newe/resetredefinem a mesma sessão ACP associada no lugar./acp closefecha a sessão ACP e remove a associação.
Associações persistentes configuradas são compatíveis por meio de entradas bindings[] de nível superior com type: "acp" e match.channel: "imessage".
match.peer.id pode usar:
- identificador de DM normalizado como
+15555550123ouuser@example.com chat_id:<id>(recomendado para associações de grupo estáveis)chat_guid:<guid>chat_identifier:<identifier>
Exemplo:
{ 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" }, }, ],}Veja Agentes ACP para o comportamento compartilhado de associação ACP.
Padrões de implantação
Usuário macOS dedicado para bot (identidade iMessage separada)
Use um Apple ID dedicado e um usuário macOS para que o tráfego do bot fique isolado do seu perfil pessoal do Messages.
Fluxo típico:
- Crie/entre em um usuário macOS dedicado.
- Entre no Messages com o Apple ID do bot nesse usuário.
- Instale
imsgnesse usuário. - Crie um wrapper SSH para que o OpenClaw possa executar
imsgno contexto desse usuário. - Aponte
channels.imessage.accounts.<id>.cliPathe.dbPathpara esse perfil de usuário.
A primeira execução pode exigir aprovações pela GUI (Automação + Acesso Total ao Disco) nessa sessão de usuário do bot.
Mac remoto via Tailscale (exemplo)
Topologia comum:
- gateway executa em Linux/VM
- iMessage +
imsgexecuta em um Mac na sua tailnet - o wrapper
cliPathusa SSH para executarimsg remoteHosthabilita buscas de anexos via SCP
Exemplo:
{ 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 bashexec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@"Use chaves SSH para que tanto SSH quanto SCP sejam não interativos.
Garanta primeiro 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 várias contas
iMessage aceita configuração por conta em channels.imessage.accounts.
Cada conta pode sobrescrever 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 semear novas sessões de mensagem direta com histórico recente decodificado de imsg para essa conversa. Use channels.imessage.dms["<sender>"].historyLimit para sobrescritas por remetente, incluindo 0 para desabilitar histórico para um remetente.
O histórico de DM do iMessage é buscado sob demanda em imsg. Deixar dmHistoryLimit indefinido desabilita a semeadura global de histórico de DM, mas um channels.imessage.dms["<sender>"].historyLimit positivo por remetente ainda habilita a semeadura para esse remetente.
Mídia, fragmentação e alvos de entrega
Anexos e mídia
- a ingestão de anexos recebidos fica desativada por padrão — defina
channels.imessage.includeAttachments: truepara encaminhar fotos, memorandos de voz, vídeos e outros anexos ao agente. Com ela desativada, iMessages contendo somente anexos são descartadas antes de chegar ao agente e podem não produzir nenhuma linha de logInbound message. - caminhos de anexos remotos podem ser buscados via SCP quando
remoteHostestá definido - caminhos de anexos precisam corresponder às raízes permitidas:
channels.imessage.attachmentRoots(local)channels.imessage.remoteAttachmentRoots(modo SCP remoto)- padrão de raiz padrão:
/Users/*/Library/Messages/Attachments
- SCP usa verificação rigorosa de chave de host (
StrictHostKeyChecking=yes) - o tamanho de mídia enviada usa
channels.imessage.mediaMaxMb(padrão 16 MB)
Divisão de envios em partes
- limite de parte de texto:
channels.imessage.textChunkLimit(padrão 4000) - modo de divisão em partes:
channels.imessage.chunkModelength(padrão)newline(divisão priorizando parágrafos)
Formatos de endereçamento
Destinos explícitos preferidos:
chat_id:123(recomendado para roteamento estável)chat_guid:...chat_identifier:...
Destinos por identificador também são aceitos:
imessage:+1555...sms:+1555...user@example.com
imsg chats --limit 20Ações de API privada
Quando imsg launch está em execução e openclaw channels status --probe relata privateApi.available: true, a ferramenta de mensagens pode usar ações nativas do iMessage além dos envios de texto normais.
{ 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). Tapbacks aceitos mapeiam para amar, curtir, não curtir, rir, enfatizar e interrogação. - reply: Envia uma resposta em thread para uma mensagem existente (
messageId,textoumessage, maischatGuid,chatId,chatIdentifierouto). - sendWithEffect: Envia texto com um efeito do iMessage (
textoumessage,effectoueffectId). - edit: Edita uma mensagem enviada em versões compatíveis do macOS/API privada (
messageId,textounewText). - unsend: Retrai uma mensagem enviada em versões compatíveis do macOS/API privada (
messageId). - upload-file: Envia mídia/arquivos (
buffercomo base64 ou ummedia/path/filePathhidratado,filename,asVoiceopcional). Alias legado:sendAttachment. - renameGroup, setGroupIcon, addParticipant, removeParticipant, leaveGroup: Gerencia chats em grupo quando o destino atual é uma conversa em grupo.
- poll: Cria uma enquete nativa do Apple Messages (
pollQuestion,pollOptionrepetido de 2 a 12 vezes, maischatGuid,chatId,chatIdentifierouto). Destinatários no iOS/iPadOS/macOS 26+ veem e votam nela nativamente; versões mais antigas do sistema operacional recebem um fallback de texto "Enviou uma enquete". Requerselectors.pollPayloadMessage. - poll-vote: Vota em uma enquete existente (
pollIdoumessageId, mais exatamente um depollOptionIndex,pollOptionIdoupollOptionText). Requerselectors.pollVoteMessagee o método RPCpoll.vote.
Enquetes recebidas aceitas são renderizadas para o agente com a pergunta, rótulos numerados das opções, contagens de votos e o ID da mensagem de enquete necessário para poll-vote.
IDs de mensagem
O contexto recebido do iMessage inclui valores MessageSid curtos e GUIDs completos de mensagem quando disponíveis. IDs curtos são limitados ao cache recente de respostas com suporte em SQLite e são verificados contra o chat atual antes do uso. Se um ID curto expirou ou pertence a outro chat, tente novamente com o MessageSidFull completo.
Detecção de capacidade
O OpenClaw oculta ações de API privada somente quando o status em cache da sondagem indica que a ponte está indisponível. Se o status for desconhecido, as ações permanecem visíveis e disparam sondagens sob demanda para que a primeira ação possa ter sucesso após imsg launch sem uma atualização manual separada de status.
Confirmações de leitura e digitação
Quando a ponte de API privada está ativa, chats recebidos aceitos são marcados como lidos e chats diretos mostram 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 de leitura com:
{ channels: { imessage: { sendReadReceipts: false, }, },}Builds mais antigas do imsg anteriores à lista de capacidades por método bloquearão digitação/leitura silenciosamente; o OpenClaw registra um aviso único por reinicialização para que a confirmação ausente seja atribuível.
Tapbacks recebidos
O OpenClaw assina tapbacks do iMessage e roteia reações aceitas como eventos de sistema em vez de texto de mensagem normal, então um tapback de usuário não dispara um loop de resposta comum.
O modo de notificação é controlado por channels.imessage.reactionNotifications:
"own"(padrão): notifica somente quando usuários reagem a mensagens escritas pelo bot."all": notifica para 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 é roteada para o iMessage, o gateway entrega uma solicitação de aprovação nativamente e aceita um tapback para resolvê-la:
👍(tapback de curtir) →allow-once👎(tapback de não curtir) →denyallow-alwayscontinua sendo um fallback manual: envie/approve <id> allow-alwayscomo uma resposta comum.
O tratamento de reações exige que o identificador do usuário que reagiu seja 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 Apple ID dele. A entrada curinga "*" é respeitada, mas permite que qualquer remetente aprove. O atalho de reação ignora intencionalmente reactionNotifications, dmPolicy e groupAllowFrom porque a lista de permissões de aprovadores explícitos é o único controle que importa para a resolução de aprovações.
Mudança de comportamento nesta versão: Quando channels.imessage.allowFrom não está vazio, o comando de texto /approve <id> <decision> agora é autorizado contra essa lista de aprovadores (não contra a lista de permissões de DM mais ampla). Remetentes permitidos na lista de permissões de DM, mas não em allowFrom, receberão uma negação explícita. Adicione a allowFrom todos os operadores que devem poder aprovar via /approve (e via reações) para preservar o comportamento anterior. Quando allowFrom está vazio, o fallback legado de "mesmo chat" continua em vigor e /approve continua autorizando qualquer pessoa permitida pela lista de permissões de DM.
Observações para operadores:
- O vínculo da reação é armazenado tanto em memória (com TTL correspondente à expiração da aprovação) quanto no armazenamento persistente chaveado do gateway, então um tapback que chega logo após uma reinicialização do gateway ainda resolve a aprovação.
- Tapbacks entre dispositivos com
is_from_me=true(a própria reação do operador em um dispositivo Apple emparelhado) são intencionalmente ignorados para que o bot não possa se autoaprovar. - Tapbacks legados em estilo de texto (
Liked "…"como texto simples de clientes Apple muito antigos) não podem resolver aprovações porque não carregam GUID de mensagem; a resolução por reação exige os metadados estruturados de tapback emitidos por clientes macOS / iOS atuais.
Escritas de configuração
O iMessage permite escritas de configuração iniciadas pelo canal por padrão (para /config set|unset quando commands.config: true).
Desative:
{ channels: { imessage: { configWrites: false, }, },}Coalescência de DMs com envio dividido (comando + URL em uma 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 chat.db separadas:
- Uma mensagem de texto (
"Dump"). - 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 ~0,8-2,0 s de diferença na maioria das configurações. Sem coalescência, o agente recebe somente o comando no turno 1, responde (muitas vezes "envie-me a URL") e só vê a URL no turno 2 — momento em que o contexto do comando já foi perdido. Esse é o pipeline de envio da Apple, não algo introduzido pelo OpenClaw ou pelo imsg.
channels.imessage.coalesceSameSenderDms opta uma DM por armazenar em buffer linhas consecutivas do mesmo remetente. Quando imsg expõe o marcador estrutural de pré-visualização de URL balloon_bundle_id: "com.apple.messages.URLBalloonProvider" em uma das linhas de origem, o OpenClaw mescla somente esse envio dividido real e mantém quaisquer outras linhas em buffer como turnos separados. Em builds 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, então recorre a mesclar o balde. Isso preserva o comportamento anterior aos metadados em vez de regredir envios divididos Dump <url> para dois turnos. Chats em grupo continuam despachando por mensagem, para que a estrutura de turnos multiusuário seja preservada.
Quando ativar
Ative quando:
- Você distribui Skills que esperam
comando + payloadem uma mensagem (despejar, colar, salvar, enfileirar etc.). - Seus usuários colam URLs junto com comandos.
- Você pode aceitar a latência adicional do turno de DM (veja abaixo).
Deixe desativado quando:
- Você precisa de latência mínima de comando para gatilhos de DM de uma única palavra.
- Todos os seus fluxos são comandos únicos sem acompanhamentos de payload.
Ativação
{ channels: { imessage: { coalesceSameSenderDms: true, // opt in (default: false) }, },}Com a flag ativada e sem messages.inbound.byChannel.imessage explícito nem 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 a cadência de envio dividido de pré-visualização de URL da Apple pode se estender por vários segundos enquanto o Messages.app emite a linha de pré-visualização.
Para ajustar a janela você mesmo:
{ messages: { inbound: { byChannel: { // 7000 ms covers observed Messages.app URL-preview delays. imessage: 7000, }, }, },}Trade-offs
- Mesclagem precisa precisa dos metadados atuais de payload do
imsg. Quando a linha da URL incluiballoon_bundle_id, apenas esse split-send real é mesclado e outras linhas em buffer permanecem separadas. Em builds antigos doimsgque não expõem metadados de balão, o OpenClaw recorre à mesclagem do bucket em buffer para que split-sendsDump <url>não regridam para dois turnos (compatibilidade provisória, removida assim que oimsgcoalescer split-sends upstream). - Latência adicionada para mensagens de DM. Com o sinalizador ativado, toda DM (incluindo comandos de controle independentes e follow-ups de texto único) espera até a janela de debounce antes do despacho, caso uma linha de prévia de URL esteja chegando. Mensagens de chat em grupo mantêm despacho instantâneo.
- A saída mesclada é limitada. O texto mesclado é limitado a 4000 caracteres com um marcador explícito
…[truncated]; anexos são limitados a 20; entradas de origem são limitadas a 10 (a primeira mais as mais recentes são mantidas além disso). Todo GUID de origem é rastreado emcoalescedMessageGuidspara telemetria downstream. - Apenas DM. Chats em grupo seguem para despacho por mensagem para que o bot continue responsivo quando várias pessoas estiverem digitando.
- Opt-in, por canal. Outros canais (Telegram, WhatsApp, Slack, …) não são afetados. Configurações legadas do BlueBubbles que definem
channels.bluebubbles.coalesceSameSenderDmsdevem migrar esse valor parachannels.imessage.coalesceSameSenderDms.
Cenários e o que o agente vê
A coluna "Sinalizador ativado" mostra o comportamento em um build do imsg que emite balloon_bundle_id. Em builds antigos 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 split-send de envios separados, então preserva a mesclagem anterior aos metadados. A separação precisa é ativada quando o build emite metadados de balão.
| Usuário compõe | 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 ~1 s de intervalo | Dois turnos do agente: "Dump" sozinho, 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 depois que metadados são observados; um turno mesclado em sessões antigas/pré-latch sem metadados |
/status (comando independente) |
1 linha | Despacho instantâneo | Espera até a janela, depois despacha |
| URL colada sozinha | 1 linha | Despacho instantâneo | Espera até a janela, depois despacha |
| Texto + URL enviados como duas mensagens separadas deliberadas, com minutos de intervalo | 2 linhas fora da janela | Dois turnos | Dois turnos (a janela expira entre eles) |
| Enxurrada rápida (>10 DMs pequenas dentro da janela) | N linhas sem metadados de balão de URL | N turnos | N turnos depois que metadados são observados; um turno mesclado limitado em sessões antigas/pré-latch sem metadados |
| Duas pessoas digitando em um chat em grupo | N linhas de M remetentes | M+ turnos (um por bucket de remetente) | M+ turnos — chats em grupo não são coalescidos |
Recuperação de entrada após uma reinicialização da ponte ou do gateway
O iMessage recupera mensagens perdidas enquanto o gateway estava fora do ar e, ao mesmo tempo, suprime a "bomba de backlog" obsoleta que a Apple pode liberar após uma recuperação por Push. O comportamento padrão está sempre ativado, construído sobre a deduplicação de entrada.
- Deduplicação de replay. Toda mensagem de entrada despachada é registrada por seu GUID da Apple no estado persistente do Plugin (
imessage.inbound-dedupe), reivindicada na ingestão e confirmada após o processamento (liberada em uma falha transitória para que possa tentar novamente). Qualquer coisa já processada é descartada em vez de ser despachada duas vezes. É isso que permite que a recuperação faça replay agressivamente sem bookkeeping por mensagem. - Recuperação de indisponibilidade. Na inicialização, o monitor lembra o último rowid de
chat.dbdespachado (um cursor persistido por conta) e o passa paraimsg watch.subscribecomosince_rowid, para que o imsg reproduza as linhas que chegaram enquanto o gateway estava fora do ar e depois acompanhe ao vivo. O replay é limitado às linhas mais recentes e a mensagens com até ~2 horas de idade, e a deduplicação descarta qualquer coisa já processada. - Cerca de idade para backlog obsoleto. Linhas acima do limite de inicialização são genuinamente ao vivo; uma cujo envio é mais de ~15 minutos anterior à chegada é backlog de flush do Push e é suprimida. Linhas reproduzidas (no limite ou abaixo dele) usam a janela de recuperação mais ampla, então uma mensagem perdida recentemente é entregue enquanto histórico antigo não é.
A recuperação funciona tanto em configurações locais quanto remotas de cliPath, porque o replay de since_rowid usa a mesma conexão RPC do imsg. A diferença é a janela: quando o gateway consegue ler chat.db (local), ele ancora o limite de rowid de inicialização, limita o intervalo de replay e entrega mensagens perdidas com até algumas horas de idade. Por um cliPath remoto via SSH, ele não consegue ler o banco de dados, então o replay não tem limite e toda linha usa a cerca de idade ao vivo — ele ainda recupera mensagens perdidas recentemente e ainda suprime backlog antigo, só com a janela ao vivo mais estreita. Execute o gateway no Mac do Messages para a janela de recuperação mais ampla.
Sinal visível ao operador
Backlog suprimido é registrado no nível padrão, nunca descartado silenciosamente (o sinalizador recovery mostra qual janela foi aplicada):
imessage: suppressed stale inbound backlog account=<id> sent=<iso> recovery=<bool> (<N> suppressed since start)Migração
channels.imessage.catchup.* está obsoleto — a recuperação de indisponibilidade agora é automática e não precisa de configuração para novas instalações. Configurações existentes com catchup.enabled: true continuam sendo honradas como um perfil de compatibilidade para a janela de replay de recuperação. Blocos de catchup desativados (enabled: false ou sem enabled: true) foram aposentados; openclaw doctor --fix os remove.
Solução de problemas
imsg não encontrado ou RPC sem suporte
Valide o binário e o suporte a RPC:
imsg rpc --helpimsg status --jsonopenclaw channels status --probeSe o probe informar RPC sem suporte, atualize o imsg. Se ações de API privada estiverem indisponíveis, execute imsg launch na sessão do usuário macOS conectado e execute o probe novamente. Se o Gateway não estiver rodando no macOS, use a configuração Mac remoto via SSH acima em vez do caminho local padrão do imsg.
Mensagens enviam, mas iMessages de entrada não chegam
Primeiro prove se a mensagem chegou ao Mac local. Se chat.db não mudar, o OpenClaw não consegue receber a mensagem mesmo quando imsg status --json informa uma ponte saudável.
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 mensagens enviadas pelo telefone não criarem novas linhas, repare a camada Messages do macOS e Apple Push antes de alterar a configuração do OpenClaw. Uma atualização pontual de serviço costuma bastar:
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 restartEnvie um novo iMessage pelo telefone e confirme uma nova linha em chat.db ou um evento de imsg watch antes de depurar sessões do OpenClaw. Não execute isso como um loop periódico de relançamento da ponte; imsg launch repetido mais reinicializações do gateway durante trabalho ativo podem interromper entregas e deixar execuções de canal em andamento presas.
Gateway não está rodando no macOS
O cliPath: "imsg" padrão deve rodar no Mac conectado ao Messages. No Linux ou Windows, defina channels.imessage.cliPath para um script wrapper que faça SSH para esse Mac e execute imsg "$@".
#!/usr/bin/env bashexec ssh -T messages-mac imsg "$@"Então execute:
openclaw channels status --probe --channel imessageDMs são ignoradas
Verifique:
channels.imessage.dmPolicychannels.imessage.allowFrom- aprovações de pareamento (
openclaw pairing list imessage)
Mensagens de grupo são ignoradas
Verifique:
channels.imessage.groupPolicychannels.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.remoteHostchannels.imessage.remoteAttachmentRoots- autenticação por chave SSH/SCP a partir do host do gateway
- a chave do host existe em
~/.ssh/known_hostsno host do gateway - legibilidade do caminho remoto no Mac que roda o Messages
Prompts de permissão do macOS foram perdidos
Execute novamente em um terminal GUI interativo no mesmo contexto de usuário/sessão e aprove os prompts:
imsg chats --limit 1imsg send <handle> "test"Confirme que Acesso Total ao Disco + Automação estão concedidos para o contexto de processo que executa OpenClaw/imsg.
Ponteiros de referência de configuração
Relacionados
- Visão geral de canais — todos os canais compatíveis
- Remoção do BlueBubbles e o caminho iMessage do imsg — anúncio e resumo da migração
- Vindo do BlueBubbles — tabela de tradução de configuração e cutover passo a passo
- Pareamento — autenticação de DM e fluxo de pareamento
- Grupos — comportamento de chat em grupo e controle por menção
- Roteamento de canais — roteamento de sessão para mensagens
- Segurança — modelo de acesso e hardening