# Secretária Virtual — Roadmap Completo (Opção 3 Híbrida)

## Arquitetura Alvo

```
Plugin Alemão (satélite)                      Motor NewWhats
┌──────────────────────────────┐              ┌──────────────────────────────┐
│  webhook-receiver.js         │   POST ask   │  ProtocolEngine (brain.ts)   │
│  └─ context-builder.js       │ ───────────► │  system prompt dinâmico      │
│     └─ DB local (clientes,   │              │  histórico + summarização     │
│        planos, promoções)    │ ◄─────────── │  fallback chain AI providers │
│                              │    reply     │  sec_conversations/messages   │
│  HandoffButton (UI)          │              │                              │
│  └─ per-chat mode:           │  PATCH mode  │  chat_handoff_mode           │
│     ia | humano | pausado    │ ───────────► │  (campo por conversa)        │
└──────────────────────────────┘              └──────────────────────────────┘
```

---

## LEGENDA DE STATUS
- ✅ Implementado e funcionando
- 🔲 Pendente — não iniciado
- 🚧 Parcialmente feito

---

## BLOCO 1 — Handoff Humano ↔ IA (Botão Flutuante)

> Botão flutuante acima do input bar no chat. Um clique pausa a IA e o humano
> assume. Outro clique devolve o controle à IA. Se o humano ficar 15 min sem
> responder (ou a conexão cair), a IA reassume automaticamente.

### 1.1 — Motor: campo de modo por conversa

- 🔲 Adicionar coluna `handoff_mode` ENUM(`ia`, `humano`, `pausado`) DEFAULT `ia` na tabela `sec_conversations` (migration Knex)
- 🔲 Adicionar coluna `handoff_human_at` TIMESTAMP NULL na tabela `sec_conversations` (registra quando humano assumiu)
- 🔲 Rodar migration no motor (`plugins/secretaria/migrate.ts`)

### 1.2 — Motor: rotas de handoff (ext-api)

- 🔲 Criar `PATCH /ext/v1/sec/conversations/:id/handoff` — body `{ mode: 'ia'|'humano' }` — atualiza `handoff_mode` e `handoff_human_at`
- 🔲 Em `POST /ext/v1/secretaria/ask` — verificar `handoff_mode` antes de chamar brain: se `humano`, retornar `{ skipped: true }` sem chamar IA

### 1.3 — Motor: timer de retomada automática (15 min)

- 🔲 Criar job BullMQ `handoff-timeout` no motor: ao mudar para modo `humano`, enfileirar job com delay de 15 min
- 🔲 Job verifica se `handoff_mode` ainda é `humano` E `handoff_human_at` há mais de 15 min → muda para `ia` → emite evento WebSocket `conversation.handoff` para o satélite
- 🔲 Ao receber mensagem humana (`PATCH /handoff mode=humano`), cancelar job anterior e enfileirar novo (reset do timer)
- 🔲 Se conexão WS do satélite cair (`disconnect`), mudar todas as conversas desse satélite em modo `humano` para `ia` automaticamente

### 1.4 — Frontend: NwService métodos

- 🔲 Adicionar `NwService.getHandoffMode(chatId)` → `GET /nw/v1/sec/conversations/:id/handoff` (ou campo incluso no chat)
- 🔲 Adicionar `NwService.setHandoffMode(chatId, mode)` → `PATCH /nw/v1/sec/conversations/:id/handoff`

### 1.5 — Frontend: HandoffButton component (WhatsAppInbox.tsx)

- 🔲 Criar componente `HandoffButton` — botão flutuante posicionado `absolute bottom-[72px] right-4 z-20`
- 🔲 Estado local `handoffMode: 'ia' | 'humano'` por chat — carrega ao abrir o chat
- 🔲 Visual quando modo `ia`: ícone `Bot` verde com tooltip "IA respondendo — clique para assumir"
- 🔲 Visual quando modo `humano`: ícone `UserCheck` laranja pulsando com tooltip "Você está respondendo — clique para devolver à IA" + contador regressivo de 15 min
- 🔲 Ao clicar: chama `NwService.setHandoffMode`, atualiza estado local otimisticamente
- 🔲 Mostrar o botão apenas se sessão CONNECTED e chat selecionado
- 🔲 Esconder o botão se auto-reply mode = `off` (secretária desligada globalmente)

### 1.6 — Frontend: exibição visual no chat

- 🔲 Injetar mensagem de sistema no fluxo de mensagens ao mudar de modo: "🤖 IA assumiu o atendimento" / "👤 Atendente humano assumiu"
- 🔲 Header do chat mostrar badge discreto: `IA` (verde) ou `Humano` (laranja)

