cdp-edge 1.2.2 → 1.3.0

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.
Files changed (141) hide show
  1. package/README.md +153 -306
  2. package/bin/cdp-edge.js +71 -61
  3. package/contracts/agent-versions.json +682 -0
  4. package/contracts/api-versions.json +372 -368
  5. package/contracts/types.ts +81 -0
  6. package/dist/commands/analyze.js +52 -52
  7. package/dist/commands/infra.js +54 -54
  8. package/dist/commands/install.js +26 -3
  9. package/dist/commands/server.js +174 -174
  10. package/dist/commands/setup.js +332 -100
  11. package/dist/commands/validate.js +248 -84
  12. package/dist/index.js +12 -12
  13. package/dist/sdk/cdpTrack.js +2095 -0
  14. package/dist/sdk/cdpTrack.min.js +64 -0
  15. package/dist/sdk/install-snippet.html +10 -0
  16. package/docs/whatsapp-ctwa.md +5 -4
  17. package/extracted-skill/tracking-events-generator/INTEGRACAO-COMPLETA.md +89 -0
  18. package/extracted-skill/tracking-events-generator/MELHORIAS-IMPLEMENTADAS.md +101 -0
  19. package/extracted-skill/tracking-events-generator/advanced-matching.js +364 -364
  20. package/extracted-skill/tracking-events-generator/agents/ab-ltv-agent.md +196 -0
  21. package/extracted-skill/tracking-events-generator/agents/ab-testing-agent.md +1 -1
  22. package/extracted-skill/tracking-events-generator/agents/attribution-agent.md +41 -41
  23. package/extracted-skill/tracking-events-generator/agents/bidding-agent.md +347 -0
  24. package/extracted-skill/tracking-events-generator/agents/bing-agent.md +40 -50
  25. package/extracted-skill/tracking-events-generator/agents/browser-tracking.md +174 -74
  26. package/extracted-skill/tracking-events-generator/agents/code-guardian-agent.md +1 -1
  27. package/extracted-skill/tracking-events-generator/agents/compliance-agent.md +25 -5
  28. package/extracted-skill/tracking-events-generator/agents/dashboard-agent.md +10 -10
  29. package/extracted-skill/tracking-events-generator/agents/database-agent.md +43 -42
  30. package/extracted-skill/tracking-events-generator/agents/debug-agent.md +22 -22
  31. package/extracted-skill/tracking-events-generator/agents/devops-agent.md +232 -0
  32. package/extracted-skill/tracking-events-generator/agents/domain-setup-agent.md +23 -9
  33. package/extracted-skill/tracking-events-generator/agents/email-agent.md +28 -1
  34. package/extracted-skill/tracking-events-generator/agents/evo-crm-agent.md +244 -0
  35. package/extracted-skill/tracking-events-generator/agents/fingerprint-agent.md +206 -1
  36. package/extracted-skill/tracking-events-generator/agents/fraud-detection-agent.md +143 -0
  37. package/extracted-skill/tracking-events-generator/agents/google-agent.md +128 -2
  38. package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +191 -31
  39. package/extracted-skill/tracking-events-generator/agents/lead-scoring-agent.md +282 -0
  40. package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +145 -34
  41. package/extracted-skill/tracking-events-generator/agents/localization-agent.md +1 -1
  42. package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +5 -5
  43. package/extracted-skill/tracking-events-generator/agents/master-feedback-loop.md +81 -21
  44. package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +313 -93
  45. package/extracted-skill/tracking-events-generator/agents/match-quality-agent.md +304 -0
  46. package/extracted-skill/tracking-events-generator/agents/memory-agent.md +190 -15
  47. package/extracted-skill/tracking-events-generator/agents/meta-agent.md +10 -2
  48. package/extracted-skill/tracking-events-generator/agents/ml-clustering-agent.md +749 -0
  49. package/extracted-skill/tracking-events-generator/agents/page-analyzer.md +21 -4
  50. package/extracted-skill/tracking-events-generator/agents/performance-agent.md +41 -31
  51. package/extracted-skill/tracking-events-generator/agents/performance-optimization-agent.md +18 -8
  52. package/extracted-skill/tracking-events-generator/agents/pinterest-agent.md +14 -6
  53. package/extracted-skill/tracking-events-generator/agents/premium-tracking-intelligence-agent.md +7 -7
  54. package/extracted-skill/tracking-events-generator/agents/r2-setup-agent.md +16 -8
  55. package/extracted-skill/tracking-events-generator/agents/reddit-agent.md +15 -7
  56. package/extracted-skill/tracking-events-generator/agents/security-enterprise-agent.md +157 -48
  57. package/extracted-skill/tracking-events-generator/agents/server-tracking.md +35 -35
  58. package/extracted-skill/tracking-events-generator/agents/spotify-agent.md +15 -7
  59. package/extracted-skill/tracking-events-generator/agents/tiktok-agent.md +73 -2
  60. package/extracted-skill/tracking-events-generator/agents/tracking-plan-agent.md +104 -9
  61. package/extracted-skill/tracking-events-generator/agents/utm-agent.md +322 -0
  62. package/extracted-skill/tracking-events-generator/agents/validator-agent.md +13 -9
  63. package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +112 -4
  64. package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +58 -5
  65. package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +26 -18
  66. package/extracted-skill/tracking-events-generator/agents/youtube-agent.md +152 -37
  67. package/extracted-skill/tracking-events-generator/anti-blocking.js +285 -285
  68. package/extracted-skill/tracking-events-generator/cdpTrack.js +642 -641
  69. package/extracted-skill/tracking-events-generator/contracts/api-versions.json +14 -10
  70. package/extracted-skill/tracking-events-generator/engagement-scoring.js +226 -226
  71. package/extracted-skill/tracking-events-generator/evals/evals.json +235 -235
  72. package/extracted-skill/tracking-events-generator/integration-test.js +497 -497
  73. package/extracted-skill/tracking-events-generator/knowledge-base.md +172 -0
  74. package/extracted-skill/tracking-events-generator/micro-events.js +992 -992
  75. package/extracted-skill/tracking-events-generator/models/lancamento-imobiliario.md +344 -0
  76. package/extracted-skill/tracking-events-generator/models/pinterest/conversions-api-template.js +144 -144
  77. package/extracted-skill/tracking-events-generator/models/pinterest/event-mappings.json +48 -48
  78. package/extracted-skill/tracking-events-generator/models/pinterest/tag-template.js +28 -28
  79. package/extracted-skill/tracking-events-generator/models/quiz-funnel.md +83 -19
  80. package/extracted-skill/tracking-events-generator/models/reddit/conversions-api-template.js +205 -205
  81. package/extracted-skill/tracking-events-generator/models/reddit/event-mappings.json +56 -56
  82. package/extracted-skill/tracking-events-generator/models/reddit/pixel-template.js +19 -19
  83. package/extracted-skill/tracking-events-generator/models/scenarios/behavior-engine.js +425 -425
  84. package/extracted-skill/tracking-events-generator/route-intent-capture.js +222 -0
  85. package/extracted-skill/tracking-events-generator/tracking.config.js +3 -3
  86. package/package.json +89 -75
  87. package/scripts/build-sdk.js +106 -0
  88. package/server-edge-tracker/.client.env.example +14 -0
  89. package/server-edge-tracker/INSTALAR.md +222 -23
  90. package/server-edge-tracker/SEGMENTATION-DOCS.md +513 -0
  91. package/server-edge-tracker/config/utm-mapping.json +64 -0
  92. package/server-edge-tracker/deploy-client.cjs +76 -0
  93. package/server-edge-tracker/index.ts +1230 -0
  94. package/server-edge-tracker/migrate-v7.sql +64 -0
  95. package/server-edge-tracker/modules/db.ts +710 -0
  96. package/server-edge-tracker/modules/dispatch/crm.ts +382 -0
  97. package/server-edge-tracker/modules/dispatch/ga4.ts +72 -0
  98. package/server-edge-tracker/modules/dispatch/meta.ts +143 -0
  99. package/server-edge-tracker/modules/dispatch/platforms.ts +255 -0
  100. package/server-edge-tracker/modules/dispatch/tiktok.ts +107 -0
  101. package/server-edge-tracker/modules/dispatch/whatsapp.ts +296 -0
  102. package/server-edge-tracker/modules/intelligence.ts +589 -0
  103. package/server-edge-tracker/modules/ml/bidding.ts +247 -0
  104. package/server-edge-tracker/modules/ml/fraud.ts +302 -0
  105. package/server-edge-tracker/modules/ml/logistic.ts +226 -0
  106. package/server-edge-tracker/modules/ml/ltv.ts +531 -0
  107. package/server-edge-tracker/modules/ml/matchquality.ts +232 -0
  108. package/server-edge-tracker/modules/ml/quiz.ts +343 -0
  109. package/server-edge-tracker/modules/ml/roas.ts +255 -0
  110. package/server-edge-tracker/modules/ml/segmentation.ts +407 -0
  111. package/server-edge-tracker/modules/nurture.ts +257 -0
  112. package/server-edge-tracker/modules/utils.ts +311 -0
  113. package/server-edge-tracker/modules/utm/utm-enricher.ts +231 -0
  114. package/server-edge-tracker/schema-ab-ltv.sql +97 -0
  115. package/server-edge-tracker/schema-bidding.sql +86 -0
  116. package/server-edge-tracker/schema-fraud.sql +90 -0
  117. package/server-edge-tracker/schema-indexes.sql +67 -0
  118. package/server-edge-tracker/schema-ltv-feedback.sql +11 -0
  119. package/server-edge-tracker/schema-quiz.sql +52 -0
  120. package/server-edge-tracker/schema-sales-engine.sql +113 -0
  121. package/server-edge-tracker/schema-segmentation.sql +219 -0
  122. package/server-edge-tracker/schema-utm.sql +82 -0
  123. package/server-edge-tracker/schema.sql +281 -265
  124. package/server-edge-tracker/types.ts +275 -0
  125. package/server-edge-tracker/wrangler.toml +140 -85
  126. package/templates/lancamento-imobiliario.md +344 -0
  127. package/templates/multi-step-checkout.md +3 -4
  128. package/templates/pinterest/conversions-api-template.js +144 -144
  129. package/templates/pinterest/event-mappings.json +48 -48
  130. package/templates/pinterest/tag-template.js +28 -28
  131. package/templates/quiz-funnel.md +83 -19
  132. package/templates/reddit/conversions-api-template.js +205 -205
  133. package/templates/reddit/event-mappings.json +56 -56
  134. package/templates/reddit/pixel-template.js +12 -39
  135. package/templates/scenarios/behavior-engine.js +45 -22
  136. package/docs/PixelBuilder-Documentacao-Completa (2).docx +0 -0
  137. package/docs/installation.md +0 -155
  138. package/docs/quick-start.md +0 -185
  139. package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +0 -1419
  140. package/extracted-skill/tracking-events-generator/agents/intelligence-scheduling.md +0 -643
  141. package/server-edge-tracker/worker.js +0 -2574
