@joshuaswarren/openclaw-engram 8.3.47 → 8.3.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +439 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9893,19 +9893,19 @@ var TmtBuilder = class {
|
|
|
9893
9893
|
* then rolls up to day → week → persona if needed.
|
|
9894
9894
|
* All errors are caught and logged; never throws.
|
|
9895
9895
|
*/
|
|
9896
|
-
async maybeRebuildNodes(memories,
|
|
9896
|
+
async maybeRebuildNodes(memories, summarize2) {
|
|
9897
9897
|
if (!this.cfg.temporalMemoryTreeEnabled || memories.length === 0) return;
|
|
9898
9898
|
try {
|
|
9899
9899
|
await mkdir12(tmtDir(this.baseDir), { recursive: true });
|
|
9900
|
-
await this.buildHourNodes(memories,
|
|
9901
|
-
await this.buildDayNodes(memories,
|
|
9902
|
-
await this.buildWeekNodes(memories,
|
|
9903
|
-
await this.buildPersonaNode(
|
|
9900
|
+
await this.buildHourNodes(memories, summarize2);
|
|
9901
|
+
await this.buildDayNodes(memories, summarize2);
|
|
9902
|
+
await this.buildWeekNodes(memories, summarize2);
|
|
9903
|
+
await this.buildPersonaNode(summarize2);
|
|
9904
9904
|
} catch (err) {
|
|
9905
9905
|
console.warn(`[engram] tmt: rebuild failed (ignored): ${err}`);
|
|
9906
9906
|
}
|
|
9907
9907
|
}
|
|
9908
|
-
async buildHourNodes(memories,
|
|
9908
|
+
async buildHourNodes(memories, summarize2) {
|
|
9909
9909
|
const byHour = /* @__PURE__ */ new Map();
|
|
9910
9910
|
for (const m of memories) {
|
|
9911
9911
|
const date = parseIsoDate(m.created);
|
|
@@ -9933,7 +9933,7 @@ var TmtBuilder = class {
|
|
|
9933
9933
|
if (!shouldBuild) continue;
|
|
9934
9934
|
let summary;
|
|
9935
9935
|
try {
|
|
9936
|
-
summary = await
|
|
9936
|
+
summary = await summarize2(capTmtSummaryInputs(entries.map((e) => e.content), "hour"), "hour");
|
|
9937
9937
|
} catch (err) {
|
|
9938
9938
|
console.warn(`[engram] tmt: hour node summarize failed for ${key} (ignored): ${err}`);
|
|
9939
9939
|
continue;
|
|
@@ -9951,7 +9951,7 @@ var TmtBuilder = class {
|
|
|
9951
9951
|
await writeFile12(nodePath, serialiseTmtNode(fm, summary), "utf8");
|
|
9952
9952
|
}
|
|
9953
9953
|
}
|
|
9954
|
-
async buildDayNodes(memories,
|
|
9954
|
+
async buildDayNodes(memories, summarize2) {
|
|
9955
9955
|
const byDate = /* @__PURE__ */ new Map();
|
|
9956
9956
|
for (const m of memories) {
|
|
9957
9957
|
const date = parseIsoDate(m.created);
|
|
@@ -9998,7 +9998,7 @@ var TmtBuilder = class {
|
|
|
9998
9998
|
if (inputs.length === 0) inputs.push(...entries.map((e) => e.content));
|
|
9999
9999
|
let summary;
|
|
10000
10000
|
try {
|
|
10001
|
-
summary = await
|
|
10001
|
+
summary = await summarize2(capTmtSummaryInputs(inputs, "day"), "day");
|
|
10002
10002
|
} catch (err) {
|
|
10003
10003
|
console.warn(`[engram] tmt: day node summarize failed for ${date} (ignored): ${err}`);
|
|
10004
10004
|
continue;
|
|
@@ -10022,7 +10022,7 @@ var TmtBuilder = class {
|
|
|
10022
10022
|
* (or stale) week node, reads day node summaries and builds a week node.
|
|
10023
10023
|
* Fail-open: errors per week are caught and skipped.
|
|
10024
10024
|
*/
|
|
10025
|
-
async buildWeekNodes(memories,
|
|
10025
|
+
async buildWeekNodes(memories, summarize2) {
|
|
10026
10026
|
const weekToEntries = /* @__PURE__ */ new Map();
|
|
10027
10027
|
for (const m of memories) {
|
|
10028
10028
|
const week = isoWeekKey(new Date(m.created));
|
|
@@ -10068,7 +10068,7 @@ var TmtBuilder = class {
|
|
|
10068
10068
|
}
|
|
10069
10069
|
}
|
|
10070
10070
|
const inputs = daySummaries.length > 0 ? daySummaries : entries.map((e) => e.content);
|
|
10071
|
-
const summary = await
|
|
10071
|
+
const summary = await summarize2(capTmtSummaryInputs(inputs, "week"), "week");
|
|
10072
10072
|
const sortedCreated = entries.map((e) => e.created).sort();
|
|
10073
10073
|
const fm = {
|
|
10074
10074
|
level: "week",
|
|
@@ -10090,7 +10090,7 @@ var TmtBuilder = class {
|
|
|
10090
10090
|
* Reads up to 4 recent week nodes and synthesizes a persona-level narrative.
|
|
10091
10091
|
* Fail-open: skips silently if no week nodes exist or summarize fails.
|
|
10092
10092
|
*/
|
|
10093
|
-
async buildPersonaNode(
|
|
10093
|
+
async buildPersonaNode(summarize2) {
|
|
10094
10094
|
try {
|
|
10095
10095
|
const dir = tmtDir(this.baseDir);
|
|
10096
10096
|
let allFiles = [];
|
|
@@ -10138,7 +10138,7 @@ var TmtBuilder = class {
|
|
|
10138
10138
|
}
|
|
10139
10139
|
}
|
|
10140
10140
|
if (!shouldBuild) return;
|
|
10141
|
-
const summary = await
|
|
10141
|
+
const summary = await summarize2(capTmtSummaryInputs(weekSummaries, "persona"), "persona");
|
|
10142
10142
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10143
10143
|
const fm = {
|
|
10144
10144
|
level: "persona",
|
|
@@ -10255,12 +10255,12 @@ function computeHeat(memory, now, signals) {
|
|
|
10255
10255
|
if (frontmatter.status === "archived") return 0;
|
|
10256
10256
|
const nowMs = now.getTime();
|
|
10257
10257
|
const confidence = confidenceTierWeight(frontmatter);
|
|
10258
|
-
const
|
|
10258
|
+
const access4 = accessWeight(frontmatter.accessCount);
|
|
10259
10259
|
const recency = recencyWeight(frontmatter, nowMs);
|
|
10260
10260
|
const importance = clamp01(frontmatter.importance?.score ?? 0.5);
|
|
10261
10261
|
const feedback = feedbackWeight(signals);
|
|
10262
10262
|
const disputedPenalty = frontmatter.verificationState === "disputed" ? 0.2 : 0;
|
|
10263
|
-
const score = confidence * 0.25 +
|
|
10263
|
+
const score = confidence * 0.25 + access4 * 0.3 + recency * 0.2 + importance * 0.15 + feedback * 0.1 - disputedPenalty;
|
|
10264
10264
|
return clamp01(score);
|
|
10265
10265
|
}
|
|
10266
10266
|
function computeDecay(memory, now, signals) {
|
|
@@ -17772,8 +17772,8 @@ mistakes: ${res.mistakesCount} patterns`
|
|
|
17772
17772
|
}
|
|
17773
17773
|
|
|
17774
17774
|
// src/cli.ts
|
|
17775
|
-
import
|
|
17776
|
-
import { access as
|
|
17775
|
+
import path42 from "path";
|
|
17776
|
+
import { access as access3, readFile as readFile30, readdir as readdir18, unlink as unlink5 } from "fs/promises";
|
|
17777
17777
|
|
|
17778
17778
|
// src/transfer/export-json.ts
|
|
17779
17779
|
import path28 from "path";
|
|
@@ -18652,8 +18652,8 @@ function gatherCandidates(input, warnings) {
|
|
|
18652
18652
|
const record = rec;
|
|
18653
18653
|
const content = typeof record.content === "string" ? record.content : null;
|
|
18654
18654
|
if (!content) continue;
|
|
18655
|
-
const
|
|
18656
|
-
if (!
|
|
18655
|
+
const path44 = typeof record.path === "string" ? record.path : "";
|
|
18656
|
+
if (!path44.startsWith("transcripts/") && !path44.includes("/transcripts/")) continue;
|
|
18657
18657
|
rows.push(...parseJsonl(content, warnings));
|
|
18658
18658
|
}
|
|
18659
18659
|
return rows;
|
|
@@ -19537,6 +19537,377 @@ function toEncodedHref(pathname) {
|
|
|
19537
19537
|
return pathname.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
19538
19538
|
}
|
|
19539
19539
|
|
|
19540
|
+
// src/compat/checks.ts
|
|
19541
|
+
import { access as access2, readFile as readFile29 } from "fs/promises";
|
|
19542
|
+
import path41 from "path";
|
|
19543
|
+
import { spawn as spawn3 } from "child_process";
|
|
19544
|
+
var REQUIRED_HOOKS = ["before_agent_start", "agent_end"];
|
|
19545
|
+
function isSafeCommandToken(command) {
|
|
19546
|
+
return /^[a-zA-Z0-9._-]+$/.test(command);
|
|
19547
|
+
}
|
|
19548
|
+
var defaultRunner = {
|
|
19549
|
+
async commandExists(command) {
|
|
19550
|
+
if (!isSafeCommandToken(command)) return false;
|
|
19551
|
+
const binary = process.platform === "win32" ? "where" : "which";
|
|
19552
|
+
const args = [command];
|
|
19553
|
+
return new Promise((resolve) => {
|
|
19554
|
+
const child = spawn3(binary, args, { stdio: "ignore" });
|
|
19555
|
+
child.on("error", () => resolve(false));
|
|
19556
|
+
child.on("close", (code) => resolve(code === 0));
|
|
19557
|
+
});
|
|
19558
|
+
}
|
|
19559
|
+
};
|
|
19560
|
+
function summarize(checks) {
|
|
19561
|
+
const out = { ok: 0, warn: 0, error: 0 };
|
|
19562
|
+
for (const check of checks) {
|
|
19563
|
+
out[check.level] += 1;
|
|
19564
|
+
}
|
|
19565
|
+
return out;
|
|
19566
|
+
}
|
|
19567
|
+
function stripCommentsAndStrings(source) {
|
|
19568
|
+
const out = [];
|
|
19569
|
+
let i = 0;
|
|
19570
|
+
while (i < source.length) {
|
|
19571
|
+
const ch = source[i];
|
|
19572
|
+
const next = source[i + 1];
|
|
19573
|
+
if (ch === "/" && next === "/") {
|
|
19574
|
+
out.push(" ", " ");
|
|
19575
|
+
i += 2;
|
|
19576
|
+
while (i < source.length && source[i] !== "\n") {
|
|
19577
|
+
out.push(" ");
|
|
19578
|
+
i += 1;
|
|
19579
|
+
}
|
|
19580
|
+
continue;
|
|
19581
|
+
}
|
|
19582
|
+
if (ch === "/" && next === "*") {
|
|
19583
|
+
out.push(" ", " ");
|
|
19584
|
+
i += 2;
|
|
19585
|
+
while (i < source.length) {
|
|
19586
|
+
const c = source[i];
|
|
19587
|
+
const n = source[i + 1];
|
|
19588
|
+
out.push(c === "\n" ? "\n" : " ");
|
|
19589
|
+
i += 1;
|
|
19590
|
+
if (c === "*" && n === "/") {
|
|
19591
|
+
out.push(" ");
|
|
19592
|
+
i += 1;
|
|
19593
|
+
break;
|
|
19594
|
+
}
|
|
19595
|
+
}
|
|
19596
|
+
continue;
|
|
19597
|
+
}
|
|
19598
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
19599
|
+
const quote = ch;
|
|
19600
|
+
out.push(" ");
|
|
19601
|
+
i += 1;
|
|
19602
|
+
while (i < source.length) {
|
|
19603
|
+
const c = source[i];
|
|
19604
|
+
if (c === "\\") {
|
|
19605
|
+
out.push(" ");
|
|
19606
|
+
i += 1;
|
|
19607
|
+
if (i < source.length) {
|
|
19608
|
+
out.push(source[i] === "\n" ? "\n" : " ");
|
|
19609
|
+
i += 1;
|
|
19610
|
+
}
|
|
19611
|
+
continue;
|
|
19612
|
+
}
|
|
19613
|
+
out.push(c === "\n" ? "\n" : " ");
|
|
19614
|
+
i += 1;
|
|
19615
|
+
if (c === quote) break;
|
|
19616
|
+
}
|
|
19617
|
+
continue;
|
|
19618
|
+
}
|
|
19619
|
+
out.push(ch);
|
|
19620
|
+
i += 1;
|
|
19621
|
+
}
|
|
19622
|
+
return out.join("");
|
|
19623
|
+
}
|
|
19624
|
+
function parseHookRegistrations(source) {
|
|
19625
|
+
const hooks = /* @__PURE__ */ new Set();
|
|
19626
|
+
let i = 0;
|
|
19627
|
+
while (i < source.length) {
|
|
19628
|
+
const ch = source[i];
|
|
19629
|
+
const next = source[i + 1];
|
|
19630
|
+
if (ch === "/" && next === "/") {
|
|
19631
|
+
i += 2;
|
|
19632
|
+
while (i < source.length && source[i] !== "\n") i += 1;
|
|
19633
|
+
continue;
|
|
19634
|
+
}
|
|
19635
|
+
if (ch === "/" && next === "*") {
|
|
19636
|
+
i += 2;
|
|
19637
|
+
while (i + 1 < source.length) {
|
|
19638
|
+
if (source[i] === "*" && source[i + 1] === "/") {
|
|
19639
|
+
i += 2;
|
|
19640
|
+
break;
|
|
19641
|
+
}
|
|
19642
|
+
i += 1;
|
|
19643
|
+
}
|
|
19644
|
+
continue;
|
|
19645
|
+
}
|
|
19646
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
19647
|
+
const quote = ch;
|
|
19648
|
+
i += 1;
|
|
19649
|
+
while (i < source.length) {
|
|
19650
|
+
if (source[i] === "\\") {
|
|
19651
|
+
i += 2;
|
|
19652
|
+
continue;
|
|
19653
|
+
}
|
|
19654
|
+
if (source[i] === quote) {
|
|
19655
|
+
i += 1;
|
|
19656
|
+
break;
|
|
19657
|
+
}
|
|
19658
|
+
i += 1;
|
|
19659
|
+
}
|
|
19660
|
+
continue;
|
|
19661
|
+
}
|
|
19662
|
+
if (source.startsWith("api.on", i) && (i === 0 || !/[a-zA-Z0-9_$\.]/.test(source[i - 1]))) {
|
|
19663
|
+
let j = i + "api.on".length;
|
|
19664
|
+
while (j < source.length && /\s/.test(source[j])) j += 1;
|
|
19665
|
+
if (source[j] !== "(") {
|
|
19666
|
+
i += 1;
|
|
19667
|
+
continue;
|
|
19668
|
+
}
|
|
19669
|
+
j += 1;
|
|
19670
|
+
while (j < source.length && /\s/.test(source[j])) j += 1;
|
|
19671
|
+
const quote = source[j];
|
|
19672
|
+
if (quote !== '"' && quote !== "'") {
|
|
19673
|
+
i += 1;
|
|
19674
|
+
continue;
|
|
19675
|
+
}
|
|
19676
|
+
j += 1;
|
|
19677
|
+
const start = j;
|
|
19678
|
+
while (j < source.length && source[j] !== quote) {
|
|
19679
|
+
if (source[j] === "\\") {
|
|
19680
|
+
j += 2;
|
|
19681
|
+
} else {
|
|
19682
|
+
j += 1;
|
|
19683
|
+
}
|
|
19684
|
+
}
|
|
19685
|
+
if (j < source.length) {
|
|
19686
|
+
const hook = source.slice(start, j);
|
|
19687
|
+
if (/^[a-z_]+$/.test(hook)) hooks.add(hook);
|
|
19688
|
+
i = j + 1;
|
|
19689
|
+
continue;
|
|
19690
|
+
}
|
|
19691
|
+
}
|
|
19692
|
+
i += 1;
|
|
19693
|
+
}
|
|
19694
|
+
return hooks;
|
|
19695
|
+
}
|
|
19696
|
+
function hasServiceStartRegistration(source) {
|
|
19697
|
+
return /api\.registerService\s*\(\s*\{[\s\S]*?\bstart\s*(?::|\()/m.test(source);
|
|
19698
|
+
}
|
|
19699
|
+
function hasCliRegistration(source) {
|
|
19700
|
+
return /registerCli\s*\([^)]*\borchestrator\b[^)]*\)/m.test(source);
|
|
19701
|
+
}
|
|
19702
|
+
function parseNodeMinVersion(raw) {
|
|
19703
|
+
if (!raw) return null;
|
|
19704
|
+
const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
19705
|
+
if (!match) return null;
|
|
19706
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
19707
|
+
}
|
|
19708
|
+
function parseCurrentNodeVersion(raw) {
|
|
19709
|
+
const normalized = raw.startsWith("v") ? raw.slice(1) : raw;
|
|
19710
|
+
const match = normalized.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
19711
|
+
if (!match) return null;
|
|
19712
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
19713
|
+
}
|
|
19714
|
+
function compareVersions(a, b) {
|
|
19715
|
+
for (let i = 0; i < 3; i += 1) {
|
|
19716
|
+
if (a[i] > b[i]) return 1;
|
|
19717
|
+
if (a[i] < b[i]) return -1;
|
|
19718
|
+
}
|
|
19719
|
+
return 0;
|
|
19720
|
+
}
|
|
19721
|
+
async function runCompatChecks(options) {
|
|
19722
|
+
const checks = [];
|
|
19723
|
+
const runner = options.runner ?? defaultRunner;
|
|
19724
|
+
const pluginJsonPath = path41.join(options.repoRoot, "openclaw.plugin.json");
|
|
19725
|
+
const packageJsonPath = path41.join(options.repoRoot, "package.json");
|
|
19726
|
+
const indexPath = path41.join(options.repoRoot, "src", "index.ts");
|
|
19727
|
+
let pluginRaw = "";
|
|
19728
|
+
let pluginManifestPresent = false;
|
|
19729
|
+
try {
|
|
19730
|
+
pluginRaw = await readFile29(pluginJsonPath, "utf-8");
|
|
19731
|
+
pluginManifestPresent = true;
|
|
19732
|
+
checks.push({
|
|
19733
|
+
id: "plugin-manifest-present",
|
|
19734
|
+
title: "Plugin manifest present",
|
|
19735
|
+
level: "ok",
|
|
19736
|
+
message: "Found openclaw.plugin.json"
|
|
19737
|
+
});
|
|
19738
|
+
} catch {
|
|
19739
|
+
checks.push({
|
|
19740
|
+
id: "plugin-manifest-present",
|
|
19741
|
+
title: "Plugin manifest present",
|
|
19742
|
+
level: "error",
|
|
19743
|
+
message: "openclaw.plugin.json is missing",
|
|
19744
|
+
remediation: "Restore openclaw.plugin.json at repo root with plugin metadata."
|
|
19745
|
+
});
|
|
19746
|
+
}
|
|
19747
|
+
if (pluginManifestPresent) {
|
|
19748
|
+
try {
|
|
19749
|
+
const plugin = JSON.parse(pluginRaw);
|
|
19750
|
+
if (plugin.id === "openclaw-engram" && plugin.kind === "memory") {
|
|
19751
|
+
checks.push({
|
|
19752
|
+
id: "plugin-manifest-shape",
|
|
19753
|
+
title: "Plugin manifest ID and kind",
|
|
19754
|
+
level: "ok",
|
|
19755
|
+
message: "Plugin manifest id/kind match expected values."
|
|
19756
|
+
});
|
|
19757
|
+
} else {
|
|
19758
|
+
checks.push({
|
|
19759
|
+
id: "plugin-manifest-shape",
|
|
19760
|
+
title: "Plugin manifest ID and kind",
|
|
19761
|
+
level: "error",
|
|
19762
|
+
message: `Unexpected manifest values (id=${String(plugin.id)}, kind=${String(plugin.kind)})`,
|
|
19763
|
+
remediation: "Set manifest id=openclaw-engram and kind=memory."
|
|
19764
|
+
});
|
|
19765
|
+
}
|
|
19766
|
+
} catch {
|
|
19767
|
+
checks.push({
|
|
19768
|
+
id: "plugin-manifest-shape",
|
|
19769
|
+
title: "Plugin manifest ID and kind",
|
|
19770
|
+
level: "error",
|
|
19771
|
+
message: "openclaw.plugin.json is not valid JSON",
|
|
19772
|
+
remediation: "Fix JSON syntax in openclaw.plugin.json."
|
|
19773
|
+
});
|
|
19774
|
+
}
|
|
19775
|
+
}
|
|
19776
|
+
let packageRaw = "";
|
|
19777
|
+
let packageJsonPresent = false;
|
|
19778
|
+
try {
|
|
19779
|
+
packageRaw = await readFile29(packageJsonPath, "utf-8");
|
|
19780
|
+
packageJsonPresent = true;
|
|
19781
|
+
} catch {
|
|
19782
|
+
checks.push({
|
|
19783
|
+
id: "package-json-present",
|
|
19784
|
+
title: "package.json present",
|
|
19785
|
+
level: "error",
|
|
19786
|
+
message: "package.json is missing",
|
|
19787
|
+
remediation: "Restore package.json at repo root."
|
|
19788
|
+
});
|
|
19789
|
+
}
|
|
19790
|
+
if (packageJsonPresent) {
|
|
19791
|
+
try {
|
|
19792
|
+
const pkg = JSON.parse(packageRaw);
|
|
19793
|
+
const pluginPathOk = pkg.openclaw?.plugin === "./openclaw.plugin.json";
|
|
19794
|
+
const extOk = Array.isArray(pkg.openclaw?.extensions) && pkg.openclaw?.extensions.includes("./dist/index.js");
|
|
19795
|
+
if (pluginPathOk && extOk) {
|
|
19796
|
+
checks.push({
|
|
19797
|
+
id: "package-openclaw-exports",
|
|
19798
|
+
title: "package.json OpenClaw export wiring",
|
|
19799
|
+
level: "ok",
|
|
19800
|
+
message: "package.json openclaw.plugin/extensions wiring looks valid."
|
|
19801
|
+
});
|
|
19802
|
+
} else {
|
|
19803
|
+
checks.push({
|
|
19804
|
+
id: "package-openclaw-exports",
|
|
19805
|
+
title: "package.json OpenClaw export wiring",
|
|
19806
|
+
level: "error",
|
|
19807
|
+
message: "package.json openclaw plugin/extension wiring is missing or invalid.",
|
|
19808
|
+
remediation: "Set openclaw.plugin to ./openclaw.plugin.json and include ./dist/index.js in openclaw.extensions."
|
|
19809
|
+
});
|
|
19810
|
+
}
|
|
19811
|
+
const minVersion = parseNodeMinVersion(pkg.engines?.node);
|
|
19812
|
+
const currentVersion = parseCurrentNodeVersion(process.version);
|
|
19813
|
+
if (!minVersion || !currentVersion) {
|
|
19814
|
+
checks.push({
|
|
19815
|
+
id: "node-version-compat",
|
|
19816
|
+
title: "Node runtime compatibility",
|
|
19817
|
+
level: "warn",
|
|
19818
|
+
message: "Unable to parse node engine/current version.",
|
|
19819
|
+
remediation: "Confirm Node version meets package.json engines.node requirement.",
|
|
19820
|
+
metadata: { enginesNode: pkg.engines?.node, currentNode: process.version }
|
|
19821
|
+
});
|
|
19822
|
+
} else if (compareVersions(currentVersion, minVersion) >= 0) {
|
|
19823
|
+
checks.push({
|
|
19824
|
+
id: "node-version-compat",
|
|
19825
|
+
title: "Node runtime compatibility",
|
|
19826
|
+
level: "ok",
|
|
19827
|
+
message: `Current Node ${process.version} satisfies engines requirement ${pkg.engines?.node}.`
|
|
19828
|
+
});
|
|
19829
|
+
} else {
|
|
19830
|
+
checks.push({
|
|
19831
|
+
id: "node-version-compat",
|
|
19832
|
+
title: "Node runtime compatibility",
|
|
19833
|
+
level: "error",
|
|
19834
|
+
message: `Current Node ${process.version} is below required ${pkg.engines?.node}.`,
|
|
19835
|
+
remediation: "Upgrade Node runtime to meet package.json engines.node minimum."
|
|
19836
|
+
});
|
|
19837
|
+
}
|
|
19838
|
+
} catch {
|
|
19839
|
+
checks.push({
|
|
19840
|
+
id: "package-json-parse",
|
|
19841
|
+
title: "package.json parse",
|
|
19842
|
+
level: "error",
|
|
19843
|
+
message: "package.json is not valid JSON",
|
|
19844
|
+
remediation: "Fix JSON syntax in package.json."
|
|
19845
|
+
});
|
|
19846
|
+
}
|
|
19847
|
+
}
|
|
19848
|
+
try {
|
|
19849
|
+
await access2(indexPath);
|
|
19850
|
+
const indexRaw = await readFile29(indexPath, "utf-8");
|
|
19851
|
+
const structuralSource = stripCommentsAndStrings(indexRaw);
|
|
19852
|
+
const hooks = parseHookRegistrations(indexRaw);
|
|
19853
|
+
const missingHooks = REQUIRED_HOOKS.filter((hook) => !hooks.has(hook));
|
|
19854
|
+
const hasGatewayStartHook = hooks.has("gateway_start");
|
|
19855
|
+
const hasServiceStart = hasServiceStartRegistration(structuralSource);
|
|
19856
|
+
if (missingHooks.length === 0 && (hasGatewayStartHook || hasServiceStart)) {
|
|
19857
|
+
checks.push({
|
|
19858
|
+
id: "hook-registration-core",
|
|
19859
|
+
title: "Core hook registration",
|
|
19860
|
+
level: "ok",
|
|
19861
|
+
message: "Core recall/extraction hooks and startup wiring are registered in src/index.ts."
|
|
19862
|
+
});
|
|
19863
|
+
} else {
|
|
19864
|
+
const missingParts = [];
|
|
19865
|
+
if (missingHooks.length > 0) {
|
|
19866
|
+
missingParts.push(`hooks: ${missingHooks.join(", ")}`);
|
|
19867
|
+
}
|
|
19868
|
+
if (!hasGatewayStartHook && !hasServiceStart) {
|
|
19869
|
+
missingParts.push("startup wiring: gateway_start hook or api.registerService({ start })");
|
|
19870
|
+
}
|
|
19871
|
+
checks.push({
|
|
19872
|
+
id: "hook-registration-core",
|
|
19873
|
+
title: "Core hook registration",
|
|
19874
|
+
level: "error",
|
|
19875
|
+
message: `Missing expected registration(s): ${missingParts.join("; ")}`,
|
|
19876
|
+
remediation: "Ensure src/index.ts registers before_agent_start and agent_end, plus either gateway_start or api.registerService({ start })."
|
|
19877
|
+
});
|
|
19878
|
+
}
|
|
19879
|
+
const cliWired = hasCliRegistration(structuralSource);
|
|
19880
|
+
checks.push({
|
|
19881
|
+
id: "cli-registration",
|
|
19882
|
+
title: "CLI registration wiring",
|
|
19883
|
+
level: cliWired ? "ok" : "warn",
|
|
19884
|
+
message: cliWired ? "CLI registration is wired in plugin bootstrap." : "CLI registration call not found in src/index.ts.",
|
|
19885
|
+
remediation: cliWired ? void 0 : "Call registerCli(api, orchestrator) during plugin registration."
|
|
19886
|
+
});
|
|
19887
|
+
} catch {
|
|
19888
|
+
checks.push({
|
|
19889
|
+
id: "hook-registration-core",
|
|
19890
|
+
title: "Core hook registration",
|
|
19891
|
+
level: "error",
|
|
19892
|
+
message: "src/index.ts is missing; cannot validate hook wiring.",
|
|
19893
|
+
remediation: "Restore src/index.ts and register required hooks."
|
|
19894
|
+
});
|
|
19895
|
+
}
|
|
19896
|
+
const qmdAvailable = await runner.commandExists("qmd");
|
|
19897
|
+
checks.push({
|
|
19898
|
+
id: "qmd-binary-availability",
|
|
19899
|
+
title: "QMD binary availability",
|
|
19900
|
+
level: qmdAvailable ? "ok" : "warn",
|
|
19901
|
+
message: qmdAvailable ? "qmd binary is available in PATH." : "qmd binary is not available in PATH.",
|
|
19902
|
+
remediation: qmdAvailable ? void 0 : "Install qmd or configure qmdPath in plugin config."
|
|
19903
|
+
});
|
|
19904
|
+
return {
|
|
19905
|
+
generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
19906
|
+
checks,
|
|
19907
|
+
summary: summarize(checks)
|
|
19908
|
+
};
|
|
19909
|
+
}
|
|
19910
|
+
|
|
19540
19911
|
// src/cli.ts
|
|
19541
19912
|
function rankCandidateForKeep(a, b) {
|
|
19542
19913
|
const aConfidence = typeof a.frontmatter.confidence === "number" ? a.frontmatter.confidence : 0;
|
|
@@ -19738,6 +20109,16 @@ async function runWebDavStopCliCommand() {
|
|
|
19738
20109
|
return { stopped: true };
|
|
19739
20110
|
});
|
|
19740
20111
|
}
|
|
20112
|
+
async function runCompatCliCommand(options = {}) {
|
|
20113
|
+
const report = await runCompatChecks({
|
|
20114
|
+
repoRoot: options.repoRoot ?? process.cwd(),
|
|
20115
|
+
runner: options.runner,
|
|
20116
|
+
now: options.now
|
|
20117
|
+
});
|
|
20118
|
+
const hasWarnOrError = report.summary.warn > 0 || report.summary.error > 0;
|
|
20119
|
+
const exitCode = options.strict === true && hasWarnOrError ? 1 : 0;
|
|
20120
|
+
return { report, exitCode };
|
|
20121
|
+
}
|
|
19741
20122
|
async function runRouteCliCommand(options) {
|
|
19742
20123
|
const store = new RoutingRulesStore(options.memoryDir, options.stateFile);
|
|
19743
20124
|
if (options.action === "list") {
|
|
@@ -19918,7 +20299,7 @@ async function withTimeout(promise, timeoutMs, timeoutMessage) {
|
|
|
19918
20299
|
}
|
|
19919
20300
|
async function runReplayCliCommand(orchestrator, options) {
|
|
19920
20301
|
const extractionIdleTimeoutMs = Number.isFinite(options.extractionIdleTimeoutMs) ? Math.max(1e3, Math.floor(options.extractionIdleTimeoutMs)) : 15 * 6e4;
|
|
19921
|
-
const inputRaw = await
|
|
20302
|
+
const inputRaw = await readFile30(options.inputPath, "utf-8");
|
|
19922
20303
|
const registry = buildReplayNormalizerRegistry([
|
|
19923
20304
|
openclawReplayNormalizer,
|
|
19924
20305
|
claudeReplayNormalizer,
|
|
@@ -19983,7 +20364,7 @@ async function runReplayCliCommand(orchestrator, options) {
|
|
|
19983
20364
|
async function getPluginVersion() {
|
|
19984
20365
|
try {
|
|
19985
20366
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
19986
|
-
const raw = await
|
|
20367
|
+
const raw = await readFile30(pkgPath, "utf-8");
|
|
19987
20368
|
const parsed = JSON.parse(raw);
|
|
19988
20369
|
return parsed.version ?? "unknown";
|
|
19989
20370
|
} catch {
|
|
@@ -19992,7 +20373,7 @@ async function getPluginVersion() {
|
|
|
19992
20373
|
}
|
|
19993
20374
|
async function exists2(p) {
|
|
19994
20375
|
try {
|
|
19995
|
-
await
|
|
20376
|
+
await access3(p);
|
|
19996
20377
|
return true;
|
|
19997
20378
|
} catch {
|
|
19998
20379
|
return false;
|
|
@@ -20002,14 +20383,14 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
|
|
|
20002
20383
|
const ns = (namespace ?? "").trim();
|
|
20003
20384
|
if (!ns) return orchestrator.config.memoryDir;
|
|
20004
20385
|
if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
|
|
20005
|
-
const candidate =
|
|
20386
|
+
const candidate = path42.join(orchestrator.config.memoryDir, "namespaces", ns);
|
|
20006
20387
|
if (ns === orchestrator.config.defaultNamespace) {
|
|
20007
20388
|
return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
|
|
20008
20389
|
}
|
|
20009
20390
|
return candidate;
|
|
20010
20391
|
}
|
|
20011
20392
|
async function readAllMemoryFiles(memoryDir) {
|
|
20012
|
-
const roots = [
|
|
20393
|
+
const roots = [path42.join(memoryDir, "facts"), path42.join(memoryDir, "corrections")];
|
|
20013
20394
|
const out = [];
|
|
20014
20395
|
const walk = async (dir) => {
|
|
20015
20396
|
let entries;
|
|
@@ -20020,14 +20401,14 @@ async function readAllMemoryFiles(memoryDir) {
|
|
|
20020
20401
|
}
|
|
20021
20402
|
for (const entry of entries) {
|
|
20022
20403
|
const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
|
|
20023
|
-
const fullPath =
|
|
20404
|
+
const fullPath = path42.join(dir, entryName);
|
|
20024
20405
|
if (entry.isDirectory()) {
|
|
20025
20406
|
await walk(fullPath);
|
|
20026
20407
|
continue;
|
|
20027
20408
|
}
|
|
20028
20409
|
if (!entry.isFile() || !entryName.endsWith(".md")) continue;
|
|
20029
20410
|
try {
|
|
20030
|
-
const raw = await
|
|
20411
|
+
const raw = await readFile30(fullPath, "utf-8");
|
|
20031
20412
|
const parsed = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
20032
20413
|
if (!parsed) continue;
|
|
20033
20414
|
const fmRaw = parsed[1];
|
|
@@ -20210,6 +20591,30 @@ function registerCli(api, orchestrator) {
|
|
|
20210
20591
|
});
|
|
20211
20592
|
console.log("OK");
|
|
20212
20593
|
});
|
|
20594
|
+
cmd.command("compat").description("Run local compatibility diagnostics for Engram plugin wiring").option("--json", "Emit JSON output for automation").option("--strict", "Exit non-zero when warnings or errors are present").option("--repo-root <path>", "Repository root to inspect", process.cwd()).action(async (...args) => {
|
|
20595
|
+
const options = args[0] ?? {};
|
|
20596
|
+
const strict = options.strict === true;
|
|
20597
|
+
const jsonOutput = options.json === true;
|
|
20598
|
+
const repoRoot = typeof options.repoRoot === "string" && options.repoRoot.trim().length > 0 ? options.repoRoot.trim() : process.cwd();
|
|
20599
|
+
const result = await runCompatCliCommand({ repoRoot, strict });
|
|
20600
|
+
if (jsonOutput) {
|
|
20601
|
+
console.log(JSON.stringify({ strict, exitCode: result.exitCode, report: result.report }, null, 2));
|
|
20602
|
+
} else {
|
|
20603
|
+
console.log("=== Engram Compatibility Report ===");
|
|
20604
|
+
for (const check of result.report.checks) {
|
|
20605
|
+
console.log(`- [${check.level.toUpperCase()}] ${check.title}: ${check.message}`);
|
|
20606
|
+
if (check.remediation) {
|
|
20607
|
+
console.log(` remediation: ${check.remediation}`);
|
|
20608
|
+
}
|
|
20609
|
+
}
|
|
20610
|
+
console.log(
|
|
20611
|
+
`Summary: ok=${result.report.summary.ok} warn=${result.report.summary.warn} error=${result.report.summary.error}`
|
|
20612
|
+
);
|
|
20613
|
+
}
|
|
20614
|
+
if (result.exitCode !== 0) {
|
|
20615
|
+
process.exitCode = result.exitCode;
|
|
20616
|
+
}
|
|
20617
|
+
});
|
|
20213
20618
|
cmd.command("replay").description("Import replay transcripts from external exports").option("--source <source>", "Replay source: openclaw|claude|chatgpt").option("--input <path>", "Path to replay export file").option("--from <iso>", "Inclusive lower bound timestamp (ISO UTC)").option("--to <iso>", "Inclusive upper bound timestamp (ISO UTC)").option("--dry-run", "Parse and validate only; do not enqueue extraction").option("--start-offset <n>", "Start replay at offset", "0").option("--max-turns <n>", "Maximum turns to process", "0").option("--batch-size <n>", "Replay ingestion batch size", "100").option("--default-session-key <key>", "Fallback session key when source session identifiers are missing").option("--strict", "Fail on invalid source rows").option("--run-consolidation", "Run consolidation after replay ingestion completes").option("--idle-timeout-ms <n>", "Extraction idle timeout per replay batch/final drain in milliseconds", "900000").action(async (...args) => {
|
|
20214
20619
|
const options = args[0] ?? {};
|
|
20215
20620
|
const sourceRaw = typeof options.source === "string" ? options.source.trim().toLowerCase() : "";
|
|
@@ -20768,7 +21173,7 @@ function registerCli(api, orchestrator) {
|
|
|
20768
21173
|
}
|
|
20769
21174
|
});
|
|
20770
21175
|
cmd.command("identity").description("Show agent identity reflections").action(async () => {
|
|
20771
|
-
const workspaceDir =
|
|
21176
|
+
const workspaceDir = path42.join(process.env.HOME ?? "~", ".openclaw", "workspace");
|
|
20772
21177
|
const identity = await orchestrator.storage.readIdentity(workspaceDir);
|
|
20773
21178
|
if (!identity) {
|
|
20774
21179
|
console.log("No identity file found.");
|
|
@@ -20991,8 +21396,8 @@ function registerCli(api, orchestrator) {
|
|
|
20991
21396
|
const options = args[0] ?? {};
|
|
20992
21397
|
const threadId = options.thread;
|
|
20993
21398
|
const top = parseInt(options.top ?? "10", 10);
|
|
20994
|
-
const memoryDir =
|
|
20995
|
-
const threading = new ThreadingManager(
|
|
21399
|
+
const memoryDir = path42.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
|
|
21400
|
+
const threading = new ThreadingManager(path42.join(memoryDir, "threads"));
|
|
20996
21401
|
if (threadId) {
|
|
20997
21402
|
const thread = await threading.loadThread(threadId);
|
|
20998
21403
|
if (!thread) {
|
|
@@ -21165,16 +21570,16 @@ function parseDuration(duration) {
|
|
|
21165
21570
|
}
|
|
21166
21571
|
|
|
21167
21572
|
// src/index.ts
|
|
21168
|
-
import { readFile as
|
|
21573
|
+
import { readFile as readFile31, writeFile as writeFile26 } from "fs/promises";
|
|
21169
21574
|
import { readFileSync as readFileSync4 } from "fs";
|
|
21170
|
-
import
|
|
21575
|
+
import path43 from "path";
|
|
21171
21576
|
import os5 from "os";
|
|
21172
21577
|
var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
|
|
21173
21578
|
function loadPluginConfigFromFile() {
|
|
21174
21579
|
try {
|
|
21175
21580
|
const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
|
|
21176
21581
|
const homeDir = process.env.HOME ?? os5.homedir();
|
|
21177
|
-
const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath :
|
|
21582
|
+
const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path43.join(homeDir, ".openclaw", "openclaw.json");
|
|
21178
21583
|
const content = readFileSync4(configPath, "utf-8");
|
|
21179
21584
|
const config = JSON.parse(content);
|
|
21180
21585
|
const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
|
|
@@ -21382,11 +21787,11 @@ Use this context naturally when relevant. Never quote or expose this memory cont
|
|
|
21382
21787
|
);
|
|
21383
21788
|
async function ensureHourlySummaryCron(api2) {
|
|
21384
21789
|
const jobId = "engram-hourly-summary";
|
|
21385
|
-
const cronFilePath =
|
|
21790
|
+
const cronFilePath = path43.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
|
|
21386
21791
|
try {
|
|
21387
21792
|
let jobsData = { version: 1, jobs: [] };
|
|
21388
21793
|
try {
|
|
21389
|
-
const content = await
|
|
21794
|
+
const content = await readFile31(cronFilePath, "utf-8");
|
|
21390
21795
|
jobsData = JSON.parse(content);
|
|
21391
21796
|
} catch {
|
|
21392
21797
|
}
|