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...