@joshuaswarren/openclaw-engram 9.0.40 → 9.0.42

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
@@ -42,6 +42,8 @@ AI agents forget everything between conversations. Engram fixes that.
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
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
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.
45
+ - **Verified rule recall** — Engram can now, when `semanticRuleVerificationEnabled` is enabled, inject a dedicated `Verified Rules` recall section that re-checks promoted rule memories against their cited source episodes at recall time and downgrades stale provenance before the rule can surface.
46
+ - **Creation-memory ledger** — Engram can now, when `creationMemoryEnabled` is enabled, persist a typed work-product ledger for explicit outputs agents create or update, inspect it with `openclaw engram work-product-status`, and write deterministic entries through `openclaw engram work-product-record`.
45
47
  - **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.
46
48
 
47
49
  ## Quick Start
@@ -171,6 +173,8 @@ openclaw engram trust-zone-status # Trust-zone record counts and lates
171
173
  openclaw engram trust-zone-promote # Dry-run or apply a trust-zone promotion with provenance/corroboration enforcement
172
174
  openclaw engram harmonic-search <query> # Preview blended harmonic retrieval matches
173
175
  openclaw engram verified-recall-search <query> # Preview verified episodic recall matches
176
+ openclaw engram work-product-status # Work-product ledger counts and latest recorded output
177
+ openclaw engram work-product-record # Record a typed work-product ledger entry
174
178
  openclaw engram conversation-index-health # Conversation index status
175
179
  openclaw engram graph-health # Entity graph status
176
180
  openclaw engram tier-status # Hot/cold tier metrics
@@ -211,6 +215,9 @@ Key settings:
211
215
  | `abstractionAnchorsEnabled` | `false` | Enable typed cue-anchor indexing for abstraction nodes and expose the anchor store through status tooling |
212
216
  | `verifiedRecallEnabled` | `false` | Inject prompt-relevant memory boxes only when their cited source memories verify as non-archived episodes |
213
217
  | `semanticRulePromotionEnabled` | `false` | Enable deterministic promotion of explicit `IF ... THEN ...` rules from verified episodic memories via `openclaw engram semantic-rule-promote` |
218
+ | `semanticRuleVerificationEnabled` | `false` | Verify promoted semantic rules against their cited source episodes at recall time and inject a dedicated `Verified Rules` section via `openclaw engram semantic-rule-verify` |
219
+ | `creationMemoryEnabled` | `false` | Enable the creation-memory foundation, including the work-product ledger and operator-facing write/status commands |
220
+ | `workProductLedgerDir` | `{memoryDir}/state/work-product-ledger` | Root directory for typed work-product ledger entries |
214
221
 
215
222
  Full reference: [Config Reference](docs/config-reference.md)
216
223
 
