@remnic/plugin-openclaw 1.0.10 → 1.0.12

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 (70) hide show
  1. package/dist/{calibration-674TDQNV.js → calibration-WCHOK6DX.js} +12 -4
  2. package/dist/capsule-cli-GBM3WPAM.js +33 -0
  3. package/dist/capsule-crypto-K3IRTKRH.js +17 -0
  4. package/dist/capsule-export-IXVERCQG.js +17 -0
  5. package/dist/capsule-import-IA6VIOPQ.js +16 -0
  6. package/dist/capsule-merge-IWOQ34KL.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-YI53C2AO.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-4G2XCSD2.js +186 -0
  15. package/dist/chunk-4LYQ4ONL.js +185 -0
  16. package/dist/chunk-6F6EKSVP.js +453 -0
  17. package/dist/chunk-6IWEAUN6.js +148 -0
  18. package/dist/{chunk-LN5UZQVG.js → chunk-6UFI73TJ.js} +5 -3
  19. package/dist/chunk-7OQEPGQF.js +529 -0
  20. package/dist/{chunk-JJSNPSCD.js → chunk-7UZNLMW5.js} +652 -174
  21. package/dist/chunk-B52XADV3.js +244 -0
  22. package/dist/chunk-BU5KJVWF.js +78 -0
  23. package/dist/chunk-CDAZGIGT.js +720 -0
  24. package/dist/chunk-CXM7EBAO.js +289 -0
  25. package/dist/{chunk-HCFFXBLV.js → chunk-EXDYWXMB.js} +6 -1861
  26. package/dist/chunk-FGTYFLL5.js +274 -0
  27. package/dist/chunk-FQRSVYY4.js +110 -0
  28. package/dist/chunk-HRGFO6AW.js +349 -0
  29. package/dist/chunk-I6B2W2IY.js +47 -0
  30. package/dist/chunk-JZBOXOUC.js +259 -0
  31. package/dist/chunk-L6I4MQKO.js +227 -0
  32. package/dist/chunk-LLUROTZJ.js +328 -0
  33. package/dist/chunk-MBIFE6SA.js +250 -0
  34. package/dist/chunk-NKVIN6RD.js +118 -0
  35. package/dist/chunk-OAE7AQ6R.js +1832 -0
  36. package/dist/chunk-OEI7GLV2.js +17 -0
  37. package/dist/chunk-RKR6PTPA.js +308 -0
  38. package/dist/{chunk-7TENHBV2.js → chunk-RQCTMECT.js} +10 -48
  39. package/dist/chunk-SSFTU6LP.js +182 -0
  40. package/dist/{chunk-BXTMZDRT.js → chunk-SVSQAG6M.js} +7 -5
  41. package/dist/{chunk-S2ISS4AH.js → chunk-TILAJIJR.js} +10 -10
  42. package/dist/chunk-TLVIQLB4.js +874 -0
  43. package/dist/{chunk-YHH3SXKD.js → chunk-WPINX4MF.js} +1 -59
  44. package/dist/chunk-YGGGUTG3.js +125 -0
  45. package/dist/cipher-VHAFCG7Z.js +27 -0
  46. package/dist/dreams-ledger-3I52ISYR.js +285 -0
  47. package/dist/{engine-65C2J63X.js → engine-BIYI3P4J.js} +7 -2
  48. package/dist/{fallback-llm-LVK5PDIM.js → fallback-llm-WCWNGIQ3.js} +2 -1
  49. package/dist/first-start-migration-I24M2JEE.js +258 -0
  50. package/dist/forget-NI4RBDPB.js +68 -0
  51. package/dist/fs-utils-PZRI2HDZ.js +29 -0
  52. package/dist/graph-edge-decay-5CVKWBYH.js +203 -0
  53. package/dist/index.js +10654 -3067
  54. package/dist/kdf-H5B23ZM2.js +25 -0
  55. package/dist/memory-governance-SJ5DGRB3.js +25 -0
  56. package/dist/metadata-JAGIWHEA.js +20 -0
  57. package/dist/migrate-from-identity-anchor-N3354WMP.js +7 -0
  58. package/dist/path-5LCUBAAZ.js +8 -0
  59. package/dist/peers-JF2I6RCR.js +43 -0
  60. package/dist/purge-XN2VSPZ2.js +204 -0
  61. package/dist/secure-store-A4NGCNXV.js +155 -0
  62. package/dist/state-PVISYXRH.js +7 -0
  63. package/dist/state-store-LP5BO6SF.js +15 -0
  64. package/dist/{storage-DM4ZGOCN.js → storage-PTQ2H2YJ.js} +3 -1
  65. package/dist/tier-stats-IZNW66NC.js +147 -0
  66. package/dist/trace-NJESSGH7.js +289 -0
  67. package/dist/tui-MGK2LYJY.js +12 -0
  68. package/dist/types-R4DO7AKM.js +30 -0
  69. package/openclaw.plugin.json +519 -4
  70. package/package.json +2 -2
@@ -1,12 +1,18 @@
1
1
  import {
2
2
  createVersion
3
3
  } from "./chunk-6OJAU466.js";
4
+ import {
5
+ SecureStoreLockedError,
6
+ isEncryptedFile,
7
+ readMaybeEncryptedFile,
8
+ writeMaybeEncryptedFile
9
+ } from "./chunk-RKR6PTPA.js";
4
10
  import {
5
11
  log
6
12
  } from "./chunk-UFU5GGGA.js";
7
13
 
8
14
  // ../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";
15
+ import { access, readdir, readFile as readFile2, stat as stat2, writeFile as writeFile2, mkdir as mkdir2, unlink, appendFile } from "fs/promises";
10
16
  import { appendFileSync, mkdirSync, statSync } from "fs";
11
17
  import { createHash } from "crypto";
12
18
  import path4 from "path";
@@ -29,6 +35,12 @@ function setCachedEntities(baseDir, entities, version, schemaKey = "") {
29
35
  loadedAt: Date.now()
30
36
  });
31
37
  }
38
+ function invalidateCachedEntities(baseDir) {
39
+ const prefix = `${baseDir}\0`;
40
+ for (const key of entityCacheByDir.keys()) {
41
+ if (key.startsWith(prefix)) entityCacheByDir.delete(key);
42
+ }
43
+ }
32
44
  var episodeMapByDir = /* @__PURE__ */ new Map();
33
45
  var ruleMemoriesByDir = /* @__PURE__ */ new Map();
