flowstate / src /lib /agent-engine.ts
muthuk1's picture
Complete FlowState project - 6 pages, 3 API routes, all components, build verified
de40b1a verified
raw
history blame
2.92 kB
/**
* FlowState AI Agent Engine — Autonomous churn detection & retention
* 5-signal scoring: inactivity, volume decline, protocol diversity, streak, liquidation
*/
import { ChurnRisk } from './types'
import { sendCustomEvent, createCampaign } from './torque-mcp'
export function calculateChurnScore(activity: {
daysInactive: number; volumeDropPct: number; uniqueProtocols: number;
currentStreak: number; hasLiquidation: boolean
}): { score: number; risk: ChurnRisk; signals: string[] } {
let score = 0
const signals: string[] = []
// Signal 1: Inactivity (0-30pts)
const inactScore = Math.min(activity.daysInactive * 3, 30)
score += inactScore
if (activity.daysInactive >= 7) signals.push('Inactive ' + activity.daysInactive + ' days')
// Signal 2: Volume decline (0-25pts)
if (activity.volumeDropPct > 0) {
score += Math.min(activity.volumeDropPct / 4, 25)
signals.push('Volume dropped ' + activity.volumeDropPct.toFixed(0) + '%')
}
// Signal 3: Protocol diversity (0-15pts)
if (activity.uniqueProtocols <= 1) { score += 15; signals.push('Single protocol — low engagement') }
else if (activity.uniqueProtocols <= 2) { score += 8; signals.push('Limited protocol diversity') }
// Signal 4: Streak broken (0-15pts)
if (activity.currentStreak === 0) { score += 15; signals.push('Activity streak broken') }
else if (activity.currentStreak < 3) score += 5
// Signal 5: Liquidation (0-15pts)
if (activity.hasLiquidation) { score += 15; signals.push('Recent liquidation event') }
score = Math.min(Math.max(score, 0), 100)
let risk: ChurnRisk
if (score >= 80) risk = 'critical'
else if (score >= 60) risk = 'high'
else if (score >= 40) risk = 'medium'
else if (score >= 20) risk = 'low'
else risk = 'safe'
return { score, risk, signals }
}
export function selectResponse(risk: ChurnRisk, wallet: string): { action: string; description: string }[] {
const responses: { action: string; description: string }[] = []
responses.push({ action: 'detect', description: 'Detected ' + risk + ' churn risk for ' + wallet.slice(0, 8) + '...' })
switch (risk) {
case 'critical':
responses.push({ action: 'gift', description: 'Sending 0.5 SOL gift via Anti-Churn campaign' })
responses.push({ action: 'raffle', description: 'Enrolling in Comeback Raffle with 5x multiplier' })
break
case 'high':
responses.push({ action: 'raffle', description: 'Enrolling in Comeback Raffle with 3x multiplier' })
break
case 'medium':
responses.push({ action: 'rebate', description: 'Activating 2x rebate boost for 48 hours' })
break
}
return responses
}
export async function executeResponse(wallet: string, action: string) {
return sendCustomEvent(wallet, action === 'gift' ? 'churn_risk_high' : action === 'raffle' ? 'churn_risk_high' : 'churn_risk_medium', { action, timestamp: new Date().toISOString() })
}