@remnic/core 9.3.662 → 9.3.664

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 (143) hide show
  1. package/dist/access-cli.js +25 -23
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.js +20 -18
  4. package/dist/access-mcp.js +19 -17
  5. package/dist/access-schema.js +4 -3
  6. package/dist/access-service.js +17 -15
  7. package/dist/briefing.js +5 -4
  8. package/dist/{capsule-merge-T2JRE46P.js → capsule-merge-GK5E647P.js} +3 -2
  9. package/dist/{capsule-merge-T2JRE46P.js.map → capsule-merge-GK5E647P.js.map} +1 -1
  10. package/dist/causal-consolidation.js +6 -5
  11. package/dist/causal-consolidation.js.map +1 -1
  12. package/dist/{chunk-2KDQI363.js → chunk-2HEZXPYU.js} +4 -4
  13. package/dist/chunk-5GPPACXK.js +16 -0
  14. package/dist/chunk-5GPPACXK.js.map +1 -0
  15. package/dist/{chunk-F6O7IOS3.js → chunk-6JBKHTQD.js} +2 -2
  16. package/dist/{chunk-TBLGI2LT.js → chunk-7ILWCUWH.js} +5 -3
  17. package/dist/{chunk-TBLGI2LT.js.map → chunk-7ILWCUWH.js.map} +1 -1
  18. package/dist/{chunk-AL4RAJL5.js → chunk-7XH7VJN4.js} +6 -4
  19. package/dist/chunk-7XH7VJN4.js.map +1 -0
  20. package/dist/{chunk-Q4CAQGKQ.js → chunk-AER6MT24.js} +12 -21
  21. package/dist/chunk-AER6MT24.js.map +1 -0
  22. package/dist/{chunk-DHGSZ3UD.js → chunk-ARV3AUOM.js} +2 -2
  23. package/dist/{chunk-PXVFMQLD.js → chunk-BZG2CWOQ.js} +3 -3
  24. package/dist/{chunk-ANJOULTP.js → chunk-C7AF236A.js} +2 -2
  25. package/dist/{chunk-FZC2WSDB.js → chunk-DOCTITOP.js} +2 -2
  26. package/dist/{chunk-WOQIHC67.js → chunk-DQY7NJ5L.js} +2 -2
  27. package/dist/{chunk-NMPEJV5M.js → chunk-DSLUOQDY.js} +2 -2
  28. package/dist/{chunk-A7EF2XRO.js → chunk-EXXBA5OM.js} +30 -8
  29. package/dist/chunk-EXXBA5OM.js.map +1 -0
  30. package/dist/{chunk-QXHBWFR3.js → chunk-IHG6CC7T.js} +2 -2
  31. package/dist/{chunk-4KDLCMLK.js → chunk-IROWLAWG.js} +5 -5
  32. package/dist/{chunk-ILXTATKK.js → chunk-J2HSAU72.js} +5 -5
  33. package/dist/chunk-J2HSAU72.js.map +1 -0
  34. package/dist/{chunk-DFAXGZKI.js → chunk-JIX3ZL2J.js} +8 -8
  35. package/dist/{chunk-GY3V3SUI.js → chunk-KHGE6PMF.js} +2 -2
  36. package/dist/{chunk-TGOOJCGA.js → chunk-LIERUFPO.js} +76 -54
  37. package/dist/chunk-LIERUFPO.js.map +1 -0
  38. package/dist/{chunk-HSCJYHYV.js → chunk-NLF54XMD.js} +49 -19
  39. package/dist/chunk-NLF54XMD.js.map +1 -0
  40. package/dist/{chunk-TWAJICBN.js → chunk-OHJFJ4HI.js} +2 -2
  41. package/dist/{chunk-WSQG37DV.js → chunk-OUWAQVDJ.js} +2 -2
  42. package/dist/{chunk-ZLDUQWT2.js → chunk-PWWWLD7D.js} +2 -2
  43. package/dist/{chunk-ZJH723NM.js → chunk-Q5ZU3RNY.js} +2 -2
  44. package/dist/{chunk-35HP3TGR.js → chunk-ROHLEUTH.js} +4 -4
  45. package/dist/{chunk-5RIRL3XL.js → chunk-RS25QOKZ.js} +2 -2
  46. package/dist/{chunk-RQGR3ETH.js → chunk-T2AN3BSP.js} +2 -2
  47. package/dist/{chunk-UAU5U5ML.js → chunk-UDJLF3BO.js} +2 -2
  48. package/dist/{chunk-ALEPI75L.js → chunk-VF4XKTX3.js} +6 -4
  49. package/dist/{chunk-ALEPI75L.js.map → chunk-VF4XKTX3.js.map} +1 -1
  50. package/dist/{chunk-AX5O25EF.js → chunk-VH6EIKVS.js} +152 -190
  51. package/dist/chunk-VH6EIKVS.js.map +1 -0
  52. package/dist/chunk-VS2IYZRU.js +43 -0
  53. package/dist/chunk-VS2IYZRU.js.map +1 -0
  54. package/dist/{chunk-YYQRVNSV.js → chunk-XB5P5P2L.js} +6 -6
  55. package/dist/{chunk-D2EFNQMY.js → chunk-XW3W4PV4.js} +2 -2
  56. package/dist/{chunk-5AYAZN45.js → chunk-YKX63GBK.js} +5 -5
  57. package/dist/{chunk-TYIXG4VR.js → chunk-YW52BQSU.js} +2 -2
  58. package/dist/{cli-C6twwe84.d.ts → cli-BQRqR9N-.d.ts} +12 -1
  59. package/dist/cli.d.ts +1 -1
  60. package/dist/cli.js +32 -28
  61. package/dist/compounding/engine.js +5 -4
  62. package/dist/connectors/codex-materialize-runner.js +5 -4
  63. package/dist/connectors/index.js +5 -4
  64. package/dist/consolidation-provenance-check.js +3 -2
  65. package/dist/consolidation-undo.js +2 -1
  66. package/dist/consolidation-undo.js.map +1 -1
  67. package/dist/entity-retrieval.js +5 -4
  68. package/dist/index.d.ts +1 -1
  69. package/dist/index.js +39 -36
  70. package/dist/index.js.map +1 -1
  71. package/dist/maintenance/memory-governance.js +6 -4
  72. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -4
  73. package/dist/maintenance/rebuild-memory-projection.js +7 -5
  74. package/dist/namespaces/migrate.js +13 -11
  75. package/dist/namespaces/search.js +8 -6
  76. package/dist/namespaces/storage.js +5 -4
  77. package/dist/offline-sync.js +3 -2
  78. package/dist/operator-toolkit.js +16 -14
  79. package/dist/orchestrator.js +21 -19
  80. package/dist/page-versioning.js +2 -1
  81. package/dist/schemas.d.ts +22 -22
  82. package/dist/search/document-scanner.d.ts +11 -7
  83. package/dist/search/document-scanner.js +3 -1
  84. package/dist/search/factory.js +7 -5
  85. package/dist/search/index.js +7 -5
  86. package/dist/search/lancedb-backend.js +4 -2
  87. package/dist/search/meilisearch-backend.js +4 -2
  88. package/dist/search/orama-backend.js +4 -2
  89. package/dist/secure-store/index.js +3 -2
  90. package/dist/semantic-consolidation.js +6 -5
  91. package/dist/semantic-rule-promotion.js +5 -4
  92. package/dist/semantic-rule-verifier.js +5 -4
  93. package/dist/storage.d.ts +17 -3
  94. package/dist/storage.js +4 -3
  95. package/dist/transfer/capsule-import.js +3 -2
  96. package/dist/transfer/types.d.ts +12 -12
  97. package/dist/verified-recall.js +5 -4
  98. package/package.json +1 -1
  99. package/src/cli.ts +62 -23
  100. package/src/consolidation-provenance-check.ts +7 -6
  101. package/src/maintenance/memory-governance.ts +47 -7
  102. package/src/orchestrator.ts +84 -58
  103. package/src/page-versioning.ts +7 -4
  104. package/src/search/document-scanner.test.ts +29 -0
  105. package/src/search/document-scanner.ts +17 -29
  106. package/src/secure-store/secure-fs.ts +19 -5
  107. package/src/secure-store/secure-store.test.ts +28 -0
  108. package/src/storage.ts +42 -43
  109. package/src/training-export/converter.test.ts +19 -0
  110. package/src/training-export/converter.ts +8 -5
  111. package/src/utils/category-dir.ts +10 -4
  112. package/src/utils/path-containment.ts +40 -0
  113. package/dist/chunk-A7EF2XRO.js.map +0 -1
  114. package/dist/chunk-AL4RAJL5.js.map +0 -1
  115. package/dist/chunk-AX5O25EF.js.map +0 -1
  116. package/dist/chunk-HSCJYHYV.js.map +0 -1
  117. package/dist/chunk-ILXTATKK.js.map +0 -1
  118. package/dist/chunk-Q4CAQGKQ.js.map +0 -1
  119. package/dist/chunk-TGOOJCGA.js.map +0 -1
  120. /package/dist/{chunk-2KDQI363.js.map → chunk-2HEZXPYU.js.map} +0 -0
  121. /package/dist/{chunk-F6O7IOS3.js.map → chunk-6JBKHTQD.js.map} +0 -0
  122. /package/dist/{chunk-DHGSZ3UD.js.map → chunk-ARV3AUOM.js.map} +0 -0
  123. /package/dist/{chunk-PXVFMQLD.js.map → chunk-BZG2CWOQ.js.map} +0 -0
  124. /package/dist/{chunk-ANJOULTP.js.map → chunk-C7AF236A.js.map} +0 -0
  125. /package/dist/{chunk-FZC2WSDB.js.map → chunk-DOCTITOP.js.map} +0 -0
  126. /package/dist/{chunk-WOQIHC67.js.map → chunk-DQY7NJ5L.js.map} +0 -0
  127. /package/dist/{chunk-NMPEJV5M.js.map → chunk-DSLUOQDY.js.map} +0 -0
  128. /package/dist/{chunk-QXHBWFR3.js.map → chunk-IHG6CC7T.js.map} +0 -0
  129. /package/dist/{chunk-4KDLCMLK.js.map → chunk-IROWLAWG.js.map} +0 -0
  130. /package/dist/{chunk-DFAXGZKI.js.map → chunk-JIX3ZL2J.js.map} +0 -0
  131. /package/dist/{chunk-GY3V3SUI.js.map → chunk-KHGE6PMF.js.map} +0 -0
  132. /package/dist/{chunk-TWAJICBN.js.map → chunk-OHJFJ4HI.js.map} +0 -0
  133. /package/dist/{chunk-WSQG37DV.js.map → chunk-OUWAQVDJ.js.map} +0 -0
  134. /package/dist/{chunk-ZLDUQWT2.js.map → chunk-PWWWLD7D.js.map} +0 -0
  135. /package/dist/{chunk-ZJH723NM.js.map → chunk-Q5ZU3RNY.js.map} +0 -0
  136. /package/dist/{chunk-35HP3TGR.js.map → chunk-ROHLEUTH.js.map} +0 -0
  137. /package/dist/{chunk-5RIRL3XL.js.map → chunk-RS25QOKZ.js.map} +0 -0
  138. /package/dist/{chunk-RQGR3ETH.js.map → chunk-T2AN3BSP.js.map} +0 -0
  139. /package/dist/{chunk-UAU5U5ML.js.map → chunk-UDJLF3BO.js.map} +0 -0
  140. /package/dist/{chunk-YYQRVNSV.js.map → chunk-XB5P5P2L.js.map} +0 -0
  141. /package/dist/{chunk-D2EFNQMY.js.map → chunk-XW3W4PV4.js.map} +0 -0
  142. /package/dist/{chunk-5AYAZN45.js.map → chunk-YKX63GBK.js.map} +0 -0
  143. /package/dist/{chunk-TYIXG4VR.js.map → chunk-YW52BQSU.js.map} +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-DRD2Q7HQ.js";
