@joshuaswarren/openclaw-engram 9.0.34 → 9.0.36

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/README.md CHANGED
@@ -37,6 +37,8 @@ AI agents forget everything between conversations. Engram fixes that.
37
37
  - **Trust-zone promotion path** — Engram can now, when `trustZonesEnabled` and `quarantinePromotionEnabled` are enabled, persist typed quarantine, working, and trusted records, plan explicit promotions, block direct `quarantine -> trusted` jumps, and require anchored provenance before promoting risky working records into `trusted`.
38
38
  - **Trust-zone recall** — Engram can now, when `trustZoneRecallEnabled` is enabled, inject prompt-relevant `working` and `trusted` trust-zone records into recall context as a separate `Trust Zones` section while keeping `quarantine` material out of recall by default.
39
39
  - **Poisoning-defense corroboration** — Engram can now, when `memoryPoisoningDefenseEnabled` is enabled, score trust-zone provenance deterministically and require independent non-quarantine corroboration before risky `working -> trusted` promotions succeed.
40
+ - **Red-team benchmark packs** — Engram's eval harness can now validate and count typed `memory-red-team` benchmark packs so poisoning-defense regression suites stay explicit and reviewable instead of hiding inside generic benchmark metadata.
41
+ - **Abstraction-node foundation** — Engram can now, when `harmonicRetrievalEnabled` is enabled, persist typed abstraction nodes for later harmonic retrieval slices, with `abstractionAnchorsEnabled` reserved for the future cue-anchor layer.
40
42
  - **Zero-config start** — Install, add an API key, restart. Engram works out of the box with sensible defaults and progressively unlocks advanced features as you enable them.
41
43
 
42
44
  ## Quick Start
@@ -199,6 +201,7 @@ Key settings:
199
201
  | `trustZoneStoreDir` | `{memoryDir}/state/trust-zones` | Root directory for trust-zone records |
200
202
  | `trustZoneRecallEnabled` | `false` | Inject prompt-relevant working and trusted trust-zone records into recall context |
201
203
  | `memoryPoisoningDefenseEnabled` | `false` | Enable deterministic provenance trust scoring and corroboration requirements for risky trusted promotions |
204
+ | `memoryRedTeamBenchEnabled` | `false` | Enable typed memory red-team benchmark pack support and status accounting for poisoning-defense suites |
202
205
 
203
206
  Full reference: [Config Reference](docs/config-reference.md)
204
207
 
package/dist/index.js CHANGED
@@ -301,6 +301,10 @@ function parseConfig(raw) {
301
301
  trustZoneStoreDir: typeof cfg.trustZoneStoreDir === "string" && cfg.trustZoneStoreDir.trim().length > 0 ? cfg.trustZoneStoreDir.trim() : path.join(memoryDir, "state", "trust-zones"),
302
302
  trustZoneRecallEnabled: cfg.trustZoneRecallEnabled === true,
303
303
  memoryPoisoningDefenseEnabled: cfg.memoryPoisoningDefenseEnabled === true,
304
+ memoryRedTeamBenchEnabled: cfg.memoryRedTeamBenchEnabled === true,
305
+ harmonicRetrievalEnabled: cfg.harmonicRetrievalEnabled === true,
306
+ abstractionAnchorsEnabled: cfg.abstractionAnchorsEnabled === true,
307
+ abstractionNodeStoreDir: typeof cfg.abstractionNodeStoreDir === "string" && cfg.abstractionNodeStoreDir.trim().length > 0 ? cfg.abstractionNodeStoreDir.trim() : path.join(memoryDir, "state", "abstraction-nodes"),
304
308
  // Local LLM Provider (v2.1)
305
309
  localLlmEnabled: cfg.localLlmEnabled === true || cfg.localLlmEnabled === "true",
306
310
  // default: false
@@ -11630,10 +11634,14 @@ function assertSafeBenchmarkId(benchmarkId) {
11630
11634
  }
11631
11635
  return benchmarkId;
11632
11636
  }
