openskynet / src /omega /jepa-drive-enhancement.ts
Darochin's picture
Mirror OpenSkyNet workspace snapshot from Git HEAD
fc93158 verified
/**
* src/omega/jepa-drive-enhancement.ts
*
* JEPA Tension Bridge Enhancement para Drives
*
* Cuando JEPA detecta alta frustraci贸n, puede:
* 1. Elevar urgencia de drives existentes
* 2. Activar drives que estar铆an dormidas
* 3. Cambiar target de curiosidad si hay pattern
*/
import type { InnerDriveSignal } from "./inner-life/index.js";
export interface JepaTensionMetrics {
frustration: number; // 0-2 (heur铆stica: failureRate * 2)
confidence: number; // 0-1
surprise?: number; // 0-1 worsening vs prior recent window
error?: string;
}
/**
* Enhance a drive signal using JEPA tension metrics.
*
* Reglas de enhancement (v2 - optimized thresholds):
* - Si frustration > 0.5: elevar urgencia (antes era 1.0, creaba dead zone)
* - Si frustration > 1.5 y drive es idle: activar entropy_alert (exploraci贸n forzada)
* - Removed: Low frustration reduction (was counterproductive)
*
* Rationale:
* - Decision frequency increases when system struggles (frustration > 0.5)
* - Entropy alert activates when really stuck (frustration > 1.5)
* - No negative boost: autonomy shouldn't decrease when things work
*/
export function enhanceDriveWithJepaTension(
driveSignal: InnerDriveSignal,
jepaTension: JepaTensionMetrics,
): InnerDriveSignal {
const surprise = Math.max(0, Math.min(1, jepaTension.surprise ?? 0));
// Si hay error en JEPA, retornar se帽al original sin cambios
if (jepaTension.error || jepaTension.confidence < 0.3) {
return driveSignal;
}
// Caso 1: Drive idle pero JEPA detecta frustraci贸n extrema
// Cuando frustraci贸n > 1.5 (75% failures), fuerza exploraci贸n
if (
driveSignal.kind === "idle" &&
(jepaTension.frustration > 1.5 || (jepaTension.frustration > 1.0 && surprise > 0.28))
) {
return {
kind: "entropy_alert",
silentMs: 0, // JEPA triggered, no es silencio temporal
reason: `JEPA-EMERGENCY: frustration=${jepaTension.frustration.toFixed(2)} surprise=${surprise.toFixed(2)}`,
urgency: Math.min(0.98, 0.58 + jepaTension.frustration * 0.12 + surprise * 0.2),
};
}
// Caso 2: Drive existing pero JEPA eleva urgencia por frustraci贸n
// Threshold lowered from 1.0 to 0.5 to capture mid-range struggles
if (driveSignal.kind !== "idle" && (jepaTension.frustration > 0.5 || surprise > 0.22)) {
const baseUrgency = driveSignal.urgency ?? 0.5;
const boostAmount = Math.max(jepaTension.frustration * 0.12, surprise * 0.22);
const newUrgency = Math.min(0.99, baseUrgency + boostAmount);
return {
...driveSignal,
urgency: newUrgency,
reason: `${driveSignal.reason} [JEPA-ENHANCED: +${(boostAmount * 100).toFixed(0)}% surprise=${surprise.toFixed(2)}]`,
};
}
// Caso 3: Idle drive con moderate frustration (0.5-1.5)
// Don't activate entropy_alert yet, but prepare higher urgency
if (
driveSignal.kind === "idle" &&
(jepaTension.frustration > 0.5 || surprise > 0.24) &&
!(jepaTension.frustration > 1.5 || (jepaTension.frustration > 1.0 && surprise > 0.28))
) {
// Slightly elevate homeostasis or curiosity instead of entropy_alert
const nextDrive =
jepaTension.frustration > 0.95 || surprise > 0.35 ? "curiosity" : "homeostasis";
const urgency = 0.36 + jepaTension.frustration * 0.12 + surprise * 0.2;
if (nextDrive === "curiosity") {
return {
kind: "curiosity",
target: "memory/",
reason: `JEPA-ENGAGED: moderate frustration=${jepaTension.frustration.toFixed(2)} surprise=${surprise.toFixed(2)}`,
urgency: Math.min(0.84, urgency),
};
} else {
return {
kind: "homeostasis",
reason: `JEPA-ENGAGED: moderate frustration=${jepaTension.frustration.toFixed(2)} surprise=${surprise.toFixed(2)}`,
urgency: Math.min(0.82, urgency),
};
}
}
// Default: No change for very low frustration or non-idle drives
return driveSignal;
}
/**
* Metrics from JEPA predictor about frustration
*
* Heur铆stica simplificada:
* - Timeline analysis: % de fallos recientes
* - frustration = failureRate * 2 (0-2 scale)
* - confidence = 1 - failureRate
*
* Con datos reales de SKYNET_OMEGA/jepa_predictor.py:
* - VICReg loss como proxy de "sorpresa"
* - Embedding divergence como proxy de "incertidumbre"
*
* Compatible with both KernelTimelineEntry and OmegaSessionTimelineEntry:
* - If `turn` field is missing, derives it from array index
* - Works with any entry type that has an `outcome` field
*/
type TimelineEntryForJepa = {
outcome?: { status: string; errorKind?: string };
turn?: number;
};
export function parseJepaTensionFromKernelTimeline(
timeline: TimelineEntryForJepa[],
): JepaTensionMetrics {
if (!timeline || timeline.length < 2) {
return { frustration: 0, confidence: 0, surprise: 0 };
}
const recentEntries = timeline.slice(-Math.min(5, timeline.length));
const priorEntries = timeline.slice(
Math.max(0, timeline.length - Math.min(10, timeline.length)),
Math.max(0, timeline.length - Math.min(5, timeline.length)),
);
const failures = recentEntries.filter((e) => e.outcome?.status === "error").length;
const failureRate = failures / recentEntries.length;
const priorFailureRate =
priorEntries.length === 0
? failureRate
: priorEntries.filter((e) => e.outcome?.status === "error").length / priorEntries.length;
return {
frustration: Math.min(2, failureRate * 2),
confidence: 1 - failureRate,
surprise: Math.max(0, Math.min(1, failureRate - priorFailureRate)),
};
}