5
5
  import {
6
6
  StorageManager
7
- } from "./chunk-AX5O25EF.js";
7
+ } from "./chunk-VH6EIKVS.js";
8
8
  import {
9
9
  parseContinuityImprovementLoops
10
10
  } from "./chunk-LDXUBPMO.js";
@@ -1261,4 +1261,4 @@ export {
1261
1261
  defaultTierMigrationCycleBudget,
1262
1262
  CompoundingEngine
1263
1263
  };
1264
- //# sourceMappingURL=chunk-TWAJICBN.js.map
1264
+ //# sourceMappingURL=chunk-OHJFJ4HI.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  scanMemoryDir
3
- } from "./chunk-Q4CAQGKQ.js";
3
+ } from "./chunk-AER6MT24.js";
4
4
  import {
5
5
  isSearchAborted,
6
6
  throwIfSearchAborted
@@ -491,4 +491,4 @@ function isMissingVectorProviderColumnError(err) {
491
491
  export {
492
492
  LanceDbBackend
493
493
  };
494
- //# sourceMappingURL=chunk-WSQG37DV.js.map
494
+ //# sourceMappingURL=chunk-OUWAQVDJ.js.map
@@ -10,7 +10,7 @@ import {
10
10
  import {
11
11
  MAGIC_HEADER_SIZE,
12
12
  isEncryptedFile
13
- } from "./chunk-ILXTATKK.js";
13
+ } from "./chunk-J2HSAU72.js";
14
14
  import {
15
15
  parseFlexibleIsoTimestamp
16
16
  } from "./chunk-P7FMDTKL.js";
@@ -1642,4 +1642,4 @@ export {
1642
1642
  normalizeOfflineSyncState,
1643
1643
  fileStatesFromSnapshot
1644
1644
  };
1645
- //# sourceMappingURL=chunk-ZLDUQWT2.js.map
1645
+ //# sourceMappingURL=chunk-PWWWLD7D.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  scanMemoryDir
3
- } from "./chunk-Q4CAQGKQ.js";
3
+ } from "./chunk-AER6MT24.js";
4
4
  import {
5
5
  isSearchAborted,
6
6
  throwIfSearchAborted
@@ -605,4 +605,4 @@ export {
605
605
  resolveOramaCollectionDbFilePath,
606
606
  OramaBackend
607
607
  };
608
- //# sourceMappingURL=chunk-ZJH723NM.js.map
608
+ //# sourceMappingURL=chunk-Q5ZU3RNY.js.map
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  MeilisearchBackend
3
- } from "./chunk-FZC2WSDB.js";
3
+ } from "./chunk-DOCTITOP.js";
4
4
  import {
5
5
  NoopSearchBackend
6
6
  } from "./chunk-CYEPCZN5.js";
7
7
  import {
8
8
  OramaBackend
9
- } from "./chunk-ZJH723NM.js";
9
+ } from "./chunk-Q5ZU3RNY.js";
10
10
  import {
11
11
  RemoteSearchBackend
12
12
  } from "./chunk-JOASJWQR.js";
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-RN7MUWON.js";
16
16
  import {
17
17
  LanceDbBackend
18
- } from "./chunk-WSQG37DV.js";
18
+ } from "./chunk-OUWAQVDJ.js";
19
19
  import {
20
20
  createConversationIndexBackend
21
21
  } from "./chunk-7OGJQP7T.js";
