@joshuaswarren/openclaw-engram 9.0.38 → 9.0.39
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 +7 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ AI agents forget everything between conversations. Engram fixes that.
|
|
|
40
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
41
|
- **Cue-anchor index foundation** — Engram can now, when `harmonicRetrievalEnabled` and `abstractionAnchorsEnabled` are enabled, persist typed cue anchors for entities, files, tools, outcomes, constraints, and dates, inspect them with `openclaw engram cue-anchor-status`, and keep harmonic retrieval grounded in explicit abstraction-to-cue links before blending logic lands.
|
|
42
42
|
- **Harmonic retrieval diagnostics** — Engram can now, when `harmonicRetrievalEnabled` is enabled, blend abstraction-node evidence with cue-anchor matches into a dedicated `Harmonic Retrieval` recall section and inspect those blended results with `openclaw engram harmonic-search`.
|
|
43
|
+
- **Verified episodic recall** — Engram can now, when `verifiedRecallEnabled` is enabled, inject a dedicated `Verified Episodes` recall section that reuses memory boxes but only surfaces boxes whose cited source memories still verify as non-archived episodes.
|
|
43
44
|
- **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.
|
|
44
45
|
|
|
45
46
|
## Quick Start
|
|
@@ -167,6 +168,8 @@ openclaw engram objective-state-status # Objective-state snapshot counts a
|
|
|
167
168
|
openclaw engram causal-trajectory-status # Causal-trajectory record counts and latest stored chain
|
|
168
169
|
openclaw engram trust-zone-status # Trust-zone record counts and latest stored record
|
|
169
170
|
openclaw engram trust-zone-promote # Dry-run or apply a trust-zone promotion with provenance/corroboration enforcement
|
|
171
|
+
openclaw engram harmonic-search <query> # Preview blended harmonic retrieval matches
|
|
172
|
+
openclaw engram verified-recall-search <query> # Preview verified episodic recall matches
|
|
170
173
|
openclaw engram conversation-index-health # Conversation index status
|
|
171
174
|
openclaw engram graph-health # Entity graph status
|
|
172
175
|
openclaw engram tier-status # Hot/cold tier metrics
|
|
@@ -203,6 +206,10 @@ Key settings:
|
|
|
203
206
|
| `trustZoneRecallEnabled` | `false` | Inject prompt-relevant working and trusted trust-zone records into recall context |
|
|
204
207
|
| `memoryPoisoningDefenseEnabled` | `false` | Enable deterministic provenance trust scoring and corroboration requirements for risky trusted promotions |
|
|
205
208
|
| `memoryRedTeamBenchEnabled` | `false` | Enable typed memory red-team benchmark pack support and status accounting for poisoning-defense suites |
|
|
209
|
+
| `harmonicRetrievalEnabled` | `false` | Enable harmonic retrieval blending over abstraction nodes and cue anchors, including the dedicated recall section and `harmonic-search` diagnostics |
|
|
210
|
+
| `abstractionAnchorsEnabled` | `false` | Enable typed cue-anchor indexing for abstraction nodes and expose the anchor store through status tooling |
|
|
211
|
+
| `verifiedRecallEnabled` | `false` | Inject prompt-relevant memory boxes only when their cited source memories verify as non-archived episodes |
|
|
212
|
+
| `semanticRulePromotionEnabled` | `false` | Reserve semantic-rule promotion surfaces until verified episodic recall has been benchmarked in follow-on slices |
|
|
206
213
|
|
|
207
214
|
Full reference: [Config Reference](docs/config-reference.md)
|
|
208
215
|
|
package/dist/index.js
CHANGED
|
@@ -304,6 +304,8 @@ function parseConfig(raw) {
|
|
|
304
304
|
memoryRedTeamBenchEnabled: cfg.memoryRedTeamBenchEnabled === true,
|
|
305
305
|
harmonicRetrievalEnabled: cfg.harmonicRetrievalEnabled === true,
|
|
306
306
|
abstractionAnchorsEnabled: cfg.abstractionAnchorsEnabled === true,
|
|
307
|
+
verifiedRecallEnabled: cfg.verifiedRecallEnabled === true,
|
|
308
|
+
semanticRulePromotionEnabled: cfg.semanticRulePromotionEnabled === true,
|
|
307
309
|
abstractionNodeStoreDir: typeof cfg.abstractionNodeStoreDir === "string" && cfg.abstractionNodeStoreDir.trim().length > 0 ? cfg.abstractionNodeStoreDir.trim() : path.join(memoryDir, "state", "abstraction-nodes"),
|
|
308
310
|
// Local LLM Provider (v2.1)
|
|
309
311
|
localLlmEnabled: cfg.localLlmEnabled === true || cfg.localLlmEnabled === "true",
|
|
@@ -587,6 +589,12 @@ function buildDefaultRecallPipeline(cfg) {
|
|
|
587
589
|
maxResults: 3,
|
|
588
590
|
maxChars: 2200
|
|
589
591
|
},
|
|
592
|
+
{
|
|
593
|
+
id: "verified-episodes",
|
|
594
|
+
enabled: cfg.verifiedRecallEnabled === true,
|
|
595
|
+
maxResults: 3,
|
|
596
|
+
maxChars: 1800
|
|
597
|
+
},
|
|
590
598
|
{
|
|
591
599
|
id: "memories",
|
|
592
600
|
enabled: true,
|
|
@@ -15336,6 +15344,92 @@ async function searchHarmonicRetrieval(options) {
|
|
|
15336
15344
|
).slice(0, options.maxResults);
|
|
15337
15345
|
}
|
|
15338
15346
|
|
|
15347
|
+
// src/verified-recall.ts
|
|
15348
|
+
function createReadOnlyBoxBuilder(memoryDir) {
|
|
15349
|
+
return new BoxBuilder(memoryDir, {
|
|
15350
|
+
memoryBoxesEnabled: true,
|
|
15351
|
+
traceWeaverEnabled: false,
|
|
15352
|
+
boxTopicShiftThreshold: 0.35,
|
|
15353
|
+
boxTimeGapMs: 30 * 60 * 1e3,
|
|
15354
|
+
boxMaxMemories: 50,
|
|
15355
|
+
traceWeaverLookbackDays: 7,
|
|
15356
|
+
traceWeaverOverlapThreshold: 0.4
|
|
15357
|
+
});
|
|
15358
|
+
}
|
|
15359
|
+
function scoreVerifiedEpisodeCandidate(box, verifiedMemories, queryTokens) {
|
|
15360
|
+
const matchedFields = /* @__PURE__ */ new Set();
|
|
15361
|
+
let score = 0;
|
|
15362
|
+
const topicMatches = countRecallTokenOverlap(queryTokens, box.topics.join(" "));
|
|
15363
|
+
if (topicMatches > 0) {
|
|
15364
|
+
score += topicMatches * 3;
|
|
15365
|
+
matchedFields.add("topics");
|
|
15366
|
+
}
|
|
15367
|
+
const goalMatches = countRecallTokenOverlap(queryTokens, box.goal);
|
|
15368
|
+
if (goalMatches > 0) {
|
|
15369
|
+
score += goalMatches * 4;
|
|
15370
|
+
matchedFields.add("goal");
|
|
15371
|
+
}
|
|
15372
|
+
const toolMatches = countRecallTokenOverlap(queryTokens, box.toolsUsed?.join(" "));
|
|
15373
|
+
if (toolMatches > 0) {
|
|
15374
|
+
score += toolMatches * 2;
|
|
15375
|
+
matchedFields.add("toolsUsed");
|
|
15376
|
+
}
|
|
15377
|
+
let episodeContentMatches = 0;
|
|
15378
|
+
for (const memory of verifiedMemories) {
|
|
15379
|
+
episodeContentMatches += countRecallTokenOverlap(queryTokens, memory.content);
|
|
15380
|
+
}
|
|
15381
|
+
if (episodeContentMatches > 0) {
|
|
15382
|
+
score += episodeContentMatches * 4;
|
|
15383
|
+
matchedFields.add("episodeContent");
|
|
15384
|
+
}
|
|
15385
|
+
return { score, matchedFields };
|
|
15386
|
+
}
|
|
15387
|
+
function resolveVerifiedEpisodeMemoriesFromMap(memoryById, memoryIds) {
|
|
15388
|
+
const verified = [];
|
|
15389
|
+
for (const memoryId of memoryIds) {
|
|
15390
|
+
try {
|
|
15391
|
+
const memory = memoryById.get(memoryId);
|
|
15392
|
+
if (!memory) continue;
|
|
15393
|
+
if (memory.frontmatter.status === "archived") continue;
|
|
15394
|
+
if (memory.frontmatter.memoryKind !== "episode") continue;
|
|
15395
|
+
verified.push(memory);
|
|
15396
|
+
} catch {
|
|
15397
|
+
}
|
|
15398
|
+
}
|
|
15399
|
+
return verified;
|
|
15400
|
+
}
|
|
15401
|
+
async function searchVerifiedEpisodes(options) {
|
|
15402
|
+
const queryTokens = new Set(normalizeRecallTokens(options.query, ["what", "which"]));
|
|
15403
|
+
if (queryTokens.size === 0 || options.maxResults <= 0) return [];
|
|
15404
|
+
const storage = new StorageManager(options.memoryDir);
|
|
15405
|
+
const verifiedMemoryById = new Map(
|
|
15406
|
+
(await storage.readAllMemories()).filter((memory) => memory.frontmatter.status !== "archived").filter((memory) => memory.frontmatter.memoryKind === "episode").map((memory) => [memory.frontmatter.id, memory])
|
|
15407
|
+
);
|
|
15408
|
+
const boxes = await createReadOnlyBoxBuilder(options.memoryDir).readRecentBoxes(Math.max(1, Math.floor(options.boxRecallDays ?? 3))).catch(() => []);
|
|
15409
|
+
const candidates = [];
|
|
15410
|
+
for (const box of boxes) {
|
|
15411
|
+
const verifiedMemories = resolveVerifiedEpisodeMemoriesFromMap(verifiedMemoryById, box.memoryIds);
|
|
15412
|
+
if (verifiedMemories.length === 0) continue;
|
|
15413
|
+
const { score, matchedFields } = scoreVerifiedEpisodeCandidate(box, verifiedMemories, queryTokens);
|
|
15414
|
+
if (score <= 0) continue;
|
|
15415
|
+
candidates.push({
|
|
15416
|
+
box,
|
|
15417
|
+
score,
|
|
15418
|
+
matchedFields,
|
|
15419
|
+
verifiedMemories
|
|
15420
|
+
});
|
|
15421
|
+
}
|
|
15422
|
+
return candidates.map((candidate) => ({
|
|
15423
|
+
box: candidate.box,
|
|
15424
|
+
score: candidate.score,
|
|
15425
|
+
verifiedEpisodeCount: candidate.verifiedMemories.length,
|
|
15426
|
+
verifiedMemoryIds: candidate.verifiedMemories.map((memory) => memory.frontmatter.id),
|
|
15427
|
+
matchedFields: [...candidate.matchedFields].sort()
|
|
15428
|
+
})).sort(
|
|
15429
|
+
(left, right) => right.score - left.score || right.verifiedEpisodeCount - left.verifiedEpisodeCount || right.box.sealedAt.localeCompare(left.box.sealedAt)
|
|
15430
|
+
).slice(0, options.maxResults);
|
|
15431
|
+
}
|
|
15432
|
+
|
|
15339
15433
|
// src/replay/types.ts
|
|
15340
15434
|
var VALID_SOURCES = /* @__PURE__ */ new Set(["openclaw", "claude", "chatgpt"]);
|
|
15341
15435
|
var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
@@ -19120,6 +19214,26 @@ ${r.snippet.trim()}
|
|
|
19120
19214
|
timings.harmonicRetrieval = `${Date.now() - t0}ms`;
|
|
19121
19215
|
return results.length > 0 ? this.formatHarmonicRetrievalResults(results) : null;
|
|
19122
19216
|
})();
|
|
19217
|
+
const verifiedRecallPromise = (async () => {
|
|
19218
|
+
const t0 = Date.now();
|
|
19219
|
+
if (!this.config.verifiedRecallEnabled || !this.isRecallSectionEnabled("verified-episodes", this.config.verifiedRecallEnabled === true)) {
|
|
19220
|
+
timings.verifiedRecall = "skip";
|
|
19221
|
+
return null;
|
|
19222
|
+
}
|
|
19223
|
+
const maxResults = this.getRecallSectionNumber("verified-episodes", "maxResults") ?? 3;
|
|
19224
|
+
if (maxResults <= 0) {
|
|
19225
|
+
timings.verifiedRecall = "skip(limit=0)";
|
|
19226
|
+
return null;
|
|
19227
|
+
}
|
|
19228
|
+
const results = await searchVerifiedEpisodes({
|
|
19229
|
+
memoryDir: this.config.memoryDir,
|
|
19230
|
+
query: retrievalQuery,
|
|
19231
|
+
maxResults,
|
|
19232
|
+
boxRecallDays: this.config.boxRecallDays
|
|
19233
|
+
});
|
|
19234
|
+
timings.verifiedRecall = `${Date.now() - t0}ms`;
|
|
19235
|
+
return results.length > 0 ? this.formatVerifiedEpisodeResults(results) : null;
|
|
19236
|
+
})();
|
|
19123
19237
|
const qmdPromise = (async () => {
|
|
19124
19238
|
if (recallResultLimit <= 0) {
|
|
19125
19239
|
timings.qmd = "skip(limit=0)";
|
|
@@ -19327,6 +19441,7 @@ ${formatted}`;
|
|
|
19327
19441
|
causalTrajectorySection,
|
|
19328
19442
|
trustZoneSection,
|
|
19329
19443
|
harmonicRetrievalSection,
|
|
19444
|
+
verifiedRecallSection,
|
|
19330
19445
|
qmdResult,
|
|
19331
19446
|
transcriptSection,
|
|
19332
19447
|
compactionSection,
|
|
@@ -19343,6 +19458,7 @@ ${formatted}`;
|
|
|
19343
19458
|
causalTrajectoryPromise,
|
|
19344
19459
|
trustZonePromise,
|
|
19345
19460
|
harmonicRetrievalPromise,
|
|
19461
|
+
verifiedRecallPromise,
|
|
19346
19462
|
qmdPromise,
|
|
19347
19463
|
transcriptPromise,
|
|
19348
19464
|
compactionPromise,
|
|
@@ -19409,6 +19525,9 @@ ${tmtNode.summary}`);
|
|
|
19409
19525
|
if (harmonicRetrievalSection) {
|
|
19410
19526
|
this.appendRecallSection(sectionBuckets, "harmonic-retrieval", harmonicRetrievalSection);
|
|
19411
19527
|
}
|
|
19528
|
+
if (verifiedRecallSection) {
|
|
19529
|
+
this.appendRecallSection(sectionBuckets, "verified-episodes", verifiedRecallSection);
|
|
19530
|
+
}
|
|
19412
19531
|
if (qmdResult) {
|
|
19413
19532
|
const t0 = Date.now();
|
|
19414
19533
|
const { memoryResultsLists, globalResults } = qmdResult;
|
|
@@ -21609,6 +21728,29 @@ ${details.join("\n")}`;
|
|
|
21609
21728
|
});
|
|
21610
21729
|
return `## Harmonic Retrieval
|
|
21611
21730
|
|
|
21731
|
+
${lines.join("\n\n")}`;
|
|
21732
|
+
}
|
|
21733
|
+
formatVerifiedEpisodeResults(results) {
|
|
21734
|
+
const lines = results.map(({ box, verifiedEpisodeCount, matchedFields }, index) => {
|
|
21735
|
+
const header = [
|
|
21736
|
+
`[${index + 1}] ${box.sealedAt.replace("T", " ").slice(0, 16)}`,
|
|
21737
|
+
box.traceId ? `trace:${box.traceId.slice(0, 12)}` : "trace:none"
|
|
21738
|
+
].join(" | ");
|
|
21739
|
+
const details = [
|
|
21740
|
+
box.goal ?? `topics: ${box.topics.join(", ")}`,
|
|
21741
|
+
`verified episodes: ${verifiedEpisodeCount}`
|
|
21742
|
+
];
|
|
21743
|
+
if (box.toolsUsed && box.toolsUsed.length > 0) {
|
|
21744
|
+
details.push(`tools: ${box.toolsUsed.join(", ")}`);
|
|
21745
|
+
}
|
|
21746
|
+
if (matchedFields.length > 0) {
|
|
21747
|
+
details.push(`matched: ${matchedFields.join(", ")}`);
|
|
21748
|
+
}
|
|
21749
|
+
return `${header}
|
|
21750
|
+
${details.join("\n")}`;
|
|
21751
|
+
});
|
|
21752
|
+
return `## Verified Episodes
|
|
21753
|
+
|
|
21612
21754
|
${lines.join("\n\n")}`;
|
|
21613
21755
|
}
|
|
21614
21756
|
summarizeIdentityText(raw, maxLines, maxChars) {
|
|
@@ -27342,6 +27484,15 @@ async function runHarmonicSearchCliCommand(options) {
|
|
|
27342
27484
|
anchorsEnabled: options.abstractionAnchorsEnabled
|
|
27343
27485
|
});
|
|
27344
27486
|
}
|
|
27487
|
+
async function runVerifiedRecallSearchCliCommand(options) {
|
|
27488
|
+
if (!options.verifiedRecallEnabled) return [];
|
|
27489
|
+
return searchVerifiedEpisodes({
|
|
27490
|
+
memoryDir: options.memoryDir,
|
|
27491
|
+
query: options.query,
|
|
27492
|
+
maxResults: Math.max(1, Math.floor(options.maxResults ?? 3)),
|
|
27493
|
+
boxRecallDays: options.boxRecallDays
|
|
27494
|
+
});
|
|
27495
|
+
}
|
|
27345
27496
|
async function runTrustZonePromoteCliCommand(options) {
|
|
27346
27497
|
const result = await promoteTrustZoneRecord({
|
|
27347
27498
|
memoryDir: options.memoryDir,
|
|
@@ -28587,6 +28738,20 @@ function registerCli(api, orchestrator) {
|
|
|
28587
28738
|
console.log(JSON.stringify(result, null, 2));
|
|
28588
28739
|
console.log("OK");
|
|
28589
28740
|
});
|
|
28741
|
+
cmd.command("verified-recall-search").description("Preview verified episodic recall over recent memory boxes").argument("<query>", "Prompt-like query to evaluate against verified episodic recall").option("--max-results <count>", "Maximum number of verified episodic results to return", "3").action(async (...args) => {
|
|
28742
|
+
const query = typeof args[0] === "string" ? args[0] : "";
|
|
28743
|
+
const options = args[1] ?? {};
|
|
28744
|
+
const maxResults = typeof options.maxResults === "string" ? Number.parseInt(options.maxResults, 10) : 3;
|
|
28745
|
+
const results = await runVerifiedRecallSearchCliCommand({
|
|
28746
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
28747
|
+
verifiedRecallEnabled: orchestrator.config.verifiedRecallEnabled,
|
|
28748
|
+
query,
|
|
28749
|
+
maxResults: Number.isFinite(maxResults) ? maxResults : 3,
|
|
28750
|
+
boxRecallDays: orchestrator.config.boxRecallDays
|
|
28751
|
+
});
|
|
28752
|
+
console.log(JSON.stringify(results, null, 2));
|
|
28753
|
+
console.log("OK");
|
|
28754
|
+
});
|
|
28590
28755
|
cmd.command("conversation-index-health").description("Show conversation index backend health and index stats").action(async () => {
|
|
28591
28756
|
const health = await runConversationIndexHealthCliCommand(orchestrator);
|
|
28592
28757
|
console.log(JSON.stringify(health, null, 2));
|