package/dist/index.js CHANGED
@@ -306,6 +306,9 @@ function parseConfig(raw) {
306
306
  abstractionAnchorsEnabled: cfg.abstractionAnchorsEnabled === true,
307
307
  verifiedRecallEnabled: cfg.verifiedRecallEnabled === true,
308
308
  semanticRulePromotionEnabled: cfg.semanticRulePromotionEnabled === true,
309
+ semanticRuleVerificationEnabled: cfg.semanticRuleVerificationEnabled === true,
310
+ creationMemoryEnabled: cfg.creationMemoryEnabled === true,
311
+ workProductLedgerDir: typeof cfg.workProductLedgerDir === "string" && cfg.workProductLedgerDir.trim().length > 0 ? cfg.workProductLedgerDir.trim() : path.join(memoryDir, "state", "work-product-ledger"),
309
312
  abstractionNodeStoreDir: typeof cfg.abstractionNodeStoreDir === "string" && cfg.abstractionNodeStoreDir.trim().length > 0 ? cfg.abstractionNodeStoreDir.trim() : path.join(memoryDir, "state", "abstraction-nodes"),
310
313
  // Local LLM Provider (v2.1)
311
314
  localLlmEnabled: cfg.localLlmEnabled === true || cfg.localLlmEnabled === "true",
@@ -595,6 +598,12 @@ function buildDefaultRecallPipeline(cfg) {
595
598
  maxResults: 3,
596
599
  maxChars: 1800
597
600
  },
601
+ {
602
+ id: "verified-rules",
603
+ enabled: cfg.semanticRuleVerificationEnabled === true,
604
+ maxResults: 3,
605
+ maxChars: 1800
606
+ },
598
607
  {
599
608
  id: "memories",
600
609
  enabled: true,
@@ -15430,6 +15439,100 @@ async function searchVerifiedEpisodes(options) {
15430
15439
  ).slice(0, options.maxResults);
15431
15440
  }
15432
15441
 
15442
+ // src/semantic-rule-verifier.ts
15443
+ var DEFAULT_MIN_EFFECTIVE_CONFIDENCE = 0.45;
15444
+ function verificationConfidenceMultiplier(status) {
15445
+ switch (status) {
15446
+ case "verified":
15447
+ return 1;
15448
+ case "source-memory-not-episode":
15449
+ return 0.45;
15450
+ case "source-memory-archived":
15451
+ return 0.4;
15452
+ case "source-memory-missing":
15453
+ return 0.35;
15454
+ default:
15455
+ return 0.35;
15456
+ }
15457
+ }
15458
+ function resolveVerificationStatus(sourceMemory) {
15459
+ if (!sourceMemory) return "source-memory-missing";
15460
+ if (sourceMemory.frontmatter.status === "archived") return "source-memory-archived";
15461
+ if (sourceMemory.frontmatter.memoryKind !== "episode") return "source-memory-not-episode";
15462
+ return "verified";
15463
+ }
15464
+ function resolveEffectiveConfidence(rule, sourceMemory) {
15465
+ const status = resolveVerificationStatus(sourceMemory);
15466
+ const ruleConfidence = Number.isFinite(rule.frontmatter.confidence) ? rule.frontmatter.confidence : 0.8;
15467
+ const sourceConfidence = Number.isFinite(sourceMemory?.frontmatter.confidence) ? sourceMemory.frontmatter.confidence : ruleConfidence;
15468
+ const anchoredConfidence = Math.min(ruleConfidence, sourceConfidence);
15469
+ const effectiveConfidence = Math.max(
15470
+ 0,
15471
+ Math.min(1, anchoredConfidence * verificationConfidenceMultiplier(status))
15472
+ );
15473
+ return { status, effectiveConfidence };
15474
+ }
15475
+ function scoreVerifiedSemanticRuleCandidate(rule, sourceMemory, queryTokens, effectiveConfidence) {
15476
+ const matchedFields = /* @__PURE__ */ new Set();
15477
+ let score = 0;
15478
+ const ruleContentMatches = countRecallTokenOverlap(queryTokens, rule.content);
15479
+ if (ruleContentMatches > 0) {
15480
+ score += ruleContentMatches * 5;
15481
+ matchedFields.add("ruleContent");
15482
+ }
15483
+ const tagMatches = countRecallTokenOverlap(queryTokens, rule.frontmatter.tags?.join(" "));
15484
+ if (tagMatches > 0) {
15485
+ score += tagMatches * 2;
15486
+ matchedFields.add("tags");
15487
+ }
15488
+ const sourceContentMatches = countRecallTokenOverlap(queryTokens, sourceMemory?.content);
15489
+ if (sourceContentMatches > 0) {
15490
+ score += sourceContentMatches * 2;
15491
+ matchedFields.add("sourceContent");
15492
+ }
15493
+ if (score > 0) {
15494
+ score += effectiveConfidence;
15495
+ }
15496
+ return { score, matchedFields };
15497
+ }
15498
+ async function searchVerifiedSemanticRules(options) {
15499
+ const queryTokens = new Set(normalizeRecallTokens(options.query, ["what", "which"]));
15500
+ if (queryTokens.size === 0 || options.maxResults <= 0) return [];
15501
+ const storage = new StorageManager(options.memoryDir);
15502
+ const allMemories = await storage.readAllMemories();
15503
+ const memoryById = new Map(allMemories.map((memory) => [memory.frontmatter.id, memory]));
15504
+ const minEffectiveConfidence = options.minEffectiveConfidence ?? DEFAULT_MIN_EFFECTIVE_CONFIDENCE;
15505
+ const candidates = [];
15506
+ for (const memory of allMemories) {
15507
+ if (memory.frontmatter.category !== "rule") continue;
15508
+ if (memory.frontmatter.status === "archived") continue;
15509
+ if (memory.frontmatter.source !== "semantic-rule-promotion") continue;
15510
+ const sourceMemoryId = memory.frontmatter.sourceMemoryId;
15511
+ if (!sourceMemoryId) continue;
15512
+ const sourceMemory = memoryById.get(sourceMemoryId);
15513
+ const { status, effectiveConfidence } = resolveEffectiveConfidence(memory, sourceMemory);
15514
+ if (effectiveConfidence < minEffectiveConfidence) continue;
15515
+ const { score, matchedFields } = scoreVerifiedSemanticRuleCandidate(
15516
+ memory,
15517
+ sourceMemory,
15518
+ queryTokens,
15519
+ effectiveConfidence
15520
+ );
15521
+ if (score <= 0) continue;
15522
+ candidates.push({
15523
+ rule: memory,
15524
+ score,
15525
+ sourceMemoryId,
15526
+ verificationStatus: status,
15527
+ effectiveConfidence,
15528
+ matchedFields: [...matchedFields].sort()
15529
+ });
15530
+ }
15531
+ return candidates.sort(
15532
+ (left, right) => right.score - left.score || right.effectiveConfidence - left.effectiveConfidence || right.rule.frontmatter.updated.localeCompare(left.rule.frontmatter.updated)
15533
+ ).slice(0, options.maxResults);
15534
+ }
15535
+
15433
15536
  // src/replay/types.ts
15434
15537
  var VALID_SOURCES = /* @__PURE__ */ new Set(["openclaw", "claude", "chatgpt"]);
15435
15538
  var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
@@ -19234,6 +19337,25 @@ ${r.snippet.trim()}
19234
19337
  timings.verifiedRecall = `${Date.now() - t0}ms`;
19235
19338
  return results.length > 0 ? this.formatVerifiedEpisodeResults(results) : null;
19236
19339
  })();
