@joshuaswarren/openclaw-engram 9.0.38 → 9.0.40
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 +8 -0
- package/dist/index.js +304 -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,8 @@ 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.
|
|
44
|
+
- **Semantic rule promotion** — Engram can now, when `semanticRulePromotionEnabled` is enabled, promote explicit `IF ... THEN ...` rules from verified episodic memories into durable `rule` memories with lineage, source-memory provenance, duplicate suppression, and the operator-facing `openclaw engram semantic-rule-promote` CLI.
|
|
43
45
|
- **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
46
|
|
|
45
47
|
## Quick Start
|
|
@@ -167,6 +169,8 @@ openclaw engram objective-state-status # Objective-state snapshot counts a
|
|
|
167
169
|
openclaw engram causal-trajectory-status # Causal-trajectory record counts and latest stored chain
|
|
168
170
|
openclaw engram trust-zone-status # Trust-zone record counts and latest stored record
|
|
169
171
|
openclaw engram trust-zone-promote # Dry-run or apply a trust-zone promotion with provenance/corroboration enforcement
|
|
172
|
+
openclaw engram harmonic-search <query> # Preview blended harmonic retrieval matches
|
|
173
|
+
openclaw engram verified-recall-search <query> # Preview verified episodic recall matches
|
|
170
174
|
openclaw engram conversation-index-health # Conversation index status
|
|
171
175
|
openclaw engram graph-health # Entity graph status
|
|
172
176
|
openclaw engram tier-status # Hot/cold tier metrics
|
|
@@ -203,6 +207,10 @@ Key settings:
|
|
|
203
207
|
| `trustZoneRecallEnabled` | `false` | Inject prompt-relevant working and trusted trust-zone records into recall context |
|
|
204
208
|
| `memoryPoisoningDefenseEnabled` | `false` | Enable deterministic provenance trust scoring and corroboration requirements for risky trusted promotions |
|
|
205
209
|
| `memoryRedTeamBenchEnabled` | `false` | Enable typed memory red-team benchmark pack support and status accounting for poisoning-defense suites |
|
|
210
|
+
| `harmonicRetrievalEnabled` | `false` | Enable harmonic retrieval blending over abstraction nodes and cue anchors, including the dedicated recall section and `harmonic-search` diagnostics |
|
|
211
|
+
| `abstractionAnchorsEnabled` | `false` | Enable typed cue-anchor indexing for abstraction nodes and expose the anchor store through status tooling |
|
|
212
|
+
| `verifiedRecallEnabled` | `false` | Inject prompt-relevant memory boxes only when their cited source memories verify as non-archived episodes |
|
|
213
|
+
| `semanticRulePromotionEnabled` | `false` | Enable deterministic promotion of explicit `IF ... THEN ...` rules from verified episodic memories via `openclaw engram semantic-rule-promote` |
|
|
206
214
|
|
|
207
215
|
Full reference: [Config Reference](docs/config-reference.md)
|
|
208
216
|
|
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) {
|
|
@@ -27106,6 +27248,126 @@ async function runCompatChecks(options) {
|
|
|
27106
27248
|
};
|
|
27107
27249
|
}
|
|
27108
27250
|
|
|
27251
|
+
// src/semantic-rule-promotion.ts
|
|
27252
|
+
function normalizeRuleWhitespace(value) {
|
|
27253
|
+
return value.replace(/\s+/g, " ").trim();
|
|
27254
|
+
}
|
|
27255
|
+
function stripTrailingClausePunctuation(value) {
|
|
27256
|
+
return value.replace(/[,:;]+$/g, "").trim();
|
|
27257
|
+
}
|
|
27258
|
+
function canonicalizeRuleContent(value) {
|
|
27259
|
+
return extractExplicitIfThenRule(value) ?? normalizeRuleWhitespace(value);
|
|
27260
|
+
}
|
|
27261
|
+
function canonicalizeRuleKey(value) {
|
|
27262
|
+
return canonicalizeRuleContent(value).toLowerCase();
|
|
27263
|
+
}
|
|
27264
|
+
function extractExplicitIfThenRule(content) {
|
|
27265
|
+
const match = content.match(/\bif\b([\s\S]+?)\bthen\b([\s\S]+?)(?:[.!?](?:\s|$)|$)/i);
|
|
27266
|
+
if (!match) return null;
|
|
27267
|
+
const condition = stripTrailingClausePunctuation(normalizeRuleWhitespace(match[1] ?? ""));
|
|
27268
|
+
const outcome = stripTrailingClausePunctuation(normalizeRuleWhitespace(match[2] ?? ""));
|
|
27269
|
+
if (condition.length === 0 || outcome.length === 0) return null;
|
|
27270
|
+
return `IF ${condition} THEN ${outcome}.`;
|
|
27271
|
+
}
|
|
27272
|
+
function promotionConfidence(memory) {
|
|
27273
|
+
const base = Number.isFinite(memory.frontmatter.confidence) ? memory.frontmatter.confidence : 0.8;
|
|
27274
|
+
return Math.max(0.6, Math.min(0.98, base));
|
|
27275
|
+
}
|
|
27276
|
+
function promotionTags(memory) {
|
|
27277
|
+
return Array.from(/* @__PURE__ */ new Set([...memory.frontmatter.tags ?? [], "semantic-rule", "promoted-rule"]));
|
|
27278
|
+
}
|
|
27279
|
+
function buildSupportLinks(sourceMemoryId, confidence) {
|
|
27280
|
+
return [
|
|
27281
|
+
{
|
|
27282
|
+
targetId: sourceMemoryId,
|
|
27283
|
+
linkType: "supports",
|
|
27284
|
+
strength: confidence,
|
|
27285
|
+
reason: "Promoted from verified episodic memory"
|
|
27286
|
+
}
|
|
27287
|
+
];
|
|
27288
|
+
}
|
|
27289
|
+
async function promoteSemanticRuleFromMemory(options) {
|
|
27290
|
+
const report = {
|
|
27291
|
+
enabled: options.enabled,
|
|
27292
|
+
dryRun: options.dryRun === true,
|
|
27293
|
+
promoted: [],
|
|
27294
|
+
skipped: []
|
|
27295
|
+
};
|
|
27296
|
+
if (!options.enabled) {
|
|
27297
|
+
report.skipped.push({
|
|
27298
|
+
sourceMemoryId: options.sourceMemoryId,
|
|
27299
|
+
reason: "disabled"
|
|
27300
|
+
});
|
|
27301
|
+
return report;
|
|
27302
|
+
}
|
|
27303
|
+
const storage = new StorageManager(options.memoryDir);
|
|
27304
|
+
const sourceMemory = await storage.getMemoryById(options.sourceMemoryId);
|
|
27305
|
+
if (!sourceMemory) {
|
|
27306
|
+
report.skipped.push({
|
|
27307
|
+
sourceMemoryId: options.sourceMemoryId,
|
|
27308
|
+
reason: "source-memory-missing"
|
|
27309
|
+
});
|
|
27310
|
+
return report;
|
|
27311
|
+
}
|
|
27312
|
+
if (sourceMemory.frontmatter.status === "archived" || sourceMemory.frontmatter.memoryKind !== "episode") {
|
|
27313
|
+
report.skipped.push({
|
|
27314
|
+
sourceMemoryId: options.sourceMemoryId,
|
|
27315
|
+
reason: "source-memory-not-episode"
|
|
27316
|
+
});
|
|
27317
|
+
return report;
|
|
27318
|
+
}
|
|
27319
|
+
const content = extractExplicitIfThenRule(sourceMemory.content);
|
|
27320
|
+
if (!content) {
|
|
27321
|
+
report.skipped.push({
|
|
27322
|
+
sourceMemoryId: options.sourceMemoryId,
|
|
27323
|
+
reason: "no-explicit-rule"
|
|
27324
|
+
});
|
|
27325
|
+
return report;
|
|
27326
|
+
}
|
|
27327
|
+
const ruleKey = canonicalizeRuleKey(content);
|
|
27328
|
+
const existingRule = (await storage.readAllMemories()).find(
|
|
27329
|
+
(memory) => memory.frontmatter.category === "rule" && memory.frontmatter.status !== "archived" && canonicalizeRuleKey(memory.content) === ruleKey
|
|
27330
|
+
);
|
|
27331
|
+
if (existingRule) {
|
|
27332
|
+
report.skipped.push({
|
|
27333
|
+
sourceMemoryId: options.sourceMemoryId,
|
|
27334
|
+
reason: "duplicate-rule",
|
|
27335
|
+
existingRuleId: existingRule.frontmatter.id
|
|
27336
|
+
});
|
|
27337
|
+
return report;
|
|
27338
|
+
}
|
|
27339
|
+
const confidence = promotionConfidence(sourceMemory);
|
|
27340
|
+
const candidateBase = {
|
|
27341
|
+
sourceMemoryId: options.sourceMemoryId,
|
|
27342
|
+
content,
|
|
27343
|
+
confidence,
|
|
27344
|
+
tags: promotionTags(sourceMemory),
|
|
27345
|
+
memoryKind: "note",
|
|
27346
|
+
lineage: [options.sourceMemoryId]
|
|
27347
|
+
};
|
|
27348
|
+
if (options.dryRun === true) {
|
|
27349
|
+
report.promoted.push({
|
|
27350
|
+
id: `dry-run:${options.sourceMemoryId}`,
|
|
27351
|
+
...candidateBase
|
|
27352
|
+
});
|
|
27353
|
+
return report;
|
|
27354
|
+
}
|
|
27355
|
+
const id = await storage.writeMemory("rule", content, {
|
|
27356
|
+
confidence,
|
|
27357
|
+
tags: candidateBase.tags,
|
|
27358
|
+
source: "semantic-rule-promotion",
|
|
27359
|
+
lineage: candidateBase.lineage,
|
|
27360
|
+
sourceMemoryId: options.sourceMemoryId,
|
|
27361
|
+
memoryKind: "note",
|
|
27362
|
+
links: buildSupportLinks(options.sourceMemoryId, confidence)
|
|
27363
|
+
});
|
|
27364
|
+
report.promoted.push({
|
|
27365
|
+
id,
|
|
27366
|
+
...candidateBase
|
|
27367
|
+
});
|
|
27368
|
+
return report;
|
|
27369
|
+
}
|
|
27370
|
+
|
|
27109
27371
|
// src/cli.ts
|
|
27110
27372
|
function rankCandidateForKeep(a, b) {
|
|
27111
27373
|
const aConfidence = typeof a.frontmatter.confidence === "number" ? a.frontmatter.confidence : 0;
|
|
@@ -27342,6 +27604,23 @@ async function runHarmonicSearchCliCommand(options) {
|
|
|
27342
27604
|
anchorsEnabled: options.abstractionAnchorsEnabled
|
|
27343
27605
|
});
|
|
27344
27606
|
}
|
|
27607
|
+
async function runVerifiedRecallSearchCliCommand(options) {
|
|
27608
|
+
if (!options.verifiedRecallEnabled) return [];
|
|
27609
|
+
return searchVerifiedEpisodes({
|
|
27610
|
+
memoryDir: options.memoryDir,
|
|
27611
|
+
query: options.query,
|
|
27612
|
+
maxResults: Math.max(1, Math.floor(options.maxResults ?? 3)),
|
|
27613
|
+
boxRecallDays: options.boxRecallDays
|
|
27614
|
+
});
|
|
27615
|
+
}
|
|
27616
|
+
async function runSemanticRulePromoteCliCommand(options) {
|
|
27617
|
+
return promoteSemanticRuleFromMemory({
|
|
27618
|
+
memoryDir: options.memoryDir,
|
|
27619
|
+
enabled: options.semanticRulePromotionEnabled,
|
|
27620
|
+
sourceMemoryId: options.sourceMemoryId,
|
|
27621
|
+
dryRun: options.dryRun
|
|
27622
|
+
});
|
|
27623
|
+
}
|
|
27345
27624
|
async function runTrustZonePromoteCliCommand(options) {
|
|
27346
27625
|
const result = await promoteTrustZoneRecord({
|
|
27347
27626
|
memoryDir: options.memoryDir,
|
|
@@ -28587,6 +28866,31 @@ function registerCli(api, orchestrator) {
|
|
|
28587
28866
|
console.log(JSON.stringify(result, null, 2));
|
|
28588
28867
|
console.log("OK");
|
|
28589
28868
|
});
|
|
28869
|
+
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) => {
|
|
28870
|
+
const query = typeof args[0] === "string" ? args[0] : "";
|
|
28871
|
+
const options = args[1] ?? {};
|
|
28872
|
+
const maxResults = typeof options.maxResults === "string" ? Number.parseInt(options.maxResults, 10) : 3;
|
|
28873
|
+
const results = await runVerifiedRecallSearchCliCommand({
|
|
28874
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
28875
|
+
verifiedRecallEnabled: orchestrator.config.verifiedRecallEnabled,
|
|
28876
|
+
query,
|
|
28877
|
+
maxResults: Number.isFinite(maxResults) ? maxResults : 3,
|
|
28878
|
+
boxRecallDays: orchestrator.config.boxRecallDays
|
|
28879
|
+
});
|
|
28880
|
+
console.log(JSON.stringify(results, null, 2));
|
|
28881
|
+
console.log("OK");
|
|
28882
|
+
});
|
|
28883
|
+
cmd.command("semantic-rule-promote").description("Promote an explicit IF/THEN rule from a verified episodic memory").requiredOption("--memory-id <memoryId>", "Verified episodic memory id to promote from").option("--dry-run", "Preview the promoted semantic rule without writing it").action(async (...args) => {
|
|
28884
|
+
const options = args[0] ?? {};
|
|
28885
|
+
const result = await runSemanticRulePromoteCliCommand({
|
|
28886
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
28887
|
+
semanticRulePromotionEnabled: orchestrator.config.semanticRulePromotionEnabled,
|
|
28888
|
+
sourceMemoryId: String(options.memoryId ?? ""),
|
|
28889
|
+
dryRun: options.dryRun === true
|
|
28890
|
+
});
|
|
28891
|
+
console.log(JSON.stringify(result, null, 2));
|
|
28892
|
+
console.log("OK");
|
|
28893
|
+
});
|
|
28590
28894
|
cmd.command("conversation-index-health").description("Show conversation index backend health and index stats").action(async () => {
|
|
28591
28895
|
const health = await runConversationIndexHealthCliCommand(orchestrator);
|
|
28592
28896
|
console.log(JSON.stringify(health, null, 2));
|