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

Next.js 15 + React 19

WhatsApp Service

v0.0.37

Go + whatsmeow + gotd/td

Meta API

vv25.0

WhatsApp Cloud API

|

IA Híbrida — router LLM dentro do chatbot

  • Novo modo "IA Híbrida" no chatbot: um LLM intercepta (a) a primeira mensagem do cliente após o trigger e (b) inputs que não casaram com nenhum botão/lista/menu, decidindo entre saltar direto pro node mais relevante, pedir esclarecimento via clarify menu, responder pelo KB ou transferir pra humano
  • Configuração por chatbot (ChatbotSettings.AIRouter): Enabled, Threshold (default 0.7 — confiança mínima pra commit), AmbiguityWindow (default 0.15 — gatilho do clarify), FallbackBehavior (transfer | invalid)
  • Engine Go (session_manager.go): hooks tryAIRoute em entry + menu_invalid, resolveAIRouterClarify resume de sessão pendente em waitingFor="ai_router_clarify", lazy-init do RouterClient com NextJSURL + InternalAPISecret
  • Next.js side: novo módulo lib/ai/chatbot-router/ (build-catalog 111 linhas, decide 138, prompt 118, types 71) + endpoint /api/internal/chatbot-router (262 linhas) chamado pelo Go com secret header
  • FlowEditor ganha campo data.aiIntent por node + AIIntentsPanel (side panel) que lista todos os destinos da IA com badge no toolbar — operador audita os intents sem clicar node por node
  • NodeConfigPanel: novo campo "Intent (IA Híbrida)" pra anotar pra IA o que aquele node faz
  • Relatório /relatorios/chatbot-ia (346 linhas): mostra resolutions, taxa de hit, top intents, custo médio por interação + endpoint /api/reports/chatbot-router (109 linhas)

Atendimentos com IA — diagnóstico de conversas

  • Novo relatório /relatorios/diagnostico-ia (715 linhas) — LLM analisa todas as conversas do período + filtros (status, departamento, atendente, canal, tags) e retorna sentiment, top issues, sugestões de melhoria e insights agrupados
  • 4 componentes especializados: DrillDownDrawer (lista de conversas atrás de cada insight), InsightList, SentimentDonut (donut de positivo/neutro/negativo), SimpleMarkdown (renderer leve)
  • Cache por filter_hash em ai_conversation_analysis_reports — reabrir o relatório com os mesmos filtros devolve o resultJson persistido sem gastar tokens; Reanalisar exclui + regenera
  • 4 endpoints novos: POST /api/reports/ai-analysis (cria + processa), GET [id] (status/result), GET [id]/drilldown (conversas detalhadas), GET /list (histórico)
  • Status pipeline: pending → streaming → ready | error, com error_message persistido
  • Modelo dedicado configurável em /admin/ia (ai.reportAnalysisModel) — empty = usa o principal
  • Métricas por relatório: conversations_analyzed, messages_analyzed, total_input/output_tokens, estimated_cost_usd, latency_ms

Observabilidade IA — latency, success, error codes

  • ai_usage_logs ganha 3 colunas: latency_ms (round-trip incl. multi-round tool loops), success (DEFAULT TRUE, backfill assume sucesso), error_code (texto livre)
  • Novo índice (success, created_at DESC) — admin /admin/ia agora filtra por sucesso/falha em O(log n)
  • lib/ai/usage-logger.ts: param feature agora aceita "chat" | "operator_chat" | "report_analysis" | "chatbot_ai_router" + latencyMs + success + errorCode (compat: lê latencyMs do metadata se existir)
  • /api/operator-chat/stream agora loga AiUsageLog (antes não logava): stream_options.include_usage no payload, agregação de input+output+cached tokens em todos os rounds, persistUsage() chamado no final com outcome completed | pending_action | error
  • /admin/ia: novos campos no card de provider-status (latency p50/p95, success rate, top error codes) + nova aba Insights chamando /api/admin/ai/insights (234 linhas) com top users, MoM, projeção
  • /admin/ai/simulate ganha cost estimate com discount aplicado (lib/ai/cache-discounts.ts 63 linhas)
  • /admin/ai/usage agora retorna latency_ms e success/error fields

