#!/usr/bin/env node /** * PR-C Deprecation Check Script * Scans OpenClaw config and plugin state for deprecated modules. * * Run: node scripts/check-deprecations.mjs * * Checks: * 1. Active Memory blocking mode * 2. Dreaming REM phase enabled * 3. Dream Diary generation * 4. QMD search mode / QMD engine references * 5. Honcho memory plugin enabled * 6. LanceDB memory plugin enabled * 7. memory-wiki plugin loaded * 8. Commitments auto-infer * 9. Grounded Backfill / REM Backfill CLI usage */ import { readFileSync, existsSync, readdirSync } from "node:fs"; import { join } from "node:path"; import { homedir } from "node:os"; import { warnOnce, warnMany } from "./lib/deprecation-warning.mjs"; const AGENTS_HOME = join(homedir(), ".openclaw"); const CONFIG_PATH = join(AGENTS_HOME, "openclaw.json"); const OPENCLAW_HOME = process.env.OPENCLAW_HOME || "/opt/homebrew/lib/node_modules/openclaw"; const EXTENSIONS_DIR = join(OPENCLAW_HOME, "dist", "extensions"); // Load user config function loadConfig() { if (!existsSync(CONFIG_PATH)) return null; try { return JSON.parse(readFileSync(CONFIG_PATH, "utf8")); } catch { return null; } } // Load plugin entry for a given extension id function loadPlugin(id) { const pluginJsonPath = join(EXTENSIONS_DIR, id, "openclaw.plugin.json"); if (!existsSync(pluginJsonPath)) return null; try { return JSON.parse(readFileSync(pluginJsonPath, "utf8")); } catch { return null; } } // Check if a plugin is enabled in user config function isPluginEnabled(config, pluginId) { const entries = config?.plugins?.entries; if (!entries) return false; const entry = entries[pluginId]; if (!entry) return false; return entry.enabled !== false; } // ─── Check Functions ──────────────────────────────── function checkActiveMemoryBlocking(config) { const warnings = []; const entry = config?.plugins?.entries?.["active-memory"]; if (!entry) return warnings; const mode = entry.config?.mode; if (mode === "blocking") { warnings.push({ key: "active-memory.blocking", message: "active-memory blocking mode is deprecated. Set mode to 'precompute' instead." }); } return warnings; } function checkDreamingRemPhase(config) { const warnings = []; const entry = config?.plugins?.entries?.["memory-core"]; if (!entry) return warnings; const dreaming = entry.config?.dreaming; if (!dreaming) return warnings; // Check REM phase enabled const remEnabled = dreaming?.phases?.rem?.enabled; if (remEnabled === true) { warnings.push({ key: "dreaming.rem-phase", message: "Dreaming REM phase is deprecated. It will be replaced by Collect/Promote." }); } return warnings; } function checkDreamDiary(config) { const warnings = []; const entry = config?.plugins?.entries?.["memory-core"]; if (!entry) return warnings; const dreaming = entry.config?.dreaming; if (!dreaming) return warnings; // Dream Diary is implied by REM phase or certain storage modes const storage = dreaming?.storage; const hasDiaryMode = storage?.mode === "separate" || storage?.mode === "both"; const separateReports = storage?.separateReports === true; if (hasDiaryMode || separateReports) { warnings.push({ key: "dreaming.dream-diary", message: "Dream Diary generation is deprecated and will be removed." }); } // Also check if dreaming has a configured model (used for diary) if (dreaming?.model) { warnings.push({ key: "dreaming.diary-model", message: "Dreaming diary model override is deprecated." }); } return warnings; } function checkQmdReference(config) { const warnings = []; // Check memory search config for QMD const memorySearch = config?.agents?.defaults?.memorySearch; if (memorySearch?.provider === "qmd" || memorySearch?.backend === "qmd") { warnings.push({ key: "qmd.memory-search", message: "QMD memory engine is legacy. Migrate to Builtin MemoryCore." }); } // Check active-memory QMD reference const activeCfg = config?.plugins?.entries?.["active-memory"]?.config; if (activeCfg?.qmd?.searchMode) { warnings.push({ key: "qmd.active-memory", message: "QMD search mode in active-memory is legacy. Remove qmd config." }); } return warnings; } function checkHonchoLanceDB(config) { const warnings = []; // Check if honcho or lancedb plugins are loaded const pluginEntries = config?.plugins?.entries || {}; if (pluginEntries["memory-honcho"]?.enabled !== false && isPluginInExtensions("honcho")) { warnings.push({ key: "honcho.plugin", message: "Honcho memory plugin is legacy. Existing data readable during migration." }); } if (pluginEntries["memory-lancedb"]?.enabled !== false && isPluginInExtensions("lancedb")) { warnings.push({ key: "lancedb.plugin", message: "LanceDB memory plugin is legacy. Existing data readable during migration." }); } return warnings; } function checkMemoryWiki(config) { const warnings = []; const entry = config?.plugins?.entries?.["memory-wiki"]; if (entry?.enabled !== false) { // Check if memory-wiki extension exists and isn't disabled const wikiPlugin = loadPlugin("memory-wiki"); if (wikiPlugin) { warnings.push({ key: "memory-wiki.plugin", message: "memory-wiki plugin is legacy. wiki_search/wiki_get/wiki_apply/wiki_lint/wiki_status remain available during migration." }); } } return warnings; } function checkCommitmentsAutoInfer(config) { const warnings = []; // Commitments auto-infer is part of the session-memory bundled module const commitments = config?.commitments; if (commitments?.autoInfer === true) { warnings.push({ key: "commitments.auto-infer", message: "Commitments auto-infer is deprecated. Use explicit cron tasks instead." }); } return warnings; } function checkGroundedBackfill(config) { const warnings = []; // Grounded Backfill is a CLI feature — we check if related config exists const dreaming = config?.plugins?.entries?.["memory-core"]?.config?.dreaming; if (!dreaming) return warnings; // If dreaming has unusual storage paths that suggest backfill usage const storage = dreaming?.storage; if (storage?.mode === "separate") { warnings.push({ key: "backfill.storage-mode", message: "Grounded Backfill / REM Backfill is deprecated and will not evolve further." }); } return warnings; } // ─── Helpers ──────────────────────────────────────── function isPluginInExtensions(name) { try { const dirs = readdirSync(EXTENSIONS_DIR, { withFileTypes: true }) .filter(d => d.isDirectory()); for (const d of dirs) { if (d.name.includes(name)) return true; // Check plugin.json const pluginPath = join(EXTENSIONS_DIR, d.name, "openclaw.plugin.json"); if (existsSync(pluginPath)) { const plugin = JSON.parse(readFileSync(pluginPath, "utf8")); if (plugin.id?.includes(name)) return true; } } } catch {} return false; } // ─── Legacy Registry (PR-1 Freeze) ─────────────────── // Maps deprecation keys to replacement info and migration window. // Used to enhance check-deprecations output. const LEGACY_REGISTRY = { "active-memory.blocking": { module: "active-memory blocking mode", status: "Active. Deprecated.", replacement: 'Set mode to "precompute" in active-memory config', migrationWindow: "90 days from 2026-06-04", removalTarget: "2026-09-04", }, "dreaming.rem-phase": { module: "Dreaming REM phase", status: "Active. Deprecated.", replacement: "Collect/Promote two-phase dreaming (PR-5)", migrationWindow: "90 days from 2026-06-04", removalTarget: "2026-09-04", }, "dreaming.dream-diary": { module: "Dream Diary", status: "Active. Deprecated.", replacement: "Inline dreaming output (no separate diary storage)", migrationWindow: "90 days from 2026-06-04", removalTarget: "2026-09-04", }, "dreaming.diary-model": { module: "Dream Diary model override", status: "Active. Deprecated.", replacement: "Use default dreaming model", migrationWindow: "90 days from 2026-06-04", removalTarget: "2026-09-04", }, "qmd.memory-search": { module: "QMD memory search engine", status: "Active. Deprecated.", replacement: "Builtin MemoryCore FTS5 + BM25", migrationWindow: "90 days from 2026-06-04", removalTarget: "2026-09-04", }, "qmd.active-memory": { module: "QMD active-memory search mode", status: "Active. Deprecated.", replacement: "Remove qmd config block from active-memory", migrationWindow: "90 days from 2026-06-04", removalTarget: "2026-09-04", }, "honcho.plugin": { module: "Honcho memory plugin", status: "Legacy. Data readable during migration.", replacement: "memory-core (FTS5 + BM25)", migrationWindow: "60 days from 2026-06-04", removalTarget: "2026-08-04", }, "lancedb.plugin": { module: "LanceDB memory plugin", status: "Legacy. Data readable during migration.", replacement: "memory-core (FTS5 + BM25)", migrationWindow: "60 days from 2026-06-04", removalTarget: "2026-08-04", }, "memory-wiki.plugin": { module: "memory-wiki plugin", status: "Active. Deprecated.", replacement: "memory-core wiki compilation", migrationWindow: "90 days from 2026-06-04", removalTarget: "2026-09-04", }, "commitments.auto-infer": { module: "Commitments auto-infer", status: "Active. Deprecated.", replacement: "Explicit cron tasks (use cron tool)", migrationWindow: "90 days from 2026-06-04", removalTarget: "2026-09-04", }, "backfill.storage-mode": { module: "Grounded Backfill / REM Backfill CLI", status: "Active. Deprecated.", replacement: "No replacement. Stop using immediately.", migrationWindow: "Immediate — effective 2026-06-04", removalTarget: "2026-06-04", }, }; function getLegacyInfo(key) { return LEGACY_REGISTRY[key] || null; } function formatLegacyTable(warnings) { // Group warnings by their legacy info const seen = new Set(); const rows = []; for (const w of warnings) { if (seen.has(w.key)) continue; seen.add(w.key); const info = LEGACY_REGISTRY[w.key] || { module: w.key, status: "Unknown", replacement: "N/A", migrationWindow: "N/A", removalTarget: "N/A", }; rows.push(info); } return rows; } // ─── Main ──────────────────────────────────────────── function runAllChecks() { const config = loadConfig(); const allWarnings = []; const checkResults = []; if (!config) { console.warn("[DEPRECATED][OpenClaw v2] config: Could not load openclaw.json"); return { warnings: [], results: [{ name: "config", error: "Could not load config" }] }; } const checks = [ { name: "active-memory blocking mode", fn: () => checkActiveMemoryBlocking(config) }, { name: "dreaming REM phase", fn: () => checkDreamingRemPhase(config) }, { name: "dream diary", fn: () => checkDreamDiary(config) }, { name: "QMD reference", fn: () => checkQmdReference(config) }, { name: "Honcho / LanceDB", fn: () => checkHonchoLanceDB(config) }, { name: "memory-wiki", fn: () => checkMemoryWiki(config) }, { name: "commitments auto-infer", fn: () => checkCommitmentsAutoInfer(config) }, { name: "grounded backfill", fn: () => checkGroundedBackfill(config) }, ]; console.log(""); console.log("═══════════════════════════════════════"); console.log(" PR-C: Deprecation Warning Check"); console.log(` Architecture Freeze: 2026-06-04`); console.log(` ${new Date().toISOString()}`); console.log("═══════════════════════════════════════"); console.log(""); for (const check of checks) { try { const warnings = check.fn(); checkResults.push({ name: check.name, warnings, passed: warnings.length === 0 }); allWarnings.push(...warnings); } catch (e) { checkResults.push({ name: check.name, warnings: [], passed: false, error: e.message }); } } // Emit warnings (deduplicated) if (allWarnings.length > 0) { console.log(" ⚠ Deprecation warnings found:\n"); for (const w of allWarnings) { warnOnce(w.key, w.message); } } else { console.log(" ✓ No deprecation warnings found."); } // Summary console.log(""); const checksWithWarnings = checkResults.filter(c => c.warnings.length > 0); const checksFailed = checkResults.filter(c => c.error); console.log(` Checks run: ${checks.length}`); console.log(` With warnings: ${checksWithWarnings.length}`); console.log(` Total warnings: ${allWarnings.length}`); if (checksFailed.length > 0) { console.log(` Check errors: ${checksFailed.length}`); for (const c of checksFailed) { console.log(` ⚠ ${c.name}: ${c.error}`); } } // ─── PR-1 Enhanced Output: Legacy Table ───────────── if (allWarnings.length > 0) { const legacyRows = formatLegacyTable(allWarnings); console.log(""); console.log(" ─────────────────────────────────────────────"); console.log(" Deprecated Modules (PR-1 Legacy Inventory):"); console.log(" ─────────────────────────────────────────────"); console.log(""); for (const row of legacyRows) { console.log(` Module: ${row.module}`); console.log(` Status: ${row.status}`); console.log(` Replacement: ${row.replacement}`); console.log(` Migration Window: ${row.migrationWindow}`); console.log(` Removal Target: ${row.removalTarget}`); console.log(""); } // Migration timeline console.log(" ─────────────────────────────────────────────"); console.log(" Migration Timeline:"); console.log(""); console.log(" Day 0 2026-06-04 Freeze active"); console.log(" Day 30 2026-07-04 Legacy warnings active"); console.log(" Day 60 2026-08-04 Honcho/LanceDB read-only cutoff"); console.log(" Day 90 2026-09-04 All legacy modules removed"); console.log(""); // Per-module key list for scripting console.log(" Deprecated keys:"); for (const w of allWarnings) { const info = LEGACY_REGISTRY[w.key]; const target = info ? info.removalTarget : "N/A"; console.log(` • ${w.key} (removal: ${target})`); } } console.log(""); return { warnings: allWarnings, results: checkResults }; } // Run if called as main if (process.argv[1]?.includes("check-deprecations")) { const result = runAllChecks(); process.exit(0); // Never fail on deprecation warnings } export { runAllChecks, loadConfig, LEGACY_REGISTRY, getLegacyInfo, formatLegacyTable, checkActiveMemoryBlocking, checkDreamingRemPhase, checkDreamDiary, checkQmdReference, checkHonchoLanceDB, checkMemoryWiki, checkCommitmentsAutoInfer, checkGroundedBackfill };