@@ -170,4 +170,4 @@ export {
170
170
  createConversationSearchBackend,
171
171
  createConversationIndexRuntime
172
172
  };
173
- //# sourceMappingURL=chunk-35HP3TGR.js.map
173
+ //# sourceMappingURL=chunk-ROHLEUTH.js.map
@@ -19,7 +19,7 @@ import {
19
19
  import {
20
20
  decryptMemoryDirToPlaintext,
21
21
  migrateMemoryDirToEncrypted
22
- } from "./chunk-ILXTATKK.js";
22
+ } from "./chunk-J2HSAU72.js";
23
23
  import {
24
24
  generateSalt
25
25
  } from "./chunk-A6XUJE5D.js";
@@ -499,4 +499,4 @@ export {
499
499
  renderStatusReport,
500
500
  createPassphraseReader
501
501
  };
502
- //# sourceMappingURL=chunk-5RIRL3XL.js.map
502
+ //# sourceMappingURL=chunk-RS25QOKZ.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  compareEntityTimestamps,
6
6
  normalizeEntityName
7
- } from "./chunk-AX5O25EF.js";
7
+ } from "./chunk-VH6EIKVS.js";
8
8
  import {
9
9
  sanitizeMemoryContent
10
10
  } from "./chunk-FVQJYWH7.js";
@@ -679,4 +679,4 @@ export {
679
679
  entityIndexVersion,
680
680
  entityRecentTranscriptLookbackHours
681
681
  };
682
- //# sourceMappingURL=chunk-RQGR3ETH.js.map
682
+ //# sourceMappingURL=chunk-T2AN3BSP.js.map
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
9
9
  OFFLINE_SYNC_MAX_MTIME_MS
10
- } from "./chunk-ZLDUQWT2.js";
10
+ } from "./chunk-PWWWLD7D.js";
11
11
  import {
12
12
  validateArchiveRelativePath
13
13
  } from "./chunk-GCGJW34D.js";
@@ -433,4 +433,4 @@ export {
433
433
  actionConfidenceRequestSchema,
434
434
  validateRequest
435
435
  };
436
- //# sourceMappingURL=chunk-UAU5U5ML.js.map
436
+ //# sourceMappingURL=chunk-UDJLF3BO.js.map
@@ -1,3 +1,7 @@
1
+ import {
2
+ ALL_CATEGORY_DIRS
3
+ } from "./chunk-VS2IYZRU.js";
4
+
1
5
  // src/page-versioning.ts
2
6
  import { createHash } from "crypto";
3
7
  import path from "path";
@@ -230,12 +234,10 @@ function computeLCS(a, b) {
230
234
  }