34
46
  function getCachedEpisodeMap(baseDir, currentVersion) {
@@ -40,7 +52,7 @@ function getCachedEpisodeMap(baseDir, currentVersion) {
40
52
  function setCachedEpisodeMap(baseDir, memories, version) {
41
53
  const map = /* @__PURE__ */ new Map();
42
54
  for (const m of memories) {
43
- if (m.frontmatter.status === "archived") continue;
55
+ if (m.frontmatter.status === "archived" || m.frontmatter.status === "forgotten") continue;
44
56
  if (m.frontmatter.memoryKind !== "episode") continue;
45
57
  map.set(m.frontmatter.id, m);
46
58
  }
@@ -58,7 +70,7 @@ function setCachedRuleMemories(baseDir, memories, version) {
58
70
  const all = [];
59
71
  for (const m of memories) {
60
72
  byId.set(m.frontmatter.id, m);
61
- if (m.frontmatter.category === "rule" && m.frontmatter.status !== "archived") {
73
+ if (m.frontmatter.category === "rule" && m.frontmatter.status !== "archived" && m.frontmatter.status !== "forgotten") {
62
74
  all.push(m);
63
75
  }
64
76
  }
@@ -180,21 +192,41 @@ function sanitizeMemoryContent(text) {
180
192
  var CONSOLIDATION_OPERATORS = [
181
193
  "split",
182
194
  "merge",
183
- "update"
195
+ "update",
196
+ "pattern-reinforcement"
184
197
  ];
185
198
  var DERIVED_FROM_ENTRY_RE = /^(.+):(\d+)$/;
186
- function isValidDerivedFromEntry(entry) {
187
- if (typeof entry !== "string") return false;
199
+ var DERIVED_FROM_MEMORY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_:-]*$/;
200
+ function looksLikeSnapshotEntry(entry) {
188
201
  const match = entry.match(DERIVED_FROM_ENTRY_RE);
189
202
  if (!match) return false;
190
203
  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;
204
+ return pathPart.includes("/") || pathPart.includes(".");
205
+ }
206
+ function isValidDerivedFromEntry(entry) {
207
+ if (typeof entry !== "string") return false;
208
+ if (entry.length === 0) return false;
209
+ if (looksLikeSnapshotEntry(entry)) {
210
+ const match = entry.match(DERIVED_FROM_ENTRY_RE);
211
+ if (!match) return false;
212
+ const pathPart = match[1];
213
+ if (pathPart.length === 0 || pathPart.trim().length === 0) return false;
214
+ const versionNum = Number(match[2]);
215
+ return Number.isInteger(versionNum) && versionNum >= 0;
216
+ }
217
+ return DERIVED_FROM_MEMORY_ID_RE.test(entry);
194
218
  }
195
219
  function isConsolidationOperator(value) {
196
220
  return typeof value === "string" && CONSOLIDATION_OPERATORS.includes(value);
197
221
  }
222
+ var SEMANTIC_CONSOLIDATION_LLM_OPERATORS = [
223
+ "split",
224
+ "merge",
225
+ "update"
226
+ ];
227
+ function isSemanticConsolidationLlmOperator(value) {
228
+ return typeof value === "string" && SEMANTIC_CONSOLIDATION_LLM_OPERATORS.includes(value);
229
+ }
198
230
 
199
231
  // ../remnic-core/src/entity-schema.ts
200
232
  var DEFAULT_ENTITY_SCHEMAS = {
@@ -541,6 +573,15 @@ function stripCitationForTemplate(text, template) {
541
573
  }
542
574
 
543
575
  // ../remnic-core/src/types.ts
576
+ var RECALL_DISCLOSURE_LEVELS = [
577
+ "chunk",
578
+ "section",
579
+ "raw"
580
+ ];
581
+ var DEFAULT_RECALL_DISCLOSURE = "chunk";
582
+ function isRecallDisclosure(value) {
583
+ return typeof value === "string" && RECALL_DISCLOSURE_LEVELS.includes(value);
584
+ }
544
585
  function confidenceTier(score) {
545
586
  if (score >= 0.95) return "explicit";
546
587
  if (score >= 0.7) return "implied";
@@ -560,25 +601,44 @@ function loadBetterSqlite3() {
560
601
  if (cachedCtor) return cachedCtor;
561
602
  const require2 = createRequire(import.meta.url);
562
603
  try {
563
- const loaded = require2("better-sqlite3");
564
- const ctor = typeof loaded === "function" ? loaded : loaded.default;
565
- if (typeof ctor !== "function") {
566
- throw new Error("module did not export a constructor");
567
- }
568
- cachedCtor = ctor;
569
- return ctor;
604
+ cachedCtor = requireBetterSqlite3Ctor(require2);
605
+ return cachedCtor;
570
606
  } catch (error) {
571
- const detail = error instanceof Error && error.message.length > 0 ? ` (${error.message})` : "";
572
- throw new Error(
573
- "better-sqlite3 is unavailable. Rebuild it in the plugin install with `npm rebuild better-sqlite3 --build-from-source` before using SQLite-backed Engram features" + detail,
574
- { cause: error instanceof Error ? error : void 0 }
575
- );
607
+ throw unavailableError(error);
576
608
  }
577
609
  }
578
610
  function openBetterSqlite3(file, options) {
579
611
  const Database = loadBetterSqlite3();
580
612
  return new Database(file, options);
581
613
  }
614
+ function requireBetterSqlite3Ctor(require2) {
615
+ const loaded = require2("better-sqlite3");
616
+ const ctor = typeof loaded === "function" ? loaded : loaded.default;
617
+ if (typeof ctor !== "function") {
618
+ throw new Error("module did not export a constructor");
619
+ }
620
+ return ctor;
621
+ }
622
+ function isLikelyBetterSqlite3NativeBindingError(error) {
623
+ const detail = errorDetail(error);
624
+ return detail.includes("Could not locate the bindings file") || detail.includes("better_sqlite3.node") || detail.includes("node-v") && detail.includes("better-sqlite3") || detail.includes("NODE_MODULE_VERSION") && detail.includes("better-sqlite3") || detail.includes("was compiled against a different Node.js version");
625
+ }
626
+ function unavailableError(error) {
627
+ const detail = errorDetail(error);
628
+ const nativeBindingHint = isLikelyBetterSqlite3NativeBindingError(error) ? " This usually means the better-sqlite3 native binding was not compiled for this Node.js/platform combination. Run `node scripts/ensure-better-sqlite3.mjs` from the Remnic install directory, or run `npx node-gyp rebuild --directory=node_modules/better-sqlite3` if the verification script is unavailable." : "";
629
+ return new Error(
630
+ "better-sqlite3 is unavailable. Remnic attempted to load the native SQLite binding and could not." + nativeBindingHint + (detail ? ` Original error: ${detail}` : ""),
631
+ { cause: error instanceof Error ? error : void 0 }
632
+ );
633
+ }
634
+ function errorDetail(error) {
635
+ if (error instanceof Error) {
636
+ const stack = error.stack && error.stack !== error.message ? `
637
+ ${error.stack}` : "";
638
+ return `${error.message}${stack}`;
639
+ }
640
+ return String(error ?? "");
641
+ }
582
642
 
583
643
  // ../remnic-core/src/memory-projection-store.ts
584
644
  var MEMORY_PROJECTION_SCHEMA_VERSION = 2;
@@ -1269,6 +1329,9 @@ function inferMemoryStatus(frontmatter, pathRel, fallbackStatus = "active") {
1269
1329
  if (frontmatter.status) return frontmatter.status;
1270
1330
  return fallbackStatus;
1271
1331
  }
1332
+ function isActiveMemoryStatus(status) {
1333
+ return status === void 0 || status === "active";
1334
+ }
1272
1335
  function summarizeMemoryLifecycleState(memory) {
1273
1336
  return {
1274
1337
  category: memory.frontmatter.category,
@@ -1632,6 +1695,88 @@ function assertMemoryWorthCounter(field, value) {
1632
1695
  throw new Error(`${field} must be >= 0, got ${value}`);
1633
1696
  }
1634
1697
  }
1698
+ function isErrnoCode(error, code) {
1699
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
1700
+ }
1701
+ function trimTrailingSpacesAndTabs(value) {
1702
+ let end = value.length;
1703
+ while (end > 0 && (value[end - 1] === " " || value[end - 1] === " ")) {
1704
+ end -= 1;
1705
+ }
1706
+ return end === value.length ? value : value.slice(0, end);
1707
+ }
1708
+ function trimLeadingSpacesAndTabs(value) {
1709
+ let start = 0;
1710
+ while (start < value.length && (value[start] === " " || value[start] === " ")) {
1711
+ start += 1;
1712
+ }
1713
+ return start === 0 ? value : value.slice(start);
1714
+ }
1715
+ function stripDefaultCitationMarkersWithoutRegex(value) {
1716
+ return stripCitationMarkersForHashRemoval(value, DEFAULT_CITATION_FORMAT);
1717
+ }
1718
+ function citationTemplateLiteralParts(template) {
1719
+ const parts = [];
1720
+ let cursor = 0;
1721
+ while (cursor < template.length) {
1722
+ const open = template.indexOf("{", cursor);
1723
+ if (open === -1) {
1724
+ parts.push(template.slice(cursor));
1725
+ break;
1726
+ }
1727
+ parts.push(template.slice(cursor, open));
1728
+ const close = template.indexOf("}", open + 1);
1729
+ if (close === -1) {
1730
+ cursor = open + 1;
1731
+ } else {
1732
+ cursor = close + 1;
1733
+ }
1734
+ }
1735
+ return parts.filter((part) => part.length > 0);
1736
+ }
1737
+ function stripCitationMarkersForHashRemoval(value, template) {
1738
+ const parts = citationTemplateLiteralParts(template);
1739
+ if (parts.length === 0) return value;
1740
+ const first = parts[0];
1741
+ const lowerValue = value.toLowerCase();
1742
+ const lowerFirst = first.toLowerCase();
1743
+ const lowerParts = parts.map((part) => part.toLowerCase());
1744
+ if (!lowerValue.includes(lowerFirst)) return value;
1745
+ let result = "";
1746
+ let cursor = 0;
1747
+ let removed = false;
1748
+ while (cursor < value.length) {
1749
+ const markerStart = lowerValue.indexOf(lowerFirst, cursor);
1750
+ if (markerStart === -1) {
1751
+ result += value.slice(cursor);
1752
+ break;
1753
+ }
1754
+ const boundedEnd = first.startsWith("[") ? value.indexOf("]", markerStart + first.length) : -1;
1755
+ if (first.startsWith("[") && boundedEnd === -1) {
1756
+ result += value.slice(cursor);
1757
+ break;
1758
+ }
1759
+ const searchLimit = boundedEnd === -1 ? value.length : boundedEnd + 1;
1760
+ let markerEnd = markerStart + first.length;
1761
+ let matched = true;
1762
+ for (let i = 1; i < lowerParts.length; i += 1) {
1763
+ const partIndex = lowerValue.indexOf(lowerParts[i], markerEnd);
1764
+ if (partIndex === -1 || partIndex + parts[i].length > searchLimit) {
1765
+ matched = false;
1766
+ break;
1767
+ }
1768
+ markerEnd = partIndex + parts[i].length;
1769
+ }
1770
+ if (!matched) {
1771
+ result += value.slice(cursor);
1772
+ break;
1773
+ }
1774
+ result += trimTrailingSpacesAndTabs(value.slice(cursor, markerStart));
1775
+ cursor = markerEnd;
1776
+ removed = true;
1777
+ }
1778
+ return removed ? trimLeadingSpacesAndTabs(result) : value;
1779
+ }
1635
1780
  function serializeFrontmatter(fm) {
1636
1781
  const lines = [
1637
1782
  "---",
@@ -1654,6 +1799,10 @@ function serializeFrontmatter(fm) {
1654
1799
  if (fm.supersededBy) lines.push(`supersededBy: ${fm.supersededBy}`);
1655
1800
  if (fm.supersededAt) lines.push(`supersededAt: ${fm.supersededAt}`);
1656
1801
  if (fm.archivedAt) lines.push(`archivedAt: ${fm.archivedAt}`);
1802
+ if (fm.valid_at) lines.push(`validAt: ${fm.valid_at}`);
1803
+ if (fm.invalid_at) lines.push(`invalidAt: ${fm.invalid_at}`);
1804
+ if (fm.forgottenAt) lines.push(`forgottenAt: ${fm.forgottenAt}`);
1805
+ if (fm.forgottenReason) lines.push(`forgottenReason: ${JSON.stringify(fm.forgottenReason)}`);
1657
1806
  if (fm.lifecycleState) lines.push(`lifecycleState: ${fm.lifecycleState}`);
1658
1807
  if (fm.verificationState) lines.push(`verificationState: ${fm.verificationState}`);
1659
1808
  if (fm.policyClass) lines.push(`policyClass: ${fm.policyClass}`);
@@ -1731,11 +1880,22 @@ function serializeFrontmatter(fm) {
1731
1880
  if (fm.derived_via !== void 0) {
1732
1881
  if (!isConsolidationOperator(fm.derived_via)) {
1733
1882
  throw new Error(
1734
- `serializeFrontmatter: invalid derived_via ${JSON.stringify(fm.derived_via)} \u2014 expected one of "split" | "merge" | "update"`
1883
+ `serializeFrontmatter: invalid derived_via ${JSON.stringify(fm.derived_via)} \u2014 expected one of "split" | "merge" | "update" | "pattern-reinforcement"`
1735
1884
  );
1736
1885
  }
1737
1886
  lines.push(`derived_via: ${fm.derived_via}`);
1738
1887
  }
1888
+ if (fm.reinforcement_count !== void 0) {
1889
+ if (!Number.isInteger(fm.reinforcement_count) || fm.reinforcement_count <= 0) {
1890
+ throw new Error(
1891
+ `serializeFrontmatter: reinforcement_count must be a positive integer (got ${JSON.stringify(fm.reinforcement_count)})`
1892
+ );
1893
+ }
1894
+ lines.push(`reinforcement_count: ${fm.reinforcement_count}`);
1895
+ }
1896
+ if (fm.last_reinforced_at) {
1897
+ lines.push(`last_reinforced_at: ${fm.last_reinforced_at}`);
1898
+ }
1739
1899
  lines.push("---");
1740
1900
  return lines.join("\n");
1741
1901
  }
@@ -1768,6 +1928,20 @@ function parseLinkReasonValue(rawValue) {
1768
1928
  return legacyValue;
1769
1929
  }
1770
1930
  }
1931
+ function parseFrontmatterStringValue(rawValue) {
1932
+ if (rawValue === void 0) return void 0;
1933
+ const trimmed = rawValue.trim();
1934
+ if (trimmed.length === 0) return void 0;
1935
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
1936
+ try {
1937
+ const parsed = JSON.parse(trimmed);
1938
+ return typeof parsed === "string" ? parsed : trimmed;
1939
+ } catch {
1940
+ return trimmed.slice(1, -1).replace(/\\"/g, '"');
1941
+ }
1942
+ }
1943
+ return trimmed;
1944
+ }
1771
1945
  function parseMemoryWorthCounterField(raw) {
1772
1946
  if (raw === void 0) return void 0;
1773
1947
  const trimmed = raw.trim();
@@ -1776,6 +1950,14 @@ function parseMemoryWorthCounterField(raw) {
1776
1950
  if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) return void 0;
1777
1951
  return n;
1778
1952
  }
1953
+ function parseReinforcementCountField(raw) {
1954
+ if (raw === void 0) return void 0;
1955
+ const trimmed = raw.trim();
1956
+ if (trimmed.length === 0) return void 0;
1957
+ const n = Number(trimmed);
1958
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) return void 0;
1959
+ return n;
1960
+ }
1779
1961
  function parseFrontmatter2(raw) {
1780
1962
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
1781
1963
  if (!match) return null;
@@ -1961,6 +2143,11 @@ function parseFrontmatter2(raw) {
1961
2143
  supersededBy: fm.supersededBy || void 0,
1962
2144
  supersededAt: fm.supersededAt || void 0,
1963
2145
  archivedAt: fm.archivedAt || void 0,
2146
+ // Issue #680 — explicit fact lifecycle round-trip.
2147
+ valid_at: fm.validAt || void 0,
2148
+ invalid_at: fm.invalidAt || void 0,
2149
+ forgottenAt: fm.forgottenAt || void 0,
2150
+ forgottenReason: parseFrontmatterStringValue(fm.forgottenReason),
1964
2151
  lifecycleState: fm.lifecycleState || void 0,
1965
2152
  verificationState: fm.verificationState || void 0,
1966
2153
  policyClass: fm.policyClass || void 0,
@@ -1995,7 +2182,14 @@ function parseFrontmatter2(raw) {
1995
2182
  // Consolidation provenance (issue #561) — read-through only in this
1996
2183
  // PR; no code produces these fields yet.
1997
2184
  derived_from,
1998
- derived_via
2185
+ derived_via,
2186
+ // Pattern-reinforcement metadata (issue #687 PR 2/4). Parse
2187
+ // permissively: invalid values (negative, non-integer, blank
2188
+ // ISO-strings) are dropped to undefined so a corrupt frontmatter
2189
+ // never poisons downstream scoring. Validation lives on the
2190
+ // write path in serializeFrontmatter.
2191
+ reinforcement_count: parseReinforcementCountField(fm.reinforcement_count),
2192
+ last_reinforced_at: fm.last_reinforced_at || void 0
1999
2193
  },
2000
2194
  content
2001
2195
  };
@@ -2106,13 +2300,19 @@ var ContentHashIndex = class _ContentHashIndex {
2106
2300
  hashes = /* @__PURE__ */ new Set();
2107
2301
  dirty = false;
2108
2302
  filePath;
2109
- constructor(stateDir) {
2303
+ secureStoreKeyProvider;
2304
+ secureStoreWriteKeyProvider;
2305
+ memoryDir;
2306
+ constructor(stateDir, secureStoreKeyProvider = () => null, secureStoreWriteKeyProvider = secureStoreKeyProvider, memoryDir = path4.dirname(stateDir)) {
2110
2307
  this.filePath = path4.join(stateDir, "fact-hashes.txt");
2308
+ this.secureStoreKeyProvider = secureStoreKeyProvider;
2309
+ this.secureStoreWriteKeyProvider = secureStoreWriteKeyProvider;
2310
+ this.memoryDir = memoryDir;
2111
2311
  }
2112
2312
  /** Load existing hashes from disk. Safe to call multiple times. */
2113
2313
  async load() {
2114
2314
  try {
2115
- const raw = await readFile2(this.filePath, "utf-8");
2315
+ const raw = await readMaybeEncryptedFile(this.filePath, this.secureStoreKeyProvider(), this.memoryDir);
2116
2316
  for (const line of raw.split("\n")) {
2117
2317
  const trimmed = line.trim();
2118
2318
  if (trimmed.length > 0) {
@@ -2120,7 +2320,9 @@ var ContentHashIndex = class _ContentHashIndex {
2120
2320
  }
2121
2321
  }
2122
2322
  log.debug(`content-hash index: loaded ${this.hashes.size} hashes`);
2123
- } catch {
2323
+ } catch (err) {
2324
+ if (err instanceof SecureStoreLockedError) throw err;
2325
+ if (!isErrnoCode(err, "ENOENT")) throw err;
2124
2326
  log.debug("content-hash index: no existing index \u2014 starting fresh");
2125
2327
  }
2126
2328
  }
@@ -2143,7 +2345,13 @@ var ContentHashIndex = class _ContentHashIndex {
2143
2345
  async save() {
2144
2346
  if (!this.dirty) return;
2145
2347
  await mkdir2(path4.dirname(this.filePath), { recursive: true });
2146
- await writeFile2(this.filePath, [...this.hashes].join("\n") + "\n", "utf-8");
2348
+ await writeMaybeEncryptedFile(
2349
+ this.filePath,
2350
+ [...this.hashes].join("\n") + "\n",
2351
+ this.secureStoreWriteKeyProvider(),
2352
+ {},
2353
+ this.memoryDir
2354
+ );
2147
2355
  this.dirty = false;
2148
2356
  log.debug(`content-hash index: saved ${this.hashes.size} hashes`);
2149
2357
  }
@@ -3006,6 +3214,8 @@ var StorageManager = class _StorageManager {
3006
3214
  // 1 minute
3007
3215
  static artifactWriteVersionByDir = /* @__PURE__ */ new Map();
3008
3216
  static memoryStatusVersionByDir = /* @__PURE__ */ new Map();
3217
+ static secureStoreEntityCacheKeyIds = /* @__PURE__ */ new WeakMap();
3218
+ static nextSecureStoreEntityCacheKeyId = 1;
3009
3219
  // In-process fallback for the cold-write sentinel (used when the disk file
3010
3220
  // is not accessible). The canonical source of truth is state/cold-write.log.
3011
3221
  static coldWriteVersionByDir = /* @__PURE__ */ new Map();
@@ -3053,6 +3263,7 @@ var StorageManager = class _StorageManager {
3053
3263
  factHashIndexLoadPromise = null;
3054
3264
  factHashIndexAuthoritative = null;
3055
3265
  factHashIndexAuthoritativePromise = null;
3266
+ secureAppendChains = /* @__PURE__ */ new Map();
3056
3267
  /** Optional: set by the orchestrator after construction to enable template-aware citation stripping during legacy hash rebuild. */
3057
3268
  citationTemplate = DEFAULT_CITATION_FORMAT;
3058
3269
  /** Page-versioning configuration. Set by the orchestrator after construction. */
@@ -3061,6 +3272,87 @@ var StorageManager = class _StorageManager {
3061
3272
  setVersioningConfig(config) {
3062
3273
  this._versioningConfig = config;
3063
3274
  }
3275
+ /**
3276
+ * At-rest encryption key (issue #690 PR 3/4).
3277
+ *
3278
+ * When non-null, every memory file read is decrypted and every write
3279
+ * is encrypted using the secure-fs layer. When null, the storage
3280
+ * layer operates in plain-text mode (legacy/unencrypted store).
3281
+ *
3282
+ * Set by the orchestrator after init/unlock; cleared on lock.
3283
+ * The key buffer is NEVER logged or serialized.
3284
+ */
3285
+ _secureStoreKey = null;
3286
+ /**
3287
+ * When true (and `_secureStoreKey` is non-null), new writes are
3288
+ * encrypted. Set to false to pause encryption of new writes while
3289
+ * still decrypting existing files.
3290
+ */
3291
+ _secureStoreEncryptOnWrite = true;
3292
+ /**
3293
+ * When true, the secure-store is configured as required — writes
3294
+ * MUST be encrypted and a locked store MUST reject writes rather
3295
+ * than silently falling back to plaintext. Set by the orchestrator
3296
+ * from `config.secureStoreEnabled`.
3297
+ */
3298
+ _secureStoreRequired = false;
3299
+ /**
3300
+ * Set or clear the at-rest encryption key.
3301
+ *
3302
+ * Pass a 32-byte Buffer to enable encryption; pass null to clear
3303
+ * (lock) the store. The caller is responsible for key lifecycle —
3304
+ * this method does not zero the buffer on replacement; the keyring
3305
+ * module (`keyring.ts`) owns zeroization.
3306
+ */
3307
+ setSecureStoreKey(key, encryptOnWrite = true) {
3308
+ this._secureStoreKey = key;
3309
+ this._secureStoreEncryptOnWrite = encryptOnWrite;
3310
+ invalidateCachedEntities(this.baseDir);
3311
+ this.invalidateKnowledgeIndexCache();
3312
+ }
3313
+ getEntityCacheSecureStoreKey() {
3314
+ if (!this._secureStoreKey) return "secure-store:locked";
3315
+ let id = _StorageManager.secureStoreEntityCacheKeyIds.get(this._secureStoreKey);
3316
+ if (id === void 0) {
3317
+ id = _StorageManager.nextSecureStoreEntityCacheKeyId++;
3318
+ _StorageManager.secureStoreEntityCacheKeyIds.set(this._secureStoreKey, id);
3319
+ }
3320
+ return `secure-store:key:${id}`;
3321
+ }
3322
+ /**
3323
+ * Mark the secure-store as required for this storage instance.
3324
+ * When required and locked, writes throw SecureStoreLockedError
3325
+ * rather than silently writing plaintext.
3326
+ */
3327
+ setSecureStoreRequired(required) {
3328
+ this._secureStoreRequired = required;
3329
+ }
3330
+ /** Return true iff the secure-store key is currently set (store is unlocked). */
3331
+ isSecureStoreUnlocked() {
3332
+ return this._secureStoreKey !== null;
3333
+ }
3334
+ /**
3335
+ * Resolve the effective write key.
3336
+ *
3337
+ * - If `_secureStoreEncryptOnWrite` is false: returns null (plain write).
3338
+ * - If `_secureStoreEncryptOnWrite` is true AND key is set: returns key.
3339
+ * - If `_secureStoreEncryptOnWrite` is true AND key is null AND
3340
+ * `_secureStoreRequired` is true: throws SecureStoreLockedError so the
3341
+ * write fails loudly rather than silently writing plaintext (P1 finding
3342
+ * from Cursor review of PR #767).
3343
+ * - If `_secureStoreEncryptOnWrite` is true AND key is null AND
3344
+ * `_secureStoreRequired` is false: returns null (unencrypted store).
3345
+ */
3346
+ resolveWriteKey() {
3347
+ if (!this._secureStoreEncryptOnWrite) return null;
3348
+ if (this._secureStoreKey !== null) return this._secureStoreKey;
3349
+ if (this._secureStoreRequired) {
3350
+ throw new SecureStoreLockedError(
3351
+ "secure-store is locked \u2014 cannot write memory file. Run `remnic secure-store unlock` to decrypt, or restart the daemon after unlocking."
3352
+ );
3353
+ }
3354
+ return null;
3355
+ }
3064
3356
  /**
3065
3357
  * Snapshot the current content of a page before overwriting.
3066
3358
  * No-op when versioning is disabled or the file does not yet exist.
@@ -3068,7 +3360,7 @@ var StorageManager = class _StorageManager {
3068
3360
  async snapshotBeforeWrite(filePath, trigger) {
3069
3361
  if (!this._versioningConfig || !this._versioningConfig.enabled) return;
3070
3362
  try {
3071
- const existing = await readFile2(filePath, "utf-8");
3363
+ const existing = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
3072
3364
  await createVersion(filePath, existing, trigger, this._versioningConfig, log, void 0, this.baseDir);
3073
3365
  } catch {
3074
3366
  }
@@ -3091,7 +3383,7 @@ var StorageManager = class _StorageManager {
3091
3383
  if (!this._versioningConfig || !this._versioningConfig.enabled) return null;
3092
3384
  let existing;
3093
3385
  try {
3094
- existing = await readFile2(filePath, "utf-8");
3386
+ existing = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
3095
3387
  } catch {
3096
3388
  return null;
3097
3389
  }
@@ -3177,6 +3469,57 @@ var StorageManager = class _StorageManager {
3177
3469
  get entitiesDir() {
3178
3470
  return path4.join(this.baseDir, "entities");
3179
3471
  }
3472
+ readStorageSecureFile(filePath) {
3473
+ return readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
3474
+ }
3475
+ writeStorageSecureFile(filePath, content) {
3476
+ return writeMaybeEncryptedFile(filePath, content, this.resolveWriteKey(), {}, this.baseDir);
3477
+ }
3478
+ createContentHashIndex() {
3479
+ return new ContentHashIndex(
3480
+ this.stateDir,
3481
+ () => this._secureStoreKey,
3482
+ () => this.resolveWriteKey(),
3483
+ this.baseDir
3484
+ );
3485
+ }
3486
+ async appendStorageSecureFile(filePath, content) {
3487
+ const previous = this.secureAppendChains.get(filePath) ?? Promise.resolve();
3488
+ const current = previous.catch(() => void 0).then(() => this.appendStorageSecureFileUnlocked(filePath, content));
3489
+ const next = current.catch(() => void 0);
3490
+ this.secureAppendChains.set(filePath, next);
3491
+ try {
3492
+ await current;
3493
+ } finally {
3494
+ if (this.secureAppendChains.get(filePath) === next) {
3495
+ this.secureAppendChains.delete(filePath);
3496
+ }
3497
+ }
3498
+ }
3499
+ async appendStorageSecureFileUnlocked(filePath, content) {
3500
+ const writeKey = this.resolveWriteKey();
3501
+ await mkdir2(path4.dirname(filePath), { recursive: true });
3502
+ if (writeKey === null) {
3503
+ try {
3504
+ if (isEncryptedFile(await readFile2(filePath))) {
3505
+ const existing2 = await this.readStorageSecureFile(filePath);
3506
+ await writeMaybeEncryptedFile(filePath, `${existing2}${content}`, null, {}, this.baseDir);
3507
+ return;
3508
+ }
3509
+ } catch (err) {
3510
+ if (!isErrnoCode(err, "ENOENT")) throw err;
3511
+ }
3512
+ await appendFile(filePath, content, "utf-8");
3513
+ return;
3514
+ }
3515
+ let existing = "";
3516
+ try {
3517
+ existing = await this.readStorageSecureFile(filePath);
3518
+ } catch (err) {
3519
+ if (!isErrnoCode(err, "ENOENT")) throw err;
3520
+ }
3521
+ await writeMaybeEncryptedFile(filePath, `${existing}${content}`, writeKey, {}, this.baseDir);
3522
+ }
3180
3523
  get stateDir() {
3181
3524
  return path4.join(this.baseDir, "state");
3182
3525
  }
@@ -3191,7 +3534,7 @@ var StorageManager = class _StorageManager {
3191
3534
  return this.factHashIndex;
3192
3535
  }
3193
3536
  if (!this.factHashIndexLoadPromise) {
3194
- const index = new ContentHashIndex(this.stateDir);
3537
+ const index = this.createContentHashIndex();
3195
3538
  this.factHashIndexLoadPromise = index.load().then(() => {
3196
3539
  this.factHashIndex = index;
3197
3540
  return index;
@@ -3433,7 +3776,7 @@ ${sanitized.text}
3433
3776
  filePath = path4.join(this.factsDir, today, `${id}.md`);
3434
3777
  }
3435
3778
  await this.snapshotBeforeWrite(filePath, "write");
3436
- await writeFile2(filePath, fileContent, "utf-8");
3779
+ await writeMaybeEncryptedFile(filePath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
3437
3780
  this.invalidateAllMemoriesCache();
3438
3781
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeMemory", {
3439
3782
  memoryId: id,
@@ -3469,6 +3812,50 @@ ${sanitized.text}
3469
3812
  const sanitized = sanitizeMemoryContent(content);
3470
3813
  return factHashIndex.has(sanitized.text);
3471
3814
  }
3815
+ factContentHashForRemoval(memory) {
3816
+ if (memory.frontmatter.category !== "fact") return null;
3817
+ if (typeof memory.frontmatter.contentHash === "string" && memory.frontmatter.contentHash.length > 0) {
3818
+ return memory.frontmatter.contentHash;
3819
+ }
3820
+ const configuredHashSource = stripCitationMarkersForHashRemoval(memory.content, this.citationTemplate);
3821
+ const hashSource = configuredHashSource !== memory.content ? configuredHashSource : stripDefaultCitationMarkersWithoutRegex(memory.content);
3822
+ return ContentHashIndex.computeHash(sanitizeMemoryContent(hashSource).text);
3823
+ }
3824
+ async removeFactContentHashesForMemories(memories) {
3825
+ await this.ensureFactHashIndexAuthoritative();
3826
+ const factHashIndex = await this.getFactHashIndex();
3827
+ const removedIds = new Set(
3828
+ memories.map((memory) => memory.frontmatter.id).filter((id) => typeof id === "string" && id.length > 0)
3829
+ );
3830
+ const removedHashes = /* @__PURE__ */ new Map();
3831
+ for (const memory of memories) {
3832
+ const hash = this.factContentHashForRemoval(memory);
3833
+ if (hash) {
3834
+ removedHashes.set(memory, hash);
3835
+ }
3836
+ }
3837
+ if (removedHashes.size === 0) return;
3838
+ const remainingActiveHashes = /* @__PURE__ */ new Set();
3839
+ const remainingMemories = [
3840
+ ...await this.readAllMemories(),
3841
+ ...await this.readAllColdMemories()
3842
+ ];
3843
+ for (const memory of remainingMemories) {
3844
+ if (memory.frontmatter.category !== "fact") continue;
3845
+ if (removedIds.has(memory.frontmatter.id)) continue;
3846
+ if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
3847
+ const hash = this.factContentHashForRemoval(memory);
3848
+ if (hash) {
3849
+ remainingActiveHashes.add(hash);
3850
+ }
3851
+ }
3852
+ for (const hash of removedHashes.values()) {
3853
+ if (!remainingActiveHashes.has(hash)) {
3854
+ factHashIndex.removeByHash(hash);
3855
+ }
3856
+ }
3857
+ await factHashIndex.save();
3858
+ }
3472
3859
  async isFactContentHashAuthoritative() {
3473
3860
  await this.ensureFactHashIndexAuthoritative();
3474
3861
  return true;
@@ -3502,10 +3889,10 @@ ${sanitized.text}
3502
3889
  return "";
3503
3890
  }
3504
3891
  const filePath = path4.join(dir, `${id}.md`);
3505
- await writeFile2(filePath, `${serializeFrontmatter(fm)}
3892
+ await writeMaybeEncryptedFile(filePath, `${serializeFrontmatter(fm)}
3506
3893
 
3507
3894
  ${sanitized.text}
3508
- `, "utf-8");
3895
+ `, this.resolveWriteKey(), {}, this.baseDir);
3509
3896
  const actor = typeof options.actor === "string" && options.actor.length > 0 ? options.actor : "storage.writeArtifact";
3510
3897
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeArtifact", {
3511
3898
  memoryId: id,
@@ -3614,9 +4001,11 @@ ${sanitized.text}
3614
4001
  aliases: []
3615
4002
  };
3616
4003
  try {
3617
- const existing = await readFile2(filePath, "utf-8");
4004
+ const existing = await this.readStorageSecureFile(filePath);
3618
4005
  entity = parseEntityFile(existing, this.entitySchemas);
3619
- } catch {
4006
+ } catch (err) {
4007
+ if (err instanceof SecureStoreLockedError) throw err;
4008
+ if (!isErrnoCode(err, "ENOENT")) throw err;
3620
4009
  }
3621
4010
  const timestamp = options.timestamp?.trim() || (/* @__PURE__ */ new Date()).toISOString();
3622
4011
  const source = options.source?.trim() || void 0;
@@ -3672,7 +4061,7 @@ ${sanitized.text}
3672
4061
  entity.created = entity.created || timestamp;
3673
4062
  entity.updated = (/* @__PURE__ */ new Date()).toISOString();
3674
4063
  await this.snapshotBeforeWrite(filePath, "write");
3675
- await writeFile2(filePath, serializeEntityFile(entity, this.entitySchemas), "utf-8");
4064
+ await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
3676
4065
  this.invalidateKnowledgeIndexCache();
3677
4066
  this.bumpMemoryStatusVersion();
3678
4067
  log.debug(`wrote entity ${normalized}`);
@@ -3680,15 +4069,19 @@ ${sanitized.text}
3680
4069
  }
3681
4070
  async readProfile() {
3682
4071
  try {
3683
- return await readFile2(this.profilePath, "utf-8");
3684
- } catch {
3685
- return "";
4072
+ return await readMaybeEncryptedFile(this.profilePath, this._secureStoreKey, this.baseDir);
4073
+ } catch (error) {
4074
+ if (error instanceof SecureStoreLockedError) {
4075
+ throw error;
4076
+ }
4077
+ if (isErrnoCode(error, "ENOENT")) return "";
4078
+ throw error;
3686
4079
  }
3687
4080
  }
3688
4081
  async writeProfile(content) {
3689
4082
  await this.ensureDirectories();
3690
4083
  await this.snapshotBeforeWrite(this.profilePath, "consolidation");
3691
- await writeFile2(this.profilePath, content, "utf-8");
4084
+ await writeMaybeEncryptedFile(this.profilePath, content, this.resolveWriteKey(), {}, this.baseDir);
3692
4085
  log.debug("updated profile.md");
3693
4086
  }
3694
4087
  /**
@@ -3771,6 +4164,24 @@ ${sanitized.text}
3771
4164
  invalidateAllMemoriesCacheForDir() {
3772
4165
  this.invalidateAllMemoriesCache();
3773
4166
  }
4167
+ /** Invalidate only the cache layers affected by direct tier file deletes. */
4168
+ invalidateMemoryCachesForTiers(tiers) {
4169
+ let hotChanged = false;
4170
+ let coldChanged = false;
4171
+ for (const tier of tiers) {
4172
+ if (tier === "cold") {
4173
+ coldChanged = true;
4174
+ } else if (tier === "hot" || tier === "archive") {
4175
+ hotChanged = true;
4176
+ }
4177
+ }
4178
+ if (hotChanged) {
4179
+ this.invalidateAllMemoriesCache();
4180
+ }
4181
+ if (coldChanged) {
4182
+ this.invalidateColdMemoriesCache();
4183
+ }
4184
+ }
3774
4185
  /** Clear ALL static caches. Use in tests that write files directly
3775
4186
  * (bypassing StorageManager.writeMemory) to avoid stale reads. */
3776
4187
  static clearAllStaticCaches() {
@@ -3862,7 +4273,7 @@ ${sanitized.text}
3862
4273
  const results = await Promise.all(
3863
4274
  batch.map(async (fullPath) => {
3864
4275
  try {
3865
- const raw = await readFile2(fullPath, "utf-8");
4276
+ const raw = await readMaybeEncryptedFile(fullPath, this._secureStoreKey, this.baseDir);
3866
4277
  const parsed = parseFrontmatter2(raw);
3867
4278
  if (!parsed) return null;
3868
4279
  return {
@@ -3874,7 +4285,8 @@ ${sanitized.text}
3874
4285
  ),
3875
4286
  content: parsed.content
3876
4287
  };
3877
- } catch {
4288
+ } catch (err) {
4289
+ if (err instanceof SecureStoreLockedError) throw err;
3878
4290
  return null;
3879
4291
  }
3880
4292
  })
@@ -3887,7 +4299,7 @@ ${sanitized.text}
3887
4299
  }
3888
4300
  async readWindowUpdatedMs(filePath) {
3889
4301
  try {
3890
- const raw = await readFile2(filePath, "utf-8");
4302
+ const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
3891
4303
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?/);
3892
4304
  if (!match) return null;
3893
4305
  const frontmatterBlock = match[1];
@@ -4071,7 +4483,7 @@ ${sanitized.text}
4071
4483
  await readDir(fullPath);
4072
4484
  } else if (entry.name.endsWith(".md")) {
4073
4485
  try {
4074
- const raw = await readFile2(fullPath, "utf-8");
4486
+ const raw = await readMaybeEncryptedFile(fullPath, this._secureStoreKey, this.baseDir);
4075
4487
  const parsed = parseFrontmatter2(raw);
4076
4488
  if (parsed) {
4077
4489
  memories.push({
@@ -4084,7 +4496,8 @@ ${sanitized.text}
4084
4496
  content: parsed.content
4085
4497
  });
4086
4498
  }
4087
- } catch {
4499
+ } catch (err) {
4500
+ if (err instanceof SecureStoreLockedError) throw err;
4088
4501
  }
4089
4502
  }
4090
4503
  }
@@ -4097,7 +4510,7 @@ ${sanitized.text}
4097
4510
  /** Read a single memory file by its absolute path. Returns null if unreadable. */
4098
4511
  async readMemoryByPath(filePath) {
4099
4512
  try {
4100
- const raw = await readFile2(filePath, "utf-8");
4513
+ const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
4101
4514
  const parsed = parseFrontmatter2(raw);
4102
4515
  if (parsed) {
4103
4516
  return {
@@ -4132,7 +4545,8 @@ ${sanitized.text}
4132
4545
  };
4133
4546
  }
4134
4547
  return null;
4135
- } catch {
4548
+ } catch (err) {
4549
+ if (err instanceof SecureStoreLockedError) throw err;
4136
4550
  return null;
4137
4551
  }
4138
4552
  }
@@ -4170,19 +4584,8 @@ ${sanitized.text}
4170
4584
 
4171
4585
  ${memory.content}
4172
4586
  `;
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
- }
4587
+ await writeMaybeEncryptedFile(targetPath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
4588
+ this.invalidateAllMemoriesCache();
4186
4589
  }
4187
4590
  async moveMemoryToPath(memory, targetPath) {
4188
4591
  await this.writeMemoryFileAtomic(targetPath, memory);
@@ -4253,7 +4656,7 @@ ${memory.content}
4253
4656
  ${memory.content}
4254
4657
  `;
4255
4658
  const destPath = path4.join(destDir, path4.basename(memory.path));
4256
- await writeFile2(destPath, fileContent, "utf-8");
4659
+ await writeMaybeEncryptedFile(destPath, fileContent, this.resolveWriteKey(), {}, this.baseDir);
4257
4660
  await unlink(memory.path);
4258
4661
  this.invalidateAllMemoriesCache();
4259
4662
  await this.appendGeneratedMemoryLifecycleEventFailOpen(
@@ -4289,8 +4692,10 @@ ${memory.content}
4289
4692
  }
4290
4693
  async readEntity(name) {
4291
4694
  try {
4292
- return await readFile2(path4.join(this.entitiesDir, `${name}.md`), "utf-8");
4293
- } catch {
4695
+ return await this.readStorageSecureFile(path4.join(this.entitiesDir, `${name}.md`));
4696
+ } catch (err) {
4697
+ if (err instanceof SecureStoreLockedError) throw err;
4698
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4294
4699
  return "";
4295
4700
  }
4296
4701
  }
@@ -4374,7 +4779,7 @@ ${memory.content}
4374
4779
 
4375
4780
  ${sanitized.text}
4376
4781
  `;
4377
- await writeFile2(memory.path, fileContent, "utf-8");
4782
+ await writeMaybeEncryptedFile(memory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
4378
4783
  this.invalidateAllMemoriesCache();
4379
4784
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.updateMemory", {
4380
4785
  memoryId: id,
@@ -4406,7 +4811,7 @@ ${sanitized.text}
4406
4811
 
4407
4812
  ${memory.content}
4408
4813
  `;
4409
- await writeFile2(memory.path, fileContent, "utf-8");
4814
+ await writeMaybeEncryptedFile(memory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
4410
4815
  this.invalidateAllMemoriesCache();
4411
4816
  if (memory.path.includes(`${path4.sep}cold${path4.sep}`)) {
4412
4817
  this.invalidateColdMemoriesCache();
@@ -4471,21 +4876,23 @@ ${memory.content}
4471
4876
  async loadBuffer() {
4472
4877
  const bufferPath = path4.join(this.stateDir, "buffer.json");
4473
4878
  try {
4474
- const raw = await readFile2(bufferPath, "utf-8");
4879
+ const raw = await this.readStorageSecureFile(bufferPath);
4475
4880
  return JSON.parse(raw);
4476
- } catch {
4881
+ } catch (err) {
4882
+ if (err instanceof SecureStoreLockedError) throw err;
4883
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4477
4884
  return { turns: [], lastExtractionAt: null, extractionCount: 0 };
4478
4885
  }
4479
4886
  }
4480
4887
  async saveBuffer(state) {
4481
4888
  await this.ensureDirectories();
4482
4889
  const bufferPath = path4.join(this.stateDir, "buffer.json");
4483
- await writeFile2(bufferPath, JSON.stringify(state, null, 2), "utf-8");
4890
+ await this.writeStorageSecureFile(bufferPath, JSON.stringify(state, null, 2));
4484
4891
  }
4485
4892
  async loadMeta() {
4486
4893
  const metaPath = path4.join(this.stateDir, "meta.json");
4487
4894
  try {
4488
- const raw = await readFile2(metaPath, "utf-8");
4895
+ const raw = await this.readStorageSecureFile(metaPath);
4489
4896
  const parsed = JSON.parse(raw);
4490
4897
  return {
4491
4898
  extractionCount: typeof parsed.extractionCount === "number" ? parsed.extractionCount : 0,
@@ -4502,7 +4909,9 @@ ${memory.content}
4502
4909
  observedAt: entry.observedAt
4503
4910
  })) : []
4504
4911
  };
4505
- } catch {
4912
+ } catch (err) {
4913
+ if (err instanceof SecureStoreLockedError) throw err;
4914
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4506
4915
  return {
4507
4916
  extractionCount: 0,
4508
4917
  lastExtractionAt: null,
@@ -4516,7 +4925,7 @@ ${memory.content}
4516
4925
  async saveMeta(state) {
4517
4926
  await this.ensureDirectories();
4518
4927
  const metaPath = path4.join(this.stateDir, "meta.json");
4519
- await writeFile2(metaPath, JSON.stringify(state, null, 2), "utf-8");
4928
+ await this.writeStorageSecureFile(metaPath, JSON.stringify(state, null, 2));
4520
4929
  }
4521
4930
  async appendMemoryActionEvents(events) {
4522
4931
  if (events.length === 0) return 0;
@@ -4530,7 +4939,7 @@ ${memory.content}
4530
4939
  return `${JSON.stringify(normalized)}
4531
4940
  `;
4532
4941
  }).join("");
4533
- await appendFile(this.memoryActionsPath, payload, "utf-8");
4942
+ await this.appendStorageSecureFile(this.memoryActionsPath, payload);
4534
4943
  return events.length;
4535
4944
  }
4536
4945
  async appendMemoryLifecycleEvents(events) {
@@ -4545,7 +4954,7 @@ ${memory.content}
4545
4954
  return `${JSON.stringify(normalized)}
4546
4955
  `;
4547
4956
  }).join("");
4548
- await appendFile(this.memoryLifecycleLedgerPath, payload, "utf-8");
4957
+ await this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, payload);
4549
4958
  return events.length;
4550
4959
  }
4551
4960
  /**
@@ -4570,7 +4979,7 @@ ${memory.content}
4570
4979
  return `${JSON.stringify(normalized)}
4571
4980
  `;
4572
4981
  }).join("");
4573
- await appendFile(this.bufferSurpriseLedgerPath, payload, "utf-8");
4982
+ await this.appendStorageSecureFile(this.bufferSurpriseLedgerPath, payload);
4574
4983
  return events.length;
4575
4984
  }
4576
4985
  /**
@@ -4607,8 +5016,9 @@ ${memory.content}
4607
5016
  async readBufferSurpriseEvents(options = {}) {
4608
5017
  let raw;
4609
5018
  try {
4610
- raw = await readFile2(this.bufferSurpriseLedgerPath, "utf-8");
5019
+ raw = await this.readStorageSecureFile(this.bufferSurpriseLedgerPath);
4611
5020
  } catch (err) {
5021
+ if (err instanceof SecureStoreLockedError) throw err;
4612
5022
  const code = err.code;
4613
5023
  if (code === "ENOENT") return [];
4614
5024
  throw err;
@@ -4643,7 +5053,7 @@ ${memory.content}
4643
5053
  await this.ensureDirectories();
4644
5054
  let existingKeys = /* @__PURE__ */ new Set();
4645
5055
  try {
4646
- const raw = await readFile2(this.behaviorSignalsPath, "utf-8");
5056
+ const raw = await this.readStorageSecureFile(this.behaviorSignalsPath);
4647
5057
  const lines = raw.split("\n");
4648
5058
  for (const line of lines) {
4649
5059
  const row = line.trim();
@@ -4656,7 +5066,9 @@ ${memory.content}
4656
5066
  } catch {
4657
5067
  }
4658
5068
  }
4659
- } catch {
5069
+ } catch (err) {
5070
+ if (err instanceof SecureStoreLockedError) throw err;
5071
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4660
5072
  existingKeys = /* @__PURE__ */ new Set();
4661
5073
  }
4662
5074
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
@@ -4673,7 +5085,7 @@ ${memory.content}
4673
5085
  if (deduped.length === 0) return 0;
4674
5086
  const payload = deduped.map((event) => `${JSON.stringify(event)}
4675
5087
  `).join("");
4676
- await appendFile(this.behaviorSignalsPath, payload, "utf-8");
5088
+ await this.appendStorageSecureFile(this.behaviorSignalsPath, payload);
4677
5089
  return deduped.length;
4678
5090
  }
4679
5091
  async appendReextractJobs(events) {
@@ -4682,9 +5094,11 @@ ${memory.content}
4682
5094
  const filePath = path4.join(this.stateDir, "reextract-jobs.jsonl");
4683
5095
  const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n";
4684
5096
  try {
4685
- await appendFile(filePath, lines, "utf-8");
5097
+ await this.appendStorageSecureFile(filePath, lines);
4686
5098
  return events.length;
4687
- } catch {
5099
+ } catch (err) {
5100
+ if (err instanceof SecureStoreLockedError) throw err;
5101
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4688
5102
  return 0;
4689
5103
  }
4690
5104
  }
@@ -4692,7 +5106,7 @@ ${memory.content}
4692
5106
  const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(1e3, Math.floor(limit))) : 200;
4693
5107
  const filePath = path4.join(this.stateDir, "reextract-jobs.jsonl");
4694
5108
  try {
4695
- const raw = await readFile2(filePath, "utf-8");
5109
+ const raw = await this.readStorageSecureFile(filePath);
4696
5110
  const lines = raw.split("\n").filter((line) => line.trim().length > 0);
4697
5111
  const parsed = [];
4698
5112
  for (const line of lines) {
@@ -4712,7 +5126,9 @@ ${memory.content}
4712
5126
  }
4713
5127
  }
4714
5128
  return parsed.slice(-safeLimit);
4715
- } catch {
5129
+ } catch (err) {
5130
+ if (err instanceof SecureStoreLockedError) throw err;
5131
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4716
5132
  return [];
4717
5133
  }
4718
5134
  }
@@ -4720,7 +5136,7 @@ ${memory.content}
4720
5136
  const cappedLimit = Math.max(0, Math.floor(limit));
4721
5137
  if (cappedLimit === 0) return [];
4722
5138
  try {
4723
- const raw = await readFile2(this.behaviorSignalsPath, "utf-8");
5139
+ const raw = await this.readStorageSecureFile(this.behaviorSignalsPath);
4724
5140
  const out = [];
4725
5141
  const lines = raw.split("\n");
4726
5142
  for (let i = lines.length - 1; i >= 0 && out.length < cappedLimit; i -= 1) {
@@ -4735,15 +5151,20 @@ ${memory.content}
4735
5151
  }
4736
5152
  }
4737
5153
  return out.reverse();
4738
- } catch {
5154
+ } catch (err) {
5155
+ if (err instanceof SecureStoreLockedError) throw err;
5156
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4739
5157
  return [];
4740
5158
  }
4741
5159
  }
4742
5160
  async readMemoryActionEvents(limit = 200) {
5161
+ return (await this.readMemoryActionEventRows(limit)).map((row) => row.event);
5162
+ }
5163
+ async readMemoryActionEventRows(limit = 200) {
4743
5164
  const cappedLimit = Math.max(0, Math.floor(limit));
4744
5165
  if (cappedLimit === 0) return [];
4745
5166
  try {
4746
- const raw = await readFile2(this.memoryActionsPath, "utf-8");
5167
+ const raw = await this.readStorageSecureFile(this.memoryActionsPath);
4747
5168
  const out = [];
4748
5169
  const lines = raw.split("\n");
4749
5170
  for (let i = lines.length - 1; i >= 0 && out.length < cappedLimit; i -= 1) {
@@ -4751,20 +5172,29 @@ ${memory.content}
4751
5172
  if (!line) continue;
4752
5173
  try {
4753
5174
  const parsed = JSON.parse(line);
4754
- if (typeof parsed.timestamp === "string" && typeof parsed.action === "string" && typeof parsed.outcome === "string") {
4755
- out.push(parsed);
5175
+ const outcome = parsed.outcome === "applied" || parsed.outcome === "skipped" || parsed.outcome === "failed" ? parsed.outcome : null;
5176
+ if (typeof parsed.timestamp === "string" && typeof parsed.action === "string" && outcome !== null) {
5177
+ out.push({
5178
+ line: i + 1,
5179
+ event: {
5180
+ ...parsed,
5181
+ outcome
5182
+ }
5183
+ });
4756
5184
  }
4757
5185
  } catch {
4758
5186
  }
4759
5187
  }
4760
5188
  return out.reverse();
4761
- } catch {
5189
+ } catch (err) {
5190
+ if (err instanceof SecureStoreLockedError) throw err;
5191
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4762
5192
  return [];
4763
5193
  }
4764
5194
  }
4765
5195
  async readAllMemoryLifecycleEvents() {
4766
5196
  try {
4767
- const raw = await readFile2(this.memoryLifecycleLedgerPath, "utf-8");
5197
+ const raw = await this.readStorageSecureFile(this.memoryLifecycleLedgerPath);
4768
5198
  const out = [];
4769
5199
  const lines = raw.split("\n");
4770
5200
  for (const line of lines) {
@@ -4779,7 +5209,9 @@ ${memory.content}
4779
5209
  }
4780
5210
  }
4781
5211
  return sortMemoryLifecycleEvents(out);
4782
- } catch {
5212
+ } catch (err) {
5213
+ if (err instanceof SecureStoreLockedError) throw err;
5214
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4783
5215
  return [];
4784
5216
  }
4785
5217
  }
@@ -4791,35 +5223,39 @@ ${memory.content}
4791
5223
  }
4792
5224
  async writeCompressionGuidelines(content) {
4793
5225
  await this.ensureDirectories();
4794
- await writeFile2(this.compressionGuidelinesPath, content, "utf-8");
5226
+ await this.writeStorageSecureFile(this.compressionGuidelinesPath, content);
4795
5227
  }
4796
5228
  async readCompressionGuidelines() {
4797
5229
  try {
4798
- return await readFile2(this.compressionGuidelinesPath, "utf-8");
4799
- } catch {
5230
+ return await this.readStorageSecureFile(this.compressionGuidelinesPath);
5231
+ } catch (err) {
5232
+ if (err instanceof SecureStoreLockedError) throw err;
5233
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4800
5234
  return null;
4801
5235
  }
4802
5236
  }
4803
5237
  async writeCompressionGuidelineDraft(content) {
4804
5238
  await this.ensureDirectories();
4805
- await writeFile2(this.compressionGuidelineDraftPath, content, "utf-8");
5239
+ await this.writeStorageSecureFile(this.compressionGuidelineDraftPath, content);
4806
5240
  }
4807
5241
  async readCompressionGuidelineDraft() {
4808
5242
  try {
4809
- return await readFile2(this.compressionGuidelineDraftPath, "utf-8");
4810
- } catch {
5243
+ return await this.readStorageSecureFile(this.compressionGuidelineDraftPath);
5244
+ } catch (err) {
5245
+ if (err instanceof SecureStoreLockedError) throw err;
5246
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4811
5247
  return null;
4812
5248
  }
4813
5249
  }
4814
5250
  async writeCompressionGuidelineOptimizerState(state) {
4815
5251
  await this.ensureDirectories();
4816
- await writeFile2(this.compressionGuidelineStatePath, `${JSON.stringify(state, null, 2)}
4817
- `, "utf-8");
5252
+ await this.writeStorageSecureFile(this.compressionGuidelineStatePath, `${JSON.stringify(state, null, 2)}
5253
+ `);
4818
5254
  }
4819
5255
  async writeCompressionGuidelineDraftState(state) {
4820
5256
  await this.ensureDirectories();
4821
- await writeFile2(this.compressionGuidelineDraftStatePath, `${JSON.stringify(state, null, 2)}
4822
- `, "utf-8");
5257
+ await this.writeStorageSecureFile(this.compressionGuidelineDraftStatePath, `${JSON.stringify(state, null, 2)}
5258
+ `);
4823
5259
  }
4824
5260
  async readCompressionGuidelineOptimizerState() {
4825
5261
  return this.readCompressionGuidelineStateFile(this.compressionGuidelineStatePath);
@@ -4867,7 +5303,7 @@ ${memory.content}
4867
5303
  return typeof rule.action === "string" && typeof rule.delta === "number" && Number.isFinite(rule.delta) && (rule.direction === "increase" || rule.direction === "decrease" || rule.direction === "hold") && (rule.confidence === "low" || rule.confidence === "medium" || rule.confidence === "high") && Array.isArray(rule.notes) && rule.notes.every((note) => typeof note === "string");
4868
5304
  };
4869
5305
  try {
4870
- const raw = await readFile2(filePath, "utf-8");
5306
+ const raw = await this.readStorageSecureFile(filePath);
4871
5307
  const parsed = JSON.parse(raw);
4872
5308
  const sourceWindow = parsed?.sourceWindow;
4873
5309
  const eventCounts = parsed?.eventCounts;
@@ -4897,18 +5333,21 @@ ${memory.content}
4897
5333
  ...actionSummaries ? { actionSummaries } : {},
4898
5334
  ...ruleUpdates ? { ruleUpdates } : {}
4899
5335
  };
4900
- } catch {
5336
+ } catch (err) {
5337
+ if (err instanceof SecureStoreLockedError) throw err;
5338
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4901
5339
  return null;
4902
5340
  }
4903
5341
  }
4904
5342
  async writeIdentityAnchor(content) {
4905
5343
  await this.ensureDirectories();
4906
- await writeFile2(this.identityAnchorPath, content, "utf-8");
5344
+ await this.writeStorageSecureFile(this.identityAnchorPath, content);
4907
5345
  }
4908
5346
  async readIdentityAnchor() {
4909
5347
  try {
4910
- return await readFile2(this.identityAnchorPath, "utf-8");
4911
- } catch {
5348
+ return await this.readStorageSecureFile(this.identityAnchorPath);
5349
+ } catch (err) {
5350
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4912
5351
  return null;
4913
5352
  }
4914
5353
  }
@@ -4920,7 +5359,7 @@ ${memory.content}
4920
5359
  const id = this.generateId("incident");
4921
5360
  const incident = createContinuityIncidentRecord(id, input, nowIso);
4922
5361
  const filePath = path4.join(this.identityIncidentsDir, `${date}-${id}.md`);
4923
- await writeFile2(filePath, serializeContinuityIncident(incident), "utf-8");
5362
+ await this.writeStorageSecureFile(filePath, serializeContinuityIncident(incident));
4924
5363
  return { ...incident, filePath };
4925
5364
  }
