@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 +3 -0
- package/dist/index.js +188 -31
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +39 -0
- package/package.json +1 -1
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(
|
|
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
|
|
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
|
|
25060
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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 = [
|
|
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 =
|
|
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 =
|
|
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 =
|
|
28978
|
-
const threading = new ThreadingManager(
|
|
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
|
|
29611
|
+
import { readFile as readFile38, writeFile as writeFile34 } from "fs/promises";
|
|
29455
29612
|
import { readFileSync as readFileSync4 } from "fs";
|
|
29456
|
-
import
|
|
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 :
|
|
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 =
|
|
29871
|
+
const signalPath = path56.join(
|
|
29715
29872
|
workspaceDir,
|
|
29716
29873
|
`.compaction-reset-signal-${safeSessionKey}`
|
|
29717
29874
|
);
|
|
29718
|
-
await
|
|
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 =
|
|
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
|
|
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);
|