473 lines
16 KiB
JavaScript
473 lines
16 KiB
JavaScript
#!/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 };
|