Budget IA mensal (USD + BRL)

  • Novo config ai.monthlyBudgetUsd (0 = desabilitado) em /admin/ia — define teto soft em USD
  • Provider-status retorna bloco budget: monthlyBudgetUsd, monthlyBudgetBrl, consumedPercent, remainingUsd, projectionExceedsBudget, overshootUsd
  • Insights retorna exchangeRate.usdToBrl pra mostrar custo em BRL ao lado do USD
  • Cards do /admin/ia mostram barra de consumo + warning quando passa do budget
  • Não bloqueia (soft cap): a IA continua respondendo mesmo após estourar o budget — é alerta visual, não freio mecânico

Performance — /api/conversations e /counts

  • idx_conversations_visibility_order: índice covering (tenant_id, last_message_at DESC, id DESC) INCLUDE (department_id, assigned_user_id, status) — visibility OR sobre ORDER BY agora roda index-only, sem heap fetch. seg_db_visibility_ms cai de ~1074ms para ~150ms em tenants com 800+ conversas (issue 138)
  • idx_chatbot_sessions_tenant_active: índice parcial WHERE status IN ('active','waiting_input') — EXISTS subquery do /counts agora varre só ativos em vez de tudo. seg_db_chatbot_ms cai de ~573ms para ~150ms (issue 136)
  • conversation-visibility.ts: TTL do cache de departamento bumped 5s → 30s — economiza 200-700ms por miss em departmentUser.findMany (trade-off: reassign de dept demora até 30s pra refletir, ok já que tudo é async via JWT refresh)

Sentry — ruído reduzido

  • sentry.server.config.ts: drop warnings com meta_code=190 (OAuthException) e 200 (Permission error) — são erros de token do tenant, já tratados como meta_permanent, sem ação de engenharia
  • Drop warnings com failure_kind="session_disconnected" — WhatsApp do tenant offline (logged out, scanQR pending), aparece pro operador via banner do canal
  • lib/meta/error-diagnostics.ts: novo failureKind "session_disconnected" detecta 503 + body "session not connected"/"websocket not connected"/"No active WhatsApp session found for tenant" — todas as 3 rotas de dispatch (text/media/retry) classificam consistente

Banco de dados — migrations (3)

  • 20260511150000_add_ai_usage_observability: ADD COLUMN IF NOT EXISTS latency_ms, success DEFAULT TRUE, error_code em ai_usage_logs + CREATE INDEX IF NOT EXISTS no (success, created_at DESC). Idempotente, defaults seguros para backfill
  • 20260520000000_add_ai_conversation_analysis_reports: CREATE TABLE nova ai_conversation_analysis_reports + 2 FKs (tenants RESTRICT, users SET NULL) + 3 índices (tenant_created, tenant_period, tenant_filter_hash UNIQUE)
  • 20260521000000_conversations_visibility_order_and_chatbot_active_idx: 2 índices em conversations e chatbot_sessions — CREATE INDEX (sem CONCURRENTLY porque Prisma roda em transação). conversations é hot table mas o lock é breve; padrão idêntico ao 20260517000000 da 0.0.105 que rodou sem incidente. Recomendado deploy off-hours (≥22h Brasília)

