@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 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, summarize) {
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, summarize);
9901
- await this.buildDayNodes(memories, summarize);
9902
- await this.buildWeekNodes(memories, summarize);
9903
- await this.buildPersonaNode(summarize);
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, summarize) {
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 summarize(capTmtSummaryInputs(entries.map((e) => e.content), "hour"), "hour");
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, summarize) {
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 summarize(capTmtSummaryInputs(inputs, "day"), "day");
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, summarize) {
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 summarize(capTmtSummaryInputs(inputs, "week"), "week");
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(summarize) {
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 summarize(capTmtSummaryInputs(weekSummaries, "persona"), "persona");
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 access3 = accessWeight(frontmatter.accessCount);
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 + access3 * 0.3 + recency * 0.2 + importance * 0.15 + feedback * 0.1 - disputedPenalty;
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 path41 from "path";
17776
- import { access as access2, readFile as readFile29, readdir as readdir18, unlink as unlink5 } from "fs/promises";
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 path43 = typeof record.path === "string" ? record.path : "";
18656
- if (!path43.startsWith("transcripts/") && !path43.includes("/transcripts/")) continue;
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 readFile29(options.inputPath, "utf-8");
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 readFile29(pkgPath, "utf-8");
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 access2(p);
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 = path41.join(orchestrator.config.memoryDir, "namespaces", ns);
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 = [path41.join(memoryDir, "facts"), path41.join(memoryDir, "corrections")];
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 = path41.join(dir, entryName);
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 readFile29(fullPath, "utf-8");
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 = path41.join(process.env.HOME ?? "~", ".openclaw", "workspace");
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 = path41.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
20995
- const threading = new ThreadingManager(path41.join(memoryDir, "threads"));
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 readFile30, writeFile as writeFile26 } from "fs/promises";
21573
+ import { readFile as readFile31, writeFile as writeFile26 } from "fs/promises";
21169
21574
  import { readFileSync as readFileSync4 } from "fs";
21170
- import path42 from "path";
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 : path42.join(homeDir, ".openclaw", "openclaw.json");
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 = path42.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
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 readFile30(cronFilePath, "utf-8");
21794
+ const content = await readFile31(cronFilePath, "utf-8");
21390
21795
  jobsData = JSON.parse(content);
21391
21796
  } catch {
21392
21797
  }