Changelog

Acompanhe todas as atualizações, melhorias e correções do OmniWhats. Cada versão documenta o que mudou para que você esteja sempre por dentro.

Sistema

v0.0.123

Next.js 15 + React 19

WhatsApp Service

v0.0.37

Go + whatsmeow + gotd/td

Meta API

vv25.0

WhatsApp Cloud API

|

Email Marketing — segmentação + preview + resiliência SMTP

  • Novo SegmentBuilder: query builder visual para segmentos (tags, funil, campos custom, últimas interações). Compila em SQL/Prisma via lib/email/segment-compile.ts
  • EmailPreviewModal: renderiza o email com variáveis do contato antes de enviar em produção
  • lib/email/provider-caps.ts: introspecta o provedor (Gmail/Outlook/SES/generic) e ajusta batch size + rate limit + retry por capacidades
  • lib/email/smtp-errors.ts: classificação de erros SMTP (transient vs permanent) — retry só em transient, permanent marca contato como bounced
  • lib/api-error.ts: helper compartilhado pra padronizar respostas JSON de erro nas rotas
  • tenant-campaign-worker: usa provider-caps pra decidir concorrência real por tenant
  • Editor + wizard reformulados + testes

TikTok Business channel — ativado

  • Callback OAuth do TikTok (/api/integrations/tiktok/callback) + trocas de token
  • channel-service, webhook-handler, oauth, types atualizados pra fluxo end-to-end
  • Flag TIKTOK_BUSINESS_ENABLED agora com integração real (spec atualizada em docs/ai-agents)

CRM — filtro de Origem cross-Kanban + KPIs

  • Filtro de Origem no Kanban valendo pra TODOS os funis do tenant (não só o atual): útil pra separar leads por campanha, formulário, WhatsApp direto etc
  • KPIs no topo do kanban (deals abertas, valor total, ticket médio, taxa por stage) + polish visual

Admin — /admin/empresas filtros + redesign

  • Filtros combinados: status, plano, parceiro, vencimento
  • Toggle lista/grade, abrir em nova aba, delete idempotente (não erra se empresa já foi removida)

Observabilidade — Sentry

  • sentry-report agora pula persistErrorLocally durante `next build` (NEXT_PHASE=phase-production-build) — quebra a cascata de re-erros que poluía o log do Docker
  • Corrigidos OMNIWHATS-2S/2R/1R (erros ativos)

Infra — contenção contra congestion collapse (incidente 02/07)

  • Incidente de lentidão em 02/07: contenção aplicada (rate limits, connection pool tunning) — evita degradação em cascata
  • Deploy: serializa build de whatsapp-go e web pra evitar BuildKit EOF sob carga (antes rodava em paralelo e derrubava o daemon)

Banco de dados

  • Nenhuma migration nesta versão