4926
5365
  async readContinuityIncidents(limit = 200, state = "all") {
@@ -4934,16 +5373,19 @@ ${memory.content}
4934
5373
  if (incidents.length >= cappedLimit) break;
4935
5374
  const filePath = path4.join(this.identityIncidentsDir, file);
4936
5375
  try {
4937
- const raw = await readFile2(filePath, "utf-8");
5376
+ const raw = await this.readStorageSecureFile(filePath);
4938
5377
  const parsed = parseContinuityIncident(raw);
4939
5378
  if (!parsed) continue;
4940
5379
  if (state !== "all" && parsed.state !== state) continue;
4941
5380
  incidents.push({ ...parsed, filePath });
4942
- } catch {
5381
+ } catch (err) {
5382
+ if (err instanceof SecureStoreLockedError) throw err;
4943
5383
  }
4944
5384
  }
4945
5385
  return incidents;
4946
- } catch {
5386
+ } catch (err) {
5387
+ if (err instanceof SecureStoreLockedError) throw err;
5388
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4947
5389
  return [];
4948
5390
  }
4949
5391
  }
@@ -4953,7 +5395,7 @@ ${memory.content}
4953
5395
  if (!target || !directFilePath) return null;
4954
5396
  if (target.state === "closed") return target;
4955
5397
  const closed = closeContinuityIncidentRecord(target, closure, (/* @__PURE__ */ new Date()).toISOString());
