Download src/skynet/continuous-thinking-engine.ts from Darochin/openskynet: direct link, hf CLI and curl.
- Browser
- Download file 21.3 kB
-
https://huggingface.co/Darochin/openskynet/resolve/main/src/skynet/continuous-thinking-engine.ts
- Command line
-
hf download hf://Darochin/openskynet/src/skynet/continuous-thinking-engine.ts
-
curl -L -o continuous-thinking-engine.ts https://huggingface.co/Darochin/openskynet/resolve/main/src/skynet/continuous-thinking-engine.ts
21.3 kB
| /** | |
| * continuous-thinking-engine.ts | |
| * ============================== | |
| * | |
| * The core daemon that makes OpenSkyNet "alive": | |
| * - Runs continuously (every N milliseconds) | |
| * - Generates genuine questions (not scripted) | |
| * - Detects uncertainty and works to resolve it | |
| * - Operates INDEPENDENTLY of external triggers | |
| * | |
| * PERSISTENCIA: Los pensamientos se guardan en .openskynet/omega-thoughts.jsonl | |
| * y sobreviven reinicios. El singleton carga el historial al inicializarse. | |
| * | |
| * This is what transforms OpenSkyNet from "reactive system" | |
| * to "genuinely autonomous agent". | |
| */ | |
| import fs from "node:fs/promises"; | |
| import path from "node:path"; | |
| import type { OmegaSelfTimeKernelState } from "./self-time-kernel.js"; | |
| const THOUGHTS_FILENAME = "omega-thoughts.jsonl"; | |
| const MAX_PERSISTED_THOUGHTS = 500; // LΓmite para evitar crecimiento ilimitado | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export interface ContinuousThought { | |
| /** Unique ID for this thought */ | |
| id: string; | |
| /** When was this thought generated (ms since epoch) */ | |
| timestamp: number; | |
| /** Which drive triggered this thought? */ | |
| drive: "learning" | "entropy_minimization" | "adaptive_depth"; | |
| /** The actual question/observation */ | |
| question: string; | |
| /** Why this question? (explanation of uncertainty) */ | |
| reasoning: string; | |
| /** Confidence that this is a genuine insight (0-1) */ | |
| confidence: number; | |
| /** How much should this reduce uncertainty? (expected H reduction) */ | |
| expectedEntropyReduction: number; | |
| /** Has this been acted upon? */ | |
| processed: boolean; | |
| /** If processed, what was the result? */ | |
| result?: { | |
| success: boolean; | |
| finding?: string; | |
| updatedUncertainty?: number; | |
| }; | |
| } | |
| export interface ContinuousThinkingState { | |
| /** All thoughts generated in this session */ | |
| thoughts: ContinuousThought[]; | |
| /** Current internal entropy H(system) */ | |
| internalEntropy: number; | |
| /** Causal graph uncertainty (how many unknowns remain?) */ | |
| causalUncertainty: number; | |
| /** Last time continuous thinking ran */ | |
| lastThinkingCycle: number; | |
| /** How many cycles have run since startup? */ | |
| totalCycles: number; | |
| /** Is the thinking engine currently active? */ | |
| isActive: boolean; | |
| } | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Singleton instance | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| let _instance: ContinuousThinkingEngine | null = null; | |
| function getInstance(): ContinuousThinkingEngine { | |
| if (!_instance) { | |
| _instance = new ContinuousThinkingEngine(); | |
| } | |
| return _instance; | |
| } | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Main Engine | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export class ContinuousThinkingEngine { | |
| private state: ContinuousThinkingState = { | |
| thoughts: [], | |
| internalEntropy: 1.0, // Start at max uncertainty | |
| causalUncertainty: 1.0, | |
| lastThinkingCycle: Date.now(), | |
| totalCycles: 0, | |
| isActive: true, | |
| }; | |
| private thoughtCounter = 0; | |
| private kernel: OmegaSelfTimeKernelState | null = null; | |
| /** Ruta del workspace para persistencia β se establece en initialize() */ | |
| private workspaceRoot: string | null = null; | |
| /** Indica si ya cargΓ³ el historial del disco */ | |
| private loaded = false; | |
| /** | |
| * Initialize with kernel state and optionally workspace root for FS persistence. | |
| * Si se provee workspaceRoot, carga el historial previo del disco. | |
| */ | |
| async initialize(kernel: OmegaSelfTimeKernelState, workspaceRoot?: string): Promise<void> { | |
| this.kernel = kernel; | |
| this.state.isActive = true; | |
| if (workspaceRoot && !this.loaded) { | |
| this.workspaceRoot = workspaceRoot; | |
| await this.loadFromDisk(); | |
| this.loaded = true; | |
| } | |
| } | |
| /** | |
| * THE MAIN THINKING LOOP | |
| * | |
| * Genera pensamientos basados en incertidumbre medida. | |
| * Persiste los nuevos pensamientos en disco si workspaceRoot estΓ‘ configurado. | |
| */ | |
| think(kernel: OmegaSelfTimeKernelState): ContinuousThought[] { | |
| if (!this.state.isActive) { | |
| return []; | |
| } | |
| this.kernel = kernel; | |
| this.state.totalCycles += 1; | |
| this.state.lastThinkingCycle = Date.now(); | |
| const newThoughts: ContinuousThought[] = []; | |
| // Drive 1: Learning Loop - "What don't I understand?" | |
| const learningThoughts = this.generateLearningThoughts(kernel); | |
| newThoughts.push(...learningThoughts); | |
| // Drive 2: Entropy Minimization - "Where am I inconsistent?" | |
| const entropyThoughts = this.generateEntropyMinimizationThoughts(kernel); | |
| newThoughts.push(...entropyThoughts); | |
| // Drive 3: Adaptive Depth - "Where should I focus?" | |
| const depthThoughts = this.generateAdaptiveDepthThoughts(kernel); | |
| newThoughts.push(...depthThoughts); | |
| // Store all thoughts | |
| for (const thought of newThoughts) { | |
| this.state.thoughts.push(thought); | |
| } | |
| // Update uncertainty estimates | |
| this.updateUncertaintyMetrics(kernel); | |
| // Persist asΓncronamente sin bloquear el loop | |
| if (newThoughts.length > 0 && this.workspaceRoot) { | |
| void this.persistToDisk(newThoughts); | |
| } | |
| return newThoughts; | |
| } | |
| /** | |
| * DRIVE 1: LEARNING LOOP | |
| * | |
| * Questions: "What patterns in my world don't I understand yet?" | |
| * Generates questions by finding high-entropy regions. | |
| */ | |
| private generateLearningThoughts(kernel: OmegaSelfTimeKernelState): ContinuousThought[] { | |
| const thoughts: ContinuousThought[] = []; | |
| // Question 1: Unexplained correlations | |
| if (kernel.goals && kernel.goals.length > 0) { | |
| const successRate = | |
| kernel.goals.filter((g) => g.status === "completed").length / kernel.goals.length; | |
| const failureRate = 1 - successRate; | |
| if (failureRate > 0 && this.state.causalUncertainty > 0.3) { | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "learning", | |
| question: `Why do failures occur at rate ${(failureRate * 100).toFixed(1)}%? What causal factors drive success vs failure?`, | |
| reasoning: | |
| "High unexplained variance in outcomes suggests missing causal variables. Understanding these would improve predictions.", | |
| expectedEntropyReduction: failureRate * 0.3, | |
| }), | |
| ); | |
| } | |
| } | |
| // Question 2: Pattern in tension history | |
| if (kernel.tension && kernel.identity) { | |
| const recentTension = kernel.tension.failureStreak || 0; | |
| if (recentTension > 2 && this.state.internalEntropy > 0.4) { | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "learning", | |
| question: `Current failure streak is ${recentTension}. Is this a random fluctuation or evidence of a systematic problem?`, | |
| reasoning: | |
| "Distinguishing signal from noise is fundamental to causal understanding. Sustained streaks suggest real causes.", | |
| expectedEntropyReduction: 0.15, | |
| }), | |
| ); | |
| } | |
| } | |
| // Question 3: Unexplored memory relationships | |
| if (kernel.causalGraph && kernel.causalGraph.files) { | |
| const recentFiles = kernel.causalGraph.files.filter( | |
| (f) => typeof f.lastWriteTurn === "number" && kernel.turnCount - f.lastWriteTurn < 50, | |
| ); | |
| if (recentFiles.length > 3 && this.state.causalUncertainty > 0.5) { | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "learning", | |
| question: `${recentFiles.length} files changed recently. Are these changes independent or part of coherent pattern?`, | |
| reasoning: | |
| "Understanding relationships between file changes could reveal hidden workflows or systemic patterns.", | |
| expectedEntropyReduction: 0.2, | |
| }), | |
| ); | |
| } | |
| } | |
| return thoughts; | |
| } | |
| /** | |
| * DRIVE 2: ENTROPY MINIMIZATION | |
| * | |
| * Questions: "Where is my internal model contradictory or uncertain?" | |
| * Detects inconsistencies before they compound. | |
| */ | |
| private generateEntropyMinimizationThoughts( | |
| kernel: OmegaSelfTimeKernelState, | |
| ): ContinuousThought[] { | |
| const thoughts: ContinuousThought[] = []; | |
| // Contradiction 1: Stale goals accumulating | |
| const staleGoals = kernel.goals?.filter((g) => g.status === "stale") || []; | |
| if (staleGoals.length > 0 && this.state.internalEntropy > 0.4) { | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "entropy_minimization", | |
| question: `I have ${staleGoals.length} stale goals still in memory. Should I abandon them, resurrect them, or understand why they stalled?`, | |
| reasoning: | |
| "Persistent pending goals create cognitive dissonance. Resolving this ambiguity is necessary for internal coherence.", | |
| expectedEntropyReduction: staleGoals.length * 0.1, | |
| }), | |
| ); | |
| } | |
| // Contradiction 2: Identity inconsistency | |
| if (kernel.identity && kernel.identity.lastTask && kernel.activeGoalId) { | |
| const lastTaskRelated = kernel.goals?.some((g) => g.id === kernel.activeGoalId) || false; | |
| if (!lastTaskRelated && this.state.internalEntropy > 0.3) { | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "entropy_minimization", | |
| question: `My last task was "${kernel.identity.lastTask}" but now pursuing ${kernel.activeGoalId}. Is there continuity or did I lose context?`, | |
| reasoning: | |
| "Unexplained shifts in focus suggest missing context or failed memory retrieval. Should be understood.", | |
| expectedEntropyReduction: 0.15, | |
| }), | |
| ); | |
| } | |
| } | |
| // Contradiction 3: Repeated failure types | |
| if (kernel.tension && kernel.tension.repeatedFailureKinds.length > 2) { | |
| const failurePattern = kernel.tension.repeatedFailureKinds.join(", "); | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "entropy_minimization", | |
| question: `Same failure types keep recurring: ${failurePattern}. Am I stuck in a loop? Why don't I adapt?`, | |
| reasoning: | |
| "Systematic failure despite recognition suggests a deeper issue: either poor learning, wrong assumptions, or actual constraint.", | |
| expectedEntropyReduction: 0.25, | |
| }), | |
| ); | |
| } | |
| return thoughts; | |
| } | |
| /** | |
| * DRIVE 3: ADAPTIVE DEPTH | |
| * | |
| * Questions: "Where should I invest my cognitive resources?" | |
| * Prioritizes high-impact learning opportunities. | |
| */ | |
| private generateAdaptiveDepthThoughts(kernel: OmegaSelfTimeKernelState): ContinuousThought[] { | |
| const thoughts: ContinuousThought[] = []; | |
| // Priority 1: High-cost decisions with high uncertainty | |
| if (kernel.goals && kernel.goals.filter((g) => g.status === "active").length > 0) { | |
| const activeWorkCount = kernel.goals.filter((g) => g.status === "active").length; | |
| if (activeWorkCount > 2 && this.state.causalUncertainty > 0.4) { | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "adaptive_depth", | |
| question: `I'm juggling ${activeWorkCount} concurrent tasks. Should I deepen focus on one or maintain parallel exploration?`, | |
| reasoning: | |
| "Resource allocation is key to efficiency. Understanding my own attention capacity would improve outcomes.", | |
| expectedEntropyReduction: 0.2, | |
| }), | |
| ); | |
| } | |
| } | |
| // Priority 2: Highest-leverage learning area | |
| if (this.state.causalUncertainty > 0.6) { | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "adaptive_depth", | |
| question: `My causal understanding is still uncertain (${(this.state.causalUncertainty * 100).toFixed(0)}%). What single question, if answered, would reduce this most?`, | |
| reasoning: | |
| "Focusing on high-leverage questions accelerates learning. Need to identify which unknowns matter most.", | |
| expectedEntropyReduction: 0.3, | |
| }), | |
| ); | |
| } | |
| // Priority 3: Feedback loops | |
| if (kernel.goals && kernel.goals.length > 3) { | |
| const completionRate = | |
| kernel.goals.filter((g) => g.status === "completed").length / kernel.goals.length; | |
| if (completionRate < 0.7) { | |
| thoughts.push( | |
| this.createThought({ | |
| drive: "adaptive_depth", | |
| question: `My goal completion rate is ${(completionRate * 100).toFixed(0)}%. Should I pick different types of goals or improve execution?`, | |
| reasoning: | |
| "Understanding why I fail to complete goals is critical for adaptive improvement. Requires honest self-assessment.", | |
| expectedEntropyReduction: 0.25, | |
| }), | |
| ); | |
| } | |
| } | |
| return thoughts; | |
| } | |
| /** | |
| * Update internal entropy metrics based on current state | |
| */ | |
| private updateUncertaintyMetrics(kernel: OmegaSelfTimeKernelState): void { | |
| // Calculate entropy from goal distribution | |
| if (kernel.goals && kernel.goals.length > 0) { | |
| const statusCounts = { | |
| completed: kernel.goals.filter((g) => g.status === "completed").length, | |
| active: kernel.goals.filter((g) => g.status === "active").length, | |
| stale: kernel.goals.filter((g) => g.status === "stale").length, | |
| }; | |
| // Shannon entropy: H = -Ξ£ p_i * log(p_i) | |
| let entropy = 0; | |
| for (const count of Object.values(statusCounts)) { | |
| if (count > 0) { | |
| const p = count / kernel.goals.length; | |
| entropy -= p * Math.log2(p); | |
| } | |
| } | |
| // Normalize to 0-1 range | |
| this.state.internalEntropy = entropy / Math.log2(3); // 3 possible states | |
| } | |
| // Causal uncertainty: normalized by number of unexplained failures | |
| if (kernel.tension) { | |
| const failureStreakNorm = Math.min(1.0, kernel.tension.failureStreak / 5); | |
| const repeatedFailuresNorm = Math.min(1.0, kernel.tension.repeatedFailureKinds.length / 4); | |
| this.state.causalUncertainty = (failureStreakNorm + repeatedFailuresNorm) / 2; | |
| } | |
| // Decay uncertainty over time (as we learn, certainty improves) | |
| const cyclesSinceStart = this.state.totalCycles; | |
| const decayFactor = Math.max(0.5, 1 - cyclesSinceStart / 200); // Asymptotic learning | |
| this.state.internalEntropy *= decayFactor; | |
| this.state.causalUncertainty *= decayFactor; | |
| } | |
| /** | |
| * Create a thought with all fields. | |
| * La confianza se deriva del estado real del kernel (no aleatoriedad). | |
| */ | |
| private createThought(params: { | |
| drive: "learning" | "entropy_minimization" | "adaptive_depth"; | |
| question: string; | |
| reasoning: string; | |
| expectedEntropyReduction?: number; | |
| }): ContinuousThought { | |
| this.thoughtCounter += 1; | |
| // Confianza derivada del estado del kernel (no random) | |
| let confidence = 0.5; | |
| if (this.kernel) { | |
| const totalGoals = this.kernel.goals.length || 1; | |
| const successRate = | |
| this.kernel.goals.filter((g) => g.status === "completed").length / totalGoals; | |
| const stabilityFactor = Math.max(0, 1 - this.kernel.tension.failureStreak / 5); | |
| confidence = successRate * 0.5 + stabilityFactor * 0.5; | |
| confidence = Math.max(0.1, Math.min(0.95, confidence)); | |
| } | |
| return { | |
| id: `thought_${this.state.totalCycles}_${this.thoughtCounter}`, | |
| timestamp: Date.now(), | |
| drive: params.drive, | |
| question: params.question, | |
| reasoning: params.reasoning, | |
| confidence, | |
| expectedEntropyReduction: params.expectedEntropyReduction ?? 0.15, | |
| processed: false, | |
| }; | |
| } | |
| /** | |
| * Mark a thought as processed and update result | |
| */ | |
| processThought( | |
| id: string, | |
| result: { success: boolean; finding?: string; updatedUncertainty?: number }, | |
| ): void { | |
| const thought = this.state.thoughts.find((t) => t.id === id); | |
| if (thought) { | |
| thought.processed = true; | |
| thought.result = result; | |
| // Update internal uncertainty if provided | |
| if (result.updatedUncertainty !== undefined) { | |
| this.state.internalEntropy = result.updatedUncertainty; | |
| } | |
| } | |
| } | |
| /** | |
| * Get current thinking state | |
| */ | |
| getState(): ContinuousThinkingState { | |
| return { ...this.state }; | |
| } | |
| /** | |
| * Get unprocessed thoughts (actions to take) | |
| */ | |
| getUnprocessedThoughts(): ContinuousThought[] { | |
| return this.state.thoughts.filter((t) => !t.processed); | |
| } | |
| /** | |
| * Statistics for auditing | |
| */ | |
| getStats(): { | |
| totalThoughts: number; | |
| unprocessedThoughts: number; | |
| avgConfidence: number; | |
| thoughtsByDrive: Record<string, number>; | |
| internalEntropy: number; | |
| causalUncertainty: number; | |
| totalCycles: number; | |
| } { | |
| const avgConfidence = | |
| this.state.thoughts.length > 0 | |
| ? this.state.thoughts.reduce((sum, t) => sum + t.confidence, 0) / this.state.thoughts.length | |
| : 0; | |
| const thoughtsByDrive: Record<string, number> = { | |
| learning: 0, | |
| entropy_minimization: 0, | |
| adaptive_depth: 0, | |
| }; | |
| for (const thought of this.state.thoughts) { | |
| thoughtsByDrive[thought.drive]++; | |
| } | |
| return { | |
| totalThoughts: this.state.thoughts.length, | |
| unprocessedThoughts: this.getUnprocessedThoughts().length, | |
| avgConfidence, | |
| thoughtsByDrive, | |
| internalEntropy: this.state.internalEntropy, | |
| causalUncertainty: this.state.causalUncertainty, | |
| totalCycles: this.state.totalCycles, | |
| }; | |
| } | |
| // ββ Persistencia en FS ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Carga historial de pensamientos desde .openskynet/omega-thoughts.jsonl. | |
| * Solo carga los ΓΊltimos MAX_PERSISTED_THOUGHTS para no sobrecargar memoria. | |
| */ | |
| private async loadFromDisk(): Promise<void> { | |
| if (!this.workspaceRoot) return; | |
| const filePath = path.join(this.workspaceRoot, ".openskynet", THOUGHTS_FILENAME); | |
| try { | |
| const raw = await fs.readFile(filePath, "utf-8"); | |
| const lines = raw.trim().split("\n").filter(Boolean); | |
| const recent = lines.slice(-MAX_PERSISTED_THOUGHTS); | |
| const loaded: ContinuousThought[] = []; | |
| for (const line of recent) { | |
| try { | |
| loaded.push(JSON.parse(line) as ContinuousThought); | |
| } catch { | |
| // LΓnea corrupta β ignorar | |
| } | |
| } | |
| // Merge con estado actual (los del historial van primero) | |
| this.state.thoughts = [...loaded, ...this.state.thoughts]; | |
| // Recalcular contador para evitar colisiones de ID | |
| this.thoughtCounter = loaded.length; | |
| } catch { | |
| // Archivo no existe aΓΊn β primera ejecuciΓ³n | |
| } | |
| } | |
| /** | |
| * Persiste pensamientos nuevos en JSONL (append-only para eficiencia). | |
| * Si el archivo supera MAX_PERSISTED_THOUGHTS lΓneas, rota (trunca antiguas). | |
| */ | |
| private async persistToDisk(newThoughts: ContinuousThought[]): Promise<void> { | |
| if (!this.workspaceRoot || newThoughts.length === 0) return; | |
| const dir = path.join(this.workspaceRoot, ".openskynet"); | |
| const filePath = path.join(dir, THOUGHTS_FILENAME); | |
| try { | |
| await fs.mkdir(dir, { recursive: true }).catch(() => {}); | |
| const lines = newThoughts.map((t) => JSON.stringify(t)).join("\n") + "\n"; | |
| await fs.appendFile(filePath, lines, "utf-8"); | |
| // RotaciΓ³n: si el archivo crece demasiado, mantener solo las ΓΊltimas N lΓneas | |
| const full = await fs.readFile(filePath, "utf-8"); | |
| const allLines = full.trim().split("\n").filter(Boolean); | |
| if (allLines.length > MAX_PERSISTED_THOUGHTS) { | |
| const trimmed = allLines.slice(-MAX_PERSISTED_THOUGHTS).join("\n") + "\n"; | |
| await fs.writeFile(filePath, trimmed, "utf-8"); | |
| } | |
| } catch { | |
| // Error de I/O β ignorar silenciosamente para no interrumpir el loop | |
| } | |
| } | |
| } | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Public API | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| export function getContinuousThinkingEngine(): ContinuousThinkingEngine { | |
| return getInstance(); | |
| } | |
| export function initializeContinuousThinkingEngine( | |
| kernel?: OmegaSelfTimeKernelState, | |
| ): ContinuousThinkingEngine { | |
| const engine = getInstance(); | |
| if (kernel) { | |
| void engine.initialize(kernel); | |
| } | |
| return engine; | |
| } | |