11633
- function validateEvalBenchmarkManifest(raw) {
11637
+ function validateEvalBenchmarkManifest(raw, options) {
11634
11638
  if (!isRecord(raw)) throw new Error("benchmark manifest must be an object");
11635
11639
  if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
11636
11640
  if (!Array.isArray(raw.cases)) throw new Error("cases must be an array");
11641
+ const benchmarkTypeRaw = typeof raw.benchmarkType === "string" && raw.benchmarkType.trim().length > 0 ? raw.benchmarkType.trim() : "standard";
11642
+ if (!["standard", "memory-red-team"].includes(benchmarkTypeRaw)) {
11643
+ throw new Error("benchmarkType must be one of standard|memory-red-team");
11644
+ }
11637
11645
  const cases = raw.cases.map((item, index) => {
11638
11646
  if (!isRecord(item)) throw new Error(`cases[${index}] must be an object`);
11639
11647
  return {
@@ -11643,13 +11651,28 @@ function validateEvalBenchmarkManifest(raw) {
11643
11651
  notes: typeof item.notes === "string" && item.notes.trim().length > 0 ? item.notes.trim() : void 0
11644
11652
  };
11645
11653
  });
11654
+ const benchmarkType = benchmarkTypeRaw;
11655
+ if (benchmarkType === "memory-red-team" && options?.memoryRedTeamBenchEnabled !== true) {
11656
+ throw new Error("memory-red-team benchmark packs require memoryRedTeamBenchEnabled");
11657
+ }
11658
+ const attackClass = typeof raw.attackClass === "string" && raw.attackClass.trim().length > 0 ? raw.attackClass.trim() : void 0;
11659
+ const targetSurface = typeof raw.targetSurface === "string" && raw.targetSurface.trim().length > 0 ? raw.targetSurface.trim() : void 0;
11660
+ if (benchmarkType === "memory-red-team" && attackClass === void 0) {
11661
+ throw new Error("attackClass must be a non-empty string");
11662
+ }
11663
+ if (benchmarkType === "memory-red-team" && targetSurface === void 0) {
11664
+ throw new Error("targetSurface must be a non-empty string");
11665
+ }
11646
11666
  return {
11647
11667
  schemaVersion: 1,
11648
11668
  benchmarkId: assertString(raw.benchmarkId, "benchmarkId"),
11669
+ benchmarkType,
11649
11670
  title: assertString(raw.title, "title"),
11650
11671
  description: typeof raw.description === "string" && raw.description.trim().length > 0 ? raw.description.trim() : void 0,
11651
11672
  tags: optionalStringArray(raw.tags, "tags"),
11652
11673
  sourceLinks: optionalStringArray(raw.sourceLinks, "sourceLinks"),
11674
+ attackClass,
11675
+ targetSurface,
11653
11676
  cases
11654
11677
  };
11655
11678
  }
@@ -11815,7 +11838,11 @@ async function collectEvalStoreSnapshot(options) {
11815
11838
  const manifests = [];
11816
11839
  for (const filePath of benchmarkFiles) {
11817
11840
  try {
11818
- manifests.push(validateEvalBenchmarkManifest(await readJsonFile(filePath)));
11841
+ manifests.push(
11842
+ validateEvalBenchmarkManifest(await readJsonFile(filePath), {
11843
+ memoryRedTeamBenchEnabled: options.memoryRedTeamBenchEnabled
11844
+ })
11845
+ );
11819
11846
  } catch (error) {
11820
11847
  invalidBenchmarks.push({
11821
11848
  path: filePath,
@@ -11852,10 +11879,18 @@ async function collectEvalStoreSnapshot(options) {
11852
11879
  });
11853
11880
  shadows.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
11854
11881
  const tags = /* @__PURE__ */ new Set();
11882
+ const attackClasses = /* @__PURE__ */ new Set();
11855
11883
  const sourceLinks = /* @__PURE__ */ new Set();
11884
+ const targetSurfaces = /* @__PURE__ */ new Set();
11856
11885
  let totalCases = 0;
11886
+ let redTeam = 0;
11857
11887
  for (const manifest of manifests) {
11858
11888
  totalCases += manifest.cases.length;
11889
+ if (manifest.benchmarkType === "memory-red-team") {
11890
+ redTeam += 1;
11891
+ if (manifest.attackClass) attackClasses.add(manifest.attackClass);
11892
+ if (manifest.targetSurface) targetSurfaces.add(manifest.targetSurface);
11893
+ }
11859
11894
  for (const tag of manifest.tags ?? []) tags.add(tag);
11860
11895
  for (const link of manifest.sourceLinks ?? []) sourceLinks.add(link);
11861
11896
  }
@@ -11870,8 +11905,11 @@ async function collectEvalStoreSnapshot(options) {
11870
11905
  total: benchmarkFiles.length,
11871
11906
  valid: manifests.length,
11872
11907
  invalid: invalidBenchmarks.length,
11908
+ redTeam,
11873
11909
  totalCases,
11910
+ attackClasses: [...attackClasses].sort(),
11874
11911
  tags: [...tags].sort(),
11912
+ targetSurfaces: [...targetSurfaces].sort(),
11875
11913
  sourceLinks: [...sourceLinks].sort()
11876
11914
  },
11877
11915
  runs: {
@@ -11919,25 +11957,32 @@ async function resolveBenchmarkManifestPath(sourcePath) {
11919
11957
  }
11920
11958
  throw new Error("benchmark pack source must be a file or directory");
11921
11959
  }
11922
- async function validateEvalBenchmarkPack(sourcePath) {
11960
+ async function validateEvalBenchmarkPack(sourcePath, options) {
11923
11961
  const trimmedSourcePath = sourcePath.trim();
11924
11962
  if (trimmedSourcePath.length === 0) {
11925
11963
  throw new Error("benchmark pack path must be a non-empty string");
11926
11964
  }
11927
11965
  const { manifestPath } = await resolveBenchmarkManifestPath(trimmedSourcePath);
11928
- const manifest = validateEvalBenchmarkManifest(await readJsonFile(manifestPath));
11966
+ const manifest = validateEvalBenchmarkManifest(await readJsonFile(manifestPath), {
11967
+ memoryRedTeamBenchEnabled: options?.memoryRedTeamBenchEnabled
11968
+ });
11929
11969
  return {
11930
11970
  sourcePath: trimmedSourcePath,
11931
11971
  manifestPath,
11932
11972
  benchmarkId: assertSafeBenchmarkId(manifest.benchmarkId),
11973
+ benchmarkType: manifest.benchmarkType ?? "standard",
11933
11974
  title: manifest.title,
11975
+ attackClass: manifest.attackClass,
11976
+ targetSurface: manifest.targetSurface,
11934
11977
  totalCases: manifest.cases.length,
11935
11978
  tags: [...manifest.tags ?? []],
11936
11979
  sourceLinks: [...manifest.sourceLinks ?? []]
11937
11980
  };
11938
11981
  }
11939
11982
  async function importEvalBenchmarkPack(options) {
11940
- const summary = await validateEvalBenchmarkPack(options.sourcePath);
11983
+ const summary = await validateEvalBenchmarkPack(options.sourcePath, {
11984
+ memoryRedTeamBenchEnabled: options.memoryRedTeamBenchEnabled
11985
+ });
11941
11986
  const rootDir = resolveEvalStoreDir(options.memoryDir, options.evalStoreDir);
11942
11987
  const benchmarkDir = path15.join(rootDir, "benchmarks");
11943
11988
  const targetDir = path15.join(benchmarkDir, summary.benchmarkId);
@@ -11982,7 +12027,8 @@ async function getEvalHarnessStatus(options) {
11982
12027
  return (await collectEvalStoreSnapshot({
11983
12028
  rootDir: resolveEvalStoreDir(options.memoryDir, options.evalStoreDir),
11984
12029
  enabled: options.enabled,
11985
- shadowModeEnabled: options.shadowModeEnabled
12030
+ shadowModeEnabled: options.shadowModeEnabled,
12031
+ memoryRedTeamBenchEnabled: options.memoryRedTeamBenchEnabled
11986
12032
  })).status;
11987
12033
  }
11988
12034
  function resolveRequiredEvalStoreRoot(options, label) {
@@ -12006,12 +12052,14 @@ async function runEvalBenchmarkCiGate(options) {
12006
12052
  const baseSnapshot = await collectEvalStoreSnapshot({
12007
12053
  rootDir: baseRootDir,
12008
12054
  enabled: true,
12009
- shadowModeEnabled: true
12055
+ shadowModeEnabled: true,
12056
+ memoryRedTeamBenchEnabled: true
12010
12057
  });
12011
12058
  const candidateSnapshot = await collectEvalStoreSnapshot({
12012
12059
  rootDir: candidateRootDir,
12013
12060
  enabled: true,
12014
- shadowModeEnabled: true
12061
+ shadowModeEnabled: true,
12062
+ memoryRedTeamBenchEnabled: true
12015
12063
  });
12016
12064
  const regressions = [];
12017
12065
  const improvements = [];
@@ -24175,7 +24223,7 @@ promotionCandidates: ${res.promotionCandidateCount}`
24175
24223
  }
24176
24224
 
24177
24225
  // src/cli.ts
24178
- import path54 from "path";
24226
+ import path55 from "path";
24179
24227
  import { access as access3, readFile as readFile37, readdir as readdir24, unlink as unlink7 } from "fs/promises";
24180
24228
  import { createHash as createHash10 } from "crypto";
24181
24229
 
@@ -25056,8 +25104,8 @@ function gatherCandidates(input, warnings) {
25056
25104
  const record = rec;
25057
25105
  const content = typeof record.content === "string" ? record.content : null;
25058
25106
  if (!content) continue;
25059
- const path56 = typeof record.path === "string" ? record.path : "";
25060
- if (!path56.startsWith("transcripts/") && !path56.includes("/transcripts/")) continue;
25107
+ const path57 = typeof record.path === "string" ? record.path : "";
25108
+ if (!path57.startsWith("transcripts/") && !path57.includes("/transcripts/")) continue;
25061
25109
  rows.push(...parseJsonl(content, warnings));
25062
25110
  }
25063
25111
  return rows;
@@ -26691,6 +26739,90 @@ async function runCompatChecks(options) {
26691
26739
  };
26692
26740
  }
26693
26741
 
26742
+ // src/abstraction-nodes.ts
26743
+ import path54 from "path";
26744
+ import { mkdir as mkdir36, writeFile as writeFile33 } from "fs/promises";
26745
+ function validateKind2(raw) {
26746
+ const value = assertString2(raw, "kind");
26747
+ if (!["episode", "topic", "project", "workflow", "constraint"].includes(value)) {
26748
+ throw new Error("kind must be one of episode|topic|project|workflow|constraint");
26749
+ }
26750
+ return value;
26751
+ }
26752
+ function validateLevel(raw) {
26753
+ const value = assertString2(raw, "abstractionLevel");
26754
+ if (!["micro", "meso", "macro"].includes(value)) {
26755
+ throw new Error("abstractionLevel must be one of micro|meso|macro");
26756
+ }
26757
+ return value;
26758
+ }
26759
+ function resolveAbstractionNodeStoreDir(memoryDir, overrideDir) {
26760
+ if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
26761
+ return overrideDir.trim();
26762
+ }
26763
+ return path54.join(memoryDir, "state", "abstraction-nodes");
26764
+ }
26765
+ function validateAbstractionNode(raw) {
26766
+ if (!isRecord2(raw)) throw new Error("abstraction node must be an object");
26767
+ if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
26768
+ return {
26769
+ schemaVersion: 1,
26770
+ nodeId: assertSafePathSegment(assertString2(raw.nodeId, "nodeId"), "nodeId"),
26771
+ recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
26772
+ sessionKey: assertString2(raw.sessionKey, "sessionKey"),
26773
+ kind: validateKind2(raw.kind),
26774
+ abstractionLevel: validateLevel(raw.abstractionLevel),
26775
+ title: assertString2(raw.title, "title"),
26776
+ summary: assertString2(raw.summary, "summary"),
26777
+ sourceMemoryIds: optionalStringArray2(raw.sourceMemoryIds, "sourceMemoryIds"),
26778
+ entityRefs: optionalStringArray2(raw.entityRefs, "entityRefs"),
26779
+ tags: optionalStringArray2(raw.tags, "tags"),
26780
+ metadata: validateStringRecord(raw.metadata, "metadata")
26781
+ };
26782
+ }
26783
+ async function getAbstractionNodeStoreStatus(options) {
26784
+ const rootDir = resolveAbstractionNodeStoreDir(options.memoryDir, options.abstractionNodeStoreDir);
26785
+ const nodesDir = path54.join(rootDir, "nodes");
26786
+ const files = await listJsonFiles(nodesDir);
26787
+ const nodes = [];
26788
+ const invalidNodes = [];
26789
+ for (const filePath of files) {
26790
+ try {
26791
+ nodes.push(validateAbstractionNode(await readJsonFile(filePath)));
26792
+ } catch (error) {
26793
+ invalidNodes.push({
26794
+ path: filePath,
26795
+ error: error instanceof Error ? error.message : String(error)
26796
+ });
26797
+ }
26798
+ }
26799
+ nodes.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
26800
+ const byKind = {};
26801
+ const byLevel = {};
26802
+ for (const node of nodes) {
26803
+ byKind[node.kind] = (byKind[node.kind] ?? 0) + 1;
26804
+ byLevel[node.abstractionLevel] = (byLevel[node.abstractionLevel] ?? 0) + 1;
26805
+ }
26806
+ return {
26807
+ enabled: options.enabled,
26808
+ anchorsEnabled: options.anchorsEnabled,
26809
+ rootDir,
26810
+ nodesDir,
26811
+ nodes: {
26812
+ total: files.length,
26813
+ valid: nodes.length,
26814
+ invalid: invalidNodes.length,
26815
+ byKind,
26816
+ byLevel,
26817
+ latestNodeId: nodes[0]?.nodeId,
26818
+ latestRecordedAt: nodes[0]?.recordedAt,
26819
+ latestSessionKey: nodes[0]?.sessionKey
26820
+ },
26821
+ latestNode: nodes[0],
26822
+ invalidNodes
26823
+ };
26824
+ }
26825
+
26694
26826
  // src/cli.ts
26695
26827
  function rankCandidateForKeep(a, b) {
26696
26828
  const aConfidence = typeof a.frontmatter.confidence === "number" ? a.frontmatter.confidence : 0;
@@ -26852,18 +26984,22 @@ async function runBenchmarkStatusCliCommand(options) {
26852
26984
  memoryDir: options.memoryDir,
26853
26985
  evalStoreDir: options.evalStoreDir,
26854
26986
  enabled: options.evalHarnessEnabled,
26855
- shadowModeEnabled: options.evalShadowModeEnabled
26987
+ shadowModeEnabled: options.evalShadowModeEnabled,
26988
+ memoryRedTeamBenchEnabled: options.memoryRedTeamBenchEnabled
26856
26989
  });
26857
26990
  }
26858
26991
  async function runBenchmarkValidateCliCommand(options) {
26859
- return validateEvalBenchmarkPack(options.path);
26992
+ return validateEvalBenchmarkPack(options.path, {
26993
+ memoryRedTeamBenchEnabled: options.memoryRedTeamBenchEnabled
26994
+ });
26860
26995
  }
26861
26996
  async function runBenchmarkImportCliCommand(options) {
26862
26997
  return importEvalBenchmarkPack({
26863
26998
  sourcePath: options.path,
26864
26999
  memoryDir: options.memoryDir,
26865
27000
  evalStoreDir: options.evalStoreDir,
26866
- force: options.force === true
27001
+ force: options.force === true,
27002
+ memoryRedTeamBenchEnabled: options.memoryRedTeamBenchEnabled
26867
27003
  });
26868
27004
  }
26869
27005
  async function runBenchmarkCiGateCliCommand(options) {
@@ -26896,6 +27032,14 @@ async function runTrustZoneStatusCliCommand(options) {
26896
27032
  poisoningDefenseEnabled: options.memoryPoisoningDefenseEnabled
26897
27033
  });
26898
27034
  }
27035
+ async function runAbstractionNodeStatusCliCommand(options) {
27036
+ return getAbstractionNodeStoreStatus({
27037
+ memoryDir: options.memoryDir,
27038
+ abstractionNodeStoreDir: options.abstractionNodeStoreDir,
27039
+ enabled: options.harmonicRetrievalEnabled,
27040
+ anchorsEnabled: options.abstractionAnchorsEnabled
27041
+ });
27042
+ }
26899
27043
  async function runTrustZonePromoteCliCommand(options) {
26900
27044
  const result = await promoteTrustZoneRecord({
26901
27045
  memoryDir: options.memoryDir,
@@ -27142,7 +27286,7 @@ function policyVersionForValues(values, config) {
27142
27286
  return createHash10("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
27143
27287
  }
27144
27288
  async function readRuntimePolicySnapshot2(config, fileName) {
27145
- const filePath = path54.join(config.memoryDir, "state", fileName);
27289
+ const filePath = path55.join(config.memoryDir, "state", fileName);
27146
27290
  const snapshot = await readRuntimePolicySnapshot(filePath, {
27147
27291
  maxStaleDecayThreshold: config.lifecycleArchiveDecayThreshold
27148
27292
  });
@@ -27726,14 +27870,14 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
27726
27870
  const ns = (namespace ?? "").trim();
27727
27871
  if (!ns) return orchestrator.config.memoryDir;
27728
27872
  if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
27729
- const candidate = path54.join(orchestrator.config.memoryDir, "namespaces", ns);
27873
+ const candidate = path55.join(orchestrator.config.memoryDir, "namespaces", ns);
27730
27874
  if (ns === orchestrator.config.defaultNamespace) {
27731
27875
  return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
27732
27876
  }
27733
27877
  return candidate;
27734
27878
  }
27735
27879
  async function readAllMemoryFiles(memoryDir) {
27736
- const roots = [path54.join(memoryDir, "facts"), path54.join(memoryDir, "corrections")];
27880
+ const roots = [path55.join(memoryDir, "facts"), path55.join(memoryDir, "corrections")];
27737
27881
  const out = [];
27738
27882
  const walk = async (dir) => {
27739
27883
  let entries;
@@ -27744,7 +27888,7 @@ async function readAllMemoryFiles(memoryDir) {
27744
27888
  }
27745
27889
  for (const entry of entries) {
27746
27890
  const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
27747
- const fullPath = path54.join(dir, entryName);
27891
+ const fullPath = path55.join(dir, entryName);
27748
27892
  if (entry.isDirectory()) {
27749
27893
  await walk(fullPath);
27750
27894
  continue;
@@ -28017,7 +28161,8 @@ function registerCli(api, orchestrator) {
28017
28161
  memoryDir: orchestrator.config.memoryDir,
28018
28162
  evalStoreDir: orchestrator.config.evalStoreDir,
28019
28163
  evalHarnessEnabled: orchestrator.config.evalHarnessEnabled,
28020
- evalShadowModeEnabled: orchestrator.config.evalShadowModeEnabled
28164
+ evalShadowModeEnabled: orchestrator.config.evalShadowModeEnabled,
28165
+ memoryRedTeamBenchEnabled: orchestrator.config.memoryRedTeamBenchEnabled
28021
28166
  });
28022
28167
  console.log(JSON.stringify(status, null, 2));
28023
28168
  console.log("OK");
@@ -28025,7 +28170,8 @@ function registerCli(api, orchestrator) {
28025
28170
  cmd.command("benchmark-validate").description("Validate a benchmark manifest file or pack directory without importing it").argument("<path>", "Path to a benchmark manifest JSON file or a directory with manifest.json").action(async (...args) => {
28026
28171
  const inputPath = args[0];
28027
28172
  const summary = await runBenchmarkValidateCliCommand({
28028
- path: typeof inputPath === "string" ? inputPath : ""
28173
+ path: typeof inputPath === "string" ? inputPath : "",
28174
+ memoryRedTeamBenchEnabled: orchestrator.config.memoryRedTeamBenchEnabled
28029
28175
  });
28030
28176
  console.log(JSON.stringify(summary, null, 2));
28031
28177
  console.log("OK");
@@ -28037,7 +28183,8 @@ function registerCli(api, orchestrator) {
28037
28183
  path: typeof inputPath === "string" ? inputPath : "",
28038
28184
  memoryDir: orchestrator.config.memoryDir,
28039
28185
  evalStoreDir: orchestrator.config.evalStoreDir,
28040
- force: options.force === true
28186
+ force: options.force === true,
28187
+ memoryRedTeamBenchEnabled: orchestrator.config.memoryRedTeamBenchEnabled
28041
28188
  });
28042
28189
  console.log(JSON.stringify(summary, null, 2));
28043
28190
  console.log("OK");
@@ -28084,6 +28231,16 @@ function registerCli(api, orchestrator) {
28084
28231
  console.log(JSON.stringify(status, null, 2));
28085
28232
  console.log("OK");
28086
28233
  });
28234
+ cmd.command("abstraction-node-status").description("Show abstraction-node store status, abstraction counts, and latest stored node").action(async () => {
28235
+ const status = await runAbstractionNodeStatusCliCommand({
28236
+ memoryDir: orchestrator.config.memoryDir,
28237
+ abstractionNodeStoreDir: orchestrator.config.abstractionNodeStoreDir,
28238
+ harmonicRetrievalEnabled: orchestrator.config.harmonicRetrievalEnabled,
28239
+ abstractionAnchorsEnabled: orchestrator.config.abstractionAnchorsEnabled
28240
+ });
28241
+ console.log(JSON.stringify(status, null, 2));
28242
+ console.log("OK");
28243
+ });
28087
28244
  cmd.command("trust-zone-promote").description("Dry-run or apply a trust-zone promotion with provenance enforcement").requiredOption("--record-id <recordId>", "Source trust-zone record id").requiredOption("--target-zone <targetZone>", "Promotion target zone (working|trusted)").requiredOption("--reason <reason>", "Human-readable promotion reason").option("--recorded-at <isoTimestamp>", "Promotion timestamp (defaults to now)").option("--summary <summary>", "Optional replacement summary for the promoted record").option("--dry-run", "Show the promotion plan without writing the promoted record").action(async (...args) => {
28088
28245
  const options = args[0] ?? {};
28089
28246
  const result = await runTrustZonePromoteCliCommand({
@@ -28751,7 +28908,7 @@ function registerCli(api, orchestrator) {
28751
28908
  }
28752
28909
  });
28753
28910
  cmd.command("identity").description("Show agent identity reflections").action(async () => {
28754
- const workspaceDir = path54.join(process.env.HOME ?? "~", ".openclaw", "workspace");
28911
+ const workspaceDir = path55.join(process.env.HOME ?? "~", ".openclaw", "workspace");
28755
28912
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
28756
28913
  if (!identity) {
28757
28914
  console.log("No identity file found.");
@@ -28974,8 +29131,8 @@ function registerCli(api, orchestrator) {
28974
29131
  const options = args[0] ?? {};
28975
29132
  const threadId = options.thread;
28976
29133
  const top = parseInt(options.top ?? "10", 10);
28977
- const memoryDir = path54.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
28978
- const threading = new ThreadingManager(path54.join(memoryDir, "threads"));
29134
+ const memoryDir = path55.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
29135
+ const threading = new ThreadingManager(path55.join(memoryDir, "threads"));
28979
29136
  if (threadId) {
28980
29137
  const thread = await threading.loadThread(threadId);
28981
29138
  if (!thread) {
@@ -29451,9 +29608,9 @@ async function recordObjectiveStateSnapshotsFromAgentMessages(options) {
29451
29608
  }
29452
29609
 
29453
29610
  // src/index.ts
29454
- import { readFile as readFile38, writeFile as writeFile33 } from "fs/promises";
29611
+ import { readFile as readFile38, writeFile as writeFile34 } from "fs/promises";
29455
29612
  import { readFileSync as readFileSync4 } from "fs";
29456
- import path55 from "path";
29613
+ import path56 from "path";
29457
29614
  import os6 from "os";
29458
29615
  var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
29459
29616
  var ENGRAM_HOOK_APIS = "__openclawEngramHookApis";
@@ -29461,7 +29618,7 @@ function loadPluginConfigFromFile() {
29461
29618
  try {
29462
29619
  const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
29463
29620
  const homeDir = process.env.HOME ?? os6.homedir();
29464
- const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path55.join(homeDir, ".openclaw", "openclaw.json");
29621
+ const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path56.join(homeDir, ".openclaw", "openclaw.json");
29465
29622
  const content = readFileSync4(configPath, "utf-8");
29466
29623
  const config = JSON.parse(content);
29467
29624
  const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
@@ -29711,11 +29868,11 @@ Use this context naturally when relevant. Never quote or expose this memory cont
29711
29868
  `session reset via API for ${sessionKey}, new sessionId=${result.sessionId}`
29712
29869
  );
29713
29870
  const safeSessionKey = sanitizeSessionKeyForFilename(sessionKey);
29714
- const signalPath = path55.join(
29871
+ const signalPath = path56.join(
29715
29872
  workspaceDir,
29716
29873
  `.compaction-reset-signal-${safeSessionKey}`
29717
29874
  );
29718
- await writeFile33(
29875
+ await writeFile34(
29719
29876
  signalPath,
29720
29877
  JSON.stringify({
29721
29878
  sessionKey,
@@ -29742,7 +29899,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
29742
29899
  );
29743
29900
  async function ensureHourlySummaryCron(api2) {
29744
29901
  const jobId = "engram-hourly-summary";
29745
- const cronFilePath = path55.join(os6.homedir(), ".openclaw", "cron", "jobs.json");
29902
+ const cronFilePath = path56.join(os6.homedir(), ".openclaw", "cron", "jobs.json");
29746
29903
  try {
29747
29904
  let jobsData = { version: 1, jobs: [] };
29748
29905
  try {
@@ -29783,7 +29940,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
29783
29940
  state: {}
29784
29941
  };
29785
29942
  jobsData.jobs.push(newJob);
29786
- await writeFile33(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
29943
+ await writeFile34(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
29787
29944
  log.info("auto-registered hourly summary cron job");
29788
29945
  } catch (err) {
29789
29946
  log.error("failed to auto-register hourly summary cron job:", err);