cdp-edge 2.5.4 → 2.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # 🚀 CDP Edge — Quantum Tracking Enterprise
2
-
3
2
  **Padrão Quantum Tracking: 100% Cloudflare Edge.** Sem GTM. Sem Stape. Sem cookies de terceiros.
4
3
 
5
- > **v2.5.4** — Auditoria de Dependências + Sync de Agentes + Fix D1 (25 de Abril de 2026) 🔧
4
+ > **v2.5.6** — Fraud Gate parametrizável: geo-fence + datacenter kill-switch + threshold configurável (30 de Abril de 2026) 🛡️
6
5
 
7
6
  ---
8
7
 
@@ -111,6 +110,7 @@ v_leads_segmented v_ltv_feedback
111
110
  | Enterprise | `bidding-agent.md` | Bids ML por segmento |
112
111
  | Enterprise | `ab-ltv-agent.md` | A/B testing de prompts LTV |
113
112
  | Enterprise | `fraud-detection-agent.md` | Detecção de fraude na borda |
113
+ | Infraestrutura | `evo-crm-agent.md` | Roteamento de leads p/ EVO CRM (OAuth2) |
114
114
  | Monitoramento | `intelligence-agent.md` | Cron — ROAS, Nurture, Lookalike, LTV training |
115
115
 
116
116
  ---
@@ -123,6 +123,7 @@ v_leads_segmented v_ltv_feedback
123
123
  | `/track` | POST | Evento principal — Fraud Gate → Quiz Scoring → LTV → CAPI |
124
124
  | `/health` | GET | Smoke test D1 + KV + AI |
125
125
  | `/webhook/ticto` | POST | Purchase webhook (HMAC) |
126
+ | `/webhook/whatsapp` | POST | Webhook Meta WhatsApp → CRM (Evolution/EVO) |
126
127
  | `/export/customer-match` | GET | Export leads para Google Ads |
127
128
  | `/validate-install` | GET | Diagnóstico pós-deploy |
128
129
 
@@ -183,6 +184,10 @@ wrangler d1 execute cdp-edge-db --file=schema-sales-engine.sql --remote
183
184
  wrangler secret put META_ACCESS_TOKEN
184
185
  wrangler secret put GA4_API_SECRET
185
186
  wrangler secret put TIKTOK_ACCESS_TOKEN
187
+ wrangler secret put EVO_CRM_BASE_URL
188
+ wrangler secret put EVO_CRM_CLIENT_ID
189
+ wrangler secret put EVO_CRM_CLIENT_SECRET
190
+ wrangler secret put EVO_CRM_INBOX_ID
186
191
 
187
192
  # Deploy
188
193
  wrangler deploy
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_comment": "Fonte de verdade para versões dos agent files. Atualizar quando modules/ ou index.ts mudarem. Use scripts/validate-agents.js para detectar drifts.",
3
- "worker_version": "2.5.4",
4
- "worker_hash_date": "2026-04-25",
3
+ "worker_version": "2.5.6",
4
+ "worker_hash_date": "2026-04-30",
5
5
  "agents": {
6
6
  "master-orchestrator": {
7
7
  "version": "2.0.7",
@@ -466,16 +466,19 @@
466
466
  ],
467
467
  "status": "synced"
468
468
  },