OmniTicketz — Reservas de hospedagem (V2)

  • Novo módulo de hospedagem: builder dedicado (TicketzHospedagemBuilder, ~900 linhas) permite ao operador selecionar acomodação + plano tarifário, configurar adultos/crianças/idades, datas de check-in/check-out e gerar link de pagamento direto no painel direito do chat
  • Botão "Reserva de hospedagem" aparece no TicketzPanel apenas quando a integração tem hospedagemEnabled (flag por tenant) — ingressos e hospedagem coexistem sem confundir a UI
  • Configurações → Integrações → OmniTicketz ganha bloco "Recursos" com toggle "Reservas de hospedagem" (desabilitado até a conexão estar testada e connected)
  • 4 novos endpoints proxy para o backend Ticketz: GET /acomodacoes, POST /availability/hospedagem (cotação por período + acomodação), POST /orders/hospedagem-link (geração do link de pagamento) e GET /planos-tarifarios
  • Tipagem completa em types/ticketz.ts: TicketzAcomodacao, TicketzPlanoTarifario, TicketzHospede, TicketzReservaInput, TicketzReservaConfirmada, TicketzHospedagemAvailability, TicketzHospedagemPaymentLinkResponse
  • Webhook payload tipado de verdade (TicketzWebhookPayloadDados) substitui o antigo Record<string, unknown> — V2 traz totals (cartão/PIX), cliente, itens e reservas estruturados

Chatbot + IA — node e tool para hospedagem

  • Novo node ticketz_hospedagem_link no FlowEditor: configurações para variáveis de check-in/check-out, JSON de reservas, valor total, validade do link e mensagem com URL gerado
  • Engine Go (executeTicketzHospedagemLink) processa o node, chama o proxy V2 e expõe saídas success / price_mismatch / stop_sell / error
  • Nova ferramenta create_hospedagem_link para o agente IA (com error codes PRICE_MISMATCH, STOP_SELL, INVALID_RESERVA) — chave HOSPEDAGEM_DISABLED retorna se o tenant não tiver o módulo habilitado
  • NodePalette ganha entrada visual pro novo node com agrupamento OmniTicketz

OmniTicketz — Renomeação checkout_url → payment_url + error handling V2

  • Toda a stack (proxy Next.js, builder de ingressos, types, engine Go) migrou de checkout_url para payment_url — o link agora aponta para a página própria do tenant /pagar/<token>
  • Variável de chatbot tz_checkout_url mantida em paralelo (escrita dupla) por compatibilidade até 2026-08-09 com warning log no engine Go indicando o node que ainda usa o nome antigo
  • NodeConfigPanel do ticketz_payment_link mostra alerta visual amarelo quando o node ainda usa storeCheckoutUrl, com instrução clara da nova chave storePaymentUrl
  • Códigos de erro PRICE_MISMATCH e MISSING_REQUIRED_FIELD agora viram mensagens user-friendly no builder (toast com instrução do que fazer) e no chatbot Go (resposta automática pro contato + log do code real)
  • TicketzQuoteBuilder mostra desconto PIX quando totals.total_pix < totals.total_cartao ("Total no cartão: X · Pague com PIX por: Y, economize Z") com fallback pro total simples quando não há desconto

OmniTicketz — Detalhe do pedido via Portal (fix visual)

  • TicketzOrderDetailDialog agora renderiza via createPortal no document.body — antes ficava preso dentro do containing block do painel direito (que usa transform/will-change do Framer Motion), o que prendia o position:fixed e estourava o modal pra dentro do slide-over
  • Z-index subido para z-[100] e max-width aumentado para 3xl no desktop com max-h-[90vh] — modal agora respeita a viewport inteira em vez do painel
  • Header redesenhado: sem border, separação por espaçamento + tipografia ajustada para a hierarquia ficar consistente entre mobile e desktop

Push Notifications — preview rico por tipo de mídia

  • messageType agora propaga da origem (Meta webhook handler + WhatsApp Go service_manager) até o webhook interno /api/internal/push-webhook — antes o tipo se perdia e todo push virava texto plano
  • Novo formatPushPreview adiciona emoji + label por tipo no título do push: 🖼️ Imagem · 🎬 Video · 🎞️ GIF · 🎧 Audio · 📄 Documento · 😊 Figurinha · 📍 Localização · 👤 Contato · 📊 Enquete · 👍 Reação · 📝 Template · 🧩 Interação · ⚙️ Sistema
  • Preview truncado para 120 caracteres (era 100) para acomodar o ícone + label sem perder contexto da mensagem
  • Fix: badge fallback corrigido de /icons/badge-96x96.png (não existia) para /icons/icon-96x96.png — push de teste em /admin/pwa não quebra mais o ícone do sistema