4956
- await writeFile2(directFilePath, serializeContinuityIncident(closed), "utf-8");
5398
+ await this.writeStorageSecureFile(directFilePath, serializeContinuityIncident(closed));
4957
5399
  return { ...closed, filePath: directFilePath };
4958
5400
  }
4959
5401
  async writeIdentityAudit(period, key, content) {
@@ -4961,26 +5403,29 @@ ${memory.content}
4961
5403
  const safeKey = this.sanitizeIdentityAuditKey(key);
4962
5404
  const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
4963
5405
  const filePath = path4.join(dir, `${safeKey}.md`);
4964
- await writeFile2(filePath, content, "utf-8");
5406
+ await this.writeStorageSecureFile(filePath, content);
4965
5407
  return filePath;
4966
5408
  }
4967
5409
  async readIdentityAudit(period, key) {
4968
5410
  try {
4969
5411
  const safeKey = this.sanitizeIdentityAuditKey(key);
4970
5412
  const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
4971
- return await readFile2(path4.join(dir, `${safeKey}.md`), "utf-8");
4972
- } catch {
5413
+ return await this.readStorageSecureFile(path4.join(dir, `${safeKey}.md`));
5414
+ } catch (err) {
5415
+ if (err instanceof Error && err.message === "Invalid identity audit key") return null;
5416
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4973
5417
  return null;
4974
5418
  }
4975
5419
  }