231
235
  function resolveMemoryDir(pagePath) {
232
236
  const knownSubdirs = /* @__PURE__ */ new Set([
233
- "facts",
234
- "corrections",
237
+ ...ALL_CATEGORY_DIRS,
235
238
  "entities",
236
239
  "state",
237
240
  "artifacts",
238
- "questions",
239
241
  "profiles"
240
242
  ]);
241
243
  let dir = path.dirname(pagePath);
@@ -261,4 +263,4 @@ export {
261
263
  revertToVersion,
262
264
  diffVersions
263
265
  };
264
- //# sourceMappingURL=chunk-ALEPI75L.js.map
266
+ //# sourceMappingURL=chunk-VF4XKTX3.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/page-versioning.ts"],"sourcesContent":["/**\n * Page-level versioning with history and revert (issue #371).\n *\n * Provides snapshot-based versioning for memory files using a sidecar\n * directory layout. Each memory page gets a `.versions/<pageName>/`\n * subdirectory containing numbered snapshots and a `manifest.json` that\n * records the version history.\n *\n * Storage layout:\n * memoryDir/\n * facts/preferences.md <- current file\n * .versions/\n * facts__preferences/\n * manifest.json <- VersionHistory\n * 1.md <- version 1 snapshot\n * 2.md <- version 2 snapshot\n */\n\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport {\n access,\n mkdir,\n readFile,\n writeFile,\n unlink,\n} from \"node:fs/promises\";\n\n// ---------------------------------------------------------------------------\n// Public interfaces\n// ---------------------------------------------------------------------------\n\nexport interface PageVersion {\n versionId: string;\n timestamp: string;\n contentHash: string;\n sizeBytes: number;\n trigger: VersionTrigger;\n note?: string;\n}\n\nexport type VersionTrigger = \"write\" | \"consolidation\" | \"revert\" | \"manual\";\n\nexport interface VersionHistory {\n pagePath: string;\n versions: PageVersion[];\n currentVersion: string;\n}\n\nexport interface VersioningConfig {\n enabled: boolean;\n maxVersionsPerPage: number;\n sidecarDir: string;\n}\n\n// ---------------------------------------------------------------------------\n// Logger interface (minimal, avoids coupling to the host logger)\n// ---------------------------------------------------------------------------\n\nexport interface VersioningLogger {\n debug(msg: string): void;\n warn(msg: string): void;\n}\n\nconst NOOP_LOGGER: VersioningLogger = {\n debug: () => {},\n warn: () => {},\n};\n\n// ---------------------------------------------------------------------------\n// Per-page write lock (promise-chain pattern, see gotcha #40)\n// ---------------------------------------------------------------------------\n\nconst writeLocks = new Map<string, Promise<void>>();\n\nfunction withPageLock<T>(pageKey: string, fn: () => Promise<T>): Promise<T> {\n const prev = writeLocks.get(pageKey) ?? Promise.resolve();\n const next = prev.then(fn, fn); // run fn after previous completes, even if previous failed\n writeLocks.set(pageKey, next.then(() => {}, () => {})); // recover chain per gotcha #40\n return next;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction contentHash(content: string): string {\n return createHash(\"sha256\").update(content, \"utf-8\").digest(\"hex\");\n}\n\n/**\n * Derive a filesystem-safe sidecar key from a page path relative to memoryDir.\n *\n * `facts/2026-01-15/pref-001.md` -> `facts__2026-01-15__pref-001`\n *\n * Exported so the `remnic doctor` consolidation-provenance check (issue\n * #561 PR 4) resolves snapshot locations using the canonical algorithm\n * without re-implementing it — preventing silent drift if the key\n * format ever changes.\n */\nexport function sidecarKey(pagePath: string): string {\n const withoutExt = pagePath.replace(/\\.md$/i, \"\");\n return withoutExt.replace(/[\\\\/]/g, \"__\");\n}\n\nfunction sidecarDir(memoryDir: string, sidecar: string, pagePath: string): string {\n return path.join(memoryDir, sidecar, sidecarKey(pagePath));\n}\n\nfunction manifestPath(memoryDir: string, sidecar: string, pagePath: string): string {\n return path.join(sidecarDir(memoryDir, sidecar, pagePath), \"manifest.json\");\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readManifest(\n memoryDir: string,\n sidecar: string,\n pagePath: string,\n): Promise<VersionHistory> {\n const mp = manifestPath(memoryDir, sidecar, pagePath);\n if (!(await fileExists(mp))) {\n return { pagePath, versions: [], currentVersion: \"0\" };\n }\n try {\n const raw = await readFile(mp, \"utf-8\");\n const parsed: unknown = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null) {\n throw new Error(\"manifest root must be an object\");\n }\n const obj = parsed as Record<string, unknown>;\n if (!Array.isArray(obj.versions)) {\n throw new Error(\"manifest versions must be an array\");\n }\n const versions = obj.versions.map((entry, index) => {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n throw new Error(`manifest version ${index} must be an object`);\n }\n const version = entry as Record<string, unknown>;\n if (\n typeof version.versionId !== \"string\" ||\n !/^\\d+$/.test(version.versionId) ||\n typeof version.timestamp !== \"string\" ||\n typeof version.contentHash !== \"string\" ||\n typeof version.sizeBytes !== \"number\" ||\n !Number.isFinite(version.sizeBytes) ||\n ![\"write\", \"consolidation\", \"revert\", \"manual\"].includes(String(version.trigger))\n ) {\n throw new Error(`manifest version ${index} has invalid shape`);\n }\n return version as unknown as PageVersion;\n });\n if (typeof obj.currentVersion !== \"string\" || !/^\\d+$/.test(obj.currentVersion)) {\n throw new Error(\"manifest currentVersion must be a numeric string\");\n }\n const currentVersion = obj.currentVersion;\n return { pagePath: typeof obj.pagePath === \"string\" ? obj.pagePath : pagePath, versions, currentVersion };\n } catch (error) {\n throw new Error(`page-versioning: invalid manifest ${mp}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\nasync function writeManifest(\n memoryDir: string,\n sidecar: string,\n pagePath: string,\n history: VersionHistory,\n): Promise<void> {\n const dir = sidecarDir(memoryDir, sidecar, pagePath);\n await mkdir(dir, { recursive: true });\n const mp = manifestPath(memoryDir, sidecar, pagePath);\n await writeFile(mp, JSON.stringify(history, null, 2) + \"\\n\", \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a new version snapshot for a page.\n *\n * Call this BEFORE overwriting the current file so the previous content is\n * preserved. If the file does not exist yet (first write), the provided\n * `content` is snapshotted as version 1.\n *\n * Pruning: when the number of versions exceeds `config.maxVersionsPerPage`,\n * the oldest snapshots (and their files) are removed.\n */\nexport async function createVersion(\n pagePath: string,\n content: string,\n trigger: VersionTrigger,\n config: VersioningConfig,\n log: VersioningLogger = NOOP_LOGGER,\n note?: string,\n memoryDir?: string,\n): Promise<PageVersion> {\n const { sidecarDir: sidecar, maxVersionsPerPage } = config;\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n const mPath = manifestPath(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));\n\n return withPageLock(mPath, async () => {\n const history = await readManifest(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));\n const nextId = String(history.versions.length > 0\n ? Math.max(...history.versions.map((v) => Number(v.versionId))) + 1\n : 1);\n\n const hash = contentHash(content);\n const version: PageVersion = {\n versionId: nextId,\n timestamp: new Date().toISOString(),\n contentHash: hash,\n sizeBytes: Buffer.byteLength(content, \"utf-8\"),\n trigger,\n ...(note !== undefined ? { note } : {}),\n };\n\n // Write snapshot file\n const dir = sidecarDir(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));\n await mkdir(dir, { recursive: true });\n const ext = path.extname(pagePath) || \".md\";\n const snapshotPath = path.join(dir, `${nextId}${ext}`);\n await writeFile(snapshotPath, content, \"utf-8\");\n\n history.versions.push(version);\n history.currentVersion = nextId;\n\n // Prune old versions if exceeding max\n if (maxVersionsPerPage > 0 && history.versions.length > maxVersionsPerPage) {\n const toRemove = history.versions.splice(0, history.versions.length - maxVersionsPerPage);\n for (const old of toRemove) {\n const oldPath = path.join(dir, `${old.versionId}${ext}`);\n try {\n await unlink(oldPath);\n } catch {\n log.debug(`page-versioning: could not remove old snapshot ${oldPath}`);\n }\n }\n }\n\n await writeManifest(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir), history);\n log.debug(`page-versioning: created version ${nextId} for ${pagePath} (trigger=${trigger})`);\n\n return version;\n });\n}\n\n/**\n * List all versions for a page.\n */\nexport async function listVersions(\n pagePath: string,\n config: VersioningConfig,\n memoryDir?: string,\n): Promise<VersionHistory> {\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n const rel = relPath(pagePath, resolvedMemoryDir);\n const history = await readManifest(resolvedMemoryDir, config.sidecarDir, rel);\n // Sort ascending by versionId (numeric)\n history.versions.sort((a, b) => Number(a.versionId) - Number(b.versionId));\n return history;\n}\n\n/**\n * Read the content of a specific version.\n */\nexport async function getVersion(\n pagePath: string,\n versionId: string,\n config: VersioningConfig,\n memoryDir?: string,\n): Promise<string> {\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n const rel = relPath(pagePath, resolvedMemoryDir);\n const ext = path.extname(pagePath) || \".md\";\n const dir = sidecarDir(resolvedMemoryDir, config.sidecarDir, rel);\n const snapshotPath = path.join(dir, `${versionId}${ext}`);\n\n if (!(await fileExists(snapshotPath))) {\n throw new Error(`Version ${versionId} not found for ${pagePath}`);\n }\n\n return readFile(snapshotPath, \"utf-8\");\n}\n\n/**\n * Revert a page to a previous version.\n *\n * 1. Reads the target version's content.\n * 2. Snapshots the CURRENT content as a new version (trigger: \"revert\").\n * 3. Writes the reverted content to the page file.\n *\n * Returns the newly created version entry for the revert snapshot.\n */\nexport async function revertToVersion(\n pagePath: string,\n versionId: string,\n config: VersioningConfig,\n log: VersioningLogger = NOOP_LOGGER,\n memoryDir?: string,\n): Promise<PageVersion> {\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n\n // Read target version content\n const targetContent = await getVersion(pagePath, versionId, config, resolvedMemoryDir);\n\n // Snapshot current content before overwriting\n let currentContent = \"\";\n try {\n currentContent = await readFile(pagePath, \"utf-8\");\n } catch {\n // File may not exist; that's okay\n }\n\n const version = await createVersion(\n pagePath,\n currentContent,\n \"revert\",\n config,\n log,\n `reverted to version ${versionId}`,\n resolvedMemoryDir,\n );\n\n // Write the reverted content to the actual page\n await writeFile(pagePath, targetContent, \"utf-8\");\n log.debug(`page-versioning: reverted ${pagePath} to version ${versionId}`);\n\n return version;\n}\n\n/**\n * Simple line-based diff between two versions.\n *\n * Returns a unified-style diff string showing added (+) and removed (-) lines.\n */\nexport async function diffVersions(\n pagePath: string,\n v1: string,\n v2: string,\n config: VersioningConfig,\n memoryDir?: string,\n): Promise<string> {\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n const content1 = await getVersion(pagePath, v1, config, resolvedMemoryDir);\n const content2 = await getVersion(pagePath, v2, config, resolvedMemoryDir);\n\n const lines1 = content1.split(\"\\n\");\n const lines2 = content2.split(\"\\n\");\n\n const result: string[] = [];\n result.push(`--- version ${v1}`);\n result.push(`+++ version ${v2}`);\n\n // Simple LCS-based diff\n const lcs = computeLCS(lines1, lines2);\n let i = 0;\n let j = 0;\n let k = 0;\n\n while (k < lcs.length) {\n // Emit removed lines before the next common line\n while (i < lines1.length && lines1[i] !== lcs[k]) {\n result.push(`-${lines1[i]}`);\n i++;\n }\n // Emit added lines before the next common line\n while (j < lines2.length && lines2[j] !== lcs[k]) {\n result.push(`+${lines2[j]}`);\n j++;\n }\n // Common line\n result.push(` ${lcs[k]}`);\n i++;\n j++;\n k++;\n }\n // Remaining removed lines\n while (i < lines1.length) {\n result.push(`-${lines1[i]}`);\n i++;\n }\n // Remaining added lines\n while (j < lines2.length) {\n result.push(`+${lines2[j]}`);\n j++;\n }\n\n return result.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// LCS helper for diffVersions\n// ---------------------------------------------------------------------------\n\nfunction computeLCS(a: string[], b: string[]): string[] {\n const m = a.length;\n const n = b.length;\n // Build DP table\n const dp: number[][] = Array.from({ length: m + 1 }, () => new Array<number>(n + 1).fill(0));\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n if (a[i - 1] === b[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1] + 1;\n } else {\n dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);\n }\n }\n }\n // Backtrack to build LCS\n const result: string[] = [];\n let i = m;\n let j = n;\n while (i > 0 && j > 0) {\n if (a[i - 1] === b[j - 1]) {\n result.unshift(a[i - 1]);\n i--;\n j--;\n } else if (dp[i - 1][j] > dp[i][j - 1]) {\n i--;\n } else {\n j--;\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Path helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Legacy fallback: given an absolute page path, heuristically resolve the\n * memory directory by walking up past known subdirectory names.\n *\n * Callers should always pass an explicit `memoryDir` instead of relying on\n * this heuristic. It is retained only for backward compatibility when the\n * optional `memoryDir` parameter is omitted.\n */\nfunction resolveMemoryDir(pagePath: string): string {\n const knownSubdirs = new Set([\n \"facts\",\n \"corrections\",\n \"entities\",\n \"state\",\n \"artifacts\",\n \"questions\",\n \"profiles\",\n ]);\n\n let dir = path.dirname(pagePath);\n // Walk up past date directories (YYYY-MM-DD) and known subdirs\n for (let depth = 0; depth < 5; depth++) {\n const base = path.basename(dir);\n if (knownSubdirs.has(base) || /^\\d{4}-\\d{2}-\\d{2}$/.test(base)) {\n dir = path.dirname(dir);\n } else {\n break;\n }\n }\n return dir;\n}\n\n/**\n * Compute relative path of a page within its memory directory.\n */\nfunction relPath(pagePath: string, memoryDir: string): string {\n return path.relative(memoryDir, pagePath);\n}\n"],"mappings":"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","names":["i","j"]}
1
+ {"version":3,"sources":["../src/page-versioning.ts"],"sourcesContent":["/**\n * Page-level versioning with history and revert (issue #371).\n *\n * Provides snapshot-based versioning for memory files using a sidecar\n * directory layout. Each memory page gets a `.versions/<pageName>/`\n * subdirectory containing numbered snapshots and a `manifest.json` that\n * records the version history.\n *\n * Storage layout:\n * memoryDir/\n * facts/preferences.md <- current file\n * .versions/\n * facts__preferences/\n * manifest.json <- VersionHistory\n * 1.md <- version 1 snapshot\n * 2.md <- version 2 snapshot\n */\n\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport {\n access,\n mkdir,\n readFile,\n writeFile,\n unlink,\n} from \"node:fs/promises\";\nimport { ALL_CATEGORY_DIRS } from \"./utils/category-dir.js\";\n\n// ---------------------------------------------------------------------------\n// Public interfaces\n// ---------------------------------------------------------------------------\n\nexport interface PageVersion {\n versionId: string;\n timestamp: string;\n contentHash: string;\n sizeBytes: number;\n trigger: VersionTrigger;\n note?: string;\n}\n\nexport type VersionTrigger = \"write\" | \"consolidation\" | \"revert\" | \"manual\";\n\nexport interface VersionHistory {\n pagePath: string;\n versions: PageVersion[];\n currentVersion: string;\n}\n\nexport interface VersioningConfig {\n enabled: boolean;\n maxVersionsPerPage: number;\n sidecarDir: string;\n}\n\n// ---------------------------------------------------------------------------\n// Logger interface (minimal, avoids coupling to the host logger)\n// ---------------------------------------------------------------------------\n\nexport interface VersioningLogger {\n debug(msg: string): void;\n warn(msg: string): void;\n}\n\nconst NOOP_LOGGER: VersioningLogger = {\n debug: () => {},\n warn: () => {},\n};\n\n// ---------------------------------------------------------------------------\n// Per-page write lock (promise-chain pattern, see gotcha #40)\n// ---------------------------------------------------------------------------\n\nconst writeLocks = new Map<string, Promise<void>>();\n\nfunction withPageLock<T>(pageKey: string, fn: () => Promise<T>): Promise<T> {\n const prev = writeLocks.get(pageKey) ?? Promise.resolve();\n const next = prev.then(fn, fn); // run fn after previous completes, even if previous failed\n writeLocks.set(pageKey, next.then(() => {}, () => {})); // recover chain per gotcha #40\n return next;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction contentHash(content: string): string {\n return createHash(\"sha256\").update(content, \"utf-8\").digest(\"hex\");\n}\n\n/**\n * Derive a filesystem-safe sidecar key from a page path relative to memoryDir.\n *\n * `facts/2026-01-15/pref-001.md` -> `facts__2026-01-15__pref-001`\n *\n * Exported so the `remnic doctor` consolidation-provenance check (issue\n * #561 PR 4) resolves snapshot locations using the canonical algorithm\n * without re-implementing it — preventing silent drift if the key\n * format ever changes.\n */\nexport function sidecarKey(pagePath: string): string {\n const withoutExt = pagePath.replace(/\\.md$/i, \"\");\n return withoutExt.replace(/[\\\\/]/g, \"__\");\n}\n\nfunction sidecarDir(memoryDir: string, sidecar: string, pagePath: string): string {\n return path.join(memoryDir, sidecar, sidecarKey(pagePath));\n}\n\nfunction manifestPath(memoryDir: string, sidecar: string, pagePath: string): string {\n return path.join(sidecarDir(memoryDir, sidecar, pagePath), \"manifest.json\");\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readManifest(\n memoryDir: string,\n sidecar: string,\n pagePath: string,\n): Promise<VersionHistory> {\n const mp = manifestPath(memoryDir, sidecar, pagePath);\n if (!(await fileExists(mp))) {\n return { pagePath, versions: [], currentVersion: \"0\" };\n }\n try {\n const raw = await readFile(mp, \"utf-8\");\n const parsed: unknown = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null) {\n throw new Error(\"manifest root must be an object\");\n }\n const obj = parsed as Record<string, unknown>;\n if (!Array.isArray(obj.versions)) {\n throw new Error(\"manifest versions must be an array\");\n }\n const versions = obj.versions.map((entry, index) => {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n throw new Error(`manifest version ${index} must be an object`);\n }\n const version = entry as Record<string, unknown>;\n if (\n typeof version.versionId !== \"string\" ||\n !/^\\d+$/.test(version.versionId) ||\n typeof version.timestamp !== \"string\" ||\n typeof version.contentHash !== \"string\" ||\n typeof version.sizeBytes !== \"number\" ||\n !Number.isFinite(version.sizeBytes) ||\n ![\"write\", \"consolidation\", \"revert\", \"manual\"].includes(String(version.trigger))\n ) {\n throw new Error(`manifest version ${index} has invalid shape`);\n }\n return version as unknown as PageVersion;\n });\n if (typeof obj.currentVersion !== \"string\" || !/^\\d+$/.test(obj.currentVersion)) {\n throw new Error(\"manifest currentVersion must be a numeric string\");\n }\n const currentVersion = obj.currentVersion;\n return { pagePath: typeof obj.pagePath === \"string\" ? obj.pagePath : pagePath, versions, currentVersion };\n } catch (error) {\n throw new Error(`page-versioning: invalid manifest ${mp}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\nasync function writeManifest(\n memoryDir: string,\n sidecar: string,\n pagePath: string,\n history: VersionHistory,\n): Promise<void> {\n const dir = sidecarDir(memoryDir, sidecar, pagePath);\n await mkdir(dir, { recursive: true });\n const mp = manifestPath(memoryDir, sidecar, pagePath);\n await writeFile(mp, JSON.stringify(history, null, 2) + \"\\n\", \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a new version snapshot for a page.\n *\n * Call this BEFORE overwriting the current file so the previous content is\n * preserved. If the file does not exist yet (first write), the provided\n * `content` is snapshotted as version 1.\n *\n * Pruning: when the number of versions exceeds `config.maxVersionsPerPage`,\n * the oldest snapshots (and their files) are removed.\n */\nexport async function createVersion(\n pagePath: string,\n content: string,\n trigger: VersionTrigger,\n config: VersioningConfig,\n log: VersioningLogger = NOOP_LOGGER,\n note?: string,\n memoryDir?: string,\n): Promise<PageVersion> {\n const { sidecarDir: sidecar, maxVersionsPerPage } = config;\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n const mPath = manifestPath(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));\n\n return withPageLock(mPath, async () => {\n const history = await readManifest(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));\n const nextId = String(history.versions.length > 0\n ? Math.max(...history.versions.map((v) => Number(v.versionId))) + 1\n : 1);\n\n const hash = contentHash(content);\n const version: PageVersion = {\n versionId: nextId,\n timestamp: new Date().toISOString(),\n contentHash: hash,\n sizeBytes: Buffer.byteLength(content, \"utf-8\"),\n trigger,\n ...(note !== undefined ? { note } : {}),\n };\n\n // Write snapshot file\n const dir = sidecarDir(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir));\n await mkdir(dir, { recursive: true });\n const ext = path.extname(pagePath) || \".md\";\n const snapshotPath = path.join(dir, `${nextId}${ext}`);\n await writeFile(snapshotPath, content, \"utf-8\");\n\n history.versions.push(version);\n history.currentVersion = nextId;\n\n // Prune old versions if exceeding max\n if (maxVersionsPerPage > 0 && history.versions.length > maxVersionsPerPage) {\n const toRemove = history.versions.splice(0, history.versions.length - maxVersionsPerPage);\n for (const old of toRemove) {\n const oldPath = path.join(dir, `${old.versionId}${ext}`);\n try {\n await unlink(oldPath);\n } catch {\n log.debug(`page-versioning: could not remove old snapshot ${oldPath}`);\n }\n }\n }\n\n await writeManifest(resolvedMemoryDir, sidecar, relPath(pagePath, resolvedMemoryDir), history);\n log.debug(`page-versioning: created version ${nextId} for ${pagePath} (trigger=${trigger})`);\n\n return version;\n });\n}\n\n/**\n * List all versions for a page.\n */\nexport async function listVersions(\n pagePath: string,\n config: VersioningConfig,\n memoryDir?: string,\n): Promise<VersionHistory> {\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n const rel = relPath(pagePath, resolvedMemoryDir);\n const history = await readManifest(resolvedMemoryDir, config.sidecarDir, rel);\n // Sort ascending by versionId (numeric)\n history.versions.sort((a, b) => Number(a.versionId) - Number(b.versionId));\n return history;\n}\n\n/**\n * Read the content of a specific version.\n */\nexport async function getVersion(\n pagePath: string,\n versionId: string,\n config: VersioningConfig,\n memoryDir?: string,\n): Promise<string> {\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n const rel = relPath(pagePath, resolvedMemoryDir);\n const ext = path.extname(pagePath) || \".md\";\n const dir = sidecarDir(resolvedMemoryDir, config.sidecarDir, rel);\n const snapshotPath = path.join(dir, `${versionId}${ext}`);\n\n if (!(await fileExists(snapshotPath))) {\n throw new Error(`Version ${versionId} not found for ${pagePath}`);\n }\n\n return readFile(snapshotPath, \"utf-8\");\n}\n\n/**\n * Revert a page to a previous version.\n *\n * 1. Reads the target version's content.\n * 2. Snapshots the CURRENT content as a new version (trigger: \"revert\").\n * 3. Writes the reverted content to the page file.\n *\n * Returns the newly created version entry for the revert snapshot.\n */\nexport async function revertToVersion(\n pagePath: string,\n versionId: string,\n config: VersioningConfig,\n log: VersioningLogger = NOOP_LOGGER,\n memoryDir?: string,\n): Promise<PageVersion> {\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n\n // Read target version content\n const targetContent = await getVersion(pagePath, versionId, config, resolvedMemoryDir);\n\n // Snapshot current content before overwriting\n let currentContent = \"\";\n try {\n currentContent = await readFile(pagePath, \"utf-8\");\n } catch {\n // File may not exist; that's okay\n }\n\n const version = await createVersion(\n pagePath,\n currentContent,\n \"revert\",\n config,\n log,\n `reverted to version ${versionId}`,\n resolvedMemoryDir,\n );\n\n // Write the reverted content to the actual page\n await writeFile(pagePath, targetContent, \"utf-8\");\n log.debug(`page-versioning: reverted ${pagePath} to version ${versionId}`);\n\n return version;\n}\n\n/**\n * Simple line-based diff between two versions.\n *\n * Returns a unified-style diff string showing added (+) and removed (-) lines.\n */\nexport async function diffVersions(\n pagePath: string,\n v1: string,\n v2: string,\n config: VersioningConfig,\n memoryDir?: string,\n): Promise<string> {\n const resolvedMemoryDir = memoryDir ?? resolveMemoryDir(pagePath);\n const content1 = await getVersion(pagePath, v1, config, resolvedMemoryDir);\n const content2 = await getVersion(pagePath, v2, config, resolvedMemoryDir);\n\n const lines1 = content1.split(\"\\n\");\n const lines2 = content2.split(\"\\n\");\n\n const result: string[] = [];\n result.push(`--- version ${v1}`);\n result.push(`+++ version ${v2}`);\n\n // Simple LCS-based diff\n const lcs = computeLCS(lines1, lines2);\n let i = 0;\n let j = 0;\n let k = 0;\n\n while (k < lcs.length) {\n // Emit removed lines before the next common line\n while (i < lines1.length && lines1[i] !== lcs[k]) {\n result.push(`-${lines1[i]}`);\n i++;\n }\n // Emit added lines before the next common line\n while (j < lines2.length && lines2[j] !== lcs[k]) {\n result.push(`+${lines2[j]}`);\n j++;\n }\n // Common line\n result.push(` ${lcs[k]}`);\n i++;\n j++;\n k++;\n }\n // Remaining removed lines\n while (i < lines1.length) {\n result.push(`-${lines1[i]}`);\n i++;\n }\n // Remaining added lines\n while (j < lines2.length) {\n result.push(`+${lines2[j]}`);\n j++;\n }\n\n return result.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// LCS helper for diffVersions\n// ---------------------------------------------------------------------------\n\nfunction computeLCS(a: string[], b: string[]): string[] {\n const m = a.length;\n const n = b.length;\n // Build DP table\n const dp: number[][] = Array.from({ length: m + 1 }, () => new Array<number>(n + 1).fill(0));\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n if (a[i - 1] === b[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1] + 1;\n } else {\n dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);\n }\n }\n }\n // Backtrack to build LCS\n const result: string[] = [];\n let i = m;\n let j = n;\n while (i > 0 && j > 0) {\n if (a[i - 1] === b[j - 1]) {\n result.unshift(a[i - 1]);\n i--;\n j--;\n } else if (dp[i - 1][j] > dp[i][j - 1]) {\n i--;\n } else {\n j--;\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Path helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Legacy fallback: given an absolute page path, heuristically resolve the\n * memory directory by walking up past known subdirectory names.\n *\n * Callers should always pass an explicit `memoryDir` instead of relying on\n * this heuristic. It is retained only for backward compatibility when the\n * optional `memoryDir` parameter is omitted.\n */\nfunction resolveMemoryDir(pagePath: string): string {\n // Derive the recall category dirs from ALL_CATEGORY_DIRS (single source of\n // truth) so newly-routed categories (decisions/, preferences/, ...) are\n // recognized when walking up to the memory root (#1546); the non-category\n // subdirs are listed explicitly.\n const knownSubdirs = new Set<string>([\n ...ALL_CATEGORY_DIRS,\n \"entities\",\n \"state\",\n \"artifacts\",\n \"profiles\",\n ]);\n\n let dir = path.dirname(pagePath);\n // Walk up past date directories (YYYY-MM-DD) and known subdirs\n for (let depth = 0; depth < 5; depth++) {\n const base = path.basename(dir);\n if (knownSubdirs.has(base) || /^\\d{4}-\\d{2}-\\d{2}$/.test(base)) {\n dir = path.dirname(dir);\n } else {\n break;\n }\n }\n return dir;\n}\n\n/**\n * Compute relative path of a page within its memory directory.\n */\nfunction relPath(pagePath: string, memoryDir: string): string {\n return path.relative(memoryDir, pagePath);\n}\n"],"mappings":";;;;;AAkBA,SAAS,kBAAkB;AAC3B,OAAO,UAAU;AACjB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAuCP,IAAM,cAAgC;AAAA,EACpC,OAAO,MAAM;AAAA,EAAC;AAAA,EACd,MAAM,MAAM;AAAA,EAAC;AACf;AAMA,IAAM,aAAa,oBAAI,IAA2B;AAElD,SAAS,aAAgB,SAAiB,IAAkC;AAC1E,QAAM,OAAO,WAAW,IAAI,OAAO,KAAK,QAAQ,QAAQ;AACxD,QAAM,OAAO,KAAK,KAAK,IAAI,EAAE;AAC7B,aAAW,IAAI,SAAS,KAAK,KAAK,MAAM;AAAA,EAAC,GAAG,MAAM;AAAA,EAAC,CAAC,CAAC;AACrD,SAAO;AACT;AAMA,SAAS,YAAY,SAAyB;AAC5C,SAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,OAAO,EAAE,OAAO,KAAK;AACnE;AAYO,SAAS,WAAW,UAA0B;AACnD,QAAM,aAAa,SAAS,QAAQ,UAAU,EAAE;AAChD,SAAO,WAAW,QAAQ,UAAU,IAAI;AAC1C;AAEA,SAAS,WAAW,WAAmB,SAAiB,UAA0B;AAChF,SAAO,KAAK,KAAK,WAAW,SAAS,WAAW,QAAQ,CAAC;AAC3D;AAEA,SAAS,aAAa,WAAmB,SAAiB,UAA0B;AAClF,SAAO,KAAK,KAAK,WAAW,WAAW,SAAS,QAAQ,GAAG,eAAe;AAC5E;AAEA,eAAe,WAAW,GAA6B;AACrD,MAAI;AACF,UAAM,OAAO,CAAC;AACd,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,aACb,WACA,SACA,UACyB;AACzB,QAAM,KAAK,aAAa,WAAW,SAAS,QAAQ;AACpD,MAAI,CAAE,MAAM,WAAW,EAAE,GAAI;AAC3B,WAAO,EAAE,UAAU,UAAU,CAAC,GAAG,gBAAgB,IAAI;AAAA,EACvD;AACA,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,IAAI,OAAO;AACtC,UAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,QAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AACA,UAAM,MAAM;AACZ,QAAI,CAAC,MAAM,QAAQ,IAAI,QAAQ,GAAG;AAChC,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AACA,UAAM,WAAW,IAAI,SAAS,IAAI,CAAC,OAAO,UAAU;AAClD,UAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AAC/D,cAAM,IAAI,MAAM,oBAAoB,KAAK,oBAAoB;AAAA,MAC/D;AACA,YAAM,UAAU;AAChB,UACE,OAAO,QAAQ,cAAc,YAC7B,CAAC,QAAQ,KAAK,QAAQ,SAAS,KAC/B,OAAO,QAAQ,cAAc,YAC7B,OAAO,QAAQ,gBAAgB,YAC/B,OAAO,QAAQ,cAAc,YAC7B,CAAC,OAAO,SAAS,QAAQ,SAAS,KAClC,CAAC,CAAC,SAAS,iBAAiB,UAAU,QAAQ,EAAE,SAAS,OAAO,QAAQ,OAAO,CAAC,GAChF;AACA,cAAM,IAAI,MAAM,oBAAoB,KAAK,oBAAoB;AAAA,MAC/D;AACA,aAAO;AAAA,IACT,CAAC;AACD,QAAI,OAAO,IAAI,mBAAmB,YAAY,CAAC,QAAQ,KAAK,IAAI,cAAc,GAAG;AAC/E,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AACA,UAAM,iBAAiB,IAAI;AAC3B,WAAO,EAAE,UAAU,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW,UAAU,UAAU,eAAe;AAAA,EAC1G,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,qCAAqC,EAAE,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,EACtH;AACF;AAEA,eAAe,cACb,WACA,SACA,UACA,SACe;AACf,QAAM,MAAM,WAAW,WAAW,SAAS,QAAQ;AACnD,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAM,KAAK,aAAa,WAAW,SAAS,QAAQ;AACpD,QAAM,UAAU,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,IAAI,MAAM,OAAO;AACtE;AAgBA,eAAsB,cACpB,UACA,SACA,SACA,QACA,MAAwB,aACxB,MACA,WACsB;AACtB,QAAM,EAAE,YAAY,SAAS,mBAAmB,IAAI;AACpD,QAAM,oBAAoB,aAAa,iBAAiB,QAAQ;AAChE,QAAM,QAAQ,aAAa,mBAAmB,SAAS,QAAQ,UAAU,iBAAiB,CAAC;AAE3F,SAAO,aAAa,OAAO,YAAY;AACrC,UAAM,UAAU,MAAM,aAAa,mBAAmB,SAAS,QAAQ,UAAU,iBAAiB,CAAC;AACnG,UAAM,SAAS,OAAO,QAAQ,SAAS,SAAS,IAC5C,KAAK,IAAI,GAAG,QAAQ,SAAS,IAAI,CAAC,MAAM,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,IAChE,CAAC;AAEL,UAAM,OAAO,YAAY,OAAO;AAChC,UAAM,UAAuB;AAAA,MAC3B,WAAW;AAAA,MACX,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,aAAa;AAAA,MACb,WAAW,OAAO,WAAW,SAAS,OAAO;AAAA,MAC7C;AAAA,MACA,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACvC;AAGA,UAAM,MAAM,WAAW,mBAAmB,SAAS,QAAQ,UAAU,iBAAiB,CAAC;AACvF,UAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,UAAM,MAAM,KAAK,QAAQ,QAAQ,KAAK;AACtC,UAAM,eAAe,KAAK,KAAK,KAAK,GAAG,MAAM,GAAG,GAAG,EAAE;AACrD,UAAM,UAAU,cAAc,SAAS,OAAO;AAE9C,YAAQ,SAAS,KAAK,OAAO;AAC7B,YAAQ,iBAAiB;AAGzB,QAAI,qBAAqB,KAAK,QAAQ,SAAS,SAAS,oBAAoB;AAC1E,YAAM,WAAW,QAAQ,SAAS,OAAO,GAAG,QAAQ,SAAS,SAAS,kBAAkB;AACxF,iBAAW,OAAO,UAAU;AAC1B,cAAM,UAAU,KAAK,KAAK,KAAK,GAAG,IAAI,SAAS,GAAG,GAAG,EAAE;AACvD,YAAI;AACF,gBAAM,OAAO,OAAO;AAAA,QACtB,QAAQ;AACN,cAAI,MAAM,kDAAkD,OAAO,EAAE;AAAA,QACvE;AAAA,MACF;AAAA,IACF;AAEA,UAAM,cAAc,mBAAmB,SAAS,QAAQ,UAAU,iBAAiB,GAAG,OAAO;AAC7F,QAAI,MAAM,oCAAoC,MAAM,QAAQ,QAAQ,aAAa,OAAO,GAAG;AAE3F,WAAO;AAAA,EACT,CAAC;AACH;AAKA,eAAsB,aACpB,UACA,QACA,WACyB;AACzB,QAAM,oBAAoB,aAAa,iBAAiB,QAAQ;AAChE,QAAM,MAAM,QAAQ,UAAU,iBAAiB;AAC/C,QAAM,UAAU,MAAM,aAAa,mBAAmB,OAAO,YAAY,GAAG;AAE5E,UAAQ,SAAS,KAAK,CAAC,GAAG,MAAM,OAAO,EAAE,SAAS,IAAI,OAAO,EAAE,SAAS,CAAC;AACzE,SAAO;AACT;AAKA,eAAsB,WACpB,UACA,WACA,QACA,WACiB;AACjB,QAAM,oBAAoB,aAAa,iBAAiB,QAAQ;AAChE,QAAM,MAAM,QAAQ,UAAU,iBAAiB;AAC/C,QAAM,MAAM,KAAK,QAAQ,QAAQ,KAAK;AACtC,QAAM,MAAM,WAAW,mBAAmB,OAAO,YAAY,GAAG;AAChE,QAAM,eAAe,KAAK,KAAK,KAAK,GAAG,SAAS,GAAG,GAAG,EAAE;AAExD,MAAI,CAAE,MAAM,WAAW,YAAY,GAAI;AACrC,UAAM,IAAI,MAAM,WAAW,SAAS,kBAAkB,QAAQ,EAAE;AAAA,EAClE;AAEA,SAAO,SAAS,cAAc,OAAO;AACvC;AAWA,eAAsB,gBACpB,UACA,WACA,QACA,MAAwB,aACxB,WACsB;AACtB,QAAM,oBAAoB,aAAa,iBAAiB,QAAQ;AAGhE,QAAM,gBAAgB,MAAM,WAAW,UAAU,WAAW,QAAQ,iBAAiB;AAGrF,MAAI,iBAAiB;AACrB,MAAI;AACF,qBAAiB,MAAM,SAAS,UAAU,OAAO;AAAA,EACnD,QAAQ;AAAA,EAER;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,uBAAuB,SAAS;AAAA,IAChC;AAAA,EACF;AAGA,QAAM,UAAU,UAAU,eAAe,OAAO;AAChD,MAAI,MAAM,6BAA6B,QAAQ,eAAe,SAAS,EAAE;AAEzE,SAAO;AACT;AAOA,eAAsB,aACpB,UACA,IACA,IACA,QACA,WACiB;AACjB,QAAM,oBAAoB,aAAa,iBAAiB,QAAQ;AAChE,QAAM,WAAW,MAAM,WAAW,UAAU,IAAI,QAAQ,iBAAiB;AACzE,QAAM,WAAW,MAAM,WAAW,UAAU,IAAI,QAAQ,iBAAiB;AAEzE,QAAM,SAAS,SAAS,MAAM,IAAI;AAClC,QAAM,SAAS,SAAS,MAAM,IAAI;AAElC,QAAM,SAAmB,CAAC;AAC1B,SAAO,KAAK,eAAe,EAAE,EAAE;AAC/B,SAAO,KAAK,eAAe,EAAE,EAAE;AAG/B,QAAM,MAAM,WAAW,QAAQ,MAAM;AACrC,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,IAAI;AAER,SAAO,IAAI,IAAI,QAAQ;AAErB,WAAO,IAAI,OAAO,UAAU,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG;AAChD,aAAO,KAAK,IAAI,OAAO,CAAC,CAAC,EAAE;AAC3B;AAAA,IACF;AAEA,WAAO,IAAI,OAAO,UAAU,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG;AAChD,aAAO,KAAK,IAAI,OAAO,CAAC,CAAC,EAAE;AAC3B;AAAA,IACF;AAEA,WAAO,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE;AACxB;AACA;AACA;AAAA,EACF;AAEA,SAAO,IAAI,OAAO,QAAQ;AACxB,WAAO,KAAK,IAAI,OAAO,CAAC,CAAC,EAAE;AAC3B;AAAA,EACF;AAEA,SAAO,IAAI,OAAO,QAAQ;AACxB,WAAO,KAAK,IAAI,OAAO,CAAC,CAAC,EAAE;AAC3B;AAAA,EACF;AAEA,SAAO,OAAO,KAAK,IAAI;AACzB;AAMA,SAAS,WAAW,GAAa,GAAuB;AACtD,QAAM,IAAI,EAAE;AACZ,QAAM,IAAI,EAAE;AAEZ,QAAM,KAAiB,MAAM,KAAK,EAAE,QAAQ,IAAI,EAAE,GAAG,MAAM,IAAI,MAAc,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;AAC3F,WAASA,KAAI,GAAGA,MAAK,GAAGA,MAAK;AAC3B,aAASC,KAAI,GAAGA,MAAK,GAAGA,MAAK;AAC3B,UAAI,EAAED,KAAI,CAAC,MAAM,EAAEC,KAAI,CAAC,GAAG;AACzB,WAAGD,EAAC,EAAEC,EAAC,IAAI,GAAGD,KAAI,CAAC,EAAEC,KAAI,CAAC,IAAI;AAAA,MAChC,OAAO;AACL,WAAGD,EAAC,EAAEC,EAAC,IAAI,KAAK,IAAI,GAAGD,KAAI,CAAC,EAAEC,EAAC,GAAG,GAAGD,EAAC,EAAEC,KAAI,CAAC,CAAC;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAmB,CAAC;AAC1B,MAAI,IAAI;AACR,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,IAAI,GAAG;AACrB,QAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG;AACzB,aAAO,QAAQ,EAAE,IAAI,CAAC,CAAC;AACvB;AACA;AAAA,IACF,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG;AACtC;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAcA,SAAS,iBAAiB,UAA0B;AAKlD,QAAM,eAAe,oBAAI,IAAY;AAAA,IACnC,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,MAAM,KAAK,QAAQ,QAAQ;AAE/B,WAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS;AACtC,UAAM,OAAO,KAAK,SAAS,GAAG;AAC9B,QAAI,aAAa,IAAI,IAAI,KAAK,sBAAsB,KAAK,IAAI,GAAG;AAC9D,YAAM,KAAK,QAAQ,GAAG;AAAA,IACxB,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,QAAQ,UAAkB,WAA2B;AAC5D,SAAO,KAAK,SAAS,WAAW,QAAQ;AAC1C;","names":["i","j"]}