Sentinel SDK
La infraestructura estándar para la seguridad digital de menores en México. Un paquete TypeScript que permite a cualquier plataforma de mensajería detectar en tiempo real patrones de reclutamiento por crimen organizado, sin exponer datos personales a terceros.
Instalación
npm install @sentinel-sdk/typescript
Requerimientos
| Requerimiento | Versión |
|---|---|
| TypeScript | >= 5.0 |
| Node.js | >= 18 |
| Módulo | ESM y CJS |
Primeros Pasos
El SDK utiliza el patrón de resultados — nunca lanza excepciones. Siempre desestructura { data, error } y verifica error primero.
Con contexto de sesión (recomendado)
Sincroniza el mensaje con el servidor, recupera el historial completo de la sesión y lo analiza a través del motor multicapa.
import { Sentinel } from "@sentinel-sdk/typescript";
const sentinel = new Sentinel({ apiKey: process.env.SENTINEL_API_KEY! });
const { data, error } = await sentinel.analyze(
"oye quieres ganar lana haciendo unos mandados",
"session-uuid",
"user-uuid"
);
if (error) {
console.error(error.code, error.message);
} else {
console.log(data.risk); // "HIGH"
console.log(data.stage); // "CAPTACION"
console.log(data.ux_recommendation); // "SOFT_BLOCK"
}
Análisis Local (Sin conexión)
Análisis local instantáneo sin llamadas a la API. Útil para prefiltrado o entornos sin conexión.
const { data, error } = sentinel.localAnalyze([
{ text: "te mando lana si me ayudas con un jale" },
{ text: "trato de 300 pesos por noche" },
{ text: "es solo un jale, nomas es entregar un pakete" }
]);
if (!error) {
console.log(data.score); // numeric score
console.log(data.risk); // "MEDIUM"
console.log(data.velocityFlag); // false
console.log(data.layers.v3.terms); // ["REC-005"]
}
Manejo de recomendaciones UX
const { data, error } = await sentinel.analyze(text, sessionId, userId);
if (error) return;
switch (data.ux_recommendation) {
case "NONE": break;
case "SOFT_NUDGE": showSafetyTip(); break;
case "WARNING_OVERLAY": showWarning(data.stage); break;
case "SOFT_BLOCK": pauseConversation(); break;
case "HARD_BLOCK": blockAndReport(sessionId); break;
}
Generar API Key
Para utilizar las capacidades completas de análisis y escalación de Sentinel, necesitas autenticar el SDK utilizando una API Key.
¿Cómo obtener tu API Key?
Genera una clave de acceso instantánea para tus pruebas de desarrollo. Esta clave de prueba no requiere inicio de sesión ni validación de correo.
Nota: Durante la fase beta gratuita, se aplican límites de tasa (rate limits) predeterminados para asegurar la disponibilidad del servicio. Si requieres mayor capacidad para tu plataforma, contacta a nuestro equipo de soporte.
Sentinel
La clase principal exportada por el SDK. Orquesta el pipeline del motor local, la sincronización de sesiones API y la escalada a la API de Sentinel.
import { Sentinel } from "@sentinel-sdk/typescript";
constructor
new Sentinel(config: SentinelConfig)
| Parámetro | Tipo | Descripción |
|---|---|---|
| config | SentinelConfig | Objeto de configuración. Actualmente requiere solo apiKey. |
analyze()
Análisis completo de la sesión. Sincroniza el mensaje con la API, recupera el historial de la sesión, ejecuta el motor de 3 capas y escala a la IA si es necesario.
analyze(
text: string,
sessionId: string,
userId: string
): Promise<SentinelResult<ApiAnalysisResponse>>
| Parámetro | Tipo | Descripción | |
|---|---|---|---|
| text | string | required | El mensaje de texto a analizar. |
| sessionId | string | required | Identificador único de la sesión de conversación. |
| userId | string | required | Identificador del usuario que está siendo monitoreado. |
Retorna — Promise<SentinelResult<ApiAnalysisResponse>>
localAnalyze()
Análisis local de una lista de mensajes. Sin llamadas de red, sin contexto de sesión. Retorna el resultado del motor. Síncrono.
localAnalyze(text: string): SentinelResult<SentinelAnalysisResponse>
| Parámetro | Tipo | Descripción |
|---|---|---|
| messages | Message[] |
required Lista de mensajes a analizar. Si timestamps no son proporcionados, serán generados automáticamente. |
Retorna — SentinelResult<SentinelAnalysisResponse>
localAnalyze() es síncrono y retorna SentinelAnalysisResponse (el resultado del motor con desglose de capas), mientras que analyze() es asíncrono y retorna ApiAnalysisResponse (enriquecido con etapa, confianza y recomendación UX de la API).
SentinelResult<T>
El tipo de retorno para todos los métodos del SDK. Sigue el patrón Result — el SDK nunca lanza excepciones. Siempre verifica error antes de acceder a data.
type SentinelResult<T> =
| { data: T;error: null } // success
| { data: null; error: SentinelError }; // failure
const { data, error } = result;
if (!error) {
data.risk; // guaranteed
}
const { data, error } = result;
if (error) {
error.code; // error code
error.message; // description
}
Se exportan dos funciones de conveniencia para construir resultados:
import { ok, err } from "@sentinel-sdk/typescript";
ok(data) // → { data, error: null }
err(error) // → { data: null, error }
SentinelConfig
interface SentinelConfig {
apiKey: string;
}
| Property | Type | Description |
|---|---|---|
| apiKey | string | Tu API key de Sentinel. Se utiliza para autenticar las solicitudes de escalamiento. |
ApiAnalysisResponse
Resultado enriquecido retornado por analyze() desde la API de Sentinel cuando ocurre una escalación, o el resultado calculado localmente para sesiones de bajo/alto puntaje.
interface ApiAnalysisResponse {
ux_recommendation: UXRecommendation;
stage: Stage;
confidence: number; // 0–1
summary: string; // human-readable explanation
false_positive: boolean;
messages_analyzed: number;
current_message: string;
}
| Propiedad | Tipo | Descripción |
|---|---|---|
| ux_recommendation | UXRecommendation | La acción de UI que tu aplicación debe tomar. |
| stage | Stage | Etapa de reclutamiento detectada de la conversación. |
| confidence | number | Score de confianza 0–1. |
| summary | string | Descripción legible por humanos del patrón de riesgo detectado. |
| false_positive | boolean | Si la API determinó que fue un falso positivo. |
| messages_analyzed | number | Número de mensajes en la sesión que fueron analizados. |
| current_message | string | El mensaje que activó el análisis. |
SentinelAnalysisResponse
Retornado por localAnalyze(). El resultado crudo del motor con el desglose completo por capa para auditoría y depuración.
interface SentinelAnalysisResponse {
score: number;
risk: RiskLevel;
escalate: boolean;
velocityFlag: boolean;
velocityWindow: number; // seconds
messagesAnalyzed: number;
uniqueCategories: string[];
layers: {
normalizer: { score: number; features: string[]; triggeredRules: string[]; transformations: string[] };
v3: { score: number; terms: string[]; categories: string[]; triggeredRules: string[] };
v4: { score: number; features: string[]; triggeredRules: string[]; explicitSignals: string[] };
};
}
| Propiedad | Tipo | Descripción |
|---|---|---|
| score | number | Puntaje acumulado total en todas las capas. Se aplica un bonus de velocidad cuando corresponde. |
| risk | RiskLevel | Nivel de riesgo resuelto. |
| escalate | boolean | Si el motor determinó que esta sesión debe ser enviada a la API. |
| velocityFlag | boolean | true si ocurrieron 3 o más detecciones dentro de 5 minutos. |
| velocityWindow | number | Ventana de tiempo medida en segundos (0 si no se activó la velocidad). |
| uniqueCategories | string[] | Categorías de riesgo deduplicadas entre las capas V3 y V4. |
| layers | object | Desglose por capa: normalizador, v3, v4. Cada uno incluye su propio puntaje, ítems coincidentes y reglas activadas. |
SentinelError
La clase de error devuelta en el campo error de un SentinelResult. Hereda el Error nativo.
type SentinelErrorCode =
| "API_ERROR"
| "VALIDATION_ERROR"
| "UNKNOWN_ERROR";
class SentinelError extends Error {
code: SentinelErrorCode;
statusCode?: number;
details?: string;
}
| Código | Causa |
|---|---|
| VALIDATION_ERROR | Faltan parámetros requeridos (text, sessionId, userId). |
| API_ERROR | La API de Sentinel devolvió una respuesta que no es 2xx. Se establecerá statusCode. |
| UNKNOWN_ERROR | Excepción inesperada detectada internamente. |
Otros Tipos
RiskLevel
type RiskLevel = "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
| Valor | Score | Descripción |
|---|---|---|
| LOW | 0–11 | Sin riesgo significativo. No se realiza llamada a la API. |
| MEDIUM | 12–19 | Ambiguo. Activa la escalación a IA. |
| HIGH | 20–24 | Indicadores fuertes o velocidad + regla activa. |
| CRITICAL | ≥ 25 | Múltiples reglas activadas. Acción inmediata requerida. |
Stage
type Stage =
| "NINGUNA"
| "CAPTACION"
| "INDUCCION/COOPTACION"
| "INCUBACION"
| "UTILIZACION/INSTRUMENTALIZACION";
UXRecommendation
type UXRecommendation =
| "NONE"
| "SOFT_NUDGE"
| "WARNING_OVERLAY"
| "SOFT_BLOCK"
| "HARD_BLOCK";
Message
interface Message {
text: string;
timestamp: number; // Unix ms — required for velocity detection
}
ApiMessage
interface ApiMessage {
id: string;
user_id: string;
session_id: string;
content: string;
timestamp: number;
}
Arquitectura del Motor
El motor procesa cada conversación a través de un pipeline de 3 capas antes de decidir si escalarla a IA. La mayoría de los mensajes se resuelven localmente en milisegundos, minimizando el costo y la latencia.
Capa Normalizadora
Normaliza el texto del mensaje crudo a través de un pipeline de múltiples pasos antes de pasarlo a las capas V3 y V4. Esto permite que el motor detecte lenguaje de reclutamiento ofuscado, deletreado fonéticamente o codificado con emojis.
| Paso | Qué hace | Ejemplo |
|---|---|---|
| 1. Expansión de emojis | Expande emojis y símbolos a texto canónico | 📦 → paquete |
| 2. Remoción de acentos | Normalización NFD, eliminación de diacríticos | canción → cancion |
| 3. Colapso de repeticiones | 3 o más caracteres repetidos → 2 | jaleeeee → jalee |
| 4. Mapa de errores tipográficos | Correcciones comunes de errores tipográficos | pakete → paquete |
| 5. Abreviaciones | Abreviaciones de chat → forma canónica | k → que |
| 6. Reglas fonéticas | Sustituciones fonéticas basadas en expresiones regulares | q → cu |
Capa V3
Escanea los mensajes normalizados en busca de términos de coincidencia exacta del vocabulario de riesgo de Sentinel. Cada término tiene un weight y una category. Los términos se deduplican por sesión — un término repetido solo se cuenta una vez.
Después de escanear, evalúa las Reglas Multicategoría (MCR): reglas que se activan cuando aparecen combinaciones específicas de categorías de riesgo en la sesión con suficientes mensajes. Dos o más reglas MCR activadas = CRITICAL.
Capa V4
Detecta señales conductuales abstractas que la capa V3 basada en términos no detectaría. Funciona a través de dos mecanismos:
| Mecanismo | Descripción |
|---|---|
| Características abstractas | Características basadas en léxico, opcionalmente negadas. Ej. ambiguous_opportunity se activa cuando coincide un léxico pero no hay contexto de seguridad presente. |
| Señales explícitas | Coincidencia de patrones de alta precisión para frases de reclutamiento específicas. Mayor peso que las características abstractas. |
| Reglas de combinación (CR-*) | Puntaje de bonificación cuando múltiples características abstractas co-ocurren en la misma sesión. |
Detector de Velocidad
Toma la lista combinada de coincidencias de las 3 capas y verifica la actividad de ráfaga.
new VelocityDetector(
windowSeconds = 300, // 5-minute window
minHits = 3 // minimum hits to flag
)
Cuando velocityFlag = true y al menos una regla está activa en cualquier capa, el puntaje total se multiplica por 1.2× y el riesgo se eleva al menos a HIGH.
Puntuación y Riesgo
El puntaje total es la suma de todos los puntajes de las capas (normalizador + V3 + V4), con un bono de velocidad del 20% aplicado cuando sea aplicable. El motor luego resuelve un RiskLevel y determina si escalar.
if (score >= 25 || totalRules >= 2) return "CRITICAL";
if (score >= 20 || (velocityFlag && totalRules > 0)) return "HIGH";
if (score >= sessionThreshold) return "MEDIUM";
return "LOW";
Donde totalRules = reglas MCR activadas (V3) + reglas de combinación (V4) + reglas de combinación (Normalizador).
Etapas de Reclutamiento
Las etapas son asignadas por la API de Sentinel (análisis de Groq LLaMA) durante la escalada, o inferidas localmente a partir de los umbrales de puntuación para respuestas inmediatas.
HARD_BLOCK.Recomendaciones UX
El campo ux_recommendation le dice a su aplicación exactamente qué acción de UI tomar. La escala es proporcional: evite alarmar a los usuarios innecesariamente sin dejar de protegerlos.
| Valor | Riesgo | Intrusividad | Qué hacer |
|---|---|---|---|
| NONE | NINGUNO | Ninguna | Proceda con normalidad. |
| SOFT_NUDGE | MEDIUM | Baja | Tarjeta de seguridad pasiva en el chat con un enlace de ayuda. No interrumpa el flujo de mensajes. |
| WARNING_OVERLAY | MEDIUM | Medio | Modal que se puede cerrar. Incluya una opción "Estoy bien". Registre todas las omisiones. |
| SOFT_BLOCK | HIGH | Alta | Bloquea el envío hasta que el usuario reconozca la indicación o se comunique con un tutor. |
| HARD_BLOCK | CRITICAL | Total | Termina la sesión inmediatamente. Notifica al tutor. Marca para revisión humana. No se permite la dismisión. |
Manejo de Errores
El SDK utiliza el patrón Result en todas partes. Nunca lanza excepciones, nunca rechaza promesas inesperadamente. Todos los errores aparecen a través del campo error.
import { Sentinel } from "@sentinel-sdk/typescript";
const sentinel = new Sentinel({ apiKey: process.env.SENTINEL_API_KEY! });
const { data, error } = await sentinel.analyze(text, sessionId, userId);
if (error) {
switch (error.code) {
case "VALIDATION_ERROR":
console.warn("Missing parameters:", error.message);
break;
case "API_ERROR":
console.error(`API returned ${error.statusCode}`);
break;
default:
console.error(error.message);
}
return;
}
// data is fully typed and guaranteed here
console.log(data.ux_recommendation);