Agregado4
Chamada de voz pelo WhatsApp — desligada por padrão, e com botão de desligar de verdade
O sistema passa a poder fazer e receber chamadas de voz pelo WhatsApp, e ela chega desligada. Atualizar não liga nada: nenhum número seu é conectado a nada, nenhum serviço novo sobe na sua VPS, e nada muda na sua tela até você decidir.
A razão de tanto cuidado está escrita na própria tela, antes do botão: para fazer chamadas, o sistema precisa conectar um segundo aparelho ao mesmo número de WhatsApp que você já usa para atender — e essa conexão não é feita pelo caminho oficial do WhatsApp. Se ele entender isso como uso indevido, quem é bloqueada é a conta, não só a chamada: você perde também as mensagens desse número. Por isso ligar é decisão de quem administra a empresa, exige marcar que leu o aviso, e fica registrado quem aceitou e quando.
E desligar desliga mesmo. Antes, o único botão que existia era o de conectar — não havia caminho de volta: apagar a configuração escondia a tela e deixava o aparelho vinculado ao seu número para sempre, do lado do WhatsApp. Agora, ao desligar, o sistema desconecta o aparelho de verdade e só então marca como desligado; se a desconexão falhar, ele avisa e mantém tudo como estava, em vez de dizer que acabou com o aparelho ainda lá.
No servidor, o serviço de chamada de voz também nasce desligado: ele só é criado quando quem administra a instalação o liga no arquivo de configuração. Quem não usar a chamada de voz não paga por ela — nem em memória da VPS, nem em superfície exposta. O serviço usado é o oficial do projeto WaCalls, fixado por versão exata e com login obrigatório; ele não é acessível pela internet, apenas pelo próprio sistema.
Trabalho original de @eudanielhenrique.
Os e-mails de acesso passam a funcionar (e a ter marca) num Supabase próprio
Quem roda Supabase self-hosted ganha o que só existia na nuvem: e-mail de confirmação de conta e de redefinição de senha com a marca da instalação, e — o que importa mais — com o link que fecha a sessão.
O app passa a servir os dois moldes em /email-templates/confirmation e /email-templates/recovery. Aponte o GoTrue para eles:
GOTRUE_MAILER_TEMPLATES_CONFIRMATION=https://SEU_DOMINIO/email-templates/confirmation
GOTRUE_MAILER_TEMPLATES_RECOVERY=https://SEU_DOMINIO/email-templates/recovery
GOTRUE_MAILER_SUBJECTS_CONFIRMATION="Confirme seu e-mail · SUA MARCA"
GOTRUE_MAILER_SUBJECTS_RECOVERY="Redefinir sua senha · SUA MARCA"Nada muda para quem não apontar, e nada muda na nuvem do Supabase — lá o caminho continua sendo o marca-emails.sh pela Management API.
O kit ensina e confere, mas não escreve — e o motivo é honesto. O GoTrue não é serviço deste compose: o kit sobe app, worker, scheduler, waha, redis, srh e caddy, e o Supabase próprio é outra stack, que pode nem estar na mesma máquina. Escrever nela seria o instalador editar instalação de terceiro. Então o install.sh passa a imprimir as quatro linhas exatas quando a topologia é própria (antes ele mandava o self-hoster para supabase.com/dashboard, que ele não tem), e bash hostgator-setup-kit/healthcheck.sh ganhou uma seção que mede o estado: se o app serve o molde, se algum GoTrue desta máquina aponta para ele, e se o valor configurado é URL — acusando em vermelho o caminho de arquivo que falha calado.
Por que isso conserta e não só embeleza. O modelo padrão do GoTrue linka para /auth/v1/verify, que devolve um code PKCE. O verificador desse code vive num cookie SameSite=Strict, e clique vindo de webmail é navegação cross-site: o cookie não viaja e a sessão nunca fecha. A conta é confirmada, a pessoa entra pela senha, e fica sem organização e sem menu. Os moldes do app linkam com token_hash, que não depende de cookie nenhum.
A marca passa a seguir o banco. O marca-emails.sh lê o .env, então trocar nome ou cor em Configurações › Marca não reescrevia os e-mails de acesso. Servindo pelo app, a marca é resolvida a cada busca e o GoTrue re-busca sozinho a cada 10 minutos (GOTRUE_MAILER_TEMPLATE_MAX_AGE) — sem reiniciar nada e sem rodar script.
Se você seguiu a receita antiga, troque as variáveis. Até esta versão, docs/deploy-selfhost/README.md e o marca-emails.sh mandavam apontar GOTRUE_MAILER_TEMPLATES_* para um caminho de arquivo. Isso não funciona e falha calado: o GoTrue cola o que não começa com http no fim do SITE_URL e faz um GET, então ele busca https://SEU_DOMINIO/opt/.../confirmation.html, recebe o HTML da tela de login e manda isso para a caixa de entrada do cliente. Medido em 2026-09-09; o Gmail marcou como phishing.
Achado instalando numa VPS com Supabase próprio, seguindo a documentação do produto do começo ao fim.
Guias do assistente para quem instala, opera e contribui com um CLI de IA
Com o repositório aberto no Claude Code, Codex, Cursor, OpenCode ou Antigravity, cinco guias carregam sozinhos na hora certa: instalar e consertar a instalação, montar um cliente por nicho (agentes, roteadores, follow-ups, base de conhecimento), analisar as métricas sem expor dado pessoal, afinar o prompt de um agente com dados, e contribuir com um PR que passa na triagem de primeira. O roteiro do kit de instalação foi corrigido (a verificação em duas etapas é opcional; três provedores de IA; token do Supabase) e o banner final do instalador passa a refletir a escolha de telemetria.
Chamada de voz pelo WhatsApp — ligar e atender de dentro do CRM
O CRM passa a fazer e receber ligações de voz pelo WhatsApp. Quem administra pareia um segundo aparelho no mesmo número, em Configurações › Conexões, e o botão Chamar aparece na ficha de todo contato com telefone. Chamada recebida toca para o time inteiro, como um telefone de escritório; o painel da ligação em andamento, com mudo e desligar, é só de quem está na linha.
O que o sistema faz por conta própria enquanto isso acontece:
O assistente se cala durante a ligação naquela conversa e volta a falar quando você desliga. Ele não responde por cima de alguém que está ao telefone com o cliente.
Ligação atendida conta como contato feito
O negócio deixa de aparecer como parado no Radar de Risco, e o assistente para de propor "retomar contato" com quem você acabou de atender.
Ligação atendida conta como trabalho seu no relatório de atendentes.
Chamada perdida vira aviso na Central, com o número de quem ligou, o motivo em português e um botão para ligar de volta.
A linha do tempo do negócio diz quem atendeu, não "Sistema".
Três recusas deliberadas, porque o certo é não fazer:
Contato que pediu para não ser incomodado não recebe ligação
Quem mandou "PARAR" já não recebia mensagem; agora também não recebe telefonema.
Só quem está na linha desliga
Ninguém derruba a ligação de um colega.
Apagar o canal não apaga o histórico de ligações
— a exclusão vira arquivamento, e o diálogo diz quantas chamadas estão penduradas antes de você confirmar.
Quem exercer o direito de ser esquecido tem o telefone das chamadas apagado junto com o resto; quem pedir seus dados recebe o registro das ligações no relatório.
Nada muda para quem não parear o recurso: ele é opcional e nasce desligado.
Trabalho original de @eudanielhenrique.
Cambiado2
A marca do produto ganha símbolo e logotipo
Instalação que não configurou marca própria passa a mostrar o logotipo do Deskcomm no menu lateral, na tela de entrada e no ícone da aba do navegador — no lugar do nome em texto e da letra "D" sobre a cor de destaque. Quem já definiu nome ou logo próprio em Marca não vê nenhuma diferença: a marca configurada continua valendo em todos esses lugares.
No editor de follow-up, dá para organizar o fluxo e excluir um nó ou uma aresta
Montar um follow-up no canvas exigia arrastar cada bloco à mão, e o único botão de apagar era o do fluxo inteiro. Quem errava uma ligação tinha que desfazer o rascunho ou começar de novo.
Agora, no editor, Organizar empilha o fluxo conectado de cima para baixo; Excluir nó e Excluir aresta saem no painel do item selecionado e na barra de cima — sem apagar o fluxo. As ligações passam a ser em degrau (não diagonais por cima dos blocos), e os botões de zoom do canvas seguem o tema escuro em vez de sumirem no fundo branco da biblioteca.
Corregido8
Webhook de captação agora reconhece o formato de lead do RD Station
Ao apontar um webhook do RD Station para uma fonte de captação de leads, os envios reais não viravam lead: o RD Station empacota os dados dentro de uma lista (leads: [...]), e o leitor de campos do webhook só olhava o nível de cima, então nome, e-mail e telefone chegavam "em branco" e a captação era recusada. O botão interno "Enviar lead de teste" funcionava porque manda os campos soltos — o que escondia o problema.
Agora o webhook reconhece esse formato: extrai o nome, o e-mail e o telefone (inclusive quando o telefone vem no campo de celular do RD, e não no campo de telefone comercial, que costuma vir vazio) e cria o lead na fonte/funil/etapa configurados. Reenvio do mesmo evento pelo RD Station não gera lead duplicado.
Os formatos que já funcionavam (campos soltos, Respondi) continuam iguais. Você não precisa fazer nada para adotar — a partir desta versão os leads do RD Station passam a entrar sozinhos.
Preferências de aviso param de divergir entre o servidor e o navegador
A tela de configurações de notificação abria com o navegador discordando do HTML que o servidor tinha mandado. Quem havia desligado o push de mensagem via, por um instante, o interruptor ligado — e o React reagia a essa discordância descartando e refazendo a árvore da tela no cliente.
O valor era lido dentro do inicializador de useState, que roda de novo na hidratação. Sem window, essa leitura devolve o padrão (tudo ligado); com window, devolve o que está no localStorage. Os dois lados não tinham como concordar para quem tivesse mudado qualquer preferência — em dez interruptores e no identificador que a tela de alertas procura.
A tela passou a ler as preferências por useSyncExternalStore, o mesmo mecanismo que o seletor de tema já usa desde o #666: existe um valor determinístico para a comparação de hidratação, e só depois do commit o React troca para o valor real. O interruptor continua respondendo na hora, sem recarregar a página.
Sem mudança de configuração: nada a editar no .env e nenhum passo a mais na atualização.
Achado a partir do relato de que a divergência reaparecia a cada conserto — a leitura do navegador voltava para dentro de um inicializador novo. Junto vem a guarda que reprova esse padrão, para que a terceira instância não nasça igual.
Quem é convidado entra na empresa ao confirmar o e-mail, sem mais um clique
Confirmar o e-mail vindo de um convite passa a criar o vínculo e abrir o CRM já dentro da empresa. Antes, a confirmação levava a uma tela com um botão "Aceitar convite" — e quem não o apertava terminava autenticado, sem organização e sem menu, num CRM vazio.
Três consertos, todos no ciclo de vida do vínculo:
O convite é aceito na própria confirmação
A rota já sabia tudo o que o botão exigia, e com garantia mais forte: o e-mail do convite é comparado com o que o provedor de autenticação acabou de confirmar. Se o vínculo falhar (convite revogado, banco fora), a tela de aceite continua existindo e recebe a pessoa — nada fica sem saída.
Clicar duas vezes no link do e-mail não desloga mais ninguém
O token é de uso único: o segundo clique falhava e mandava para a tela de login quem já estava logado pelo primeiro, com o cookie de sessão intacto. A pessoa reentrava pela senha e perdia o fio do convite. Agora a rota reconhece a sessão que já existe e segue.
Acesso revogado deixa de virar convite para abrir empresa
Quem tinha o vínculo retirado caía numa tela vazia oferecendo "Configure sua organização" — uma revogação virando criação de tenant. Agora vê uma tela que nomeia o que aconteceu, e a ação de recuperação recusa com o motivo certo, em vez da mensagem sobre convite pendente que aparecia por acaso.
Nada muda na configuração: não há variável nova, passo de atualização nem mudança de schema.
Achado instalando numa VPS com Supabase self-hosted, com dois convidados reais que não conseguiram entrar.
Conectar um número de WhatsApp voltou a funcionar
Conectar um número de WhatsApp novo — no onboarding ou pela Central de Conexões — e reconectar um número que caiu falhavam com "Falha na comunicação com o WhatsApp (WAHA)" (waha_create_400), e o canal ficava preso em "Parado" pedindo reparo.
A causa: o identificador interno que o sistema gera para a sessão no WAHA tinha 69 caracteres, e a versão do WAHA que o kit usa recusa identificadores com mais de 54 — então nenhuma sessão nova chegava a ser criada do outro lado. O identificador passou a ter 45 caracteres.
Canais que já ficaram presos por causa disso são consertados na atualização (o identificador é regravado no formato novo); nenhum número já pareado é tocado. Depois de atualizar, quem estava travado é só clicar em Conectar/Reconectar de novo.
Revogar um membro deixa de ser uma porta que só abre por fora
Revogar sumia com a pessoa. Ela desaparecia da lista de Equipe, e a única forma de devolver o acesso era emitir um convite novo — um caminho longo, com três becos, todos medidos numa instalação real com alguém de verdade preso neles.
O que muda:
O membro revogado continua na lista, com o estado
Revogado, e quem administra devolve o acesso pelo menu da própria linha. Antes ele simplesmente sumia.Quem já tem conta e clica num convite deixa de receber "Não foi possível criar a conta. Tente novamente." — instrução impossível, porque tentar de novo nunca funciona. Passa a ler que já tem conta, com um botão que entra e cai direto no aceite.
A tela de acesso revogado deixa de ser beco
ela diz que, se chegou convite novo, o link do e-mail funciona mesmo dali.
Nada disso mudou o banco. O comando que aceita convite já sabia reativar quem foi revogado, desde que o convite seja posterior à revogação — e foi exatamente isso que a prova em tela confirmou. O que faltava era caminho até ele.
Reativar não promove. Ela devolve o papel que a pessoa tinha; trocar papel continua sendo outra ação, com outra rota. Juntar as duas faria uma reativação distraída virar promoção silenciosa.
Quem devolveu o acesso fica registrado (member.reactivated). A coluna que guarda a revogação volta a ficar vazia e não conta história nenhuma — a trilha é a única resposta para "quem readmitiu esta pessoa, e quando?".
Uma requisição que demora demais não vira mais um erro genérico na tela
Quando uma chamada à API não respondia a tempo, o navegador mostrava um erro genérico ("signal is aborted without reason") em vez de dizer que foi um tempo esgotado. Agora o motivo do cancelamento vem explícito, com o mesmo nome que o resto do produto já usa para timeout — quem lida com o erro consegue reconhecê-lo, e quem só vê a tela entende o que aconteceu.
A chamada de voz avisa quando não há áudio, em vez de contar o tempo em silêncio
O painel da ligação em andamento mostrava o cronômetro correndo assim que o WhatsApp atendia — e o cronômetro continuava correndo mesmo quando o som não chegava ao navegador. Uma ligação muda tinha exatamente a mesma aparência de uma ligação perfeita: nenhum aviso, nenhum sinal, só o relógio. Quem instalou numa VPS ficava sem saber se o problema era o microfone, a rede do escritório ou o produto.
Agora o painel escuta a conexão de áudio de verdade. Enquanto ela está abrindo, ele diz "Abrindo o áudio…". Se ela não abrir, ele diz "Sem áudio: o canal de voz não abriu" — e o cronômetro continua, porque a ligação existe mesmo e o outro lado está esperando. O silêncio deixa de se disfarçar de normalidade.
Se o servidor de voz demorar demais para responder, o aviso aparece em até 12 segundos, em vez de "Abrindo o áudio…" para sempre. E se a conexão se restabelecer depois de um soluço de rede, o aviso some sozinho.
Nada muda para quem não usa chamada de voz.
Trabalho original da chamada de voz de @eudanielhenrique.
O laço rápido do worker volta a montar o admin client
@react-pdf/hyphenate é ESM puro e não expunha a condição require no seu exports. Como o worker roda via tsx (CommonJS), qualquer import de @react-pdf/renderer (usado pela exportação de dados LGPD) derrubava carregarDeps() do drain loop com ERR_PACKAGE_PATH_NOT_EXPORTED — e como register-handlers.ts registra os 12 handlers do event_log num só import chain, isso tirava o laço rápido de TODOS eles, não só do LGPD, caindo pro cron de 1×/min como única rede de segurança.
Patch (patches/@react-pdf__hyphenate.patch) acrescenta a condição require ao exports map — Node 22.12+/24 já sabe carregar ESM via require() quando o mapa permite. Provado no worker real: o warning "event-log drain OFF" some do log de boot.