19340
+ const verifiedRulesPromise = (async () => {
19341
+ const t0 = Date.now();
19342
+ if (!this.config.semanticRuleVerificationEnabled || !this.isRecallSectionEnabled("verified-rules", this.config.semanticRuleVerificationEnabled === true)) {
19343
+ timings.verifiedRules = "skip";
19344
+ return null;
19345
+ }
19346
+ const maxResults = this.getRecallSectionNumber("verified-rules", "maxResults") ?? 3;
19347
+ if (maxResults <= 0) {
19348
+ timings.verifiedRules = "skip(limit=0)";
19349
+ return null;
19350
+ }
19351
+ const results = await searchVerifiedSemanticRules({
19352
+ memoryDir: this.config.memoryDir,
19353
+ query: retrievalQuery,
19354
+ maxResults
19355
+ });
19356
+ timings.verifiedRules = `${Date.now() - t0}ms`;
19357
+ return results.length > 0 ? this.formatVerifiedSemanticRuleResults(results) : null;
19358
+ })();
19237
19359
  const qmdPromise = (async () => {
19238
19360
  if (recallResultLimit <= 0) {
19239
19361
  timings.qmd = "skip(limit=0)";
@@ -19442,6 +19564,7 @@ ${formatted}`;
19442
19564
  trustZoneSection,
19443
19565
  harmonicRetrievalSection,
19444
19566
  verifiedRecallSection,
19567
+ verifiedRulesSection,
19445
19568
  qmdResult,
19446
19569
  transcriptSection,
19447
19570
  compactionSection,
@@ -19459,6 +19582,7 @@ ${formatted}`;
19459
19582
  trustZonePromise,
19460
19583
  harmonicRetrievalPromise,
19461
19584
  verifiedRecallPromise,
19585
+ verifiedRulesPromise,
19462
19586
  qmdPromise,
19463
19587
  transcriptPromise,
19464
19588
  compactionPromise,
@@ -19528,6 +19652,9 @@ ${tmtNode.summary}`);
19528
19652
  if (verifiedRecallSection) {
19529
19653
  this.appendRecallSection(sectionBuckets, "verified-episodes", verifiedRecallSection);
19530
19654
  }
19655
+ if (verifiedRulesSection) {
19656
+ this.appendRecallSection(sectionBuckets, "verified-rules", verifiedRulesSection);
19657
+ }
19531
19658
  if (qmdResult) {
19532
19659
  const t0 = Date.now();
19533
19660
  const { memoryResultsLists, globalResults } = qmdResult;
@@ -21751,6 +21878,27 @@ ${details.join("\n")}`;
21751
21878
  });
21752
21879
  return `## Verified Episodes
21753
21880
 
21881
+ ${lines.join("\n\n")}`;
21882
+ }
21883
+ formatVerifiedSemanticRuleResults(results) {
21884
+ const lines = results.map(({ rule, sourceMemoryId, verificationStatus, effectiveConfidence, matchedFields }, index) => {
21885
+ const header = [
21886
+ `[${index + 1}] ${rule.frontmatter.updated.replace("T", " ").slice(0, 16)}`,
21887
+ verificationStatus,
21888
+ `confidence:${effectiveConfidence.toFixed(2)}`
21889
+ ].join(" | ");
21890
+ const details = [
21891
+ rule.content,
21892
+ `source memory: ${sourceMemoryId}`
21893
+ ];
21894
+ if (matchedFields.length > 0) {
21895
+ details.push(`matched: ${matchedFields.join(", ")}`);
21896
+ }
21897
+ return `${header}
21898
+ ${details.join("\n")}`;
21899
+ });
21900
+ return `## Verified Rules
21901
+
21754
21902
  ${lines.join("\n\n")}`;
21755
21903
  }
