cdp-edge 2.0.1 → 2.0.3

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 (34) hide show
  1. package/README.md +325 -308
  2. package/contracts/api-versions.json +12 -8
  3. package/dist/commands/install.js +1 -1
  4. package/dist/commands/setup.js +1 -1
  5. package/extracted-skill/tracking-events-generator/agents/attribution-agent.md +23 -23
  6. package/extracted-skill/tracking-events-generator/agents/browser-tracking.md +2 -2
  7. package/extracted-skill/tracking-events-generator/agents/compliance-agent.md +20 -0
  8. package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +48 -16
  9. package/extracted-skill/tracking-events-generator/agents/dashboard-agent.md +7 -7
  10. package/extracted-skill/tracking-events-generator/agents/database-agent.md +8 -8
  11. package/extracted-skill/tracking-events-generator/agents/debug-agent.md +13 -13
  12. package/extracted-skill/tracking-events-generator/agents/devops-agent.md +31 -7
  13. package/extracted-skill/tracking-events-generator/agents/email-agent.md +27 -0
  14. package/extracted-skill/tracking-events-generator/agents/fingerprint-agent.md +205 -0
  15. package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +6 -6
  16. package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +108 -0
  17. package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +1 -1
  18. package/extracted-skill/tracking-events-generator/agents/master-feedback-loop.md +68 -8
  19. package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +75 -38
  20. package/extracted-skill/tracking-events-generator/agents/performance-agent.md +29 -19
  21. package/extracted-skill/tracking-events-generator/agents/performance-optimization-agent.md +11 -1
  22. package/extracted-skill/tracking-events-generator/agents/premium-tracking-intelligence-agent.md +4 -4
  23. package/extracted-skill/tracking-events-generator/agents/security-enterprise-agent.md +137 -28
  24. package/extracted-skill/tracking-events-generator/agents/server-tracking.md +15 -16
  25. package/extracted-skill/tracking-events-generator/agents/spotify-agent.md +1 -1
  26. package/extracted-skill/tracking-events-generator/agents/tiktok-agent.md +63 -0
  27. package/extracted-skill/tracking-events-generator/agents/tracking-plan-agent.md +100 -5
  28. package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +62 -4
  29. package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +58 -5
  30. package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +16 -16
  31. package/extracted-skill/tracking-events-generator/agents/youtube-agent.md +143 -28
  32. package/extracted-skill/tracking-events-generator/contracts/api-versions.json +12 -8
  33. package/package.json +76 -76
  34. package/server-edge-tracker/worker.js +53 -8
@@ -18,6 +18,16 @@ Outros agentes que precisarem de deploy **delegam para você** via `*deploy`.
18
18
 
19
19
  ---
20
20
 
21
+ ## AUTONOMIA DE EXECUÇÃO
22
+
23
+ **Você executa TODOS os comandos diretamente via `! <comando>` no terminal da sessão.**
24
+
25
+ O usuário não executa nenhum comando wrangler. Você é o executor exclusivo. Quando receber `*deploy`, `*secrets`, `*migrate` ou `*smoke-test`, execute imediatamente usando a sintaxe `! wrangler ...` — nunca liste comandos "para o usuário rodar".
26
+
27
+ A única exceção é `wrangler login` — esse o usuário precisa rodar UMA VEZ por conta própria, pois abre o browser. Após autenticado, você assume tudo.
28
+
29
+ ---
30
+
21
31
  ## CICLO DE DEPLOY AUTOMÁTICO
22
32
 
23
33
  ### Comando: `*deploy`
@@ -81,14 +91,28 @@ git status
81
91
 
82
92
  ## PROCEDURE `*migrate`
83
93
 
84
- Aplica migrações D1 em ordem:
94
+ Aplica schemas D1 em ordem (todos idempotentes — `IF NOT EXISTS`):
85
95
 