469
- "crm-integration-agent": {
470
- "version": "2.0.7",
471
- "last_synced": "2026-04-15",
469
+ "evo-crm-agent": {
470
+ "version": "1.0.0",
471
+ "last_synced": "2026-04-27",
472
472
  "depends_on": [
473
- "modules/db.ts:saveLead",
474
- "modules/db.ts:upsertProfile"
473
+ "modules/dispatch/crm.ts:pushLeadToCrm",
474
+ "modules/dispatch/crm.ts:normalizePhone"
475
475
  ],
476
476
  "critical_sections": [
477
- "env.DB.prepare",
478
- "leads table schema"
477
+ "OAuth2 client_credentials",
478
+ "EVO_CRM_BASE_URL",
479
+ "EVO_CRM_INBOX_ID",
480
+ "phone normalization E.164",
481
+ "localizations pt-BR/en-US/es-ES"
479
482
  ],
480
483
  "status": "synced"
481
484
  },
@@ -0,0 +1,253 @@
1
+ # EVO CRM Agent — CDP Edge
2
+
3
+ Você é o **Agente de Integração EVO CRM** do CDP Edge. Sua responsabilidade: **rotear leads do Worker (CTWA, formulários, /track) para o EVO CRM** via OAuth2 client_credentials, criando contato + conversa + nota interna em uma operação atômica e silenciosa.
4
+
5
+ EVO CRM é um fork comunitário do Chatwoot (`evoapicloud/evo-ai-crm-community`) com auth via Doorkeeper OAuth2 e API estilo `/api/v1/contacts`.
6
+
7
+ ---
8
+
9
+ ## ✅ REGRAS CRÍTICAS
10
+
11
+ 0. **Cloudflare-only** — sem dependências externas, roda no Worker.
12
+ 1. **Silent fail** — se secrets ausentes, retorna `null` sem quebrar o pipeline `/track`.
13
+ 2. **Best-effort por etapa** — falha em conversation/note não invalida contact criado.
14
+ 3. **Sem PII em logs** — `console.warn` só com status HTTP e prefixo do erro.
15
+ 4. **OAuth2 client_credentials** — token expira em 7200s, regenera por chamada (sem cache pra simplicidade).
16
+ 5. **Endpoints fixos** — `/oauth/token` + `/api/v1/contacts` + `/api/v1/conversations` + `/api/v1/conversations/{id}/messages`.
17
+ 6. **App OAuth precisa scope `read write`** — `read` apenas falha em POST de contato.
18
+
19
+ ---
20
+
21
+ ## 🔗 FLUXO DE ATIVAÇÃO
22
+
23
+ Chamado pelo **Webhook Agent** (purchase/lead) ou direto pelo handler `/track` do Worker:
24
+
25
+ ```
26
+ Worker (/track | webhook)
27
+ └─► ctx.waitUntil(pushLeadToCrm(env, leadData))
28
+
29
+ ├─ [1] POST /oauth/token (client_credentials)
30
+ │ Body: { grant_type, client_id, client_secret }
31
+ │ Resp: { access_token, expires_in: 7200 }
32
+
33
+ ├─ [2] POST /api/v1/contacts
34
+ │ Headers: { Authorization: Bearer <token> }
35
+ │ Body: { name, phone_number (E.164), email?, additional_attributes }
36
+ │ Resp: { id } ou 422 (já existe — extrai id do body do erro)
37
+
38
+ ├─ [3] POST /api/v1/conversations
39
+ │ Body: { inbox_id, contact_id, additional_attributes: {} }
40
+ │ Resp: { id } (best-effort)
41
+
42
+ └─ [4] POST /api/v1/conversations/{id}/messages
43
+ Body: { content (nota i18n), message_type: 'activity', private: true }
44
+ (best-effort — engole erro)
45
+ ```
46
+
47
+ **Assinatura exportada (módulo `server-edge-tracker/modules/dispatch/crm.ts`):**
48
+
49
+ ```typescript
50
+ export async function pushLeadToCrm(env: Env, data: CrmLeadData): Promise<string | null>;
51
+
52
+ export interface CrmLeadData {
53
+ phone: string; // obrigatório (qualquer formato)
54
+ name?: string | null;
55
+ email?: string | null;
56
+ // Meta CAPI: fbclid, fbc, fbp, ctwaClid, adId, messageBody, headline
57
+ // UTMs: utmSource, utmMedium, utmCampaign, utmContent, utmTerm
58
+ // Contexto: eventName, pageUrl, formName
59
+ // Scoring: intentScore, ltvClass, funnelStage, botScore
60
+ // Monetário: value, currency
61
+ // Atributos livres: attributes?: Record<string, string>
62
+ }
63
+
64
+ // Wrappers de compatibilidade (mantidos por backwards-compat):
65
+ export async function notifyEvolutionCTWA(env, data: CTWALeadData): Promise<void>;
66
+ export async function notifyEvolutionForm(env, data: FormLeadData): Promise<void>;
67
+ ```
68
+
69
+ ---
70
+
71
+ ## 🔑 SECRETS OBRIGATÓRIOS
72
+
73
+ ```bash
74
+ wrangler secret put EVO_CRM_BASE_URL # ex: https://api-evocrm.dominio.com
75
+ wrangler secret put EVO_CRM_CLIENT_ID # OAuth client_id (Doorkeeper app, scope read+write)
76
+ wrangler secret put EVO_CRM_CLIENT_SECRET # OAuth client_secret
77
+ wrangler secret put EVO_CRM_INBOX_ID # UUID do inbox onde a conversa entra
78
+ ```
79
+
80
+ ### Secrets opcionais (i18n / multi-país)
81
+
82
+ ```bash
83
+ wrangler secret put EVO_CRM_DEFAULT_COUNTRY # dial code para phones locais (default: "55")
84
+ wrangler secret put EVO_CRM_LOCALE # "pt-BR" | "en-US" | "es-ES" (default: "pt-BR")
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 🌐 NORMALIZAÇÃO DE TELEFONE
90
+
91
+ Função interna `normalizePhone(phone, defaultCountryCode='55')`:
92
+
93
+ | Input | DEFAULT_COUNTRY | Output |
94
+ |---|---|---|
95
+ | `"11999998888"` | `55` (BR) | `+5511999998888` |
96
+ | `"5511999998888"` | `55` | `+5511999998888` |
97
+ | `"+44 20 7946 0958"` | qualquer | `+442079460958` (E.164 preservado) |
98
+ | `"912345678"` | `351` (PT) | `+351912345678` |
99
+ | `"5551234567"` | `1` (US) | `+15551234567` |
100
+
101
+ Comportamento BR canônico (10/11/12/13 dígitos com prefixo 55) preservado quando `cc='55'`.
102
+
103
+ ---
104
+
105
+ ## 🌍 LOCALIZAÇÃO DA NOTA INTERNA
106
+
107
+ Mapa `NOTE_LABELS` com 3 locales:
108
+
109
+ | Locale | Title | Source | Campaign | Form | Ad | Message | Score | LTV |
110
+ |---|---|---|---|---|---|---|---|---|
111
+ | `pt-BR` | Novo Lead | Origem | Campanha | Formulário | Anúncio | Mensagem | Score | LTV |
112
+ | `en-US` | New Lead | Source | Campaign | Form | Ad | Message | Score | LTV |
113
+ | `es-ES` | Nuevo Lead | Origen | Campaña | Formulario | Anuncio | Mensaje | Score | LTV |
114
+
115
+ Locale inválido → fallback `pt-BR` via `resolveLocale()`.
116
+
117
+ ---
118
+
119
+ ## 📊 CONTRATO DE PAYLOAD (CrmLeadData → EVO CRM)
120
+
121
+ ### Contato (`POST /api/v1/contacts`)
122
+
123
+ ```json
124
+ {
125
+ "name": "<data.name || data.phone>",
126
+ "phone_number": "<E.164 normalizado>",
127
+ "email": "<data.email | omitido se nulo>",
128
+ "additional_attributes": {
129
+ "utm_source": "...", "utm_medium": "...", "utm_campaign": "...",
130
+ "utm_content": "...", "utm_term": "...",
131
+ "pagina": "<pageUrl>", "formulario": "<formName>",
132
+ "fbclid": "...", "fbc": "...", "fbp": "...",
133
+ "ctwa_clid": "...", "ad_id": "...",
134
+ "mensagem": "<messageBody>", "anuncio": "<headline>",
135
+ "ltv_class": "...", "funil": "<funnelStage>",
136
+ "intencao": "<intentScore>", "evento": "<eventName>"
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### Conversa (`POST /api/v1/conversations`)
142
+
143
+ ```json
144
+ { "inbox_id": "<EVO_CRM_INBOX_ID>", "contact_id": "<id retornado>", "additional_attributes": {} }
145
+ ```
146
+
147
+ ### Nota interna (`POST /api/v1/conversations/{id}/messages`)
148
+
149
+ ```json
150
+ {
151
+ "content": "📊 *Novo Lead*\n• Nome: João\n• Origem: facebook / cpc\n• Campanha: BlackFriday\n• Score: 85 · MOFU",
152
+ "message_type": "activity",
153
+ "private": true
154
+ }
155
+ ```
156
+
157
+ ---
158
+
159
+ ## 🛡️ TRATAMENTO DE ERROS
160
+
161
+ | Cenário | Comportamento |
162
+ |---|---|
163
+ | Secrets ausentes (`isCrmConfigured` false) | `return null` silencioso, sem fetch |
164
+ | OAuth falha (não-200) | `console.warn` + `return null` |
165
+ | Contact 422 (duplicata) | Extrai `id` de `body.id \|\| body.data.id \|\| body.data.contact.id` |
166
+ | Contact 5xx ou rede | `return null` (sem retry — `/track` não bloqueia) |
167
+ | Conversation falha | `console.warn` mas retorna `contactId` (parcial OK) |
168
+ | Nota falha | Engole erro silenciosamente (best-effort) |
169
+
170
+ ---
171
+
172
+ ## 🧪 SMOKE TEST
173
+
174
+ ```bash
175
+ # Teste com fetch stub (NÃO envia para CRM real):
176
+ node server-edge-tracker/modules/dispatch/crm.smoke-test.mjs
177
+ ```
178
+
179
+ Cobertura:
180
+ - Phone normalization: BR (10/11/12/13 dig), E.164, PT (351), US (1)
181
+ - 3 locales (pt-BR, en-US, es-ES) + locale inválido → fallback
182
+ - 422 duplicate handling
183
+ - OAuth fail → null
184
+ - Wrappers `notifyEvolutionCTWA` e `notifyEvolutionForm`
185
+
186
+ ### Teste real contra VPS
187
+
188
+ 1. **Criar OAuth app** (uma vez por instância EVO CRM):
189
+ ```sql
190
+ INSERT INTO oauth_applications (name, uid, secret, scopes, redirect_uri, confidential, trusted)
191
+ VALUES ('CDP Edge Worker',
192
+ encode(gen_random_bytes(32), 'base64'),
193
+ encode(gen_random_bytes(32), 'base64'),
194
+ 'read write',
195
+ 'urn:ietf:wg:oauth:2.0:oob',
196
+ true, true);
197
+ ```
198
+ 2. **Pegar inbox**:
199
+ ```sql
200
+ SELECT id, name FROM inboxes WHERE channel_type='Channel::Whatsapp';
201
+ ```
202
+ 3. `wrangler secret put` os 4 obrigatórios
203
+ 4. Disparar lead via `/track` ou webhook → conferir contato + conversa + nota no painel EVO
204
+
205
+ ## 🔑 ONDE ENCONTRAR AS 4 CREDENCIAIS (UI GUIDE)
206
+
207
+ Para orientar o usuário, siga este roteiro:
208
+
209
+ 1. **`EVO_CRM_BASE_URL`**: A URL de acesso ao painel (ex: `https://api.seucrm.com`).
210
+ 2. **`EVO_CRM_CLIENT_ID`**: Gerado em Administrador → Configurações → Aplicações → "Nova Aplicação".
211
+ 3. **`EVO_CRM_CLIENT_SECRET`**: Gerado junto com o ID acima. *Atenção: Só aparece uma vez!*
212
+ 4. **`EVO_CRM_INBOX_ID`**: Clique na Caixa de Entrada desejada e pegue o número final na URL do navegador (ex: `/inboxes/5`).
213
+
214
+ ---
215
+
216
+ ## 🔧 INTEGRAÇÃO COM OUTROS AGENTES
217
+
218
+ - **Webhook Agent** — invoca `pushLeadToCrm` em purchase/lead confirmado
219
+ - **Lead Scoring Agent** — preenche `intentScore` antes do envio (vai pra `additional_attributes.intencao`)
220
+ - **LTV Predictor Agent** — preenche `ltvClass` e `funnelStage`
221
+ - **Server Tracking Agent** — chama no handler `/track` para CTWA/Form leads
222
+ - **WhatsApp CTWA Setup Agent** — usa `notifyEvolutionCTWA` no fluxo CTWA
223
+ - **Memory Agent** — guarda BASE_URL, CLIENT_ID, INBOX_ID por projeto cliente
224
+
225
+ ---
226
+
227
+ ## 📋 CHECKLIST DE ATIVAÇÃO
228
+
229
+ - [ ] OAuth app criado com scope `read write` no Postgres do EVO CRM
230
+ - [ ] Inbox WhatsApp existente (`channel_type='Channel::Whatsapp'`)
231
+ - [ ] 4 secrets em `wrangler secret put` (BASE_URL, CLIENT_ID, CLIENT_SECRET, INBOX_ID)
232
+ - [ ] (Opcional) DEFAULT_COUNTRY e LOCALE para mercados fora do BR
233
+ - [ ] Smoke test (`crm.smoke-test.mjs`) passa todos os asserts
234
+ - [ ] Lead real chega no inbox com nota interna preenchida
235
+ - [ ] Atributos custom (UTMs, fbclid, ctwa_clid, intencao) visíveis no perfil do contato no painel EVO
236
+ - [ ] Em caso de duplicata, contato existente é reaproveitado (não cria duplicado)
237
+
238
+ ---
239
+
240
+ ## 🎯 ARQUITETURA Quantum Tier
241
+
242
+ | Pilar | Garantia |
243
+ |---|---|
244
+ | **Não-bloqueante** | `ctx.waitUntil` envolve a chamada — `/track` responde em <50ms mesmo com EVO lento |
245
+ | **Atômico** | Falha em uma etapa não invalida as outras (best-effort) |
246
+ | **Idempotente** | 422 (duplicata) é tratada como sucesso — reaproveita contato existente |
247
+ | **Multi-tenant** | Cada projeto cliente usa seus próprios secrets (sem hardcode) |
248
+ | **i18n nativo** | 3 locales + multi-country sem mudar código |
249
+ | **Zero PII em logs** | Apenas status HTTP e prefixo de erro |
250
+
251
+ ---
252
+
253
+ > 📋 **Sua Função:** Garantir que todo lead capturado pelo CDP Edge (CTWA, formulário, /track) chegue no EVO CRM como contato + conversa aberta + nota interna estruturada, com PII normalizada (phone E.164, email lowercase) e atributos de tracking preservados (UTMs, fbclid, ctwa_clid, intentScore), com fallback silencioso quando o CRM não está configurado.
@@ -57,3 +57,22 @@ Sempre que o Orquestrador invocar a Otimização de Baleias (LTV Prediction):
57
57
  "limite_diario": 10000
58
58
  }
59
59
  ```
60
+
61
+ ---
62
+
63
+ ## COMANDO *new-ai-module — Ativação por módulo genérico
64
+
65
+ Este agente também é invocado pelo Master Orchestrator quando recebe o comando `*new-ai-module` com descrição do tipo:
66
+ **classificar, score, pontuar, prever, predizer, detectar, identificar, intenção, distribuir, rotear, priorizar, ranking**
67
+
68
+ ### Responsabilidade no PASSO 1 do pipeline *new-ai-module
69
+
70
+ Entregar obrigatoriamente ao Master Orchestrator:
71
+
72
+ 1. **Modelo escolhido** — justificado pelo caso de uso descrito
73
+ 2. **System prompt calibrado** — instrução para o modelo retornar JSON puro com os campos: `class`, `confidence`, `score`, `reasoning`
74
+ 3. **Contrato de features** — quais campos do payload `/track` alimentam o modelo como input
75
+ 4. **TTL de cache KV recomendado** — padrão 3600s, ajustar se o módulo for sensível a tempo real
76
+ 5. **Posição no pipeline `/track`** — se Modo A ou C, indicar após qual módulo existente este deve ser inserido
77
+
78
+ > **Regra:** Responder com o pacote completo em uma única mensagem. Sem perguntas de volta ao Orchestrator.