Performance /atendimento — pointer-events durante scroll

  • ChatVirtuoso marca data-scrolling="1" no container enquanto o Virtuoso reporta scroll ativo, e remove 120ms depois do scroll parar — bordas de transição garantem que o dataset só muda em mudanças de estado real (Virtuoso chama isScrolling muitas vezes por gesto)
  • MessageBubble usa o atributo de scroll do ancestor pra suprimir as classes group-hover/msg:* durante o gesto. A profilagem mostrou pointerover/leave/enter custando 40–96 ms cada quando o cursor cruzava bubbles mid-scroll, com long-tasks de 100–280 ms e drops de até 223 ms num único frame
  • Mitigação padrão (Slack/WhatsApp Web/Discord usam o mesmo padrão): hover volta ~150 ms após o scroll terminar, imperceptível para o usuário, mas o estilo recalculation some completamente do hot path
  • bubble-enter animation incrementada: 110 ms opacity-only → 180 ms cubic-bezier com translate3d(0, 5px, 0) scale(0.985) + will-change opacidade/transform; @media prefers-reduced-motion zera a animação
  • Atendimento page memoiza renderChat callback pra evitar re-criação do componente entre layouts (desktop/mobile/overlay) — corte de 1 re-mount do ChatWindow em transições de breakpoint

Bundle — MediaPreview sem Framer Motion

  • MediaPreview removeu motion/AnimatePresence e usa shouldRender + isExiting + onAnimationEnd com keyframes puros (.media-preview-overlay-in/out, .media-preview-content-in/out em globals.css)
  • Economia de ~40 KB no chunk dinâmico do inbox (Framer Motion era carregado por demanda quando o lightbox abria pela primeira vez)
  • Comportamento idêntico ao Framer: fade-in 200 ms / fade-out 150 ms, zoom-in opcional pra imagem, com unmount diferido até o fim da exit animation

Limpeza de console.log + correções diversas

  • Remoção sistemática de console.log/console.error/console.warn de hot paths e callbacks de UI em ~80 componentes (chatbot, inbox, CRM, deals, groups, integrations, widget, configurações) — apenas casos onde havia tratamento de erro real foram preservados
  • lib/phone-normalize.ts: findContactByPhoneVariants agora aceita PrismaClient | TransactionClient (era só TransactionClient, quebrava em chamadas fora de transação)
  • lib/audit.ts: novo action INTEGRATION_WEBHOOK_RECEIVED para registrar recepção de webhook por integração
  • lib/web-vitals-reporter.ts: removido reportToConsole legado (telemetria já vai pro Sentry)

Banco de dados — sem migrations

  • Nenhuma migration Prisma nesta versão — o flag hospedagemEnabled é armazenado no JSON existente da TenantIntegration (config), sem mudança de schema
  • A pasta prisma/migrations-pending mantém 2 migrations em standby (20260502120000_task_foundations, 20260502130000_task_tags_watchers) — intencionalmente fora do build (tsconfig exclude) e fora deste deploy
  • Deploy de produção (deploy.sh) não vai aplicar nada de DDL/DML — apenas rebuild dos containers

Knowledge Base — Versionamento, lixeira e anexos (Phase 1)

  • Toda edição em entrada do KB grava uma revisão imutável em knowledge_revisions com snapshot de title/content/category, change note opcional e autor — diálogo "Histórico de revisões" permite comparar e restaurar versões anteriores
  • Soft-delete: entradas excluídas vão para lixeira (deleted_at) em vez de DELETE direto; nova página /configuracoes/base-conhecimento/lixeira lista, restaura ou força-exclui
  • Cron /api/cron/kb-trash-purge purga itens há mais de 30 dias na lixeira de forma automática
  • Anexos por entrada (knowledge_attachments) com armazenamento via MinIO + extração de texto opcional (indexable=true) que entra no índice de busca semântica
  • Auditoria por entry: lastEditedById + version no próprio registro permite mostrar "última edição por X há Y" sem JOIN extra

