@remnic/plugin-openclaw 1.0.10 → 1.0.11

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.
Files changed (69) hide show
  1. package/dist/{calibration-674TDQNV.js → calibration-WCHOK6DX.js} +12 -4
  2. package/dist/capsule-cli-TFKLAG3S.js +329 -0
  3. package/dist/capsule-crypto-K3IRTKRH.js +17 -0
  4. package/dist/capsule-export-CVA3CKUQ.js +265 -0
  5. package/dist/capsule-import-CFX7BY5W.js +16 -0
  6. package/dist/capsule-merge-7RVOHJK3.js +189 -0
  7. package/dist/{causal-chain-OKDZSDEB.js → causal-chain-WYN5QOPS.js} +3 -2
  8. package/dist/{causal-consolidation-5BEXLQV5.js → causal-consolidation-JD6KJJH6.js} +16 -12
  9. package/dist/{causal-retrieval-3BKBXVXD.js → causal-retrieval-NZHQOZOE.js} +6 -5
  10. package/dist/{causal-trajectory-graph-RQIT37DN.js → causal-trajectory-graph-VBPE2WPM.js} +1 -1
  11. package/dist/chunk-37NKFWSO.js +233 -0
  12. package/dist/chunk-3G7FAF6S.js +60 -0
  13. package/dist/{chunk-Z7GRLVK3.js → chunk-3GUF7RQI.js} +235 -19
  14. package/dist/chunk-3I7RHWYT.js +214 -0
  15. package/dist/chunk-4G2XCSD2.js +186 -0
  16. package/dist/chunk-6IWEAUN6.js +148 -0
  17. package/dist/{chunk-LN5UZQVG.js → chunk-6UFI73TJ.js} +5 -3
  18. package/dist/chunk-7OQEPGQF.js +529 -0
  19. package/dist/chunk-B52XADV3.js +244 -0
  20. package/dist/chunk-BU5KJVWF.js +78 -0
  21. package/dist/chunk-CXM7EBAO.js +289 -0
  22. package/dist/chunk-ETJZRIAM.js +227 -0
  23. package/dist/chunk-FQRSVYY4.js +110 -0
  24. package/dist/chunk-HRGFO6AW.js +349 -0
  25. package/dist/chunk-I6B2W2IY.js +47 -0
  26. package/dist/chunk-JZBOXOUC.js +259 -0
  27. package/dist/chunk-K7EUBNDD.js +185 -0
  28. package/dist/chunk-L4PRBB2A.js +1860 -0
  29. package/dist/chunk-MBIFE6SA.js +250 -0
  30. package/dist/chunk-N7EOZY6F.js +400 -0
  31. package/dist/chunk-NKVIN6RD.js +118 -0
  32. package/dist/chunk-OEI7GLV2.js +17 -0
  33. package/dist/{chunk-S2ISS4AH.js → chunk-P3DIW2SD.js} +10 -10
  34. package/dist/{chunk-7TENHBV2.js → chunk-RQCTMECT.js} +10 -48
  35. package/dist/chunk-SSFTU6LP.js +182 -0
  36. package/dist/{chunk-BXTMZDRT.js → chunk-SVSQAG6M.js} +7 -5
  37. package/dist/chunk-TLVIQLB4.js +874 -0
  38. package/dist/{chunk-JJSNPSCD.js → chunk-TNH24SF6.js} +352 -50
  39. package/dist/chunk-TVKKIS53.js +720 -0
  40. package/dist/{chunk-YHH3SXKD.js → chunk-WPINX4MF.js} +1 -59
  41. package/dist/{chunk-HCFFXBLV.js → chunk-XMSDA5WA.js} +5 -1861
  42. package/dist/chunk-YGGGUTG3.js +125 -0
  43. package/dist/chunk-YGXXBRV7.js +10 -0
  44. package/dist/cipher-VHAFCG7Z.js +27 -0
  45. package/dist/dreams-ledger-3I52ISYR.js +285 -0
  46. package/dist/{engine-65C2J63X.js → engine-VMTFKFGO.js} +5 -2
  47. package/dist/{fallback-llm-LVK5PDIM.js → fallback-llm-WCWNGIQ3.js} +2 -1
  48. package/dist/first-start-migration-I24M2JEE.js +258 -0
  49. package/dist/forget-NI4RBDPB.js +68 -0
  50. package/dist/fs-utils-PZRI2HDZ.js +29 -0
  51. package/dist/graph-edge-decay-5CVKWBYH.js +203 -0
  52. package/dist/index.js +9775 -2900
  53. package/dist/kdf-H5B23ZM2.js +25 -0
  54. package/dist/memory-governance-DWGFV4FX.js +25 -0
  55. package/dist/metadata-JAGIWHEA.js +20 -0
  56. package/dist/migrate-from-identity-anchor-N3354WMP.js +7 -0
  57. package/dist/path-5LCUBAAZ.js +8 -0
  58. package/dist/peers-JF2I6RCR.js +43 -0
  59. package/dist/purge-XN2VSPZ2.js +204 -0
  60. package/dist/secure-store-FWJ7LBPH.js +149 -0
  61. package/dist/state-PVISYXRH.js +7 -0
  62. package/dist/state-store-LP5BO6SF.js +15 -0
  63. package/dist/{storage-DM4ZGOCN.js → storage-T2OGFUF4.js} +3 -1
  64. package/dist/tier-stats-IZNW66NC.js +147 -0
  65. package/dist/trace-NJESSGH7.js +289 -0
  66. package/dist/tui-MGK2LYJY.js +12 -0
  67. package/dist/types-H5R5D3WF.js +30 -0
  68. package/openclaw.plugin.json +519 -4
  69. package/package.json +1 -1
