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