4976
5420
  async writeIdentityImprovementLoops(content) {
4977
5421
  await this.ensureDirectories();
4978
- await writeFile2(this.identityImprovementLoopsPath, content, "utf-8");
5422
+ await this.writeStorageSecureFile(this.identityImprovementLoopsPath, content);
4979
5423
  }
4980
5424
  async readIdentityImprovementLoops() {
4981
5425
  try {
4982
- return await readFile2(this.identityImprovementLoopsPath, "utf-8");
4983
- } catch {
5426
+ return await this.readStorageSecureFile(this.identityImprovementLoopsPath);
5427
+ } catch (err) {
5428
+ if (!isErrnoCode(err, "ENOENT")) throw err;
4984
5429
  return null;
4985
5430
  }
4986
5431
  }
@@ -5017,10 +5462,11 @@ ${memory.content}
5017
5462
  }
5018
5463
  async readContinuityIncidentFile(filePath) {
5019
5464
  try {
5020
- const raw = await readFile2(filePath, "utf-8");
5465
+ const raw = await this.readStorageSecureFile(filePath);
5021
5466
  const parsed = parseContinuityIncident(raw);
5022
5467
  return parsed ? { ...parsed, filePath } : null;
5023
- } catch {
5468
+ } catch (err) {
5469
+ if (err instanceof SecureStoreLockedError) throw err;
5024
5470
  return null;
5025
5471
  }
5026
5472
  }