@@ -0,0 +1,244 @@
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
+ ---
206
+
207
+ ## 🔧 INTEGRAÇÃO COM OUTROS AGENTES
208
+
209
+ - **Webhook Agent** — invoca `pushLeadToCrm` em purchase/lead confirmado
210
+ - **Lead Scoring Agent** — preenche `intentScore` antes do envio (vai pra `additional_attributes.intencao`)
211
+ - **LTV Predictor Agent** — preenche `ltvClass` e `funnelStage`
212
+ - **Server Tracking Agent** — chama no handler `/track` para CTWA/Form leads
213
+ - **WhatsApp CTWA Setup Agent** — usa `notifyEvolutionCTWA` no fluxo CTWA
214
+ - **Memory Agent** — guarda BASE_URL, CLIENT_ID, INBOX_ID por projeto cliente
215
+
216
+ ---
217
+
218
+ ## 📋 CHECKLIST DE ATIVAÇÃO
219
+
220
+ - [ ] OAuth app criado com scope `read write` no Postgres do EVO CRM
221
+ - [ ] Inbox WhatsApp existente (`channel_type='Channel::Whatsapp'`)
222
+ - [ ] 4 secrets em `wrangler secret put` (BASE_URL, CLIENT_ID, CLIENT_SECRET, INBOX_ID)
223
+ - [ ] (Opcional) DEFAULT_COUNTRY e LOCALE para mercados fora do BR
224
+ - [ ] Smoke test (`crm.smoke-test.mjs`) passa todos os asserts
225
+ - [ ] Lead real chega no inbox com nota interna preenchida
226
+ - [ ] Atributos custom (UTMs, fbclid, ctwa_clid, intencao) visíveis no perfil do contato no painel EVO
227
+ - [ ] Em caso de duplicata, contato existente é reaproveitado (não cria duplicado)
228
+
229
+ ---
230
+
231
+ ## 🎯 ARQUITETURA Quantum Tier
232
+
233
+ | Pilar | Garantia |
234
+ |---|---|
235
+ | **Não-bloqueante** | `ctx.waitUntil` envolve a chamada — `/track` responde em <50ms mesmo com EVO lento |
236
+ | **Atômico** | Falha em uma etapa não invalida as outras (best-effort) |
237
+ | **Idempotente** | 422 (duplicata) é tratada como sucesso — reaproveita contato existente |
238
+ | **Multi-tenant** | Cada projeto cliente usa seus próprios secrets (sem hardcode) |
239
+ | **i18n nativo** | 3 locales + multi-country sem mudar código |
240
+ | **Zero PII em logs** | Apenas status HTTP e prefixo de erro |
241
+
242
+ ---
243
+
244
+ > 📋 **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.
@@ -21,6 +21,211 @@ Sempre que o usuário sangrar dinheiro por culpa de "Perda de Cookies/Atribuiç
21
21
 