Negociações — Deal Detail Modal + analytics + filtros + timeline

  • Novo DealDetailModal: substitui o dialog antigo — abre com quick-actions (win/lose/reopen/move-stage), timeline de eventos, notas com autoria, seções colapsáveis
  • Novo endpoint GET /api/deals/[id]/timeline: audit trail unificado (criação, movimentações, notas, tags, valor, atribuição, ganhos, perdas com motivo)
  • Novos componentes deals/analytics/*: métricas por funil (leadtime médio por stage, conversion rate, funil visual, deals abertas por atendente)
  • GET /api/funnels/[id]/analytics + hook useDealAnalytics: alimentam as views de analytics do funil
  • Novo DealFiltersPopover no kanban: filtro por atendente, tag, valor, data, motivo de perda — persiste em URL params
  • Novo FunnelPicker: seletor rápido de funil no topo do kanban (troca sem sair da página)
  • ContactDealsSection reformulado (seção de negociações na sidebar do contato)
  • KanbanBoard + DealCard revisados: layout mais compacto, valor formatado, badges de tempo em stage

Negociações — Biblioteca de motivos de perda por funil

  • Migration 20260701000000_funnel_loss_reasons: tabela funnel_loss_reasons por (tenant, funnel) com label VARCHAR(120), sort_order, UNIQUE(funnel_id, label) — RLS ativado com policy tenant_isolation + bypass_rls
  • Backfill automático: cada funil existente ganha 5 motivos padrão ("Sem justificativa", "Preço", "Desconto", "Concorrência", "Não atendeu as expectativas"). Funis novos ganham os mesmos defaults via POST /api/funnels
  • Novo CRUD /api/funnels/[id]/loss-reasons: gerencia a biblioteca por funil (create/reorder/delete)
  • LostReasonDialog agora **exige seleção** de um motivo (dropdown) + observação opcional; string composta é gravada em Deal.lostReason (sem migration no Deal)
  • FunnelSettingsDialog ganha aba "Motivos de perda" para editar a biblioteca

Contatos — feed multi-nota (Contact Notes)

  • Migration 20260630140000_contact_notes: tabelas contact_notes + contact_note_revisions (espelho de deal_notes). Backfill: cada contact com Contact.notes não-vazio vira UMA row com author_id=NULL (UI mostra "Sistema"). Coluna legada Contact.notes NÃO é dropada nesta versão — observação em produção primeiro
  • CRUD /api/contacts/[id]/notes: criar, editar (com histórico em contact_note_revisions), soft-delete
  • Novo componentes/inbox/contact-notes/: feed com autor, timestamp, edição inline, exclusão com histórico
  • ContactPanel: substitui a textarea "Observações" pelo feed novo, com contador e ordem cronológica descendente
  • Docs: notas-redesign.html + contact-panel-redesign.html + contact-panel-variations.html

HubSoft — perf massiva (49s → sub-3s por cliente)

  • Cache Redis 60s por chave (tenant + cliente + tipo) em lookups do path por código; SWR (stale-while-revalidate): resposta instantânea quando cache existe, refresh em background
  • Lookup paralelo: cliente + contratos + faturas + OS + atendimentos disparam concorrente em vez de sequencial
  • Cache da base NFCom por tenant + paginação paralela: fetch de notas fiscais caiu de ~49s/cliente pra sub-3s
  • Notas fiscais carregadas em background (o painel abre sem esperar) — indicador de "carregando" só na seção NFCom
  • Prefetch on hover no painel: já dispara o fetch quando o operador passa o mouse sobre o card do cliente
  • Painel agregado + telemetria de tempo por seção (log estruturado): fica visível quais chamadas ainda são gargalo
  • Retry de rede em toda camada Hubsoft: timeout + backoff exponencial trata o "fetch failed" que aparecia em rajadas

Ticketz — paginação keyset + stats via listing + hardening

  • Paginação de /customers: usa cursor keyset (last_id/last_updated_at) em vez de offset — escala em multi-tenant grande sem escanear tudo
  • Não confia mais no has_more do upstream (era inconsistente em algumas páginas): decide pelo tamanho da página retornada
  • Stats do cliente (contadores de pedidos, valores) agora vêm agregadas do próprio listing /customers em vez de N queries — 1 query por página
  • Cron do sincronizador usa NEXTAUTH_SECRET no Bearer (evita 401 que aparecia em produção)
  • Fix: não loga mais apiKey em logs de erro do cliente Ticketz
  • Worker gate: se TicketZ não está configurado pro tenant, worker não tenta rodar e não vira ruído no Sentry

Automações — assistente IA no chat do operador

  • Novo lib/automations/ai-catalog.ts: catálogo estruturado de gatilhos/condições/ações que o LLM enxerga
  • /api/operator-chat/stream com nova ferramenta: operador descreve automação em linguagem natural e o assistente monta o JSON válido (validado contra o catálogo antes de salvar)
  • ActionBuilder, ConditionBuilder, TriggerSelect: revisão da UI (seleção mais rápida, agrupamento visual, help text)
  • Doc: docs/ai-agents/ia-assistente-automacoes.md

Modo Aviso Global

  • Configurações → nova toggle "Modo Aviso Global": exibe banner de manutenção/aviso pra todos os operadores do tenant, controlado pelo admin
  • Doc: docs/ai-agents/modo-aviso-global.md

Admin Master — Operador Fantasma

  • Ao inspecionar um tenant, o admin master ganha poderes plenos como se fosse um operador do tenant: pode enviar mensagens, resolver conversas, editar deals, criar automações — sem precisar entrar como usuário real
  • RLS respeitada: as writes rodam com o tenant_id do tenant inspecionado, então o audit trail e visibility ficam corretos
  • Diferente do modo "read-only" anterior: agora é ativação explícita e visível na UI para evitar mudanças acidentais

Admin — /admin/processos (observabilidade de processos)

  • Nova página /admin/processos: lista workers (task-reminder, invoice-reminder, campaign, hubsoft-sync, ticketz-sync, gamification, etc), schedulers e crons com última execução, status, próxima execução esperada
  • Cron sem 1ª execução não conta como atrasado (evita false positive no boot inicial)
  • Drawer com detalhes por processo: fundo sólido, logs recentes, botão de re-trigger manual quando aplicável

Meta Ads — token invalidation detection

  • fetchAdsDetails detecta token invalidated (código Meta 190/OAuthException) e marca MetaAdsConfig com token_state=invalidated + last_error + last_error_at
  • Notifica admin (email + Sentry breadcrumb) quando invalidation é detectada
  • Painel /admin/ia/meta-ads mostra o estado do token e link direto pra reconectar

Formulários — create_deal enriquecido

  • Ação create_deal do formulário público agora grava as respostas do form como DealNote no timeline da negociação criada — cliente vê contexto sem precisar entrar no contato
  • A nota é atribuída ao autor "Formulário" (não a nenhum operador) e usa divisores com espaçamento pra legibilidade
  • Retry de download de mídia no Status/Story (Meta CDN 403 transitório) + retry no create_deal quando forms tem media

Billing/EFI

  • Cobrança no cartão agora exige CPF (EFI rejeita CNPJ) — validação clara no CardPaymentForm antes de submeter
  • Aviso inteligente pra cobrança automática: modal explica o valor, forma de pagamento, 1 dia de carência no vencimento antes do bloqueio
  • Detecção de EFI 500 com body "RelatorioErroInterno" agora é tratada como transient (retry) em vez de falha imediata

Campanhas — upload de audiência robusto

  • Chunk de audience upload: divide contatos em batches de 500 pra respeitar o limite de parâmetros do Postgres (evitava insert falhar em campanhas grandes)
  • Hard limits + respondTransientOrError: se o upload passa dos limites, devolve 400 claro em vez de 500 crípticos
  • Import estático de stream (era dinâmico, quebrava em produção) + stub Edge da instrumentação
  • Campaign templates + dev panel cross-platform (Linux/Windows)

Tasks — recorrência, watchers, tags

  • Novo lib/tasks/recurrence.ts + worker recurring-task-reminder.cron.ts: tarefas recorrentes com daily/weekly/monthly + cron a cada 1h que agenda reminders para ocorrências do dia (idempotente via BullMQ jobId)
  • Watchers em tasks: CRUD /api/tasks/[id]/watchers/[userId] — usuários adicionais recebem notificação
  • Tags em tasks: CRUD /api/tasks/[id]/tags/[tagId] + tabela task_tags
  • TaskDateTimePicker + time-picker: novo componente com date + hora unificados
  • idle-conversation-scheduler.worker: dispara conversation_idle (poll PG a cada 60s) para conversas paradas atribuídas — trigger de automação nova
  • Fase 6 (recorrência) rodando silenciosa se migration ainda não aplicada (campo recurrence só ativa após migration em produção)

Operator Chat — transcrição de áudio

  • Novo /api/operator-chat/transcribe-audio: transcreve áudio enviado pelo operador via Whisper/Groq antes de mandar pro LLM (o assistente processa texto)
  • OperatorAIChatWidget: botão de gravação de áudio, upload direto, indicador de "transcrevendo…"

WhatsApp Go — waiting_since race-proof

  • waiting_since/is_from_me da conversa são derivados da mensagem MAIS NOVA sempre — antes tinha race condition quando duas mensagens chegavam quase simultâneas e o campo ficava do anterior (é o campo que alimenta o SLA de resposta na fila)
  • Coalesce de eventos avatar do WhatsApp (Go): evita rajada quando o mesmo contato muda avatar via múltiplas sessões

Infra — isolation de workers + fix crítico do deploy

  • refactor(workers): container omnichat-worker separado do omnichat-web — mesma imagem, env RUN_WORKERS=1 decide papel. web fica livre pra request handling
  • fix crítico do build: bullmq movido de transpilePackages pra serverExternalPackages (versões ESM 5.76+ usavam Node built-ins que webpack não resolvia); instrumentation-node isolada em arquivo separado + import estático (o edge é stubado via NormalModuleReplacementPlugin no next.config)
  • Guard NEXT_PHASE=phase-production-build no register(): evita workers iniciarem durante `next build` (senão worker disparava PageNotFoundError /_document e matava o build)
  • Deploy pipeline: build incremental com cache + rollback automático + gates de saúde (nginx + web + worker healthchecks antes de dar OK)
  • CI: heap 6144 no `tsc` (corrige OOM exit 134 em builds grandes)
  • Backup: retenção reduzida de 30 → 14 dumps diários (economia de disco, cronograma já cobria RPO)

Infra — CI/nginx/SSH

  • nginx: novo host modelos.omniwhats.com (biblioteca de modelos) + fix de stale-mount do config
  • CI deploy: removida probe SSH que alimentava rate-limit do UFW e causava dial timeout intermitente
  • docs/infra: ticket Hostinger com evidências mpstat pra investigação de CPU steal time

Segurança — nodemailer 9 + agent-base pin + dompurify

  • nodemailer 8 → 9 (fix CRLF injection / disableFileAccess bypass / TLS validation OAuth2)
  • agent-base pinned em 7.1.3 via overrides: 7.1.4 quebrou webpack ESM (não resolvia http/https built-ins) em web-push
  • dompurify + qs + tmp + lodash-es + @opentelemetry/core + js-yaml: overrides atualizados. Todas as vulns transitivas resolvidas — npm audit: 0 vulnerabilities

Observabilidade — enxurrada de fixes de ruído no Sentry

  • coalesce de slow-list warning por tenant (janela 7d) — evita 20+ warnings do mesmo tenant no mesmo hotspot
  • undici codes (UND_ERR_CONNECT_TIMEOUT, ECONNREFUSED etc) tratados como transient no detector: não vira issue nova, só breadcrumb
  • coalesce de search-messages-timeout 1d → 7d por tenant
  • workers Redis transient gate: connection lost/reconnecting não conta como erro
  • statement_timeout dentro do withTenantRLS: queries pesadas dentro de transação não seguram o pool
  • coalesce hubsoft fetch_failed + retry
  • move queries pesadas pra FORA do withTenantRLS: transactions ficam curtas, pool libera antes
  • gate "in recovery mode" no detector DB transient: crash-recovery inicial do PG não conta como downtime
  • FK violation em unsubscribe idempotente: 2ª chamada não erra, só no-op
  • Prisma TX abort em reopen race: reopen simultâneo do mesmo ticket agora usa upsert com WHERE guard
  • cdn-proxy transient (Meta CDN 403 curta) tratada com backoff em vez de erro
  • counts threshold ajustado (2500 → 3500ms) — tenants com 5000+ conversas arquivadas passavam o antigo mesmo com o índice partial

Banco de dados — 2 migrations

  • 20260630140000_contact_notes: CREATE TABLE contact_notes + contact_note_revisions + backfill (Contact.notes não-vazio → 1 row author_id=NULL). Coluna legada Contact.notes MANTIDA (drop num release posterior). Aditiva, sem risco
  • 20260701000000_funnel_loss_reasons: CREATE TABLE funnel_loss_reasons + RLS + backfill (5 motivos padrão por funil existente). Aditiva, sem risco
  • Ambas idempotentes-friendly. Sem NOT NULL sem DEFAULT, sem DROP, sem CASCADE destrutivo

Relatórios — barra de filtro unificada

  • Novo componente ReportFilterBar (shared/reports): padroniza presets de período (Hoje, 7 dias, 30 dias, Este mês, Mês passado, custom) + popover de calendário com locale pt-BR
  • Adoção em ~13 telas de relatórios: visao-geral, atendentes, anuncios, ai-analysis, atendimentos-com-ia, avaliacao-atendimentos, base-conhecimento, canais, chatbot-ia, diagnostico-ia, ia-observadora, origem, presenca, produtividade, setores, gamificacao
  • Componente legado DashboardPeriodFilter REMOVIDO (-134 linhas, 0 referências restantes)
  • useDashboard hook atualizado para o novo contrato

Nova conversa — resolução de JID via whatsmeow

  • Nova lib/whatsapp-resolve.ts: resolveJid() chama o bridge Go (GET /api/whatsapp/sessions/{id}/check-number/{phone} — whatsmeow IsOnWhatsApp) e devolve o JID canônico antes de criar a conversa
  • Antes: ao iniciar conversa digitando um número, o app montava JID a partir dos dígitos brutos. Para números BR a regra do 9º dígito (celulares antigos sem 9) e LIDs (linked devices) causavam JIDs inválidos — a mensagem ia pro ar mas o WhatsApp não entregava
  • Agora valida que o número existe no WhatsApp + recebe a forma correta do JID (com ou sem 9) antes de criar a Conversation
  • NewConversationModal usa esse resolver — se o número não existe, mostra mensagem clara em vez de criar conversa zumbi

HubSoft — NFcoms + status formatter + UI

  • Nova função listNfcoms em lib/hubsoft/service.ts: lista notas fiscais COM-21 do cliente no ERP
  • Novo lib/hubsoft/status-formatter.ts: traduz códigos de status do HubSoft (atendimento, OS, cliente) pra labels amigáveis + cor consistente em toda a UI
  • AbrirOsDialog reescrito (+105 linhas): seleção de plano, motivo e técnico responsável; valida config HubSoft antes de abrir
  • HubsoftPanel + ClienteDetailDrawer: refinamentos visuais e novo bloco de NFcoms
  • lib/hubsoft/message-templates: ajustes pequenos

Workers + scripts — robustez

  • campaign-worker: melhorias no envio de campanhas
  • task-reminder.worker: ajustes no timing dos lembretes
  • flowup-execution.worker + flowup/process-contact: refinamento na execução de fluxos
  • scheduled-messages-scheduler: melhor handling de janelas e canal
  • billing-send/send-invoice-whatsapp: ajustes
  • agent-tools (IA do operador): refinamento
  • Novo script merge-duplicate-contacts.ts: dedupe assistido de contatos com mesmo telefone (idempotente, com dry-run)
  • Novo explain-search-messages.sql: snippet de análise EXPLAIN do query de busca de mensagens
  • services/whatsapp-go: chatbot/engine.go ajustes

Segurança — bump Sentry + override OpenTelemetry/js-yaml

  • Surgiram 29 vulns transitivas na auditoria (1 low, 25 moderate, 3 high) — advisories publicados desde a release anterior: @babel/core (file read), @opentelemetry/core (memory DoS), nodemailer (3 CVEs CRLF/jsonTransport/TLS), vite (NTLM/path bypass), ws (DoS), js-yaml (DoS)
  • @sentry/nextjs + @sentry/profiling-node: 10.47 → 10.58 (patch dentro do major 10, sem breaking)
  • npm audit fix (sem --force) resolveu @babel/core, nodemailer, vite, ws
  • Overrides adicionados pra forçar dedupe das transitivas que sobraram: @opentelemetry/core ^2.8.0 (resolve cadeia inteira do Sentry + lighthouse) + js-yaml ^4.2.0 (via @lhci/utils)
  • npm audit final: **0 vulnerabilities**

Banco de dados

  • Nenhuma migration nesta versão — só código
PróximoPróximas atualizações em breve...