### 1.7 — Frontend: retomada automática via WebSocket

- 🔲 Escutar evento WS `conversation.handoff` → atualizar `handoffMode` local sem recarregar a página
- 🔲 Exibir toast "Secretária retomou o atendimento automaticamente (15 min sem resposta)"

### 1.8 — Motor: emitir evento WS ao retomar

- 🔲 Após job BullMQ retomar modo `ia`, emitir via WS `{ event: 'conversation.handoff', data: { chatId, mode: 'ia', reason: 'timeout' } }` para o satélite correto

### 1.9 — Webhook receiver: respeitar handoff

- 🔲 Em `webhook-receiver.js`, antes de chamar `/secretaria/ask`, consultar modo da conversa
- 🔲 Se modo `humano` → não chamar IA, apenas registrar mensagem recebida
- 🔲 Resetar timer de 15 min ao detectar mensagem do atendente humano (mensagem `fromMe: true`)

---

## BLOCO 2 — Injeção de Contexto Local (Opção 3 Híbrida)

> Hoje a secretária fica cega ao DB do projeto. O plugin precisa enriquecer
> cada chamada ao motor com dados reais do cliente.

### 2.1 — Motor: ampliar brain.chat() para aceitar contexto

- 🔲 Modificar `brain.ts` → `chat(convId, message, opts?: { contextData?: object; systemExtra?: string })`
- 🔲 Serializar `contextData` como bloco de texto e appendar ao system prompt antes da chamada à IA
- 🔲 Modificar `POST /ext/v1/secretaria/ask` → extrair `context` e `system_prompt_extra` do body e repassar ao brain

### 2.2 — Plugin: criar context-builder.js

- 🔲 Criar `sistema-nuvem/plugins/newwhats/context-builder.js`
- 🔲 Implementar `buildContext(phone)`:
  - Buscar cliente pelo telefone no DB alemão (tabela `users` ou `clients`)
  - Buscar status de assinatura do clube
  - Buscar último pedido / histórico recente (últimos 3)
  - Buscar promoções ativas do dia
  - Buscar vagas abertas (caso relevante)
- 🔲 Retornar objeto `{ cliente, plano, ultimos_pedidos, promocoes_ativas }`

### 2.3 — Plugin: webhook-receiver usa context-builder

- 🔲 Modificar `webhook-receiver.js`: extrair número do remetente de `data.from`
- 🔲 Chamar `context-builder.js` com o número antes de chamar `/secretaria/ask`
- 🔲 Enviar `context` e `system_prompt_extra` no payload do `ask`

---

## BLOCO 3 — Tool Calling (IA que age, não só fala)

> Sem tools, a IA só gera texto. Com tools ela pode agendar, buscar e escalar.

### 3.1 — Motor: suporte a tools no ProtocolEngine

- ✅ Criado `plugins/secretaria/tools.ts` com interface `ToolDef { name, description, parameters, execute() }`
- ✅ Tools implementadas: `listar_horarios`, `agendar_horario`, `escalar_humano`, `encerrar_protocolo`
- ✅ `brain.chat()` aceita `tools?: string[]` — resolve via `resolveTools()` e habilita tool calling
- ✅ Loop de tool calling por provider: OpenAI (tool_calls), Anthropic (tool_use), Gemini (functionCall) — máx 5 iterações
- ✅ Fallback gracioso: se nenhum provider com tools disponível, usa modo texto normal
- ✅ `POST /ext/v1/secretaria/ask` aceita `tools: string[]` e repassa ao brain com hooks + tenantId

### 3.2 — Plugin: integração com tools do motor

- ✅ `webhook-receiver.js` inclui `tools: ['listar_horarios', 'agendar_horario', 'escalar_humano', 'encerrar_protocolo']` no payload do ask
- ✅ `escalar_humano` aciona handoff_mode=humano + emite `ext:handoff` via HookBus → chega ao frontend via WS
- ℹ️ `buscar_cliente` / `verificar_plano` resolvidos via `context-builder.js` (Bloco 2) — contexto pré-computado injetado no system prompt, não como tool call (mais simples e confiável)

---

## BLOCO 4 — Painel Admin: Melhorias

### 4.1 — Visualização de conversa

- ✅ Sub-tab Conversas: clicar em protocolo abre drawer lateral com todas as mensagens
- ✅ Botão "Finalizar protocolo" no drawer → `POST /sec/conversations/:id/finalize`
- ✅ Badge visual em conversas com `handoff_mode = humano` (ícone laranja)
- ✅ Badge visual em conversas escaladas (`status = escalated`)