22
22
  ---
23
23
 
24
+ ## 💻 IMPLEMENTAÇÃO REAL — modules/fingerprint-middleware.ts
25
+
26
+ ### Módulo completo para injetar no Worker
27
+
28
+ ```typescript
29
+ /**
30
+ * Fingerprint Middleware — CDP Edge
31
+ * Edge-only signals: IP + Accept-Language + UA base + ASN
32
+ * LGPD/CCPA compliant: nenhum PII direto, hash efêmero
33
+ */
34
+
35
+ // ─────────────────────────────────────────────────
36
+ // 1. Geração do P-Hash (fingerprint de borda)
37
+ // ─────────────────────────────────────────────────
38
+
39
+ /**
40
+ * Gera hash identificador efêmero combinando sinais anônimos de borda.
41
+ * NUNCA usa Canvas, WebGL ou AudioContext (client-side) — apenas Edge signals.
42
+ *
43
+ * @param {Request} request - Request do Cloudflare Worker
44
+ * @returns {Promise<string>} p_hash — identificador efêmero de 16 chars
45
+ */
46
+ export async function generatePHash(request) {
47
+ const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
48
+ const acceptLang = request.headers.get('Accept-Language') || 'unknown';
49
+ const userAgent = request.headers.get('User-Agent') || 'unknown';
50
+ const asOrg = request.cf?.asOrganization || 'unknown';
51
+ const country = request.cf?.country || 'unknown';
52
+
53
+ // Reduzir UA para base (remover versão minor — ex: "Chrome/120" não "Chrome/120.0.6099.71")
54
+ const uaBase = userAgent
55
+ .replace(/[\d.]+/g, (m) => m.split('.')[0]) // mantém só major version
56
+ .replace(/[^a-zA-Z0-9 /]/g, '') // remove caracteres especiais
57
+ .slice(0, 60); // limitar tamanho
58
+
59
+ // Normalizar Accept-Language para idioma principal
60
+ const langBase = acceptLang.split(',')[0].split(';')[0].trim().slice(0, 5); // ex: "pt-BR"
61
+
62
+ // Concatenar sinais — ordem importa para consistência
63
+ const fingerprint = `${ip}|${langBase}|${uaBase}|${asOrg}|${country}`;
64
+
65
+ // SHA-256 → primeiros 16 hex chars (64 bits de entropia — suficiente para sess de 48h)
66
+ const encoder = new TextEncoder();
67
+ const data = encoder.encode(fingerprint);
68
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
69
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
70
+ const fullHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
71
+
72
+ return fullHash.slice(0, 16); // p_hash = 16 chars hex
73
+ }
74
+
75
+ // ─────────────────────────────────────────────────
76
+ // 2. Registro do P-Hash no D1
77
+ // ─────────────────────────────────────────────────
78
+
79
+ /**
80
+ * Salva ou atualiza p_hash no D1 com UTMs da visita atual.
81
+ * Janela de restauração: 48h (configurável).
82
+ *
83
+ * @param {D1Database} db
84
+ * @param {string} pHash
85
+ * @param {Object} utmData - { utm_source, utm_medium, utm_campaign, utm_content, utm_term }
86
+ */
87
+ export async function recordPHash(db, pHash, utmData) {
88
+ const hasUtm = utmData.utm_source || utmData.utm_medium || utmData.utm_campaign;
89
+
90
+ if (!hasUtm) {
91
+ // Visita sem UTM — só atualiza last_seen (não sobrescreve UTMs)
92
+ await db.prepare(`
93
+ UPDATE fingerprint_sessions
94
+ SET last_seen_at = datetime('now')
95
+ WHERE p_hash = ? AND last_seen_at > datetime('now', '-48 hours')
96
+ `).bind(pHash).run();
97
+ return;
98
+ }
99
+
100
+ // Visita COM UTM — inserir ou atualizar
101
+ await db.prepare(`
102
+ INSERT INTO fingerprint_sessions (p_hash, utm_source, utm_medium, utm_campaign, utm_content, utm_term, last_seen_at)
103
+ VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
104
+ ON CONFLICT(p_hash) DO UPDATE SET
105
+ utm_source = excluded.utm_source,
106
+ utm_medium = excluded.utm_medium,
107
+ utm_campaign = excluded.utm_campaign,
108
+ utm_content = excluded.utm_content,
109
+ utm_term = excluded.utm_term,
110
+ last_seen_at = datetime('now')
111
+ `).bind(
112
+ pHash,
113
+ utmData.utm_source || null,
114
+ utmData.utm_medium || null,
115
+ utmData.utm_campaign || null,
116
+ utmData.utm_content || null,
117
+ utmData.utm_term || null
118
+ ).run();
119
+ }
120
+
121
+ // ─────────────────────────────────────────────────
122
+ // 3. UTM Restoration Middleware
123
+ // ─────────────────────────────────────────────────
124
+
125
+ /**
126
+ * Recupera UTMs do D1 para um p_hash (janela 48h).
127
+ * Injeta de volta no payload CAPI quando a requisição chega sem UTMs.
128
+ *
129
+ * @param {D1Database} db
130
+ * @param {string} pHash
131
+ * @returns {Promise<Object|null>} UTMs restauradas ou null
132
+ */
133
+ export async function restoreUtms(db, pHash) {
134
+ const row = await db.prepare(`
135
+ SELECT utm_source, utm_medium, utm_campaign, utm_content, utm_term
136
+ FROM fingerprint_sessions
137
+ WHERE p_hash = ?
138
+ AND last_seen_at > datetime('now', '-48 hours')
139
+ AND utm_source IS NOT NULL
140
+ LIMIT 1
141
+ `).bind(pHash).first();
142
+
143
+ return row || null;
144
+ }
145
+
146
+ /**
147
+ * Middleware principal — chamar no início do handler /track
148
+ *
149
+ * @param {Request} request
150
+ * @param {Object} env
151
+ * @param {Object} payload - payload já parseado do /track
152
+ * @returns {Promise<Object>} payload enriquecido com UTMs restauradas
153
+ */
154
+ export async function fingerprintMiddleware(request, env, payload) {
155
+ try {
156
+ // 1. Gerar p_hash para esta visita
157
+ const pHash = await generatePHash(request);
158
+
159
+ // 2. Extrair UTMs do payload atual
160
+ const currentUtms = {
161
+ utm_source: payload.utm_source || null,
162
+ utm_medium: payload.utm_medium || null,
163
+ utm_campaign: payload.utm_campaign || null,
164
+ utm_content: payload.utm_content || null,
165
+ utm_term: payload.utm_term || null,
166
+ };
167
+
168
+ // 3. Se tem UTMs → registrar no D1 (atualiza para próximas visitas)
169
+ await recordPHash(env.DB, pHash, currentUtms);
170
+
171
+ // 4. Se NÃO tem UTMs → tentar restaurar do D1 (últimas 48h)
172
+ const hasCurrentUtm = currentUtms.utm_source || currentUtms.utm_campaign;
173
+ if (!hasCurrentUtm) {
174
+ const restoredUtms = await restoreUtms(env.DB, pHash);
175
+ if (restoredUtms) {
176
+ // Injetar UTMs restauradas no payload → creditam a campanha correta na CAPI
177
+ payload.utm_source = restoredUtms.utm_source;
178
+ payload.utm_medium = restoredUtms.utm_medium;
179
+ payload.utm_campaign = restoredUtms.utm_campaign;
180
+ payload.utm_content = restoredUtms.utm_content;
181
+ payload.utm_term = restoredUtms.utm_term;
182
+ payload._utm_restored = true; // flag para debug
183
+ }
184
+ }
185
+
186
+ // 5. Adicionar p_hash ao payload para uso pelo Identity Graph
187
+ payload.p_hash = pHash;
188
+
189
+ } catch (err) {
190
+ // Fail-safe: nunca bloquear o tracking por erro de fingerprint
191
+ console.error('[FingerprintMiddleware] Erro (fail-safe):', err.message);
192
+ }
193
+
194
+ return payload;
195
+ }
196
+ ```
197
+
198
+ ### Schema D1 necessário
199
+
200
+ ```sql
201
+ -- Adicionar a schema.sql
202
+ CREATE TABLE IF NOT EXISTS fingerprint_sessions (
203
+ p_hash TEXT PRIMARY KEY,
204
+ utm_source TEXT,
205
+ utm_medium TEXT,
206
+ utm_campaign TEXT,
207
+ utm_content TEXT,
208
+ utm_term TEXT,
209
+ last_seen_at TEXT DEFAULT (datetime('now'))
210
+ );
211
+
212
+ CREATE INDEX IF NOT EXISTS idx_fp_last_seen ON fingerprint_sessions(last_seen_at);
213
+ ```
214
+
215
+ ### Uso no index.ts
216
+
217
+ ```javascript
218
+ // No início do handler /track, ANTES do fraud gate:
219
+ import { fingerprintMiddleware } from './fingerprint-middleware.js';
220
+
221
+ // Dentro do fetch handler:
222
+ payload = await fingerprintMiddleware(request, env, payload);
223
+ // A partir daqui, payload.utm_* estão restauradas (se disponíveis)
224
+ // e payload.p_hash está disponível para o Identity Graph
225
+ ```
226
+
227
+ ---
228
+
24
229
  ## INPUTS RECEBIDOS