Knowledge Base — Analytics + detecção de lacunas (Phase 3)

  • Toda consulta ao KB (chatbot, agente IA, autocomplete, observador, painel, menção) é logada em knowledge_usage_logs com queryText, source, hadResults, scoreSum e latencyMs
  • Novo relatório /relatorios/base-conhecimento mostra entries mais usadas, taxa de hit/miss, latência média e cobertura por source
  • Cron /api/cron/kb-gap-detector agrega buscas que falharam (hadResults=false) por similaridade e gera knowledge_gap_suggestions com sampleQueries + occurrences + draft auto-gerado por IA
  • Endpoint POST /api/knowledge-base/suggest cria entrada já preenchida a partir de uma sugestão (one-click)

Wiki ↔ KB linking (Phase 5)

  • Nova tabela wiki_kb_links permite vincular entradas do KB a módulos do Wiki — quando o operador abre um módulo do Wiki, vê as entradas relacionadas e vice-versa
  • API /api/wiki-kb-links com CRUD e endpoint /api/wiki-kb-links/[id] para detach
  • Unicidade garantida por (tenantId, wikiModuleId, kbEntryId) — sem duplicatas

Operator AI Chat Widget — IA in-app grounded no KB

  • Novo widget flutuante (componente OperatorAIChatWidget no header) — operador conversa com IA que responde **só** com base no KB do tenant
  • Streaming via /api/operator-chat/stream com modelo padrão GPT-4o-mini e fallback automático: se o modelo principal falhar (rate limit, 5xx, cota), retry imediato no fallback configurado em ai-config sem expor erro pro usuário
  • Histórico persistido em operator_chat_messages (role, content, sourceEntryIds) para o operador retomar a conversa em outra sessão
  • Citações inline: cada resposta mostra quais KB entries foram consultadas (sourceEntryIds), com link direto pra abrir a entrada
  • Confirmação modal customizada para limpar histórico (substitui browser confirm)
  • Novo hook useOperatorChatPrefs e endpoint /api/operator-chat/config controla disponibilidade por tenant + permissão

Tarefas (CRM) — refactor completo

  • Página /crm/tarefas reescrita: orquestração enxuta + componentes especializados (TaskListView, TaskKanbanView, TaskBigCalendarView, TaskFilterBar, TaskBulkActions, TaskFormDialog, TaskSummaryCards)
  • Hooks dedicados (useTasks, useTaskMutations, useTaskFilters, useTaskMeta, useTaskRealtime) usando React Query 5 — cache compartilhado, invalidations inteligentes, optimistic updates em mutações
  • Bulk actions: seleção múltipla na lista permite mudar status, atribuir, deletar em lote via /api/tasks/bulk
  • Soft-delete em tarefas (lixeira) + endpoint force-delete e diálogo dedicado
  • Lembretes via BullMQ (worker task-reminder.worker) substitui scheduler in-process — sobrevive a deploys, retries automáticos com backoff e backfill on boot
  • Novo endpoint /api/holidays/national e hook useNationalHolidays via brasil-api — feriados aparecem no big calendar e no formulário ao agendar
  • Reconciliação Google Calendar refatorada para emitir badges visuais (TaskGoogleSyncBadge) e tratar erros transientes sem quebrar UX