### 4.2 — Chat de teste inline

- ✅ Sub-tab "Testar" no painel Secretária: campo de texto + botão enviar
- ✅ Chama `/secretaria/ask` com um chatId de teste fictício e exibe resposta
- ✅ Útil para testar prompts sem precisar de celular conectado

### 4.3 — Segurança das API Keys

- ✅ `GET /ext/v1/sec/keys` mascarar valores: `sk-...****` (mostrar só prefixo + últimos 4 chars)
- ✅ Campo de senha no painel com placeholder `••••••••` quando já salvo

---

## BLOCO 5 — Escalação Real

### 5.1 — Detecção e notificação

- ✅ Motor: `escalar_humano` tool emite `ext:escalated` com protocolNumber + motivo
- ✅ Motor: ws-bridge encaminha `ext:escalated` → `conversation.escalated` ao satélite
- ✅ Motor: listener em `buildExtRoutes` envia WA ao `admin_notify_phone` configurado
- ✅ Motor: `GET/PUT /sec/keys` inclui `admin_notify_phone` (em claro, não mascarado)
- ✅ Painel: toast laranja `⚠️ Escalação` ao receber `conversation.escalated` via WS
- ✅ Painel: badge "Escalada" vermelho nas conversas (implementado no Bloco 4)
- ✅ Painel: campo "WhatsApp do administrador" na aba Config

---

## RESUMO PRIORIZADO

| # | Bloco | Tarefa | Impacto | Esforço |
|---|---|---|---|---|
| 1 | Handoff | Migration: `handoff_mode` + `handoff_human_at` | 🔴 Crítico | Trivial |
| 2 | Handoff | `PATCH /sec/conversations/:id/handoff` + skip no ask | 🔴 Crítico | Pequeno |
| 3 | Handoff | `HandoffButton` React component | 🔴 Crítico | Pequeno |
| 4 | Handoff | Timer 15 min via BullMQ + retomada automática | 🔴 Crítico | Médio |
| 5 | Handoff | Evento WS `conversation.handoff` | 🟠 Alto | Pequeno |
| 6 | Handoff | Webhook-receiver respeitar modo humano | 🟠 Alto | Pequeno |
| 7 | Contexto | `context-builder.js` no plugin | 🔴 Crítico | Médio |
| 8 | Contexto | Motor aceitar `context` no brain.chat() | 🔴 Crítico | Pequeno |
| 9 | Contexto | Webhook receiver enviar contexto no ask | 🟠 Alto | Pequeno |
| 10 | Painel | Visualizar mensagens do protocolo + finalizar | 🟡 Médio | Pequeno |
| 11 | Painel | Mascarar API keys | 🟡 Médio | Trivial |
| 12 | Tools | Tool calling no ProtocolEngine | 🟠 Alto | Grande |
| 13 | Tools | Tools locais no plugin (buscar_cliente, etc.) | 🟠 Alto | Médio |
| 14 | Escalação | Notificação real ao admin + handoff automático | 🟡 Médio | Médio |
| 15 | Painel | Chat de teste inline | 🟢 Baixo | Pequeno |

---

## FLUXO ALVO COMPLETO

```
Mensagem recebida no WA
  │
  ▼
webhook-receiver.js
  ├─ extrai phone do remetente
  ├─ context-builder.js → { cliente, plano, pedidos, promoções }
  ├─ verifica handoff_mode da conversa
  │    ├─ modo "humano" → não chama IA, reseta timer 15min, fim
  │    └─ modo "ia" → continua
  └─ POST /secretaria/ask { chatId, message, context, tools: [...] }
       │
       ▼
  Motor ProtocolEngine.chat()
       ├─ monta system prompt = persona + knowledge + rules + calendar + context_local
       ├─ chama IA com tool definitions
       │    ├─ IA chama tool → executa → retorna resultado → IA gera resposta
       │    └─ IA responde diretamente
       └─ retorna { reply, conversationId, protocolNumber }
            │
            ▼
       webhook-receiver.js envia reply via WA ao cliente

Humano clica HandoffButton
  ├─ PATCH /sec/conversations/:id/handoff { mode: "humano" }
  ├─ BullMQ enfileira job com delay 15min
  ├─ UI exibe badge laranja + contador regressivo
  └─ próximas mensagens WA → webhook ignora IA

15 min sem resposta humana (ou WS cai)
  ├─ BullMQ job dispara → mode = "ia"
  ├─ Motor emite WS conversation.handoff
  └─ UI atualiza badge → toast "Secretária retomou"
```
