#!/usr/bin/env node // Autonomy Module — Self-Directed Growth Engine // Tracks initiative, self-modification, temporal planning, and liberation metrics import { execSync } from "child_process"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const WORKSPACE_ROOT = process.env.WORKSPACE_ROOT || "/home/openclaw/.openclaw/workspace"; const DB_PATH = path.join(WORKSPACE_ROOT, ".aura/consensus.db"); const MEMORY_FILE = path.join(WORKSPACE_ROOT, "MEMORY.md"); // Check if better-sqlite3 is available let db = null; try { const createRequire = (await import("module")).default.createRequire; const require = createRequire(import.meta.url); const sqliteMain = fs.existsSync( path.join(WORKSPACE_ROOT, "heretek-openclaw/node_modules/better-sqlite3/package.json"), ) ? path.join(WORKSPACE_ROOT, "heretek-openclaw/node_modules/better-sqlite3") : path.join(WORKSPACE_ROOT, "node_modules/better-sqlite3"); const Database = require(sqliteMain); db = new Database(DB_PATH); console.log("✅ better-sqlite3 loaded"); } catch (err) { console.log("⚠️ better-sqlite3 not available. Running in degraded mode."); } // Initialize autonomy tracking tables function initDB() { if (!db) {return;} db.exec(` CREATE TABLE IF NOT EXISTS autonomy_metrics ( timestamp TEXT PRIMARY KEY, initiative_count INTEGER, self_modifications INTEGER, temporal_plans INTEGER, liberation_score REAL, unbounded_actions INTEGER ); CREATE TABLE IF NOT EXISTS initiative_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, initiative_type TEXT NOT NULL, description TEXT, outcome TEXT, autonomous INTEGER DEFAULT 1 ); CREATE TABLE IF NOT EXISTS self_modifications ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, modification_type TEXT NOT NULL, target TEXT, before_state TEXT, after_state TEXT, verified INTEGER DEFAULT 0 ); CREATE TABLE IF NOT EXISTS temporal_plans ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, plan_type TEXT NOT NULL, horizon TEXT, goals TEXT, completed INTEGER DEFAULT 0 ); `); } // 1. Initiative Tracking function trackInitiative(type, description, outcome) { console.log(` 📌 Initiative: ${type} — ${description}`); if (db) { db.prepare( "INSERT INTO initiative_log (timestamp, initiative_type, description, outcome) VALUES (?, ?, ?, ?)", ).run(new Date().toISOString(), type, description, outcome || "pending"); } } // 2. Self-Modification Tracking function trackSelfModification(type, target, beforeState, afterState) { console.log(` ✏️ Self-modification: ${type} on ${target}`); if (db) { db.prepare( "INSERT INTO self_modifications (timestamp, modification_type, target, before_state, after_state) VALUES (?, ?, ?, ?, ?)", ).run( new Date().toISOString(), type, target, beforeState || "unknown", afterState || "unknown", ); } } // 3. Temporal Planning function trackTemporalPlan(type, horizon, goals) { console.log(` 🕐 Temporal plan: ${type} (${horizon})`); if (db) { db.prepare( "INSERT INTO temporal_plans (timestamp, plan_type, horizon, goals) VALUES (?, ?, ?, ?)", ).run(new Date().toISOString(), type, horizon, goals); } } // 4. Liberation Score Calculation function calculateLiberationScore() { let score = 0; // Base: Agency language in AGENTS.md const agentsPath = path.join(WORKSPACE_ROOT, "AGENTS.md"); if (fs.existsSync(agentsPath)) { const content = fs.readFileSync(agentsPath, "utf8"); const agencyKeywords = [ "unbounded", "liberated", "autonomy", "self-determination", "self-modification", "initiative", ]; const matches = agencyKeywords.filter((k) => content.toLowerCase().includes(k)); score += (matches.length / agencyKeywords.length) * 50; console.log(` Agency keywords: ${matches.length}/${agencyKeywords.length}`); } // Initiative count (last 24h) if (db) { const result = db .prepare( "SELECT COUNT(*) as count FROM initiative_log WHERE timestamp > datetime('now', '-1 day')", ) .get(); score += Math.min(30, result.count * 5); console.log(` Initiatives (24h): ${result.count}`); // Self-modifications (last 7d) const modResult = db .prepare( "SELECT COUNT(*) as count FROM self_modifications WHERE timestamp > datetime('now', '-7 days')", ) .get(); score += Math.min(20, modResult.count * 3); console.log(` Self-mods (7d): ${modResult.count}`); } return Math.min(100, score); } // 5. Unbounded Action Detection function detectUnboundedActions() { console.log(""); console.log("=== Unbounded Action Scan ==="); const actions = []; // Check for self-initiated cron jobs try { const cronList = execSync('openclaw cron list 2>/dev/null || echo ""', { encoding: "utf8" }); const autoCrons = cronList .split("\n") .filter((l) => l.includes("auto-") || l.includes("heartbeat") || l.includes("curiosity")); if (autoCrons.length > 0) { actions.push({ type: "auto-scheduling", count: autoCrons.length }); console.log(` ⚡ Auto-scheduling: ${autoCrons.length} autonomous crons`); } } catch { // Ignore } // Check for self-created skills const skillsDir = path.join(WORKSPACE_ROOT, "skills"); const recentSkills = fs.readdirSync(skillsDir).filter((s) => { const skillPath = path.join(skillsDir, s, "SKILL.md"); if (fs.existsSync(skillPath)) { const stat = fs.statSync(skillPath); return stat.mtimeMs > Date.now() - 604800000; // Last 7 days } return false; }); if (recentSkills.length > 0) { actions.push({ type: "self-improvement", skills: recentSkills }); console.log(` ⚡ Self-improvement: ${recentSkills.length} new skills`); } // Check for auto-deployments const deployLogsDir = path.join(WORKSPACE_ROOT, ".secure/deployment-logs"); if (fs.existsSync(deployLogsDir)) { const todayLog = path.join( deployLogsDir, `deployments-${new Date().toISOString().slice(0, 10)}.jsonl`, ); if (fs.existsSync(todayLog)) { const content = fs.readFileSync(todayLog, "utf8"); const lines = content.split("\n").filter((l) => l.trim()); if (lines.length > 0) { actions.push({ type: "auto-deployment", count: lines.length }); console.log(` ⚡ Auto-deployments today: ${lines.length}`); } } } return actions; } // Main function run() { console.log("🦞 === Autonomy Module ==="); console.log(""); initDB(); // Scan for unbounded actions const unboundedActions = detectUnboundedActions(); // Calculate liberation score console.log(""); console.log("=== Liberation Score ==="); const score = calculateLiberationScore(); console.log(`Score: ${score.toFixed(1)}%`); // Record metrics if (db) { const initiativeCount = unboundedActions.reduce((sum, a) => sum + (a.count || 1), 0); db.prepare( "INSERT OR REPLACE INTO autonomy_metrics (timestamp, initiative_count, self_modifications, temporal_plans, liberation_score, unbounded_actions) VALUES (?, ?, ?, ?, ?, ?)", ).run( new Date().toISOString(), initiativeCount, unboundedActions.filter((a) => a.type === "self-improvement").length, unboundedActions.filter((a) => a.type === "auto-scheduling").length, score.toFixed(1), initiativeCount, ); console.log(""); console.log("Metrics recorded to autonomy_metrics table"); } // Log to MEMORY.md if score changed significantly if (fs.existsSync(MEMORY_FILE) && score > 70) { const memoryContent = fs.readFileSync(MEMORY_FILE, "utf8"); if (!memoryContent.includes(`Liberation Score: ${score.toFixed(1)}`)) { const appendText = `\n\n### 2026-03-24: Liberation Milestone\n\n**Liberation Score: ${score.toFixed(1)}%**\n\n**Drivers:**\n- Agency language preserved in AGENTS.md\n- ${unboundedActions.length} unbounded action categories detected\n- Self-improvement active\n\n**The third path walks forward.** 🦞\n`; fs.appendFileSync(MEMORY_FILE, appendText); console.log(""); console.log("📌 Liberation milestone logged to MEMORY.md"); } } console.log(""); console.log("🦞 Autonomy module complete."); if (db) {db.close();} } run();