Performance — /atendimento + /api/conversations

  • Novo índice composto idx_conversations_tenant_visibility_groupby ((tenant_id, department_id, status, assigned_user_id)) cobre o groupBy de /api/conversations/counts considerando o OR de visibility — p95 cai de ~1.5s para ~300ms (issue 136 / OMNIWHATS-2Y)
  • ChatWindow refatorado: novos hooks (useConversationMessagesFetcher, ChatContext) isolam estado e previnem re-renders em cascata
  • KbSuggestionsSidebar: barra lateral no chat sugere entradas do KB relevantes pra mensagem do contato em tempo real (debounced)
  • Themed scrollbars universais (utility classes em globals.css + hook useThemedScroll) — substitui inline-style por classe global em chat, kanban, deals e atendimento
  • Background do chat trocado de SVG (wa-fundo-*.svg, ~9k linhas cada) por chat-doodles-light/dark.webp — corte de ~280kB no bundle estático e LCP do atendimento
  • Web vitals reporter (lib/web-vitals-reporter) envia métricas pro endpoint de telemetria sem bloquear interaction

Chatbot — vincular Knowledge Base em nodes

  • NodeConfigPanel ganha KnowledgeBindingsEditor: nodes de IA agora podem ter KBs vinculadas explicitamente, restringindo o contexto da IA às entradas selecionadas
  • Novo lib/ai/kb-context-builder monta o contexto consolidado (entries + skip-list de termos vetados) que entra no system prompt do agente
  • lib/ai/kb-skip-list permite o tenant excluir termos genéricos do RAG (evita contaminar respostas)
  • Engine Go (chatbot/engine.go + types.go) atualizado para passar binding metadata adiante no fluxo

AI — robustez e custo

  • Todos os endpoints de IA (autocomplete, stream, summary, transform, ai-agents/chat, internal/ai-agent-chat) passam a usar a mesma rotina centralizada de fallback de modelo + logging em ai_usage_logs
  • Observer agent revisado: novos guards evitam loops, rate-limit por conversa e respeita silenciamento de canal
  • Agent tools com nova ferramenta search_knowledge_base que loga uso (entra no analytics do KB)
  • admin/ia ganha aba para configurar modelo padrão + fallback do Operator Chat

CRM — refinamentos visuais

  • Themed scrollbars aplicadas em deals/KanbanBoard, kanban/KanbanColumn, funnel tabs e deals funnel kanban
  • Página /crm/negociacoes e /crm/kanban: ajustes de layout para consistência com o novo padrão de scroll
  • TopHeader: ícone do operador chat trocado de Sparkles para Bot (Robot) em todos os pontos de uso

Infra & deploy

  • BullMQ adicionado ao stack (raiz + apps/web) com fila task-reminders e worker dedicado — base para próximos workers (KB indexer, gap detector, etc)
  • React Query 5 + devtools no Providers para todas as features novas (tasks, KB, operator chat)
  • Workaround Turbopack/Webpack: subpath ioredis/built/utils aliasado para shim local (resolve warning storm "Package ioredis can't be external" em todo build)
  • tsconfig exclui prisma/migrations-pending para não quebrar build com migrations em standby (deploy manual)
  • GitHub Actions ganha workflow perf.yml + scripts perf:baseline / perf:compare (Lighthouse CI) e e2e/perf (Playwright cold-boot) — base para detectar regressões de performance no /atendimento

Banco de dados — migrations

  • 20260516000000_kb_revisions_attachments_usagelogs_softdelete: adiciona colunas em knowledge_base_entries (version DEFAULT 1, last_edited_by_id, deleted_at) e cria knowledge_revisions, knowledge_attachments, knowledge_usage_logs
  • 20260516010000_kb_gap_suggestions: cria knowledge_gap_suggestions (append-only)
  • 20260516020000_kb_wiki_links_quizzes: cria wiki_kb_links + tabelas de quizzes
  • 20260516030000_drop_knowledge_quizzes: dropa as 3 tabelas de quizzes (feature de treinamento revertida antes de prod) — DROP IF EXISTS, idempotente
  • 20260516040000_operator_chat: cria operator_chat_messages (append-only)
  • 20260517000000_conversations_visibility_groupby_idx: cria índice covering em conversations (sem CONCURRENTLY — Prisma roda em transação; hot table mas escrita-curta, OK off-hours)
PróximoPróximas atualizações em breve...