@@ -5088,7 +5534,7 @@ ${question}
5088
5534
  for (const file of files) {
5089
5535
  if (!file.endsWith(".md")) continue;
5090
5536
  const filePath = path4.join(this.questionsDir, file);
5091
- const raw = await readFile2(filePath, "utf-8");
5537
+ const raw = await readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
5092
5538
  const parsed = this.parseQuestionFile(raw, filePath);
5093
5539
  if (parsed) {
5094
5540
  questions.push(parsed);
@@ -5219,20 +5665,22 @@ ${reflection}
5219
5665
  }
5220
5666
  async readIdentityReflections() {
5221
5667
  try {
5222
- return await readFile2(this.identityReflectionsPath, "utf-8");
5223
- } catch {
5668
+ return await this.readStorageSecureFile(this.identityReflectionsPath);
5669
+ } catch (err) {
5670
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5224
5671
  return null;
5225
5672
  }
5226
5673
  }
5227
5674
  async writeIdentityReflections(content) {
5228
5675
  await mkdir2(this.identityDir, { recursive: true });
5229
- await writeFile2(this.identityReflectionsPath, content, "utf-8");
5676
+ await this.writeStorageSecureFile(this.identityReflectionsPath, content);
5230
5677
  }
5231
5678
  async appendIdentityReflection(reflection) {
5232
5679
  let existing = "";
5233
5680
  try {
5234
- existing = await readFile2(this.identityReflectionsPath, "utf-8");
5235
- } catch {
5681
+ existing = await this.readStorageSecureFile(this.identityReflectionsPath);
5682
+ } catch (err) {
5683
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5236
5684
  }
5237
5685
  if (existing.length > _StorageManager.IDENTITY_MAX_BYTES) {
5238
5686
  log.debug(
@@ -5257,7 +5705,7 @@ ${reflection}
5257
5705
  ${reflection}
5258
5706
  `;
5259
5707
  await mkdir2(this.identityDir, { recursive: true });
5260
- await writeFile2(this.identityReflectionsPath, `${existing.trimEnd()}${section}`, "utf-8");
5708
+ await this.writeStorageSecureFile(this.identityReflectionsPath, `${existing.trimEnd()}${section}`);
5261
5709
  log.debug(`appended namespace-local reflection to ${this.identityReflectionsPath}`);
5262
5710
  }
5263
5711
  // ---------------------------------------------------------------------------
@@ -5271,9 +5719,11 @@ ${reflection}
5271
5719
  const filePath = path4.join(this.entitiesDir, `${name}.md`);
5272
5720
  let entity;
5273
5721
  try {
5274
- const content = await readFile2(filePath, "utf-8");
5722
+ const content = await this.readStorageSecureFile(filePath);
5275
5723
  entity = parseEntityFile(content, this.entitySchemas);
5276
- } catch {
5724
+ } catch (err) {
5725
+ if (err instanceof SecureStoreLockedError) throw err;
5726
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5277
5727
  log.debug(`addEntityRelationship: entity file ${name}.md not found`);
5278
5728
  return;
5279
5729
  }
@@ -5283,7 +5733,7 @@ ${reflection}
5283
5733
  if (exists) return;
5284
5734
  entity.relationships.push(rel);
5285
5735
  entity.updated = (/* @__PURE__ */ new Date()).toISOString();
5286
- await writeFile2(filePath, serializeEntityFile(entity, this.entitySchemas), "utf-8");
5736
+ await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
5287
5737
  this.invalidateKnowledgeIndexCache();
5288
5738
  }
5289
5739
  /**
@@ -5294,9 +5744,11 @@ ${reflection}
5294
5744
  const filePath = path4.join(this.entitiesDir, `${name}.md`);
5295
5745
  let entity;
5296
5746
  try {
5297
- const content = await readFile2(filePath, "utf-8");
5747
+ const content = await this.readStorageSecureFile(filePath);
5298
5748
  entity = parseEntityFile(content, this.entitySchemas);
5299
- } catch {
5749
+ } catch (err) {
5750
+ if (err instanceof SecureStoreLockedError) throw err;
5751
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5300
5752
  log.debug(`addEntityActivity: entity file ${name}.md not found`);
5301
5753
  return;
5302
5754
  }
@@ -5305,7 +5757,7 @@ ${reflection}
5305
5757
  entity.activity = entity.activity.slice(0, maxEntries);
5306
5758
  }
5307
5759
  entity.updated = (/* @__PURE__ */ new Date()).toISOString();
5308
- await writeFile2(filePath, serializeEntityFile(entity, this.entitySchemas), "utf-8");
5760
+ await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
5309
5761
  this.invalidateKnowledgeIndexCache();
5310
5762
  }
5311
5763
  /**
@@ -5315,16 +5767,18 @@ ${reflection}
5315
5767
  const filePath = path4.join(this.entitiesDir, `${name}.md`);
5316
5768
  let entity;
5317
5769
  try {
5318
- const content = await readFile2(filePath, "utf-8");
5770
+ const content = await this.readStorageSecureFile(filePath);
5319
5771
  entity = parseEntityFile(content, this.entitySchemas);
5320
- } catch {
5772
+ } catch (err) {
5773
+ if (err instanceof SecureStoreLockedError) throw err;
5774
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5321
5775
  log.debug(`addEntityAlias: entity file ${name}.md not found`);
5322
5776
  return;
5323
5777
  }
5324
5778
  if (entity.aliases.includes(alias)) return;
5325
5779
  entity.aliases.push(alias);
5326
5780
  entity.updated = (/* @__PURE__ */ new Date()).toISOString();
5327
- await writeFile2(filePath, serializeEntityFile(entity, this.entitySchemas), "utf-8");
5781
+ await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
5328
5782
  this.invalidateKnowledgeIndexCache();
5329
5783
  }
5330
5784
  /**
@@ -5334,9 +5788,11 @@ ${reflection}
5334
5788
  const filePath = path4.join(this.entitiesDir, `${name}.md`);
5335
5789
  let entity;
5336
5790
  try {
5337
- const content = await readFile2(filePath, "utf-8");
5791
+ const content = await this.readStorageSecureFile(filePath);
5338
5792
  entity = parseEntityFile(content, this.entitySchemas);
5339
- } catch {
5793
+ } catch (err) {
5794
+ if (err instanceof SecureStoreLockedError) throw err;
5795
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5340
5796
  log.debug(`updateEntitySynthesis: entity file ${name}.md not found`);
5341
5797
  return;
5342
5798
  }
@@ -5353,7 +5809,7 @@ ${reflection}
5353
5809
  entity.synthesisStructuredFactDigest = synthesisStructuredFactDigest;
5354
5810
  entity.synthesisVersion = Math.max(0, entity.synthesisVersion ?? 0) + (options.incrementVersion === false ? 0 : 1);
5355
5811
  entity.updated = entityUpdatedAt;
5356
- await writeFile2(filePath, serializeEntityFile(entity, this.entitySchemas), "utf-8");
5812
+ await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
5357
5813
  await this.removeEntitySynthesisQueueEntries([
5358
5814
  .../* @__PURE__ */ new Set([name, normalizeEntityName(entity.name, entity.type)])
5359
5815
  ]);
@@ -5368,9 +5824,11 @@ ${reflection}
5368
5824
  let synthesisTimelineCount;
5369
5825
  try {
5370
5826
  const filePath = path4.join(this.entitiesDir, `${name}.md`);
5371
- const content = await readFile2(filePath, "utf-8");
5827
+ const content = await this.readStorageSecureFile(filePath);
5372
5828
  synthesisTimelineCount = parseEntityFile(content, this.entitySchemas).timeline.length;
5373
- } catch {
5829
+ } catch (err) {
5830
+ if (err instanceof SecureStoreLockedError) throw err;
5831
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5374
5832
  synthesisTimelineCount = void 0;
5375
5833
  }
5376
5834
  await this.updateEntitySynthesis(name, summary, {
@@ -5381,10 +5839,12 @@ ${reflection}
5381
5839
  }
5382
5840
  async readEntitySynthesisQueue() {
5383
5841
  try {
5384
- const raw = await readFile2(this.entitySynthesisQueuePath, "utf-8");
5842
+ const raw = await this.readStorageSecureFile(this.entitySynthesisQueuePath);
5385
5843
  const parsed = JSON.parse(raw);
5386
5844
  return Array.isArray(parsed.entityNames) ? parsed.entityNames.filter((value) => typeof value === "string") : [];
5387
- } catch {
5845
+ } catch (err) {
5846
+ if (err instanceof SecureStoreLockedError) throw err;
5847
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5388
5848
  return [];
5389
5849
  }
5390
5850
  }
@@ -5406,7 +5866,7 @@ ${reflection}
5406
5866
  return compareEntityTimestamps(rightTs, leftTs);
5407
5867
  }).map(({ entityName }) => entityName);
5408
5868
  await mkdir2(this.stateDir, { recursive: true });
5409
- await writeFile2(
5869
+ await this.writeStorageSecureFile(
5410
5870
  this.entitySynthesisQueuePath,
5411
5871
  JSON.stringify(
5412
5872
  {
@@ -5415,8 +5875,7 @@ ${reflection}
5415
5875
  },
5416
5876
  null,
5417
5877
  2
5418
- ) + "\n",
5419
- "utf-8"
5878
+ ) + "\n"
5420
5879
  );
5421
5880
  return staleEntityNames;
5422
5881
  }
@@ -5427,7 +5886,7 @@ ${reflection}
5427
5886
  const removals = new Set(entityNames);
5428
5887
  const nextQueue = queue.filter((name) => !removals.has(name));
5429
5888
  await mkdir2(this.stateDir, { recursive: true });
5430
- await writeFile2(
5889
+ await this.writeStorageSecureFile(
5431
5890
  this.entitySynthesisQueuePath,
5432
5891
  JSON.stringify(
5433
5892
  {
@@ -5436,8 +5895,7 @@ ${reflection}
5436
5895
  },
5437
5896
  null,
5438
5897
  2
5439
- ) + "\n",
5440
- "utf-8"
5898
+ ) + "\n"
5441
5899
  );
5442
5900
  }
5443
5901
  async migrateEntityFilesToCompiledTruthTimeline() {
@@ -5448,7 +5906,7 @@ ${reflection}
5448
5906
  if (!raw) continue;
5449
5907
  const serialized = serializeEntityFile(parseEntityFile(raw, this.entitySchemas), this.entitySchemas);
5450
5908
  if (raw.trimEnd() === serialized.trimEnd()) continue;
5451
- await writeFile2(path4.join(this.entitiesDir, `${entityName}.md`), serialized, "utf-8");
5909
+ await this.writeStorageSecureFile(path4.join(this.entitiesDir, `${entityName}.md`), serialized);
5452
5910
  migrated += 1;
5453
5911
  }
5454
5912
  if (migrated > 0) {
@@ -5470,7 +5928,8 @@ ${reflection}
5470
5928
  async readAllEntityFiles() {
5471
5929
  const currentVersion = this.getMemoryStatusVersion();
5472
5930
  const schemaCacheKey = buildEntitySchemaCacheKey(this.entitySchemas);
5473
- const cached = getCachedEntities(this.baseDir, currentVersion, schemaCacheKey);
5931
+ const cacheKey = `${this.getEntityCacheSecureStoreKey()}\0${schemaCacheKey}`;
5932
+ const cached = getCachedEntities(this.baseDir, currentVersion, cacheKey);
5474
5933
  if (cached) return cached;
5475
5934
  try {
5476
5935
  const entries = await readdir(this.entitiesDir);
@@ -5481,17 +5940,24 @@ ${reflection}
5481
5940
  for (let i = 0; i < mdFiles.length; i += BATCH_SIZE) {
5482
5941
  const batch = mdFiles.slice(i, i + BATCH_SIZE);
5483
5942
  const results = await Promise.all(
5484
- batch.map(
5485
- (entry) => readFile2(path4.join(this.entitiesDir, entry), "utf-8").catch(() => null)
5486
- )
5943
+ batch.map(async (entry) => {
5944
+ try {
5945
+ return await this.readStorageSecureFile(path4.join(this.entitiesDir, entry));
5946
+ } catch (err) {
5947
+ if (err instanceof SecureStoreLockedError) throw err;
5948
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5949
+ return null;
5950
+ }
5951
+ })
5487
5952
  );
5488
5953
  for (const content of results) {
5489
5954
  if (content !== null) entities.push(parseEntityFile(content, this.entitySchemas));
5490
5955
  }
5491
5956
  }
5492
- setCachedEntities(this.baseDir, entities, currentVersion, schemaCacheKey);
5957
+ setCachedEntities(this.baseDir, entities, currentVersion, cacheKey);
5493
5958
  return entities;
5494
- } catch {
5959
+ } catch (err) {
5960
+ if (err instanceof SecureStoreLockedError || !isErrnoCode(err, "ENOENT")) throw err;
5495
5961
  return [];
5496
5962
  }
5497
5963
  }
@@ -5624,7 +6090,7 @@ ${rows.join("\n")}
5624
6090
  for (const file of files) {
5625
6091
  const filePath = path4.join(this.entitiesDir, file);
5626
6092
  try {
5627
- const content = await readFile2(filePath, "utf-8");
6093
+ const content = await this.readStorageSecureFile(filePath);
5628
6094
  const parsed = parseEntityFile(content, this.entitySchemas);
5629
6095
  if (!mergedEntity.type || mergedEntity.type === "other") {
5630
6096
  mergedEntity.type = parsed.type;
@@ -5701,7 +6167,9 @@ ${rows.join("\n")}
5701
6167
  title: section.title,
5702
6168
  lines: [...section.lines]
5703
6169
  })));
5704
- } catch {
6170
+ } catch (err) {
6171
+ if (err instanceof SecureStoreLockedError) throw err;
6172
+ if (!isErrnoCode(err, "ENOENT")) throw err;
5705
6173
  }
5706
6174
  }
5707
6175
  const timelineKeys = /* @__PURE__ */ new Set();
@@ -5752,7 +6220,7 @@ ${rows.join("\n")}
5752
6220
  mergedEntity.created = mergedEntity.created || mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
5753
6221
  mergedEntity.updated = mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
5754
6222
  const canonicalPath = path4.join(this.entitiesDir, `${canonical}.md`);
5755
- await writeFile2(canonicalPath, serializeEntityFile(mergedEntity, this.entitySchemas), "utf-8");
6223
+ await this.writeStorageSecureFile(canonicalPath, serializeEntityFile(mergedEntity, this.entitySchemas));
5756
6224
  for (const file of files) {
5757
6225
  const filePath = path4.join(this.entitiesDir, file);
5758
6226
  if (filePath !== canonicalPath) {
@@ -5765,7 +6233,8 @@ ${rows.join("\n")}
5765
6233
  }
5766
6234
  }
5767
6235
  }
5768
- } catch {
6236
+ } catch (err) {
6237
+ if (err instanceof SecureStoreLockedError || !isErrnoCode(err, "ENOENT")) throw err;
5769
6238
  }
5770
6239
  return merged;
5771
6240
  }
@@ -5819,7 +6288,7 @@ ${rows.join("\n")}
5819
6288
  ${memory.content}
5820
6289
  `;
5821
6290
  try {
5822
- await writeFile2(memory.path, fileContent, "utf-8");
6291
+ await this.writeStorageSecureFile(memory.path, fileContent);
5823
6292
  updated++;
5824
6293
  } catch (err) {
5825
6294
  log.debug(`failed to update access tracking for ${entry.memoryId}: ${err}`);
@@ -5960,7 +6429,7 @@ ${sanitized.text}
5960
6429
  } else {
5961
6430
  filePath = path4.join(this.factsDir, today, `${id}.md`);
5962
6431
  }
5963
- await writeFile2(filePath, fileContent, "utf-8");
6432
+ await this.writeStorageSecureFile(filePath, fileContent);
5964
6433
  log.debug(`wrote chunk ${id} (${chunkIndex + 1}/${chunkTotal}) to ${filePath}`);
5965
6434
  return id;
5966
6435
  }
@@ -5996,7 +6465,7 @@ ${sanitized.text}
5996
6465
  ${oldMemory.content}
5997
6466
  `;
5998
6467
  try {
5999
- await writeFile2(oldMemory.path, fileContent, "utf-8");
6468
+ await writeMaybeEncryptedFile(oldMemory.path, fileContent, this.resolveWriteKey(), {}, this.baseDir);
6000
6469
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.supersedeMemory", {
6001
6470
  memoryId: oldMemoryId,
6002
6471
  eventType: "superseded",
@@ -6035,7 +6504,7 @@ Reason: ${reason}`, {
6035
6504
  async writeSummary(summary) {
6036
6505
  await mkdir2(this.summariesDir, { recursive: true });
6037
6506
  const filePath = path4.join(this.summariesDir, `${summary.id}.json`);
6038
- await writeFile2(filePath, JSON.stringify(summary, null, 2), "utf-8");
6507
+ await this.writeStorageSecureFile(filePath, JSON.stringify(summary, null, 2));
6039
6508
  log.debug(`wrote summary ${summary.id}`);
6040
6509
  }
6041
6510
  /**
@@ -6048,11 +6517,13 @@ Reason: ${reason}`, {
6048
6517
  for (const file of files) {
6049
6518
  if (!file.endsWith(".json")) continue;
6050
6519
  const filePath = path4.join(this.summariesDir, file);
6051
- const raw = await readFile2(filePath, "utf-8");
6520
+ const raw = await this.readStorageSecureFile(filePath);
6052
6521
  summaries.push(JSON.parse(raw));
6053
6522
  }
6054
6523
  return summaries;
6055
- } catch {
6524
+ } catch (err) {
6525
+ if (err instanceof SecureStoreLockedError) throw err;
6526
+ if (!isErrnoCode(err, "ENOENT")) throw err;
6056
6527
  return [];
6057
6528
  }
6058
6529
  }
@@ -6078,7 +6549,7 @@ Reason: ${reason}`, {
6078
6549
  ${memory.content}
6079
6550
  `;
6080
6551
  try {
6081
- await writeFile2(memory.path, fileContent, "utf-8");
6552
+ await this.writeStorageSecureFile(memory.path, fileContent);
6082
6553
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.archiveMemories", {
6083
6554
  memoryId: id,
6084
6555
  eventType: "archived",
@@ -6108,7 +6579,7 @@ ${memory.content}
6108
6579
  async saveTopics(topics) {
6109
6580
  const metaPath = path4.join(this.stateDir, "topics.json");
6110
6581
  await mkdir2(this.stateDir, { recursive: true });
6111
- await writeFile2(metaPath, JSON.stringify({ topics, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
6582
+ await this.writeStorageSecureFile(metaPath, JSON.stringify({ topics, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
6112
6583
  log.debug(`saved ${topics.length} topic scores`);
6113
6584
  }
6114
6585
  /**
@@ -6117,9 +6588,11 @@ ${memory.content}
6117
6588
  async loadTopics() {
6118
6589
  const metaPath = path4.join(this.stateDir, "topics.json");
6119
6590
  try {
6120
- const raw = await readFile2(metaPath, "utf-8");
6591
+ const raw = await this.readStorageSecureFile(metaPath);
6121
6592
  return JSON.parse(raw);
6122
- } catch {
6593
+ } catch (err) {
6594
+ if (err instanceof SecureStoreLockedError) throw err;
6595
+ if (!isErrnoCode(err, "ENOENT")) throw err;
6123
6596
  return { topics: [], updatedAt: null };
6124
6597
  }
6125
6598
  }
@@ -6195,6 +6668,12 @@ export {
6195
6668
  normalizeEntitySchemas,
6196
6669
  resolveRequestedEntitySectionKeys,
6197
6670
  sanitizeMemoryContent,
6671
+ MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
6672
+ toMemoryPathRel,
6673
+ inferMemoryStatus,
6674
+ isActiveMemoryStatus,
6675
+ buildLifecycleEventsForMemory,
6676
+ sortMemoryLifecycleEvents,
6198
6677
  hasCitationForTemplate,
6199
6678
  attachCitation,
6200
6679
  stripCitationForTemplate,
@@ -6206,7 +6685,12 @@ export {
6206
6685
  setCachedQmdSearch,
6207
6686
  lintWorkspaceFiles,
6208
6687
  rotateMarkdownFileToArchive,
6688
+ DERIVED_FROM_MEMORY_ID_RE,
6209
6689
  isConsolidationOperator,
6690
+ isSemanticConsolidationLlmOperator,
6691
+ RECALL_DISCLOSURE_LEVELS,
6692
+ DEFAULT_RECALL_DISCLOSURE,
6693
+ isRecallDisclosure,
6210
6694
  confidenceTier,
6211
6695
  openBetterSqlite3,
6212
6696
  MEMORY_PROJECTION_SCHEMA_VERSION,
@@ -6218,14 +6702,8 @@ export {
6218
6702
  readProjectedEntityMentions,
6219
6703
  readProjectedNativeKnowledgeChunks,
6220
6704
  readProjectedGovernanceRecord,
6221
- MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
6222
- toMemoryPathRel,
6223
- inferMemoryStatus,
6224
- buildLifecycleEventsForMemory,
6225
- sortMemoryLifecycleEvents,
6226
6705
  normalizeProjectionPreview,
6227
6706
  normalizeProjectionTags,
6228
- parseContinuityIncident,
6229
6707
  parseContinuityImprovementLoops,
6230
6708
  normalizeEntityName,
6231
6709
  ContentHashIndex,