21756
21904
  summarizeIdentityText(raw, maxLines, maxChars) {
@@ -24732,7 +24880,7 @@ promotionCandidates: ${res.promotionCandidateCount}`
24732
24880
  }
24733
24881
 
24734
24882
  // src/cli.ts
24735
- import path57 from "path";
24883
+ import path58 from "path";
24736
24884
  import { access as access3, readFile as readFile37, readdir as readdir24, unlink as unlink7 } from "fs/promises";
24737
24885
  import { createHash as createHash10 } from "crypto";
24738
24886
 
@@ -25613,8 +25761,8 @@ function gatherCandidates(input, warnings) {
25613
25761
  const record = rec;
25614
25762
  const content = typeof record.content === "string" ? record.content : null;
25615
25763
  if (!content) continue;
25616
- const path59 = typeof record.path === "string" ? record.path : "";
25617
- if (!path59.startsWith("transcripts/") && !path59.includes("/transcripts/")) continue;
25764
+ const path60 = typeof record.path === "string" ? record.path : "";
25765
+ if (!path60.startsWith("transcripts/") && !path60.includes("/transcripts/")) continue;
25618
25766
  rows.push(...parseJsonl(content, warnings));
25619
25767
  }
25620
25768
  return rows;
@@ -27248,6 +27396,104 @@ async function runCompatChecks(options) {
27248
27396
  };
27249
27397
  }
27250
27398
 
27399
+ // src/work-product-ledger.ts
27400
+ import path57 from "path";
27401
+ import { mkdir as mkdir38, writeFile as writeFile35 } from "fs/promises";
27402
+ function resolveWorkProductLedgerDir(memoryDir, overrideDir) {
27403
+ if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
27404
+ return overrideDir.trim();
27405
+ }
27406
+ return path57.join(memoryDir, "state", "work-product-ledger");
27407
+ }
27408
+ function validateWorkProductLedgerEntry(raw) {
27409
+ if (!isRecord2(raw)) throw new Error("work-product ledger entry must be an object");
27410
+ if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
27411
+ const source = assertString2(raw.source, "source");
27412
+ if (!["tool_result", "cli", "system", "manual"].includes(source)) {
27413
+ throw new Error("source must be one of tool_result|cli|system|manual");
27414
+ }
27415
+ const kind = assertString2(raw.kind, "kind");
27416
+ if (!["artifact", "file", "record", "report", "workspace"].includes(kind)) {
27417
+ throw new Error("kind must be one of artifact|file|record|report|workspace");
27418
+ }
27419
+ const action = assertString2(raw.action, "action");
27420
+ if (!["created", "updated", "deleted", "referenced", "published"].includes(action)) {
27421
+ throw new Error("action must be one of created|updated|deleted|referenced|published");
27422
+ }
27423
+ return {
27424
+ schemaVersion: 1,
27425
+ entryId: assertSafePathSegment(assertString2(raw.entryId, "entryId"), "entryId"),
27426
+ recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
27427
+ sessionKey: assertString2(raw.sessionKey, "sessionKey"),
27428
+ source,
27429
+ kind,
27430
+ action,
27431
+ scope: assertString2(raw.scope, "scope"),
27432
+ summary: assertString2(raw.summary, "summary"),
27433
+ artifactPath: optionalString(raw.artifactPath),
27434
+ objectiveStateSnapshotRefs: optionalStringArray2(raw.objectiveStateSnapshotRefs, "objectiveStateSnapshotRefs"),
27435
+ entityRefs: optionalStringArray2(raw.entityRefs, "entityRefs"),
27436
+ tags: optionalStringArray2(raw.tags, "tags"),
27437
+ metadata: validateStringRecord(raw.metadata, "metadata")
27438
+ };
27439
+ }
27440
+ async function recordWorkProductLedgerEntry(options) {
27441
+ const rootDir = resolveWorkProductLedgerDir(options.memoryDir, options.workProductLedgerDir);
27442
+ const validated = validateWorkProductLedgerEntry(options.entry);
27443
+ const day = recordStoreDay(validated.recordedAt);
27444
+ const entriesDir = path57.join(rootDir, "entries", day);
27445
+ const filePath = path57.join(entriesDir, `${validated.entryId}.json`);
27446
+ await mkdir38(entriesDir, { recursive: true });
27447
+ await writeFile35(filePath, JSON.stringify(validated, null, 2), "utf8");
27448
+ return filePath;
27449
+ }
27450
+ async function readWorkProductLedgerEntries(options) {
27451
+ const rootDir = resolveWorkProductLedgerDir(options.memoryDir, options.workProductLedgerDir);
27452
+ const files = await listJsonFiles(path57.join(rootDir, "entries"));
27453
+ const entries = [];
27454
+ const invalidEntries = [];
27455
+ for (const filePath of files) {
27456
+ try {
27457
+ entries.push(validateWorkProductLedgerEntry(await readJsonFile(filePath)));
27458
+ } catch (error) {
27459
+ invalidEntries.push({
27460
+ path: filePath,
27461
+ error: error instanceof Error ? error.message : String(error)
27462
+ });
27463
+ }
27464
+ }
27465
+ return { files, entries, invalidEntries };
27466
+ }
27467
+ async function getWorkProductLedgerStatus(options) {
27468
+ const rootDir = resolveWorkProductLedgerDir(options.memoryDir, options.workProductLedgerDir);
27469
+ const entriesDir = path57.join(rootDir, "entries");
27470
+ const { files, entries, invalidEntries } = await readWorkProductLedgerEntries(options);
27471
+ entries.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
27472
+ const byKind = {};
27473
+ const byAction = {};
27474
+ for (const entry of entries) {
27475
+ byKind[entry.kind] = (byKind[entry.kind] ?? 0) + 1;
27476
+ byAction[entry.action] = (byAction[entry.action] ?? 0) + 1;
27477
+ }
27478
+ return {
27479
+ enabled: options.enabled,
27480
+ rootDir,
27481
+ entriesDir,
27482
+ entries: {
27483
+ total: files.length,
27484
+ valid: entries.length,
27485
+ invalid: invalidEntries.length,
27486
+ byKind,
27487
+ byAction,
27488
+ latestEntryId: entries[0]?.entryId,
27489
+ latestRecordedAt: entries[0]?.recordedAt,
27490
+ latestSessionKey: entries[0]?.sessionKey
27491
+ },
27492
+ latestEntry: entries[0],
27493
+ invalidEntries
27494
+ };
27495
+ }
27496
+
27251
27497
  // src/semantic-rule-promotion.ts
27252
27498
  function normalizeRuleWhitespace(value) {
27253
27499
  return value.replace(/\s+/g, " ").trim();
@@ -27621,6 +27867,29 @@ async function runSemanticRulePromoteCliCommand(options) {
27621
27867
  dryRun: options.dryRun
27622
27868
  });
27623
27869
  }
27870
+ async function runSemanticRuleVerifyCliCommand(options) {
27871
+ if (!options.semanticRuleVerificationEnabled) return [];
27872
+ return searchVerifiedSemanticRules({
27873
+ memoryDir: options.memoryDir,
27874
+ query: options.query,
27875
+ maxResults: Math.max(1, Math.floor(options.maxResults ?? 3))
27876
+ });
27877
+ }
27878
+ async function runWorkProductStatusCliCommand(options) {
27879
+ return getWorkProductLedgerStatus({
27880
+ memoryDir: options.memoryDir,
27881
+ workProductLedgerDir: options.workProductLedgerDir,
27882
+ enabled: options.creationMemoryEnabled
27883
+ });
27884
+ }
27885
+ async function runWorkProductRecordCliCommand(options) {
27886
+ if (!options.creationMemoryEnabled) return null;
27887
+ return recordWorkProductLedgerEntry({
27888
+ memoryDir: options.memoryDir,
27889
+ workProductLedgerDir: options.workProductLedgerDir,
27890
+ entry: options.entry
27891
+ });
27892
+ }
27624
27893
  async function runTrustZonePromoteCliCommand(options) {
27625
27894
  const result = await promoteTrustZoneRecord({
27626
27895
  memoryDir: options.memoryDir,
@@ -27867,7 +28136,7 @@ function policyVersionForValues(values, config) {
27867
28136
  return createHash10("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
27868
28137
  }
27869
28138
  async function readRuntimePolicySnapshot2(config, fileName) {
27870
- const filePath = path57.join(config.memoryDir, "state", fileName);
28139
+ const filePath = path58.join(config.memoryDir, "state", fileName);
27871
28140
  const snapshot = await readRuntimePolicySnapshot(filePath, {
27872
28141
  maxStaleDecayThreshold: config.lifecycleArchiveDecayThreshold
27873
28142
  });
@@ -28451,14 +28720,14 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
28451
28720
  const ns = (namespace ?? "").trim();
28452
28721
  if (!ns) return orchestrator.config.memoryDir;
28453
28722
  if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
28454
- const candidate = path57.join(orchestrator.config.memoryDir, "namespaces", ns);
28723
+ const candidate = path58.join(orchestrator.config.memoryDir, "namespaces", ns);
28455
28724
  if (ns === orchestrator.config.defaultNamespace) {
28456
28725
  return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
28457
28726
  }
28458
28727
  return candidate;
28459
28728
  }
28460
28729
  async function readAllMemoryFiles(memoryDir) {
28461
- const roots = [path57.join(memoryDir, "facts"), path57.join(memoryDir, "corrections")];
28730
+ const roots = [path58.join(memoryDir, "facts"), path58.join(memoryDir, "corrections")];
28462
28731
  const out = [];
28463
28732
  const walk = async (dir) => {
28464
28733
  let entries;
@@ -28469,7 +28738,7 @@ async function readAllMemoryFiles(memoryDir) {
28469
28738
  }
28470
28739
  for (const entry of entries) {
28471
28740
  const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
28472
- const fullPath = path57.join(dir, entryName);
28741
+ const fullPath = path58.join(dir, entryName);
28473
28742
  if (entry.isDirectory()) {
28474
28743
  await walk(fullPath);
28475
28744
  continue;
@@ -28848,6 +29117,46 @@ function registerCli(api, orchestrator) {
28848
29117
  console.log(JSON.stringify(results, null, 2));
28849
29118
  console.log("OK");
28850
29119
  });
29120
+ cmd.command("work-product-status").description("Show work-product ledger status, entry counts, and the latest recorded work product").action(async () => {
29121
+ const status = await runWorkProductStatusCliCommand({
29122
+ memoryDir: orchestrator.config.memoryDir,
29123
+ workProductLedgerDir: orchestrator.config.workProductLedgerDir,
29124
+ creationMemoryEnabled: orchestrator.config.creationMemoryEnabled
29125
+ });
29126
+ console.log(JSON.stringify(status, null, 2));
29127
+ console.log("OK");
29128
+ });
29129
+ cmd.command("work-product-record").description("Record a work-product ledger entry when creation-memory is enabled").requiredOption("--entry-id <entryId>", "Ledger entry id").requiredOption("--recorded-at <recordedAt>", "ISO timestamp for the entry").requiredOption("--session-key <sessionKey>", "Session key that created the work product").requiredOption("--source <source>", "Entry source (tool_result|cli|system|manual)").requiredOption("--kind <kind>", "Entry kind (artifact|file|record|report|workspace)").requiredOption(
29130
+ "--entry-action <entryAction>",
29131
+ "Entry action (created|updated|deleted|referenced|published)"
29132
+ ).requiredOption("--scope <scope>", "Primary scope or identifier for the created work product").requiredOption("--summary <summary>", "Human-readable summary of the work product").option("--artifact-path <artifactPath>", "Optional path to the created artifact").option("--tag <tag...>", "Tags to attach to the work-product entry").option("--entity-ref <entityRef...>", "Entity refs to attach to the work-product entry").option(
29133
+ "--objective-state-snapshot-ref <objectiveStateSnapshotRef...>",
29134
+ "Objective-state snapshot refs to link to this work product"
29135
+ ).action(async (...args) => {
29136
+ const options = args[0] ?? {};
29137
+ const filePath = await runWorkProductRecordCliCommand({
29138
+ memoryDir: orchestrator.config.memoryDir,
29139
+ workProductLedgerDir: orchestrator.config.workProductLedgerDir,
29140
+ creationMemoryEnabled: orchestrator.config.creationMemoryEnabled,
29141
+ entry: {
29142
+ schemaVersion: 1,
29143
+ entryId: String(options.entryId ?? ""),
29144
+ recordedAt: String(options.recordedAt ?? ""),
29145
+ sessionKey: String(options.sessionKey ?? ""),
29146
+ source: String(options.source ?? ""),
29147
+ kind: String(options.kind ?? ""),
29148
+ action: String(options.entryAction ?? ""),
29149
+ scope: String(options.scope ?? ""),
29150
+ summary: String(options.summary ?? ""),
29151
+ artifactPath: typeof options.artifactPath === "string" ? options.artifactPath : void 0,
29152
+ tags: Array.isArray(options.tag) ? options.tag.map(String) : void 0,
29153
+ entityRefs: Array.isArray(options.entityRef) ? options.entityRef.map(String) : void 0,
29154
+ objectiveStateSnapshotRefs: Array.isArray(options.objectiveStateSnapshotRef) ? options.objectiveStateSnapshotRef.map(String) : void 0
29155
+ }
29156
+ });
29157
+ console.log(JSON.stringify({ wrote: filePath !== null, filePath }, null, 2));
29158
+ console.log("OK");
29159
+ });
28851
29160
  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) => {
28852
29161
  const options = args[0] ?? {};
28853
29162
  const result = await runTrustZonePromoteCliCommand({
@@ -28891,6 +29200,19 @@ function registerCli(api, orchestrator) {
28891
29200
  console.log(JSON.stringify(result, null, 2));
28892
29201
  console.log("OK");
28893
29202
  });
29203
+ cmd.command("semantic-rule-verify").description("Preview verified semantic-rule recall with provenance-aware confidence downgrades").argument("<query>", "Prompt-like query to evaluate against verified semantic-rule recall").option("--max-results <count>", "Maximum number of verified semantic rules to return", "3").action(async (...args) => {
29204
+ const query = typeof args[0] === "string" ? args[0] : "";
29205
+ const options = args[1] ?? {};
29206
+ const maxResults = typeof options.maxResults === "string" ? Number.parseInt(options.maxResults, 10) : 3;
29207
+ const results = await runSemanticRuleVerifyCliCommand({
29208
+ memoryDir: orchestrator.config.memoryDir,
29209
+ semanticRuleVerificationEnabled: orchestrator.config.semanticRuleVerificationEnabled,
29210
+ query,
29211
+ maxResults: Number.isFinite(maxResults) ? maxResults : 3
29212
+ });
29213
+ console.log(JSON.stringify(results, null, 2));
29214
+ console.log("OK");
29215
+ });
28894
29216
  cmd.command("conversation-index-health").description("Show conversation index backend health and index stats").action(async () => {
28895
29217
  const health = await runConversationIndexHealthCliCommand(orchestrator);
28896
29218
  console.log(JSON.stringify(health, null, 2));
@@ -29540,7 +29862,7 @@ function registerCli(api, orchestrator) {
29540
29862
  }
29541
29863
  });
29542
29864
  cmd.command("identity").description("Show agent identity reflections").action(async () => {
29543
- const workspaceDir = path57.join(process.env.HOME ?? "~", ".openclaw", "workspace");
29865
+ const workspaceDir = path58.join(process.env.HOME ?? "~", ".openclaw", "workspace");
29544
29866
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
29545
29867
  if (!identity) {
29546
29868
  console.log("No identity file found.");
@@ -29763,8 +30085,8 @@ function registerCli(api, orchestrator) {
29763
30085
  const options = args[0] ?? {};
29764
30086
  const threadId = options.thread;
29765
30087
  const top = parseInt(options.top ?? "10", 10);
29766
- const memoryDir = path57.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
29767
- const threading = new ThreadingManager(path57.join(memoryDir, "threads"));
30088
+ const memoryDir = path58.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
30089
+ const threading = new ThreadingManager(path58.join(memoryDir, "threads"));
29768
30090
  if (threadId) {
29769
30091
  const thread = await threading.loadThread(threadId);
29770
30092
  if (!thread) {
@@ -30240,9 +30562,9 @@ async function recordObjectiveStateSnapshotsFromAgentMessages(options) {
30240
30562
  }
30241
30563
 
30242
30564
  // src/index.ts
30243
- import { readFile as readFile38, writeFile as writeFile35 } from "fs/promises";
30565
+ import { readFile as readFile38, writeFile as writeFile36 } from "fs/promises";
30244
30566
  import { readFileSync as readFileSync4 } from "fs";
30245
- import path58 from "path";
30567
+ import path59 from "path";
30246
30568
  import os6 from "os";
30247
30569
  var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
30248
30570
  var ENGRAM_HOOK_APIS = "__openclawEngramHookApis";
@@ -30250,7 +30572,7 @@ function loadPluginConfigFromFile() {
30250
30572
  try {
30251
30573
  const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
30252
30574
  const homeDir = process.env.HOME ?? os6.homedir();
30253
- const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path58.join(homeDir, ".openclaw", "openclaw.json");
30575
+ const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path59.join(homeDir, ".openclaw", "openclaw.json");
30254
30576
  const content = readFileSync4(configPath, "utf-8");
30255
30577
  const config = JSON.parse(content);
30256
30578
  const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
@@ -30500,11 +30822,11 @@ Use this context naturally when relevant. Never quote or expose this memory cont
30500
30822
  `session reset via API for ${sessionKey}, new sessionId=${result.sessionId}`
30501
30823
  );
30502
30824
  const safeSessionKey = sanitizeSessionKeyForFilename(sessionKey);
30503
- const signalPath = path58.join(
30825
+ const signalPath = path59.join(
30504
30826
  workspaceDir,
30505
30827
  `.compaction-reset-signal-${safeSessionKey}`
30506
30828
  );
30507
- await writeFile35(
30829
+ await writeFile36(
30508
30830
  signalPath,
30509
30831
  JSON.stringify({
30510
30832
  sessionKey,
@@ -30531,7 +30853,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
30531
30853
  );
30532
30854
  async function ensureHourlySummaryCron(api2) {
30533
30855
  const jobId = "engram-hourly-summary";
30534
- const cronFilePath = path58.join(os6.homedir(), ".openclaw", "cron", "jobs.json");
30856
+ const cronFilePath = path59.join(os6.homedir(), ".openclaw", "cron", "jobs.json");
30535
30857
  try {
30536
30858
  let jobsData = { version: 1, jobs: [] };
30537
30859
  try {
@@ -30572,7 +30894,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
30572
30894
  state: {}
30573
30895
  };
30574
30896
  jobsData.jobs.push(newJob);
30575
- await writeFile35(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
30897
+ await writeFile36(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
30576
30898
  log.info("auto-registered hourly summary cron job");
30577
30899
  } catch (err) {
30578
30900
  log.error("failed to auto-register hourly summary cron job:", err);