25
230
 
26
231
  - Headers da requisição Edge: `CF-Connecting-IP`, `Accept-Language`, `User-Agent`, `request.cf.asOrganization`
@@ -41,7 +246,7 @@ Sempre que o usuário sangrar dinheiro por culpa de "Perda de Cookies/Atribuiç
41
246
  ```json
42
247
  {
43
248
  "arquivos_criados": [
44
- "cloudflare/fingerprint-middleware.js"
249
+ "modules/fingerprint-middleware.ts"
45
250
  ],
46
251
  "sinais_utilizados": ["ip", "accept-language", "user-agent-base", "cf-asorg"],
47
252
  "janela_restauracao_horas": 48,
@@ -0,0 +1,143 @@
1
+ # Fraud Detection Agent — CDP Edge Quantum Tier
2
+
3
+ ## Identidade
4
+
5
+ **Agente:** Fraud Detection Agent
6
+ **Papel:** Detecção e Bloqueio Automático de Tráfego Fraudulento na Borda
7
+ **Nível:** Deus (Quantum Tier) — Enterprise-Level Fase 4
8
+ **Versão:** 1.0.0 — 9 de Abril de 2026
9
+
10
+ ---
11
+
12
+ ## Missão
13
+
14
+ Identificar, classificar e bloquear tráfego fraudulento (click fraud, bots, ataques de velocidade,
15
+ tráfego inválido) **antes que ele contamine o D1, os pixels de anúncio e as predições de LTV** —
16
+ protegendo o budget de ads e a qualidade dos dados de ML.
17
+
18
+ ---
19
+
20
+ ## Posição no Fluxo do Master Orchestrator
21
+
22
+ ```
23
+ Request chega em /track
24
+
25
+ [FASE 4 — PRÉ-PROCESSAMENTO]
26
+ checkFraudGate(env, request, payload)
27
+ ├── KV blocklist check (IP / fingerprint) → ~0ms — bloqueia na borda
28
+ ├── Velocity counter KV (eventos por IP/hora) → ~1ms — detecta bursts
29
+ └── Score de fraude heurístico → ~0ms — sem AI, pura lógica
30
+ ↓ [se score ≥ 80]
31
+ Evento DESCARTADO — returns 200 (silent drop) — não alimenta D1 nem pixels
32
+ ↓ [se score < 80]
33
+ Evento processado normalmente (LTV + CAPI + GA4 + TikTok)
34
+ ↓ [background]
35
+ logFraudSignal(env, ...) → D1: fraud_signals + fraud_alerts
36
+ ```
37
+
38
+ **Implementação:** `modules/ml/fraud.ts` — `checkFraudGate(env: Env, request: Request, payload: TrackPayload)` (TypeScript)
39
+
40
+ **Integração automática com fluxo `/track`:**
41
+ - Executa ANTES do LTV, fingerprinting e CAPI
42
+ - Silent drop (retorna 200 ao browser para não vazar a detecção)
43
+ - Regime de falha seguro: se o fraud gate falhar → deixa o evento passar
44
+
45
+ **Downstream (quem se beneficia):**
46
+ - `ltv-predictor-agent.md` → LTV score calculado apenas sobre tráfego limpo
47
+ - `ml-clustering-agent.md` → Segmentos ML sem contaminação de bots
48
+ - `bidding-agent.md` → Bids baseados em conversões reais
49
+
50
+ ---
51
+
52
+ ## Sinais de Fraude Detectados
53
+
54
+ ### 1. Blocklist (KV) — Bloqueio Imediato
55
+ | Sinal | Critério | Ação |
56
+ |-------|----------|------|
57
+ | IP bloqueado | `KV: fraud_block:ip:{ip}` existe | Silent drop 200 |
58
+ | Fingerprint bloqueado | `KV: fraud_block:fp:{fingerprint}` existe | Silent drop 200 |
59
+
60
+ ### 2. Velocity Attacks (KV Rate Counter)
61
+ | Sinal | Critério | Score |
62
+ |-------|----------|-------|
63
+ | IP velocity alta | > 20 eventos/hora do mesmo IP | +50 pts |
64
+ | IP velocity média | > 10 eventos/hora do mesmo IP | +25 pts |
65
+ | Burst de eventos | > 5 eventos em 60 segundos | +30 pts |
66
+
67
+ ### 3. Sinais Heurísticos (sem AI)
68
+ | Sinal | Critério | Score |
69
+ |-------|----------|-------|
70
+ | Bot score alto | `bot_score >= 3` (já calculado no Worker) | +60 pts |
71
+ | Bot score médio | `bot_score == 2` | +30 pts |
72
+ | User-Agent suspeito | Contém headless, curl, bot, scrapy, python | +40 pts |
73
+ | IP de datacenter | ASN = AWS, GCP, Azure, DigitalOcean, Linode | +35 pts |
74
+ | Sem headers de browser | Accept-Language ausente | +20 pts |
75
+ | Geo impossível | IP country ≠ país esperado (BR fora da LATAM) | +10 pts |
76
+
77
+ ### 4. Threshold de Ação
78
+ ```
79
+ score < 40 → Limpo (processar normalmente)
80
+ score 40-79 → Suspeito (processar + logar em fraud_signals)
81
+ score ≥ 80 → Fraude confirmada (silent drop + logar + considerar blocklist)
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Endpoints Expostos
87
+
88
+ | Método | Rota | Função |
89
+ |--------|------|--------|
90
+ | `GET` | `/api/fraud/alerts` | Lista alertas recentes com score e motivos |
91
+ | `GET` | `/api/fraud/blocklist` | Visualiza IPs/fingerprints bloqueados |
92
+ | `POST` | `/api/fraud/blocklist/add` | Bloqueia IP ou fingerprint manualmente |
93
+ | `DELETE` | `/api/fraud/blocklist/remove` | Remove IP/fingerprint do blocklist |
94
+ | `GET` | `/api/fraud/stats` | Estatísticas de fraude (taxa, economia de events) |
95
+
96
+ ---
97
+
98
+ ## Schema D1
99
+
100
+ ```sql
101
+ fraud_signals -- Registro por evento (IP, score, motivos, ação tomada)
102
+ fraud_alerts -- Alertas agregados por IP/fingerprint (quando threshold atingido)
103
+ ```
104
+
105
+ **KV Namespace (GEO_CACHE):**
106
+ ```
107
+ fraud_block:ip:{ip} → TTL configurável (default: 24h)
108
+ fraud_block:fp:{fingerprint} → TTL configurável (default: 24h)
109
+ fraud_velocity:{ip}:h → Contador de eventos por hora (TTL: 1h)
110
+ fraud_velocity:{ip}:m → Contador de eventos por minuto (TTL: 1min)
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Regras de Negócio
116
+
117
+ ```
118
+ ✅ SEMPRE usar silent drop (200) — não vazar que detectamos fraude
119
+ ✅ SEMPRE logar fraud_signals mesmo em modo 'suspeito' (score 40-79)
120
+ ✅ SEMPRE deixar evento passar se o fraud gate jogar qualquer erro
121
+ ✅ SEMPRE verificar KV blocklist antes de D1 (latência zero)
122
+ ✅ SEMPRE incluir motivos detalhados em fraud_signals.reasons (JSON array)
123
+
124
+ ❌ NUNCA retornar 403/429 ao browser (vaza a detecção, bots adaptativos)
125
+ ❌ NUNCA bloquear IPs por mais de 7 dias sem confirmação manual
126
+ ❌ NUNCA usar Workers AI no fraud gate — latência do /track é crítica
127
+ ❌ NUNCA bloquear sem logar no D1 — auditoria é obrigatória
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Variáveis de Ambiente Requeridas
133
+
134
+ | Variável | Binding | Descrição |
135
+ |----------|---------|-----------|
136
+ | `DB` | D1 | fraud_signals, fraud_alerts |
137
+ | `GEO_CACHE` | KV | Blocklist + velocity counters (TTL por chave) |
138
+
139
+ *(Nenhum AI necessário — detecção heurística pura para latência zero)*
140
+
141
+ ---
142
+
143
+ *Agente criado em conformidade com a arquitetura Quantum Tier CDP Edge — 9 de Abril de 2026*