@@ -1,12 +1,17 @@
1
1
  import {
2
2
  createVersion
3
3
  } from "./chunk-6OJAU466.js";
4
+ import {
5
+ SecureStoreLockedError,
6
+ readMaybeEncryptedFile,
7
+ writeMaybeEncryptedFile
8
+ } from "./chunk-3I7RHWYT.js";
4
9
  import {
5
10
  log
6
11
  } from "./chunk-UFU5GGGA.js";
7
12
 
8
13
  // ../remnic-core/src/storage.ts
9
- import { access, readdir, readFile as readFile2, stat as stat2, writeFile as writeFile2, mkdir as mkdir2, unlink, rename, appendFile } from "fs/promises";
14
+ import { access, readdir, readFile as readFile2, stat as stat2, writeFile as writeFile2, mkdir as mkdir2, unlink, appendFile } from "fs/promises";
10
15
  import { appendFileSync, mkdirSync, statSync } from "fs";
11
16
  import { createHash } from "crypto";
12
17
  import path4 from "path";
@@ -40,7 +45,7 @@ function getCachedEpisodeMap(baseDir, currentVersion) {
40
45
  function setCachedEpisodeMap(baseDir, memories, version) {
41
46
  const map = /* @__PURE__ */ new Map();
42
47
  for (const m of memories) {
43
- if (m.frontmatter.status === "archived") continue;
48
+ if (m.frontmatter.status === "archived" || m.frontmatter.status === "forgotten") continue;
44
49
  if (m.frontmatter.memoryKind !== "episode") continue;
45
50
  map.set(m.frontmatter.id, m);
46
51
  }
@@ -58,7 +63,7 @@ function setCachedRuleMemories(baseDir, memories, version) {
58
63
  const all = [];
59
64
  for (const m of memories) {
60
65
  byId.set(m.frontmatter.id, m);
61
- if (m.frontmatter.category === "rule" && m.frontmatter.status !== "archived") {
66
+ if (m.frontmatter.category === "rule" && m.frontmatter.status !== "archived" && m.frontmatter.status !== "forgotten") {
62
67
  all.push(m);
63
68
  }
64
69
  }
@@ -180,21 +185,41 @@ function sanitizeMemoryContent(text) {
180
185
  var CONSOLIDATION_OPERATORS = [
181
186
  "split",
182
187
  "merge",
183
- "update"
188
+ "update",
189
+ "pattern-reinforcement"
184
190
  ];
185
191
  var DERIVED_FROM_ENTRY_RE = /^(.+):(\d+)$/;
186
- function isValidDerivedFromEntry(entry) {
187
- if (typeof entry !== "string") return false;
192
+ var DERIVED_FROM_MEMORY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_:-]*$/;
193
+ function looksLikeSnapshotEntry(entry) {
188
194
  const match = entry.match(DERIVED_FROM_ENTRY_RE);
189
195
  if (!match) return false;
190
196
  const pathPart = match[1];
191
- if (pathPart.length === 0 || pathPart.trim().length === 0) return false;
192
- const versionNum = Number(match[2]);
193
- return Number.isInteger(versionNum) && versionNum >= 0;
197
+ return pathPart.includes("/") || pathPart.includes(".");
198
+ }
199
+ function isValidDerivedFromEntry(entry) {
200
+ if (typeof entry !== "string") return false;
201
+ if (entry.length === 0) return false;
202
+ if (looksLikeSnapshotEntry(entry)) {
203
+ const match = entry.match(DERIVED_FROM_ENTRY_RE);
204
+ if (!match) return false;
205
+ const pathPart = match[1];
206
+ if (pathPart.length === 0 || pathPart.trim().length === 0) return false;
207
+ const versionNum = Number(match[2]);
208
+ return Number.isInteger(versionNum) && versionNum >= 0;
209
+ }
210
+ return DERIVED_FROM_MEMORY_ID_RE.test(entry);
194
211
  }
195
212
  function isConsolidationOperator(value) {
196
213
  return typeof value === "string" && CONSOLIDATION_OPERATORS.includes(value);
197
214
  }
215
+ var SEMANTIC_CONSOLIDATION_LLM_OPERATORS = [
216
+ "split",
217
+ "merge",
218
+ "update"
219
+ ];
220
+ function isSemanticConsolidationLlmOperator(value) {
221
+ return typeof value === "string" && SEMANTIC_CONSOLIDATION_LLM_OPERATORS.includes(value);
222
+ }
198
223
 
199
224
  // ../remnic-core/src/entity-schema.ts
200
225
  var DEFAULT_ENTITY_SCHEMAS = {
@@ -541,6 +566,15 @@ function stripCitationForTemplate(text, template) {
541
566
  }
542
567
 
543
568
  // ../remnic-core/src/types.ts
569
+ var RECALL_DISCLOSURE_LEVELS = [
570
+ "chunk",
571
+ "section",
572
+ "raw"
573
+ ];
574
+ var DEFAULT_RECALL_DISCLOSURE = "chunk";
575
+ function isRecallDisclosure(value) {
576
+ return typeof value === "string" && RECALL_DISCLOSURE_LEVELS.includes(value);
577
+ }
544
578
  function confidenceTier(score) {
545
579
  if (score >= 0.95) return "explicit";
546
580
  if (score >= 0.7) return "implied";
@@ -1269,6 +1303,9 @@ function inferMemoryStatus(frontmatter, pathRel, fallbackStatus = "active") {
1269
1303
  if (frontmatter.status) return frontmatter.status;
1270
1304
  return fallbackStatus;
1271
1305
  }
1306
+ function isActiveMemoryStatus(status) {
1307
+ return status === void 0 || status === "active";
1308
+ }
1272
1309
  function summarizeMemoryLifecycleState(memory) {
1273
1310
  return {
1274
1311
  category: memory.frontmatter.category,
@@ -1632,6 +1669,88 @@ function assertMemoryWorthCounter(field, value) {
1632
1669
  throw new Error(`${field} must be >= 0, got ${value}`);
1633
1670
  }
1634
1671
  }
1672
+ function isErrnoCode(error, code) {
1673
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
1674
+ }
1675
+ function trimTrailingSpacesAndTabs(value) {
1676
+ let end = value.length;
1677
+ while (end > 0 && (value[end - 1] === " " || value[end - 1] === " ")) {
1678
+ end -= 1;
1679
+ }
1680
+ return end === value.length ? value : value.slice(0, end);
1681
+ }
1682
+ function trimLeadingSpacesAndTabs(value) {
1683
+ let start = 0;
1684
+ while (start < value.length && (value[start] === " " || value[start] === " ")) {
1685
+ start += 1;
1686
+ }
1687
+ return start === 0 ? value : value.slice(start);
1688
+ }
1689
+ function stripDefaultCitationMarkersWithoutRegex(value) {
1690
+ return stripCitationMarkersForHashRemoval(value, DEFAULT_CITATION_FORMAT);
1691
+ }
1692
+ function citationTemplateLiteralParts(template) {
1693
+ const parts = [];
1694
+ let cursor = 0;
1695
+ while (cursor < template.length) {
1696
+ const open = template.indexOf("{", cursor);
1697
+ if (open === -1) {
1698
+ parts.push(template.slice(cursor));
1699
+ break;
1700
+ }
1701
+ parts.push(template.slice(cursor, open));
1702
+ const close = template.indexOf("}", open + 1);
1703
+ if (close === -1) {
1704
+ cursor = open + 1;
1705
+ } else {
1706
+ cursor = close + 1;
1707
+ }
1708
+ }
1709
+ return parts.filter((part) => part.length > 0);
1710
+ }
1711
+ function stripCitationMarkersForHashRemoval(value, template) {
1712
+ const parts = citationTemplateLiteralParts(template);
1713
+ if (parts.length === 0) return value;
1714
+ const first = parts[0];
1715
+ const lowerValue = value.toLowerCase();
1716
+ const lowerFirst = first.toLowerCase();
1717
+ const lowerParts = parts.map((part) => part.toLowerCase());
1718
+ if (!lowerValue.includes(lowerFirst)) return value;
1719
+ let result = "";
1720
+ let cursor = 0;
1721
+ let removed = false;
1722
+ while (cursor < value.length) {
1723
+ const markerStart = lowerValue.indexOf(lowerFirst, cursor);
1724
+ if (markerStart === -1) {
1725
+ result += value.slice(cursor);
1726
+ break;
1727
+ }
1728
+ const boundedEnd = first.startsWith("[") ? value.indexOf("]", markerStart + first.length) : -1;
1729
+ if (first.startsWith("[") && boundedEnd === -1) {
1730
+ result += value.slice(cursor);
1731
+ break;
1732
+ }
1733
+ const searchLimit = boundedEnd === -1 ? value.length : boundedEnd + 1;
1734
+ let markerEnd = markerStart + first.length;
1735
+ let matched = true;
1736
+ for (let i = 1; i < lowerParts.length; i += 1) {
1737
+ const partIndex = lowerValue.indexOf(lowerParts[i], markerEnd);
1738
+ if (partIndex === -1 || partIndex + parts[i].length > searchLimit) {
1739
+ matched = false;
1740
+ break;
1741
+ }
1742
+ markerEnd = partIndex + parts[i].length;
1743
+ }
1744
+ if (!matched) {
1745
+ result += value.slice(cursor);
1746
+ break;
1747
+ }
1748
+ result += trimTrailingSpacesAndTabs(value.slice(cursor, markerStart));
1749
+ cursor = markerEnd;
1750
+ removed = true;
1751
+ }
1752
+ return removed ? trimLeadingSpacesAndTabs(result) : value;
1753
+ }
1635
1754
  function serializeFrontmatter(fm) {
1636
1755
  const lines = [
1637
1756
  "---",
@@ -1654,6 +1773,10 @@ function serializeFrontmatter(fm) {
1654
1773
  if (fm.supersededBy) lines.push(`supersededBy: ${fm.supersededBy}`);
1655
1774
  if (fm.supersededAt) lines.push(`supersededAt: ${fm.supersededAt}`);
1656
1775
  if (fm.archivedAt) lines.push(`archivedAt: ${fm.archivedAt}`);
1776
+ if (fm.valid_at) lines.push(`validAt: ${fm.valid_at}`);
1777
+ if (fm.invalid_at) lines.push(`invalidAt: ${fm.invalid_at}`);
1778
+ if (fm.forgottenAt) lines.push(`forgottenAt: ${fm.forgottenAt}`);
1779
+ if (fm.forgottenReason) lines.push(`forgottenReason: ${JSON.stringify(fm.forgottenReason)}`);
1657
1780
  if (fm.lifecycleState) lines.push(`lifecycleState: ${fm.lifecycleState}`);
1658
1781
  if (fm.verificationState) lines.push(`verificationState: ${fm.verificationState}`);
1659
1782
  if (fm.policyClass) lines.push(`policyClass: ${fm.policyClass}`);
@@ -1731,11 +1854,22 @@ function serializeFrontmatter(fm) {
1731
1854
  if (fm.derived_via !== void 0) {
1732
1855
  if (!isConsolidationOperator(fm.derived_via)) {
1733
1856
  throw new Error(
1734
- `serializeFrontmatter: invalid derived_via ${JSON.stringify(fm.derived_via)} \u2014 expected one of "split" | "merge" | "update"`
1857
+ `serializeFrontmatter: invalid derived_via ${JSON.stringify(fm.derived_via)} \u2014 expected one of "split" | "merge" | "update" | "pattern-reinforcement"`
1735
1858
  );
1736
1859
  }
1737
1860
  lines.push(`derived_via: ${fm.derived_via}`);
1738
1861
  }
1862
+ if (fm.reinforcement_count !== void 0) {
1863
+ if (!Number.isInteger(fm.reinforcement_count) || fm.reinforcement_count <= 0) {
1864
+ throw new Error(
1865
+ `serializeFrontmatter: reinforcement_count must be a positive integer (got ${JSON.stringify(fm.reinforcement_count)})`
1866
+ );
1867
+ }
1868
+ lines.push(`reinforcement_count: ${fm.reinforcement_count}`);
1869
+ }
1870
+ if (fm.last_reinforced_at) {
1871
+ lines.push(`last_reinforced_at: ${fm.last_reinforced_at}`);
1872
+ }
1739
1873
  lines.push("---");
1740
1874
  return lines.join("\n");
1741
1875
  }
@@ -1768,6 +1902,20 @@ function parseLinkReasonValue(rawValue) {
1768
1902
  return legacyValue;
1769
1903
  }
1770
1904
  }
1905
+ function parseFrontmatterStringValue(rawValue) {
1906
+ if (rawValue === void 0) return void 0;
1907
+ const trimmed = rawValue.trim();
1908
+ if (trimmed.length === 0) return void 0;
1909
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
1910
+ try {
1911
+ const parsed = JSON.parse(trimmed);
1912
+ return typeof parsed === "string" ? parsed : trimmed;
1913
+ } catch {
1914
+ return trimmed.slice(1, -1).replace(/\\"/g, '"');
1915
+ }
1916
+ }
1917
+ return trimmed;
1918
+ }
1771
1919
  function parseMemoryWorthCounterField(raw) {
1772
1920
  if (raw === void 0) return void 0;
1773
1921
  const trimmed = raw.trim();
@@ -1776,6 +1924,14 @@ function parseMemoryWorthCounterField(raw) {
1776
1924
  if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) return void 0;
1777
1925
  return n;
1778
1926
  }
1927
+ function parseReinforcementCountField(raw) {
1928
+ if (raw === void 0) return void 0;
1929
+ const trimmed = raw.trim();
1930
+ if (trimmed.length === 0) return void 0;
1931
+ const n = Number(trimmed);
1932
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) return void 0;
1933
+ return n;
1934
+ }
1779
1935
  function parseFrontmatter2(raw) {
1780
1936
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
1781
1937
  if (!match) return null;
@@ -1961,6 +2117,11 @@ function parseFrontmatter2(raw) {
1961
2117
  supersededBy: fm.supersededBy || void 0,
1962
2118
  supersededAt: fm.supersededAt || void 0,
1963
2119
  archivedAt: fm.archivedAt || void 0,
2120
+ // Issue #680 — explicit fact lifecycle round-trip.
2121
+ valid_at: fm.validAt || void 0,
2122
+ invalid_at: fm.invalidAt || void 0,
2123
+ forgottenAt: fm.forgottenAt || void 0,
2124
+ forgottenReason: parseFrontmatterStringValue(fm.forgottenReason),
1964
2125
  lifecycleState: fm.lifecycleState || void 0,
1965
2126
  verificationState: fm.verificationState || void 0,
1966
2127
  policyClass: fm.policyClass || void 0,
@@ -1995,7 +2156,14 @@ function parseFrontmatter2(raw) {
1995
2156
  // Consolidation provenance (issue #561) — read-through only in this
1996
2157
  // PR; no code produces these fields yet.
1997
2158
  derived_from,
1998
- derived_via
2159
+ derived_via,
2160
+ // Pattern-reinforcement metadata (issue #687 PR 2/4). Parse
2161
+ // permissively: invalid values (negative, non-integer, blank
2162
+ // ISO-strings) are dropped to undefined so a corrupt frontmatter
2163
+ // never poisons downstream scoring. Validation lives on the
2164
+ // write path in serializeFrontmatter.
2165
+ reinforcement_count: parseReinforcementCountField(fm.reinforcement_count),
2166
+ last_reinforced_at: fm.last_reinforced_at || void 0
1999
2167
  },
2000
2168
  content
2001
2169
  };
@@ -3061,6 +3229,76 @@ var StorageManager = class _StorageManager {
3061
3229
  setVersioningConfig(config) {
3062
3230
  this._versioningConfig = config;
3063
3231
  }
3232
+ /**
3233
+ * At-rest encryption key (issue #690 PR 3/4).
3234
+ *
3235
+ * When non-null, every memory file read is decrypted and every write
3236
+ * is encrypted using the secure-fs layer. When null, the storage
3237
+ * layer operates in plain-text mode (legacy/unencrypted store).
3238
+ *
3239
+ * Set by the orchestrator after init/unlock; cleared on lock.
3240
+ * The key buffer is NEVER logged or serialized.
3241
+ */
3242
+ _secureStoreKey = null;
3243
+ /**
3244
+ * When true (and `_secureStoreKey` is non-null), new writes are
3245
+ * encrypted. Set to false to pause encryption of new writes while
3246
+ * still decrypting existing files.
3247
+ */
3248
+ _secureStoreEncryptOnWrite = true;
3249
+ /**
3250
+ * When true, the secure-store is configured as required — writes
3251
+ * MUST be encrypted and a locked store MUST reject writes rather
3252
+ * than silently falling back to plaintext. Set by the orchestrator
3253
+ * from `config.secureStoreEnabled`.
3254
+ */
3255
+ _secureStoreRequired = false;
3256
+ /**
3257
+ * Set or clear the at-rest encryption key.
3258
+ *
3259
+ * Pass a 32-byte Buffer to enable encryption; pass null to clear
3260
+ * (lock) the store. The caller is responsible for key lifecycle —
3261
+ * this method does not zero the buffer on replacement; the keyring
3262
+ * module (`keyring.ts`) owns zeroization.
3263
+ */
3264
+ setSecureStoreKey(key, encryptOnWrite = true) {
3265
+ this._secureStoreKey = key;
3266
+ this._secureStoreEncryptOnWrite = encryptOnWrite;
3267
+ }
3268
+ /**
3269
+ * Mark the secure-store as required for this storage instance.
3270
+ * When required and locked, writes throw SecureStoreLockedError
3271
+ * rather than silently writing plaintext.
3272
+ */
3273
+ setSecureStoreRequired(required) {
3274
+ this._secureStoreRequired = required;
3275
+ }
3276
+ /** Return true iff the secure-store key is currently set (store is unlocked). */
3277
+ isSecureStoreUnlocked() {
3278
+ return this._secureStoreKey !== null;
3279
+ }
3280
+ /**
3281
+ * Resolve the effective write key.
3282
+ *
3283
+ * - If `_secureStoreEncryptOnWrite` is false: returns null (plain write).
3284
+ * - If `_secureStoreEncryptOnWrite` is true AND key is set: returns key.
3285
+ * - If `_secureStoreEncryptOnWrite` is true AND key is null AND
3286
+ * `_secureStoreRequired` is true: throws SecureStoreLockedError so the
3287
+ * write fails loudly rather than silently writing plaintext (P1 finding
3288
+ * from Cursor review of PR #767).
3289
+ * - If `_secureStoreEncryptOnWrite` is true AND key is null AND
3290
+ * `_secureStoreRequired` is false: returns null (unencrypted store).
3291
+ */
3292
+ resolveWriteKey() {
3293
+ if (!this._secureStoreEncryptOnWrite) return null;
3294
+ if (this._secureStoreKey !== null) return this._secureStoreKey;
3295
+ if (this._secureStoreRequired) {
3296
+ throw new SecureStoreLockedError(
3297
+ "secure-store is locked \u2014 cannot write memory file. Run `remnic secure-store unlock` to decrypt, or restart the daemon after unlocking."
3298
+ );
3299
+ }
3300
+ return null;
3301
+ }
3064
3302
  /**
3065
3303
  * Snapshot the current content of a page before overwriting.
3066
3304
  * No-op when versioning is disabled or the file does not yet exist.
@@ -3068,7 +3306,7 @@ var StorageManager = class _StorageManager {
3068
3306
  async snapshotBeforeWrite(filePath, trigger) {
3069
3307
  if (!this._versioningConfig || !this._versioningConfig.enabled) return;
3070
3308
  try {
3071
- const existing = await readFile2(filePath, "utf-8");
3309
+ const existing = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
3072
3310
  await createVersion(filePath, existing, trigger, this._versioningConfig, log, void 0, this.baseDir);
3073
3311
  } catch {
3074
3312
  }
@@ -3091,7 +3329,7 @@ var StorageManager = class _StorageManager {
3091
3329
  if (!this._versioningConfig || !this._versioningConfig.enabled) return null;
3092
3330
  let existing;
3093
3331
  try {
3094
- existing = await readFile2(filePath, "utf-8");
3332
+ existing = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
3095
3333
  } catch {
3096
3334
  return null;
3097
3335
  }
@@ -3433,7 +3671,7 @@ ${sanitized.text}
3433
3671
  filePath = path4.join(this.factsDir, today, `${id}.md`);
3434
3672
  }
3435
3673
  await this.snapshotBeforeWrite(filePath, "write");
3436
- await writeFile2(filePath, fileContent, "utf-8");
3674
+ await writeMaybeEncryptedFile(filePath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
3437
3675
  this.invalidateAllMemoriesCache();
3438
3676
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeMemory", {
3439
3677
  memoryId: id,
@@ -3469,6 +3707,50 @@ ${sanitized.text}
3469
3707
  const sanitized = sanitizeMemoryContent(content);
3470
3708
  return factHashIndex.has(sanitized.text);
3471
3709
  }
3710
+ factContentHashForRemoval(memory) {
3711
+ if (memory.frontmatter.category !== "fact") return null;
3712
+ if (typeof memory.frontmatter.contentHash === "string" && memory.frontmatter.contentHash.length > 0) {
3713
+ return memory.frontmatter.contentHash;
3714
+ }
3715
+ const configuredHashSource = stripCitationMarkersForHashRemoval(memory.content, this.citationTemplate);
3716
+ const hashSource = configuredHashSource !== memory.content ? configuredHashSource : stripDefaultCitationMarkersWithoutRegex(memory.content);
3717
+ return ContentHashIndex.computeHash(sanitizeMemoryContent(hashSource).text);
3718
+ }
3719
+ async removeFactContentHashesForMemories(memories) {
3720
+ await this.ensureFactHashIndexAuthoritative();
3721
+ const factHashIndex = await this.getFactHashIndex();
3722
+ const removedIds = new Set(
3723
+ memories.map((memory) => memory.frontmatter.id).filter((id) => typeof id === "string" && id.length > 0)
3724
+ );
3725
+ const removedHashes = /* @__PURE__ */ new Map();
3726
+ for (const memory of memories) {
3727
+ const hash = this.factContentHashForRemoval(memory);
3728
+ if (hash) {
3729
+ removedHashes.set(memory, hash);
3730
+ }
3731
+ }
3732
+ if (removedHashes.size === 0) return;
3733
+ const remainingActiveHashes = /* @__PURE__ */ new Set();
3734
+ const remainingMemories = [
3735
+ ...await this.readAllMemories(),
3736
+ ...await this.readAllColdMemories()
3737
+ ];
3738
+ for (const memory of remainingMemories) {
3739
+ if (memory.frontmatter.category !== "fact") continue;
3740
+ if (removedIds.has(memory.frontmatter.id)) continue;
3741
+ if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
3742
+ const hash = this.factContentHashForRemoval(memory);
3743
+ if (hash) {
3744
+ remainingActiveHashes.add(hash);
3745
+ }
3746
+ }
3747
+ for (const hash of removedHashes.values()) {
3748
+ if (!remainingActiveHashes.has(hash)) {
3749
+ factHashIndex.removeByHash(hash);
3750
+ }
3751
+ }
3752
+ await factHashIndex.save();
3753
+ }
3472
3754
  async isFactContentHashAuthoritative() {
3473
3755
  await this.ensureFactHashIndexAuthoritative();
3474
3756
  return true;
@@ -3502,10 +3784,10 @@ ${sanitized.text}
3502
3784
  return "";
3503
3785
  }
3504
3786
  const filePath = path4.join(dir, `${id}.md`);
3505
- await writeFile2(filePath, `${serializeFrontmatter(fm)}
3787
+ await writeMaybeEncryptedFile(filePath, `${serializeFrontmatter(fm)}
3506
3788
 
3507
3789
  ${sanitized.text}
3508
- `, "utf-8");
3790
+ `, this.resolveWriteKey(), {}, this.baseDir);
3509
3791
  const actor = typeof options.actor === "string" && options.actor.length > 0 ? options.actor : "storage.writeArtifact";
3510
3792
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeArtifact", {
3511
3793
  memoryId: id,
@@ -3680,15 +3962,19 @@ ${sanitized.text}
3680
3962
  }
3681
3963
  async readProfile() {
3682
3964
  try {
3683
- return await readFile2(this.profilePath, "utf-8");
3684
- } catch {
3685
- return "";
3965
+ return await readMaybeEncryptedFile(this.profilePath, this._secureStoreKey, this.baseDir);
3966
+ } catch (error) {
3967
+ if (error instanceof SecureStoreLockedError) {
3968
+ throw error;
3969
+ }
3970
+ if (isErrnoCode(error, "ENOENT")) return "";
3971
+ throw error;
3686
3972
  }
3687
3973
  }
3688
3974
  async writeProfile(content) {
3689
3975
  await this.ensureDirectories();
3690
3976
  await this.snapshotBeforeWrite(this.profilePath, "consolidation");
3691
- await writeFile2(this.profilePath, content, "utf-8");
3977
+ await writeMaybeEncryptedFile(this.profilePath, content, this.resolveWriteKey(), {}, this.baseDir);
3692
3978
  log.debug("updated profile.md");
3693
3979
  }
3694
3980
  /**
@@ -3771,6 +4057,24 @@ ${sanitized.text}
3771
4057
  invalidateAllMemoriesCacheForDir() {
3772
4058
  this.invalidateAllMemoriesCache();
3773
4059
  }
4060
+ /** Invalidate only the cache layers affected by direct tier file deletes. */
4061
+ invalidateMemoryCachesForTiers(tiers) {
4062
+ let hotChanged = false;
4063
+ let coldChanged = false;
4064
+ for (const tier of tiers) {
4065
+ if (tier === "cold") {
4066
+ coldChanged = true;
4067
+ } else if (tier === "hot" || tier === "archive") {
4068
+ hotChanged = true;
4069
+ }
4070
+ }
4071
+ if (hotChanged) {
4072
+ this.invalidateAllMemoriesCache();
4073
+ }
4074
+ if (coldChanged) {
4075
+ this.invalidateColdMemoriesCache();
4076
+ }
4077
+ }
3774
4078
  /** Clear ALL static caches. Use in tests that write files directly
3775
4079
  * (bypassing StorageManager.writeMemory) to avoid stale reads. */
3776
4080
  static clearAllStaticCaches() {
@@ -3862,7 +4166,7 @@ ${sanitized.text}
3862
4166
  const results = await Promise.all(
3863
4167
  batch.map(async (fullPath) => {
3864
4168
  try {
3865
- const raw = await readFile2(fullPath, "utf-8");
4169
+ const raw = await readMaybeEncryptedFile(fullPath, this._secureStoreKey, this.baseDir);
3866
4170
  const parsed = parseFrontmatter2(raw);
3867
4171
  if (!parsed) return null;
3868
4172
  return {
@@ -3874,7 +4178,8 @@ ${sanitized.text}
3874
4178
  ),
3875
4179
  content: parsed.content
3876
4180
  };
3877
- } catch {
4181
+ } catch (err) {
4182
+ if (err instanceof SecureStoreLockedError) throw err;
3878
4183
  return null;
3879
4184
  }
3880
4185
  })
@@ -3887,7 +4192,7 @@ ${sanitized.text}
3887
4192
  }
3888
4193
  async readWindowUpdatedMs(filePath) {
3889
4194
  try {
3890
- const raw = await readFile2(filePath, "utf-8");
4195
+ const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
3891
4196
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?/);
3892
4197
  if (!match) return null;
3893
4198
  const frontmatterBlock = match[1];
@@ -4071,7 +4376,7 @@ ${sanitized.text}
4071
4376
  await readDir(fullPath);
4072
4377
  } else if (entry.name.endsWith(".md")) {
4073
4378
  try {
4074
- const raw = await readFile2(fullPath, "utf-8");
4379
+ const raw = await readMaybeEncryptedFile(fullPath, this._secureStoreKey, this.baseDir);
4075
4380
  const parsed = parseFrontmatter2(raw);
4076
4381
  if (parsed) {
4077
4382
  memories.push({
@@ -4084,7 +4389,8 @@ ${sanitized.text}
4084
4389
  content: parsed.content
4085
4390
  });
4086
4391
  }
4087
- } catch {
4392
+ } catch (err) {
4393
+ if (err instanceof SecureStoreLockedError) throw err;
4088
4394
  }
4089
4395
  }
4090
4396
  }
@@ -4097,7 +4403,7 @@ ${sanitized.text}
4097
4403
  /** Read a single memory file by its absolute path. Returns null if unreadable. */
4098
4404
  async readMemoryByPath(filePath) {
4099
4405
  try {
4100
- const raw = await readFile2(filePath, "utf-8");
4406
+ const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
4101
4407
  const parsed = parseFrontmatter2(raw);
4102
4408
  if (parsed) {
4103
4409
  return {
@@ -4132,7 +4438,8 @@ ${sanitized.text}
4132
4438
  };
4133
4439
  }
4134
4440
  return null;
4135
- } catch {
4441
+ } catch (err) {
4442
+ if (err instanceof SecureStoreLockedError) throw err;
4136
4443
  return null;
4137
4444
  }
4138
4445
  }
@@ -4170,19 +4477,8 @@ ${sanitized.text}
4170
4477
 
4171
4478
  ${memory.content}
4172
4479
  `;
4173
- await mkdir2(path4.dirname(targetPath), { recursive: true });
4174
- const tempPath = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
4175
- try {
4176
- await writeFile2(tempPath, fileContent, "utf-8");
4177
- await rename(tempPath, targetPath);
4178
- this.invalidateAllMemoriesCache();
4179
- } catch (err) {
4180
- try {
4181
- await unlink(tempPath);
4182
- } catch {
4183
- }
4184
- throw err;
4185
- }
4480
+ await writeMaybeEncryptedFile(targetPath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
4481
+ this.invalidateAllMemoriesCache();
4186
4482
  }
4187
4483
  async moveMemoryToPath(memory, targetPath) {
4188
4484
  await this.writeMemoryFileAtomic(targetPath, memory);
@@ -4253,7 +4549,7 @@ ${memory.content}
4253
4549
  ${memory.content}
4254
4550
  `;
4255
4551
  const destPath = path4.join(destDir, path4.basename(memory.path));
4256
- await writeFile2(destPath, fileContent, "utf-8");
4552
+ await writeMaybeEncryptedFile(destPath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
4257
4553
  await unlink(memory.path);
4258
4554
  this.invalidateAllMemoriesCache();
4259
4555
  await this.appendGeneratedMemoryLifecycleEventFailOpen(
@@ -4374,7 +4670,7 @@ ${memory.content}
4374
4670
 
4375
4671
  ${sanitized.text}
4376
4672
  `;
4377
- await writeFile2(memory.path, fileContent, "utf-8");
4673
+ await writeMaybeEncryptedFile(memory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
4378
4674
  this.invalidateAllMemoriesCache();
4379
4675
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.updateMemory", {
4380
4676
  memoryId: id,
@@ -4406,7 +4702,7 @@ ${sanitized.text}
4406
4702
 
4407
4703
  ${memory.content}
4408
4704
  `;
4409
- await writeFile2(memory.path, fileContent, "utf-8");
4705
+ await writeMaybeEncryptedFile(memory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
4410
4706
  this.invalidateAllMemoriesCache();
4411
4707
  if (memory.path.includes(`${path4.sep}cold${path4.sep}`)) {
4412
4708
  this.invalidateColdMemoriesCache();
@@ -5088,7 +5384,7 @@ ${question}
5088
5384
  for (const file of files) {
5089
5385
  if (!file.endsWith(".md")) continue;
5090
5386
  const filePath = path4.join(this.questionsDir, file);
5091
- const raw = await readFile2(filePath, "utf-8");
5387
+ const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
5092
5388
  const parsed = this.parseQuestionFile(raw, filePath);
5093
5389
  if (parsed) {
5094
5390
  questions.push(parsed);
@@ -5996,7 +6292,7 @@ ${sanitized.text}
5996
6292
  ${oldMemory.content}
5997
6293
  `;
5998
6294
  try {
5999
- await writeFile2(oldMemory.path, fileContent, "utf-8");
6295
+ await writeMaybeEncryptedFile(oldMemory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
6000
6296
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.supersedeMemory", {
6001
6297
  memoryId: oldMemoryId,
6002
6298
  eventType: "superseded",
@@ -6195,6 +6491,12 @@ export {
6195
6491
  normalizeEntitySchemas,
6196
6492
  resolveRequestedEntitySectionKeys,
6197
6493
  sanitizeMemoryContent,
6494
+ MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
6495
+ toMemoryPathRel,
6496
+ inferMemoryStatus,
6497
+ isActiveMemoryStatus,
6498
+ buildLifecycleEventsForMemory,
6499
+ sortMemoryLifecycleEvents,
6198
6500
  hasCitationForTemplate,
6199
6501
  attachCitation,
6200
6502
  stripCitationForTemplate,
@@ -6206,7 +6508,12 @@ export {
6206
6508
  setCachedQmdSearch,
6207
6509
  lintWorkspaceFiles,
6208
6510
  rotateMarkdownFileToArchive,
6511
+ DERIVED_FROM_MEMORY_ID_RE,
6209
6512
  isConsolidationOperator,
6513
+ isSemanticConsolidationLlmOperator,
6514
+ RECALL_DISCLOSURE_LEVELS,
6515
+ DEFAULT_RECALL_DISCLOSURE,
6516
+ isRecallDisclosure,
6210
6517
  confidenceTier,
6211
6518
  openBetterSqlite3,
6212
6519
  MEMORY_PROJECTION_SCHEMA_VERSION,
@@ -6218,11 +6525,6 @@ export {
6218
6525
  readProjectedEntityMentions,
6219
6526
  readProjectedNativeKnowledgeChunks,
6220
6527
  readProjectedGovernanceRecord,
6221
- MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
6222
- toMemoryPathRel,
6223
- inferMemoryStatus,
6224
- buildLifecycleEventsForMemory,
6225
- sortMemoryLifecycleEvents,
6226
6528
  normalizeProjectionPreview,
6227
6529
  normalizeProjectionTags,
6228
6530
  parseContinuityIncident,