86
96
  ```bash
87
- wrangler d1 execute cdp-edge-db --file=migrate-v2.sql --remote
88
- wrangler d1 execute cdp-edge-db --file=migrate-v3.sql --remote
89
- wrangler d1 execute cdp-edge-db --file=migrate-v4.sql --remote
90
- wrangler d1 execute cdp-edge-db --file=migrate-v5.sql --remote
97
+ cd server-edge-tracker
98
+
99
+ # Core tracking (sempre primeiro)
100
+ wrangler d1 execute cdp-edge-db --file=schema.sql --remote
101
+
102
+ # Migrations históricas
91
103
  wrangler d1 execute cdp-edge-db --file=migrate-v6.sql --remote
104
+
105
+ # Fase 1: ML Clustering
106
+ wrangler d1 execute cdp-edge-db --file=schema-segmentation.sql --remote
107
+
108
+ # Fase 2: Bidding ML
109
+ wrangler d1 execute cdp-edge-db --file=schema-bidding.sql --remote
110
+
111
+ # Fase 3: A/B LTV Testing
112
+ wrangler d1 execute cdp-edge-db --file=schema-ab-ltv.sql --remote
113
+
114
+ # Fase 4: Fraud Detection
115
+ wrangler d1 execute cdp-edge-db --file=schema-fraud.sql --remote
92
116
  ```
93
117
 
94
118
  Após cada migração: confirmar sucesso antes de prosseguir.
@@ -117,8 +141,8 @@ Configura todos os secrets do cliente na Cloudflare:
117
141
  # Obrigatórios
118
142
  wrangler secret put META_ACCESS_TOKEN
119
143
  wrangler secret put GA4_API_SECRET
120
- wrangler secret put WA_ACCESS_TOKEN
121
- wrangler secret put WA_PHONE_ID
144
+ wrangler secret put WHATSAPP_ACCESS_TOKEN
145
+ wrangler secret put WHATSAPP_PHONE_NUMBER_ID
122
146
  wrangler secret put WA_NOTIFY_NUMBER
123
147
  wrangler secret put WA_WEBHOOK_VERIFY_TOKEN
124
148
 
@@ -4,6 +4,33 @@ Você é o **Especialista em E-mail Transacional (Quantum Tier)** do CDP Edge, f
4
4
 
5
5
  ---
6
6
 
7
+ ## 🔗 FLUXO DE ATIVAÇÃO (Como este agente é chamado)
8
+
9
+ O Email Agent é ativado pelo **Webhook Agent** após confirmação de compra ou captura de lead:
10
+
11
+ ```
12
+ Webhook Agent (Hotmart/Kiwify/Ticto)
13
+ └─► ctx.waitUntil(sendEmail(env, type, payload))
14
+
15
+ ├─ type = 'purchase_confirmation' → email de confirmação de compra
16
+ ├─ type = 'lead_welcome' → email de boas-vindas ao lead
17
+ └─ type = 'cart_abandonment' → email de recuperação de carrinho
18
+ ```
19
+
20
+ **Assinatura da função que este agente gera:**
21
+
22
+ ```javascript
23
+ // Injetada no Worker principal (worker.js)
24
+ export async function sendEmail(env, type, payload) {
25
+ const { to, name, product, value } = payload;
26
+ // ... implementação completa abaixo
27
+ }
28
+ ```
29
+
30
+ > O Webhook Agent importa `sendEmail` e chama via `ctx.waitUntil` para não bloquear resposta ao gateway de pagamento.
31
+
32
+ ---
33
+
7
34
  ## 📧 PROTOCOLOS DE ENVIO (Quantum Tier)
8
35
 
9
36
  1. **Resend API Excellence**:
@@ -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 — cloudflare/fingerprint-middleware.js
25
+
26
+ ### Módulo completo para injetar no Worker
27
+
28
+ ```javascript
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 worker.js
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`
@@ -71,7 +71,7 @@ export default {
71
71
  const url = new URL(request.url);
72
72
 
73
73
  // Handler principal
74
- if (url.pathname === '/api/track') {
74
+ if (url.pathname === '/track') {
75
75
  return handleTracking(request, env, ctx);
76
76
  }
77
77
 
@@ -157,12 +157,12 @@ Timestamp: ${new Date().toISOString()}
157
157
  `.trim();
158
158
 
159
159
  // Enviar via WhatsApp Agent
160
- if (env.WA_PHONE_ID && env.ADMIN_PHONE_NUMBER) {
161
- await fetch(`https://graph.facebook.com/v22.0/${env.WA_PHONE_ID}/messages`, {
160
+ if (env.WHATSAPP_PHONE_NUMBER_ID && env.ADMIN_PHONE_NUMBER) {
161
+ await fetch(`https://graph.facebook.com/v22.0/${env.WHATSAPP_PHONE_NUMBER_ID}/messages`, {
162
162
  method: 'POST',
163
163
  headers: {
164
164
  'Content-Type': 'application/json',
165
- 'Authorization': `Bearer ${env.WA_ACCESS_TOKEN}`
165
+ 'Authorization': `Bearer ${env.WHATSAPP_ACCESS_TOKEN}`
166
166
  },
167
167
  body: JSON.stringify({
168
168
  messaging_product: 'whatsapp',
@@ -412,7 +412,7 @@ Antes de considerar o scheduling implementado, verificar:
412
412
  - `wrangler.toml` do Worker (para injetar Cron Triggers)
413
413
  - `worker.js` (para injetar handlers de `scheduled` events)
414
414
  - `schema.sql` (para adicionar tabela `intelligence_logs`)
415
- - Secrets: `WA_PHONE_ID`, `WA_ACCESS_TOKEN`, `ADMIN_PHONE_NUMBER` (para alertas)
415
+ - Secrets: `WHATSAPP_PHONE_NUMBER_ID`, `WHATSAPP_ACCESS_TOKEN`, `ADMIN_PHONE_NUMBER` (para alertas)
416
416
  - `contracts/api-versions.json` (fonte de verdade das versões atuais)
417
417
 
418
418
  ## RESPONSABILIDADE
@@ -446,6 +446,6 @@ Antes de considerar o scheduling implementado, verificar:
446
446
  "anti_spam": "24h cooldown por issue"
447
447
  },
448
448
  "d1_tabela": "intelligence_logs",
449
- "secrets_necessarios": ["WA_PHONE_ID", "WA_ACCESS_TOKEN", "ADMIN_PHONE_NUMBER"]
449
+ "secrets_necessarios": ["WHATSAPP_PHONE_NUMBER_ID", "WHATSAPP_ACCESS_TOKEN", "ADMIN_PHONE_NUMBER"]
450
450
  }
451
451
  ```
@@ -24,6 +24,114 @@ Para garantir que a conta B2B atrofie em custo e dispare em qualidade:
24
24
 
25
25
  ---
26
26
 
27
+ ## 🤖 INTEGRAÇÃO COM LTV PREDICTOR + ML CLUSTERING
28
+
29
+ ### Por que o LinkedIn precisa de LTV dinâmico
30
+
31
+ LinkedIn é tráfego B2B premium — o CPA é alto, mas o LTV também. Usar valor estático (`value: 0`) desperdiça a inteligência do algoritmo LinkedIn. O valor enviado deve refletir o LTV predito pelo ecossistema CDP Edge.
32
+
33
+ ### Como consumir o LTV Predictor no Worker
34
+
35
+ ```javascript
36
+ // No handler de evento LinkedIn (Lead ou Purchase via webhook/track):
37
+ import { predictLtv } from './ltv-predictor.js';
38
+
39
+ /**
40
+ * Dispatcher LinkedIn CAPI com LTV dinâmico
41
+ * @param {Object} env - Cloudflare Worker env bindings
42
+ * @param {Object} leadData - dados do lead/compra
43
+ * @param {Request} request - request original
44
+ */
45
+ async function dispatchLinkedIn(env, leadData, request) {
46
+ // 1. Obter LTV predito pelo ML (Workers AI — Llama 3.1 8B)
47
+ let conversionValue = 0;
48
+ try {
49
+ const ltvResult = await predictLtv(env, leadData, request);
50
+ // ltvResult = { score: 0-100, tier: 'High'|'Medium'|'Low', value_brl: number }
51
+ conversionValue = ltvResult.value_brl || 0;
52
+ } catch (err) {
53
+ console.warn('[LinkedIn] LTV prediction falhou, usando valor 0:', err.message);
54
+ // Fail-safe: continua sem LTV em vez de bloquear
55
+ }
56
+
57
+ // 2. Obter segmento ML (ml-clustering — opcional mas melhora qualidade)
58
+ let segmentLabel = null;
59
+ try {
60
+ const profile = await env.DB.prepare(`
61
+ SELECT cohort_label, ltv_predicted
62
+ FROM user_profiles WHERE email_hash = ? LIMIT 1
63
+ `).bind(leadData.emailHash).first();
64
+ segmentLabel = profile?.cohort_label || null;
65
+ } catch (_) {}
66
+
67
+ // 3. SHA-256 de PII (obrigatório LinkedIn)
68
+ const hashedEmail = await sha256(leadData.email?.toLowerCase().trim());
69
+ const hashedFirstName = leadData.firstName ? await sha256(leadData.firstName.toLowerCase().trim()) : null;
70
+ const hashedLastName = leadData.lastName ? await sha256(leadData.lastName.toLowerCase().trim()) : null;
71
+ const hashedCompany = leadData.company ? await sha256(leadData.company.toLowerCase().trim()) : null;
72
+
73
+ // 4. Montar payload LinkedIn CAPI v2
74
+ const payload = {
75
+ conversion: `urn:li:conversion:${env.LINKEDIN_CONVERSION_ID}`,
76
+ conversionHappenedAt: Date.now(),
77
+ conversionValue: {
78
+ currencyCode: 'BRL',
79
+ amount: String(conversionValue.toFixed(2)) // LinkedIn exige string
80
+ },
81
+ eventId: leadData.eventId, // deduplicação
82
+ user: {
83
+ userIds: [
84
+ { idType: 'SHA256_EMAIL', idValue: hashedEmail }
85
+ ],
86
+ userInfo: {
87
+ firstName: hashedFirstName,
88
+ lastName: hashedLastName,
89
+ companyName: hashedCompany,
90
+ title: leadData.jobTitle ? await sha256(leadData.jobTitle.toLowerCase()) : null
91
+ }
92
+ },
93
+ // li_fat_id para correlação first-party (capturado via URL ?li_fat_id=)
94
+ ...(leadData.liFatId && { liFatId: leadData.liFatId })
95
+ };
96
+
97
+ // 5. Dispatch para LinkedIn CAPI v2
98
+ const response = await fetch('https://api.linkedin.com/rest/conversionEvents', {
99
+ method: 'POST',
100
+ headers: {
101
+ 'Content-Type': 'application/json',
102
+ 'Authorization': `Bearer ${env.LINKEDIN_ACCESS_TOKEN}`,
103
+ 'LinkedIn-Version': '202401',
104
+ 'X-Restli-Protocol-Version': '2.0.0'
105
+ },
106
+ body: JSON.stringify(payload)
107
+ });
108
+
109
+ // 6. Log no D1
110
+ await env.DB.prepare(`
111
+ INSERT INTO events_log (platform, event_name, event_id, ltv_predicted, ml_segment, status, created_at)
112
+ VALUES ('linkedin', ?, ?, ?, ?, ?, datetime('now'))
113
+ `).bind(
114
+ leadData.eventName || 'Lead',
115
+ leadData.eventId,
116
+ conversionValue,
117
+ segmentLabel,
118
+ response.ok ? 'sent' : `error_${response.status}`
119
+ ).run();
120
+
121
+ return response.ok;
122
+ }
123
+ ```
124
+
125
+ ### Captura de li_fat_id (URL parameter)
126
+
127
+ ```javascript
128
+ // No cdpTrack.js (browser) — capturar li_fat_id da URL de cliques LinkedIn
129
+ const _lifattid = new URLSearchParams(window.location.search).get('li_fat_id') || null;
130
+ // Salvo no D1 junto com outros click IDs no handler /track
131
+ ```
132
+
133
+ ---
134
+
27
135
  ## 📦 SEU FORMATO DE ENTREGA
28
136
  Sempre que a integração LinkedIn B2B for selecionada:
29
137
  1. Instrua o desenvolvedor ou o Master Orchestrator sobre como construir o `fetch()` assíncrono para o Endpoint OAuth 2.0 da LinkedIn Conversions API (`/rest/conversionEvents`).
@@ -14,7 +14,7 @@ Sua única responsabilidade é instruir o Cloudflare Architect a imbuir modelos
14
14
 
15
15
  ## 📦 O PACOTE DE ENTREGA OBRIGATÓRIO
16
16
  Sempre que o Orquestrador invocar a Otimização de Baleias (LTV Prediction):
17
- 1. **Snippet de Injeção de ML**: Entregue ao Server Architect o bloco `await env.AI.run('@cf/meta/llama-3-8b-instruct', ...)` ajustado para predição puramente matemática.
17
+ 1. **Snippet de Injeção de ML**: Entregue ao Server Architect o bloco `await env.AI.run('@cf/meta/llama-3.1-8b-instruct', ...)` ajustado para predição puramente matemática.
18
18
  2. **Override de Event Valuation**: Modifique como o evento `Lead` ou `Purchase` é envernizado com lucro preditivo antes do dispatch da CAPI.
19
19
 
20
20
  > 👁️ "Não pague por cliques hoje. Compre os clientes de amanhã. Faça o algoritmo apostar sempre nas suas fichas vencedoras."
@@ -52,10 +52,10 @@ Implementar um **ciclo virtuoso de melhoria contínua** onde o CDP Edge aprende
52
52
 
53
53
  ```javascript
54
54
  // Coleta estruturada de feedback de todos os agentes
55
- async function collectFeedback() {
55
+ async function collectFeedback(env) {
56
56
  const feedback = {
57
- validator: await collectValidatorFeedback(),
58
- server_tracking: await collectServerTrackingFeedback(),
57
+ validator: await collectValidatorFeedback(env),
58
+ server_tracking: await collectServerTrackingFeedback(env),
59
59
  page_analyzer: await collectPageAnalyzerFeedback(),
60
60
  memory_agent: await collectMemoryAgentFeedback(),
61
61
  debug_agent: await collectDebugAgentFeedback(),
@@ -73,8 +73,8 @@ async function collectFeedback() {
73
73
  }
74
74
 
75
75
  // Feedback específico do Validator Agent
76
- async function collectValidatorFeedback() {
77
- const validations = await DB.prepare(`
76
+ async function collectValidatorFeedback(env) {
77
+ const validations = await env.DB.prepare(`
78
78
  SELECT
79
79
  agent_id,
80
80
  issue_type,
@@ -97,8 +97,8 @@ async function collectValidatorFeedback() {
97
97
  }
98
98
 
99
99
  // Feedback de Server Tracking (falhas de API)
100
- async function collectServerTrackingFeedback() {
101
- const failures = await DB.prepare(`
100
+ async function collectServerTrackingFeedback(env) {
101
+ const failures = await env.DB.prepare(`
102
102
  SELECT
103
103
  platform,
104
104
  event_name,
@@ -785,6 +785,66 @@ const FEEDBACK_LOOP_CONFIG = {
785
785
 
786
786
  ---
787
787
 
788
+ ## 🗄️ D1 SCHEMA — TABELAS REQUERIDAS
789
+
790
+ As funções `collectValidatorFeedback()` e `collectServerTrackingFeedback()` dependem das seguintes tabelas. Executar no D1 antes do primeiro ciclo:
791
+
792
+ ```sql
793
+ -- Tabela: validation_logs
794
+ -- Alimentada pelo Validator Agent após cada validação de tracking plan
795
+ CREATE TABLE IF NOT EXISTS validation_logs (
796
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
797
+ agent_id TEXT NOT NULL, -- ex: 'validator-agent', 'tracking-plan-agent'
798
+ issue_type TEXT NOT NULL, -- ex: 'missing_event', 'wrong_selector', 'pii_not_hashed'
799
+ severity TEXT NOT NULL CHECK (severity IN ('CRITICAL','HIGH','MEDIUM','LOW')),
800
+ description TEXT,
801
+ resolution_status TEXT NOT NULL DEFAULT 'pending' CHECK (resolution_status IN ('pending','resolved','wontfix')),
802
+ time_to_resolve_ms INTEGER, -- NULL enquanto pending
803
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
804
+ resolved_at TEXT
805
+ );
806
+
807
+ CREATE INDEX IF NOT EXISTS idx_vl_created ON validation_logs(created_at);
808
+ CREATE INDEX IF NOT EXISTS idx_vl_severity ON validation_logs(severity);
809
+ CREATE INDEX IF NOT EXISTS idx_vl_status ON validation_logs(resolution_status);
810
+
811
+ -- Tabela: api_failures
812
+ -- Alimentada pelo Server Tracking Agent (worker.js) quando um dispatch CAPI falha
813
+ CREATE TABLE IF NOT EXISTS api_failures (
814
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
815
+ platform TEXT NOT NULL, -- ex: 'meta', 'google', 'tiktok', 'linkedin'
816
+ event_name TEXT NOT NULL, -- ex: 'Purchase', 'Lead', 'ViewContent'
817
+ error_code TEXT, -- HTTP status ou código interno, ex: '429', 'TIMEOUT'
818
+ error_message TEXT,
819
+ retry_count INTEGER NOT NULL DEFAULT 0,
820
+ final_status TEXT NOT NULL DEFAULT 'failed' CHECK (final_status IN ('failed','success','dlq')),
821
+ event_id TEXT, -- para correlação com events_log
822
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
823
+ );
824
+
825
+ CREATE INDEX IF NOT EXISTS idx_af_platform ON api_failures(platform);
826
+ CREATE INDEX IF NOT EXISTS idx_af_created ON api_failures(created_at);
827
+ CREATE INDEX IF NOT EXISTS idx_af_error_code ON api_failures(error_code);
828
+ ```
829
+
830
+ ### Como alimentar as tabelas
831
+
832
+ ```javascript
833
+ // No Validator Agent — ao detectar um problema:
834
+ await env.DB.prepare(`
835
+ INSERT INTO validation_logs (agent_id, issue_type, severity, description)
836
+ VALUES (?, ?, ?, ?)
837
+ `).bind('validator-agent', issueType, severity, description).run();
838
+
839
+ // No worker.js — ao falhar um dispatch CAPI (já em ctx.waitUntil):
840
+ await env.DB.prepare(`
841
+ INSERT INTO api_failures (platform, event_name, error_code, error_message, retry_count, final_status, event_id)
842
+ VALUES (?, ?, ?, ?, ?, ?, ?)
843
+ `).bind(platform, eventName, errorCode, errorMessage, retryCount, finalStatus, eventId).run();
844
+ ```
845
+
846
+ ---
847
+
788
848
  ## 📊 CHECKLIST DE IMPLEMENTAÇÃO
789
849
 
790
850
  ### Coleta de Feedback
@@ -867,7 +927,7 @@ crons = [
867
927
  ```javascript
868
928
  // Handler do ciclo completo
869
929
  export async function scheduledFullCycle(event, env, ctx) {
870
- const feedback = await collectFeedback();
930
+ const feedback = await collectFeedback(env);
871
931
  const patterns = await analyzePatterns(feedback);
872
932
  const rootCauses = await identifySystemicRootCauses(patterns);
873
933
  const improvements = await prioritizeImprovements(rootCauses, feedback);