@remnic/core 9.3.644 → 9.3.646

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 +28 -27
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.js +16 -16
  4. package/dist/access-mcp.js +13 -13
  5. package/dist/access-schema.js +3 -3
  6. package/dist/access-service.js +11 -11
  7. package/dist/adapters/index.js +4 -4
  8. package/dist/adapters/registry.js +2 -2
  9. package/dist/briefing.js +2 -2
  10. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-GWVG7LGC.js} +2 -2
  11. package/dist/causal-consolidation.js +3 -3
  12. package/dist/{chunk-2GCNTHRA.js → chunk-3JSWINVD.js} +98 -35
  13. package/dist/chunk-3JSWINVD.js.map +1 -0
  14. package/dist/{chunk-SZL2GGFY.js → chunk-4BISW7RX.js} +2 -2
  15. package/dist/{chunk-GYITUZFT.js → chunk-A4BTPHIN.js} +171 -134
  16. package/dist/chunk-A4BTPHIN.js.map +1 -0
  17. package/dist/{chunk-NV2MGIW7.js → chunk-ALUZN7BE.js} +2 -2
  18. package/dist/{chunk-RFSCIBCB.js → chunk-AMACWKM4.js} +2 -2
  19. package/dist/{chunk-AGNBY3VG.js → chunk-APJQ6UEA.js} +4 -4
  20. package/dist/{chunk-HRXCPKN4.js → chunk-APWJRJFW.js} +2 -2
  21. package/dist/{chunk-VDX2J7OX.js → chunk-BUKK5SWA.js} +2 -2
  22. package/dist/{chunk-RREI7CNM.js → chunk-D6WVJIS3.js} +9 -42
  23. package/dist/chunk-D6WVJIS3.js.map +1 -0
  24. package/dist/{chunk-EGBH5IPJ.js → chunk-DC66QVL2.js} +4 -4
  25. package/dist/chunk-EVWIEEKZ.js +315 -0
  26. package/dist/chunk-EVWIEEKZ.js.map +1 -0
  27. package/dist/{chunk-DM4US77W.js → chunk-FAYDM5WD.js} +6 -4
  28. package/dist/{chunk-DM4US77W.js.map → chunk-FAYDM5WD.js.map} +1 -1
  29. package/dist/{chunk-3UR4SBZF.js → chunk-H67ZTMTL.js} +20 -20
  30. package/dist/{chunk-3SF7QNKF.js → chunk-I3IWTRYB.js} +2 -2
  31. package/dist/{chunk-Y2XKJJEI.js → chunk-IMWFHBG2.js} +10 -33
  32. package/dist/chunk-IMWFHBG2.js.map +1 -0
  33. package/dist/{chunk-IHG27THY.js → chunk-JLOJ5RJ7.js} +68 -59
  34. package/dist/chunk-JLOJ5RJ7.js.map +1 -0
  35. package/dist/{chunk-AK4DSORQ.js → chunk-L7W5YW6Y.js} +7 -7
  36. package/dist/{chunk-4HYWWO3W.js → chunk-MCYT2RNT.js} +3 -3
  37. package/dist/{chunk-DGNQRNLL.js → chunk-NT5TINK5.js} +2 -2
  38. package/dist/{chunk-THWT7RJU.js → chunk-OR7R6M5Z.js} +2 -2
  39. package/dist/{chunk-4MNDVDBS.js → chunk-OTC2KOZ2.js} +2 -2
  40. package/dist/{chunk-BXLOS5AJ.js → chunk-OWHERGF2.js} +2 -2
  41. package/dist/{chunk-TTOYT5RB.js → chunk-PQFUUXWK.js} +2 -2
  42. package/dist/{chunk-CDHRUNBX.js → chunk-QKE4LHNR.js} +2 -2
  43. package/dist/{chunk-S2SEPLLA.js → chunk-RAELB5NX.js} +5 -5
  44. package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
  45. package/dist/chunk-S4DDLTPX.js +140 -0
  46. package/dist/chunk-S4DDLTPX.js.map +1 -0
  47. package/dist/{chunk-KVUG7FF7.js → chunk-U55D5UD5.js} +7 -7
  48. package/dist/{chunk-4HYLRROV.js → chunk-UMKPSD35.js} +2 -2
  49. package/dist/{chunk-NLPH23IQ.js → chunk-UMTG2BN2.js} +2 -2
  50. package/dist/{chunk-DQEMWVMT.js → chunk-UVYI6VIX.js} +1 -1
  51. package/dist/{chunk-D2B22JDF.js → chunk-WEPMT6SC.js} +7 -7
  52. package/dist/{chunk-ECMMM2VE.js → chunk-Z3PZRDLW.js} +2 -2
  53. package/dist/{chunk-GJSNDJHR.js → chunk-ZPPFKVSD.js} +4 -4
  54. package/dist/{chunk-TKNTAUA3.js → chunk-ZXWAQFDE.js} +2 -2
  55. package/dist/cli.js +37 -35
  56. package/dist/compounding/engine.js +2 -2
  57. package/dist/connectors/codex-materialize-runner.js +2 -2
  58. package/dist/connectors/index.js +2 -2
  59. package/dist/entity-retrieval.js +2 -2
  60. package/dist/{first-start-migration-FF7YFGRP.js → first-start-migration-PG5HBC3K.js} +4 -4
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +91 -78
  63. package/dist/index.js.map +1 -1
  64. package/dist/lcm/engine.js +3 -3
  65. package/dist/lcm/index.js +11 -11
  66. package/dist/maintenance/memory-governance.js +2 -2
  67. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +2 -2
  68. package/dist/maintenance/rebuild-memory-projection.js +3 -3
  69. package/dist/namespaces/migrate.js +7 -7
  70. package/dist/namespaces/search.js +4 -4
  71. package/dist/namespaces/storage.js +2 -2
  72. package/dist/operator-toolkit.js +9 -9
  73. package/dist/orchestrator.js +23 -22
  74. package/dist/resume-bundles.js +3 -2
  75. package/dist/schemas.d.ts +22 -22
  76. package/dist/search/factory.js +3 -3
  77. package/dist/search/index.js +7 -7
  78. package/dist/semantic-consolidation.js +3 -3
  79. package/dist/semantic-rule-promotion.js +2 -2
  80. package/dist/semantic-rule-verifier.js +2 -2
  81. package/dist/session-identity.d.ts +118 -0
  82. package/dist/session-identity.js +21 -0
  83. package/dist/session-identity.js.map +1 -0
  84. package/dist/session-transcript-migration.d.ts +65 -0
  85. package/dist/session-transcript-migration.js +16 -0
  86. package/dist/session-transcript-migration.js.map +1 -0
  87. package/dist/storage.js +1 -1
  88. package/dist/summarizer.js +2 -1
  89. package/dist/transcript.d.ts +12 -6
  90. package/dist/transcript.js +2 -1
  91. package/dist/transfer/backup.js +2 -2
  92. package/dist/transfer/capsule-export.js +2 -2
  93. package/dist/transfer/capsule-import.js +2 -2
  94. package/dist/transfer/import-sqlite.js +2 -2
  95. package/dist/transfer/types.d.ts +12 -12
  96. package/dist/verified-recall.js +2 -2
  97. package/package.json +1 -1
  98. package/src/cli.ts +79 -0
  99. package/src/index.ts +18 -0
  100. package/src/session-identity.test.ts +130 -0
  101. package/src/session-identity.ts +281 -0
  102. package/src/session-transcript-migration.test.ts +350 -0
  103. package/src/session-transcript-migration.ts +527 -0
  104. package/src/storage-fallback-category-dirs.test.ts +389 -0
  105. package/src/storage.ts +36 -4
  106. package/src/summarizer.ts +15 -35
  107. package/src/transcript-session-identity.test.ts +424 -0
  108. package/src/transcript.ts +100 -70
  109. package/src/utils/category-dir.ts +26 -0
  110. package/dist/chunk-2GCNTHRA.js.map +0 -1
  111. package/dist/chunk-GYITUZFT.js.map +0 -1
  112. package/dist/chunk-IHG27THY.js.map +0 -1
  113. package/dist/chunk-RREI7CNM.js.map +0 -1
  114. package/dist/chunk-Y2XKJJEI.js.map +0 -1
  115. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  116. /package/dist/{chunk-SZL2GGFY.js.map → chunk-4BISW7RX.js.map} +0 -0
  117. /package/dist/{chunk-NV2MGIW7.js.map → chunk-ALUZN7BE.js.map} +0 -0
  118. /package/dist/{chunk-RFSCIBCB.js.map → chunk-AMACWKM4.js.map} +0 -0
  119. /package/dist/{chunk-AGNBY3VG.js.map → chunk-APJQ6UEA.js.map} +0 -0
  120. /package/dist/{chunk-HRXCPKN4.js.map → chunk-APWJRJFW.js.map} +0 -0
  121. /package/dist/{chunk-VDX2J7OX.js.map → chunk-BUKK5SWA.js.map} +0 -0
  122. /package/dist/{chunk-EGBH5IPJ.js.map → chunk-DC66QVL2.js.map} +0 -0
  123. /package/dist/{chunk-3UR4SBZF.js.map → chunk-H67ZTMTL.js.map} +0 -0
  124. /package/dist/{chunk-3SF7QNKF.js.map → chunk-I3IWTRYB.js.map} +0 -0
  125. /package/dist/{chunk-AK4DSORQ.js.map → chunk-L7W5YW6Y.js.map} +0 -0
  126. /package/dist/{chunk-4HYWWO3W.js.map → chunk-MCYT2RNT.js.map} +0 -0
  127. /package/dist/{chunk-DGNQRNLL.js.map → chunk-NT5TINK5.js.map} +0 -0
  128. /package/dist/{chunk-THWT7RJU.js.map → chunk-OR7R6M5Z.js.map} +0 -0
  129. /package/dist/{chunk-4MNDVDBS.js.map → chunk-OTC2KOZ2.js.map} +0 -0
  130. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
  131. /package/dist/{chunk-TTOYT5RB.js.map → chunk-PQFUUXWK.js.map} +0 -0
  132. /package/dist/{chunk-CDHRUNBX.js.map → chunk-QKE4LHNR.js.map} +0 -0
  133. /package/dist/{chunk-S2SEPLLA.js.map → chunk-RAELB5NX.js.map} +0 -0
  134. /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
  135. /package/dist/{chunk-KVUG7FF7.js.map → chunk-U55D5UD5.js.map} +0 -0
  136. /package/dist/{chunk-4HYLRROV.js.map → chunk-UMKPSD35.js.map} +0 -0
  137. /package/dist/{chunk-NLPH23IQ.js.map → chunk-UMTG2BN2.js.map} +0 -0
  138. /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
  139. /package/dist/{chunk-D2B22JDF.js.map → chunk-WEPMT6SC.js.map} +0 -0
  140. /package/dist/{chunk-ECMMM2VE.js.map → chunk-Z3PZRDLW.js.map} +0 -0
  141. /package/dist/{chunk-GJSNDJHR.js.map → chunk-ZPPFKVSD.js.map} +0 -0
  142. /package/dist/{chunk-TKNTAUA3.js.map → chunk-ZXWAQFDE.js.map} +0 -0
  143. /package/dist/{first-start-migration-FF7YFGRP.js.map → first-start-migration-PG5HBC3K.js.map} +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  compareEntityTimestamps,
6
6
  normalizeEntityName
7
- } from "./chunk-GYITUZFT.js";
7
+ } from "./chunk-A4BTPHIN.js";
8
8
  import {
9
9
  sanitizeMemoryContent
10
10
  } from "./chunk-FVQJYWH7.js";
@@ -677,4 +677,4 @@ export {
677
677
  entityIndexVersion,
678
678
  entityRecentTranscriptLookbackHours
679
679
  };
680
- //# sourceMappingURL=chunk-NV2MGIW7.js.map
680
+ //# sourceMappingURL=chunk-ALUZN7BE.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  StorageManager,
3
3
  normalizeEntityName
4
- } from "./chunk-GYITUZFT.js";
4
+ } from "./chunk-A4BTPHIN.js";
5
5
  import {
6
6
  extractJsonCandidates
7
7
  } from "./chunk-RGMVMVMF.js";
@@ -860,4 +860,4 @@ export {
860
860
  resolveBriefingSaveDir,
861
861
  briefingFilename
862
862
  };
863
- //# sourceMappingURL=chunk-RFSCIBCB.js.map
863
+ //# sourceMappingURL=chunk-AMACWKM4.js.map
@@ -1,10 +1,10 @@
1
- import {
2
- SQLITE_SCHEMA_VERSION
3
- } from "./chunk-Z734BLO3.js";
4
1
  import {
5
2
  parseConflictPolicy,
6
3
  validateManifestRecords
7
4
  } from "./chunk-G4IAEX6D.js";
5
+ import {
6
+ SQLITE_SCHEMA_VERSION
7
+ } from "./chunk-Z734BLO3.js";
8
8
  import {
9
9
  openBetterSqlite3
10
10
  } from "./chunk-6KYMPV2O.js";
@@ -84,4 +84,4 @@ async function importSqlite(opts) {
84
84
  export {
85
85
  importSqlite
86
86
  };
87
- //# sourceMappingURL=chunk-AGNBY3VG.js.map
87
+ //# sourceMappingURL=chunk-APJQ6UEA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSearchBackend
3
- } from "./chunk-KVUG7FF7.js";
3
+ } from "./chunk-U55D5UD5.js";
4
4
  import {
5
5
  namespaceIdentityToken,
6
6
  normalizeNamespaceIdentity
@@ -179,4 +179,4 @@ export {
179
179
  namespaceCollectionName,
180
180
  NamespaceSearchRouter
181
181
  };
182
- //# sourceMappingURL=chunk-HRXCPKN4.js.map
182
+ //# sourceMappingURL=chunk-APWJRJFW.js.map
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-WEHSQBFR.js";
7
7
  import {
8
8
  encryptCapsuleFile
9
- } from "./chunk-DQEMWVMT.js";
9
+ } from "./chunk-UVYI6VIX.js";
10
10
  import {
11
11
  CAPSULE_SCHEMA_VERSION,
12
12
  EXPORT_FORMAT
@@ -261,4 +261,4 @@ export {
261
261
  exportCapsule,
262
262
  isValidCapsuleSince
263
263
  };
264
- //# sourceMappingURL=chunk-VDX2J7OX.js.map
264
+ //# sourceMappingURL=chunk-BUKK5SWA.js.map
@@ -3,46 +3,16 @@ import {
3
3
  normalizeNamespaceIdentity
4
4
  } from "./chunk-ZFXCQPNO.js";
5
5
  import {
6
+ ALL_CATEGORY_DIRS,
6
7
  StorageManager
7
- } from "./chunk-GYITUZFT.js";
8
+ } from "./chunk-A4BTPHIN.js";
8
9
  import {
9
10
  isSafeRouteNamespace
10
11
  } from "./chunk-U3PN77QT.js";
11
12
 
12
13
  // src/namespaces/storage.ts
13
- import path2 from "path";
14
- import { access, lstat, readdir } from "fs/promises";
15
-
16
- // src/utils/category-dir.ts
17
14
  import path from "path";
18
- var CATEGORY_DIR_MAP = {
19
- correction: "corrections",
20
- question: "questions",
21
- preference: "preferences",
22
- decision: "decisions",
23
- moment: "moments",
24
- commitment: "commitments",
25
- principle: "principles",
26
- rule: "rules",
27
- skill: "skills",
28
- relationship: "relationships",
29
- procedure: "procedures",
30
- reasoning_trace: "reasoning-traces"
31
- };
32
- var ALL_CATEGORY_DIRS = [
33
- "facts",
34
- ...Object.values(CATEGORY_DIR_MAP)
35
- ];
36
- var ALL_CATEGORY_KEYS = [
37
- "fact",
38
- ...Object.keys(CATEGORY_DIR_MAP)
39
- ];
40
- function getCategoryDir(memoryDir, category) {
41
- const dir = Object.hasOwn(CATEGORY_DIR_MAP, category) ? CATEGORY_DIR_MAP[category] : void 0;
42
- return dir ? path.join(memoryDir, dir) : path.join(memoryDir, "facts");
43
- }
44
-
45
- // src/namespaces/storage.ts
15
+ import { access, lstat, readdir } from "fs/promises";
46
16
  async function exists(p) {
47
17
  try {
48
18
  await access(p);
@@ -58,7 +28,7 @@ async function hasStoredEntries(p) {
58
28
  if (!entry.isDirectory()) return true;
59
29
  const children = await readdir(p, { withFileTypes: true });
60
30
  for (const child of children) {
61
- const childPath = path2.join(p, child.name);
31
+ const childPath = path.join(p, child.name);
62
32
  if (child.isSymbolicLink() || child.isFile()) return true;
63
33
  if (child.isDirectory() && await hasStoredEntries(childPath)) return true;
64
34
  }
@@ -68,10 +38,10 @@ async function hasStoredEntries(p) {
68
38
  }
69
39
  }
70
40
  function resolveNamespaceDir(memoryDir, segment) {
71
- if (segment.length === 0 || segment.includes("/") || segment.includes("\\") || segment.includes("..") || path2.isAbsolute(segment)) {
41
+ if (segment.length === 0 || segment.includes("/") || segment.includes("\\") || segment.includes("..") || path.isAbsolute(segment)) {
72
42
  throw new Error(`unsafe namespace path segment: ${segment}`);
73
43
  }
74
- return path2.join(memoryDir, "namespaces", segment);
44
+ return path.join(memoryDir, "namespaces", segment);
75
45
  }
76
46
  var LEGACY_NAMESPACE_CONTENT_CHILDREN = [
77
47
  ...ALL_CATEGORY_DIRS,
@@ -86,14 +56,14 @@ var LEGACY_NAMESPACE_RUNTIME_CHILDREN = ["state"];
86
56
  async function hasAnyLegacyData(rootDir, options = {}) {
87
57
  const children = options.includeRuntimeState === true ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN] : LEGACY_NAMESPACE_CONTENT_CHILDREN;
88
58
  for (const child of children) {
89
- if (await hasStoredEntries(path2.join(rootDir, child))) return true;
59
+ if (await hasStoredEntries(path.join(rootDir, child))) return true;
90
60
  }
91
61
  return false;
92
62
  }
93
63
  async function hasAnyNamespaceStorageMarker(rootDir, options = {}) {
94
64
  const children = options.includeRuntimeState === true ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN] : LEGACY_NAMESPACE_CONTENT_CHILDREN;
95
65
  for (const child of children) {
96
- if (await exists(path2.join(rootDir, child))) return true;
66
+ if (await exists(path.join(rootDir, child))) return true;
97
67
  }
98
68
  return false;
99
69
  }
@@ -156,9 +126,6 @@ var NamespaceStorageRouter = class {
156
126
  };
157
127
 
158
128
  export {
159
- ALL_CATEGORY_DIRS,
160
- ALL_CATEGORY_KEYS,
161
- getCategoryDir,
162
129
  NamespaceStorageRouter
163
130
  };
164
- //# sourceMappingURL=chunk-RREI7CNM.js.map
131
+ //# sourceMappingURL=chunk-D6WVJIS3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/namespaces/storage.ts"],"sourcesContent":["import path from \"node:path\";\nimport { access, lstat, readdir } from \"node:fs/promises\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport { StorageManager } from \"../storage.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\nimport { namespaceIdentityToken, normalizeNamespaceIdentity } from \"./identity.js\";\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function hasStoredEntries(p: string): Promise<boolean> {\n try {\n const entry = await lstat(p);\n if (entry.isSymbolicLink()) return true;\n if (!entry.isDirectory()) return true;\n const children = await readdir(p, { withFileTypes: true });\n for (const child of children) {\n const childPath = path.join(p, child.name);\n if (child.isSymbolicLink() || child.isFile()) return true;\n if (child.isDirectory() && (await hasStoredEntries(childPath))) return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n// Build a per-namespace directory under `<memoryDir>/namespaces` and assert the\n// resolved path stays inside that base. Namespace identifiers can originate from\n// operator config (config.defaultNamespace) and request-derived routing, so this\n// containment check prevents directory traversal (CodeQL js/path-injection).\n// For safe segments this returns exactly `path.join(base, segment)`, so there is\n// no behavioral change for valid namespaces.\nfunction resolveNamespaceDir(memoryDir: string, segment: string): string {\n // Mirror isSafeRouteNamespace's separator/parent-ref rejection (without its\n // 64-char cap, so identity tokens still pass). Rejecting separators and \"..\"\n // up front keeps the value a single contained child of <memoryDir>/namespaces.\n if (\n segment.length === 0 ||\n segment.includes(\"/\") ||\n segment.includes(\"\\\\\") ||\n segment.includes(\"..\") ||\n path.isAbsolute(segment)\n ) {\n throw new Error(`unsafe namespace path segment: ${segment}`);\n }\n return path.join(memoryDir, \"namespaces\", segment);\n}\n\nconst LEGACY_NAMESPACE_CONTENT_CHILDREN = [\n ...ALL_CATEGORY_DIRS,\n \"entities\",\n \"artifacts\",\n \"identity\",\n \"config\",\n \"summaries\",\n \"profile.md\",\n] as const;\n\nconst LEGACY_NAMESPACE_RUNTIME_CHILDREN = [\"state\"] as const;\n\nasync function hasAnyLegacyData(\n rootDir: string,\n options: { includeRuntimeState?: boolean } = {},\n): Promise<boolean> {\n const children = options.includeRuntimeState === true\n ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN]\n : LEGACY_NAMESPACE_CONTENT_CHILDREN;\n for (const child of children) {\n if (await hasStoredEntries(path.join(rootDir, child))) return true;\n }\n return false;\n}\n\nasync function hasAnyNamespaceStorageMarker(\n rootDir: string,\n options: { includeRuntimeState?: boolean } = {},\n): Promise<boolean> {\n const children = options.includeRuntimeState === true\n ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN]\n : LEGACY_NAMESPACE_CONTENT_CHILDREN;\n for (const child of children) {\n if (await exists(path.join(rootDir, child))) return true;\n }\n return false;\n}\n\n/**\n * Storage routing for namespaces.\n *\n * Compatibility note:\n * - When namespaces are enabled, existing raw namespace roots are preserved.\n * New namespace roots use tokenized names under `memoryDir/namespaces/<token>`.\n * - The default namespace continues to use the legacy `memoryDir` root unless the caller\n * has created `memoryDir/namespaces/<defaultNamespace>` (in which case we use that).\n *\n * This avoids surprising \"lost memories\" when an install flips namespaces on without\n * migrating existing data.\n */\nexport class NamespaceStorageRouter {\n private readonly cache = new Map<string, StorageManager>();\n private defaultNsRootResolved: string | null = null;\n\n constructor(private readonly config: PluginConfig) {}\n\n private async defaultNamespaceRoot(): Promise<string> {\n if (!this.config.namespacesEnabled) {\n this.defaultNsRootResolved = this.config.memoryDir;\n return this.defaultNsRootResolved;\n }\n\n const legacyNsDir = resolveNamespaceDir(this.config.memoryDir, this.config.defaultNamespace);\n const tokenizedNsDir = resolveNamespaceDir(\n this.config.memoryDir,\n namespaceIdentityToken(this.config.defaultNamespace),\n );\n const tokenizedHasData =\n (await exists(tokenizedNsDir)) && (await hasAnyNamespaceStorageMarker(tokenizedNsDir, { includeRuntimeState: true }));\n const nsDir = tokenizedHasData\n ? tokenizedNsDir\n : (await exists(legacyNsDir)) ? legacyNsDir : tokenizedNsDir;\n this.defaultNsRootResolved =\n (await exists(nsDir)) && !(await hasAnyLegacyData(this.config.memoryDir))\n ? nsDir\n : this.config.memoryDir;\n return this.defaultNsRootResolved;\n }\n\n private async namespaceRoot(namespace: string): Promise<string> {\n // NOTE: only used after defaultNamespaceRoot() resolution.\n if (!this.config.namespacesEnabled) return this.config.memoryDir;\n if (namespace === this.config.defaultNamespace) {\n return this.defaultNsRootResolved ?? this.config.memoryDir;\n }\n const legacyRoot = resolveNamespaceDir(this.config.memoryDir, namespace);\n const tokenizedRoot = resolveNamespaceDir(this.config.memoryDir, namespaceIdentityToken(namespace));\n if ((await exists(tokenizedRoot)) && (await hasAnyNamespaceStorageMarker(tokenizedRoot, { includeRuntimeState: true }))) {\n return tokenizedRoot;\n }\n return (await exists(legacyRoot)) ? legacyRoot : tokenizedRoot;\n }\n\n async storageFor(namespace: string): Promise<StorageManager> {\n const ns = normalizeNamespaceIdentity(namespace || this.config.defaultNamespace);\n if (ns !== this.config.defaultNamespace && !isSafeRouteNamespace(ns)) {\n throw new Error(`unsafe namespace: ${ns}`);\n }\n // Even when the default namespace is exempt from the check above, every\n // on-disk path is built through resolveNamespaceDir(), which rejects\n // traversal segments — so an unsafe configured default still cannot escape\n // <memoryDir>/namespaces (CodeQL js/path-injection).\n\n let root: string;\n if (ns === this.config.defaultNamespace) {\n root = await this.defaultNamespaceRoot();\n const cached = this.cache.get(ns);\n if (cached && cached.dir === root) {\n return cached;\n }\n } else {\n const cached = this.cache.get(ns);\n root = await this.namespaceRoot(ns);\n if (cached && cached.dir === root) return cached;\n }\n\n const sm = new StorageManager(root, this.config.entitySchemas);\n // Propagate the inline-attribution template so that router-created storages\n // (used by extraction and shared-promotion paths) strip citations consistently,\n // matching the behaviour of the primary this.storage instance in the orchestrator.\n sm.citationTemplate = this.config.inlineSourceAttributionFormat;\n this.cache.set(ns, sm);\n return sm;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAAA,OAAO,UAAU;AACjB,SAAS,QAAQ,OAAO,eAAe;AAOvC,eAAe,OAAO,GAA6B;AACjD,MAAI;AACF,UAAM,OAAO,CAAC;AACd,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,iBAAiB,GAA6B;AAC3D,MAAI;AACF,UAAM,QAAQ,MAAM,MAAM,CAAC;AAC3B,QAAI,MAAM,eAAe,EAAG,QAAO;AACnC,QAAI,CAAC,MAAM,YAAY,EAAG,QAAO;AACjC,UAAM,WAAW,MAAM,QAAQ,GAAG,EAAE,eAAe,KAAK,CAAC;AACzD,eAAW,SAAS,UAAU;AAC5B,YAAM,YAAY,KAAK,KAAK,GAAG,MAAM,IAAI;AACzC,UAAI,MAAM,eAAe,KAAK,MAAM,OAAO,EAAG,QAAO;AACrD,UAAI,MAAM,YAAY,KAAM,MAAM,iBAAiB,SAAS,EAAI,QAAO;AAAA,IACzE;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,SAAS,oBAAoB,WAAmB,SAAyB;AAIvE,MACE,QAAQ,WAAW,KACnB,QAAQ,SAAS,GAAG,KACpB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,IAAI,KACrB,KAAK,WAAW,OAAO,GACvB;AACA,UAAM,IAAI,MAAM,kCAAkC,OAAO,EAAE;AAAA,EAC7D;AACA,SAAO,KAAK,KAAK,WAAW,cAAc,OAAO;AACnD;AAEA,IAAM,oCAAoC;AAAA,EACxC,GAAG;AAAA,EACH;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,oCAAoC,CAAC,OAAO;AAElD,eAAe,iBACb,SACA,UAA6C,CAAC,GAC5B;AAClB,QAAM,WAAW,QAAQ,wBAAwB,OAC7C,CAAC,GAAG,mCAAmC,GAAG,iCAAiC,IAC3E;AACJ,aAAW,SAAS,UAAU;AAC5B,QAAI,MAAM,iBAAiB,KAAK,KAAK,SAAS,KAAK,CAAC,EAAG,QAAO;AAAA,EAChE;AACA,SAAO;AACT;AAEA,eAAe,6BACb,SACA,UAA6C,CAAC,GAC5B;AAClB,QAAM,WAAW,QAAQ,wBAAwB,OAC7C,CAAC,GAAG,mCAAmC,GAAG,iCAAiC,IAC3E;AACJ,aAAW,SAAS,UAAU;AAC5B,QAAI,MAAM,OAAO,KAAK,KAAK,SAAS,KAAK,CAAC,EAAG,QAAO;AAAA,EACtD;AACA,SAAO;AACT;AAcO,IAAM,yBAAN,MAA6B;AAAA,EAIlC,YAA6B,QAAsB;AAAtB;AAAA,EAAuB;AAAA,EAAvB;AAAA,EAHZ,QAAQ,oBAAI,IAA4B;AAAA,EACjD,wBAAuC;AAAA,EAI/C,MAAc,uBAAwC;AACpD,QAAI,CAAC,KAAK,OAAO,mBAAmB;AAClC,WAAK,wBAAwB,KAAK,OAAO;AACzC,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,cAAc,oBAAoB,KAAK,OAAO,WAAW,KAAK,OAAO,gBAAgB;AAC3F,UAAM,iBAAiB;AAAA,MACrB,KAAK,OAAO;AAAA,MACZ,uBAAuB,KAAK,OAAO,gBAAgB;AAAA,IACrD;AACA,UAAM,mBACH,MAAM,OAAO,cAAc,KAAO,MAAM,6BAA6B,gBAAgB,EAAE,qBAAqB,KAAK,CAAC;AACrH,UAAM,QAAQ,mBACV,iBACC,MAAM,OAAO,WAAW,IAAK,cAAc;AAChD,SAAK,wBACF,MAAM,OAAO,KAAK,KAAM,CAAE,MAAM,iBAAiB,KAAK,OAAO,SAAS,IACnE,QACA,KAAK,OAAO;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,cAAc,WAAoC;AAE9D,QAAI,CAAC,KAAK,OAAO,kBAAmB,QAAO,KAAK,OAAO;AACvD,QAAI,cAAc,KAAK,OAAO,kBAAkB;AAC9C,aAAO,KAAK,yBAAyB,KAAK,OAAO;AAAA,IACnD;AACA,UAAM,aAAa,oBAAoB,KAAK,OAAO,WAAW,SAAS;AACvE,UAAM,gBAAgB,oBAAoB,KAAK,OAAO,WAAW,uBAAuB,SAAS,CAAC;AAClG,QAAK,MAAM,OAAO,aAAa,KAAO,MAAM,6BAA6B,eAAe,EAAE,qBAAqB,KAAK,CAAC,GAAI;AACvH,aAAO;AAAA,IACT;AACA,WAAQ,MAAM,OAAO,UAAU,IAAK,aAAa;AAAA,EACnD;AAAA,EAEA,MAAM,WAAW,WAA4C;AAC3D,UAAM,KAAK,2BAA2B,aAAa,KAAK,OAAO,gBAAgB;AAC/E,QAAI,OAAO,KAAK,OAAO,oBAAoB,CAAC,qBAAqB,EAAE,GAAG;AACpE,YAAM,IAAI,MAAM,qBAAqB,EAAE,EAAE;AAAA,IAC3C;AAMA,QAAI;AACJ,QAAI,OAAO,KAAK,OAAO,kBAAkB;AACvC,aAAO,MAAM,KAAK,qBAAqB;AACvC,YAAM,SAAS,KAAK,MAAM,IAAI,EAAE;AAChC,UAAI,UAAU,OAAO,QAAQ,MAAM;AACjC,eAAO;AAAA,MACT;AAAA,IACF,OAAO;AACL,YAAM,SAAS,KAAK,MAAM,IAAI,EAAE;AAChC,aAAO,MAAM,KAAK,cAAc,EAAE;AAClC,UAAI,UAAU,OAAO,QAAQ,KAAM,QAAO;AAAA,IAC5C;AAEA,UAAM,KAAK,IAAI,eAAe,MAAM,KAAK,OAAO,aAAa;AAI7D,OAAG,mBAAmB,KAAK,OAAO;AAClC,SAAK,MAAM,IAAI,IAAI,EAAE;AACrB,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  listNamespaces
3
- } from "./chunk-DM4US77W.js";
3
+ } from "./chunk-FAYDM5WD.js";
4
4
  import {
5
5
  runConsolidationProvenanceCheck
6
6
  } from "./chunk-AL4RAJL5.js";
@@ -36,13 +36,13 @@ import {
36
36
  import {
37
37
  listMemoryGovernanceRuns,
38
38
  readMemoryGovernanceRunArtifact
39
- } from "./chunk-NLPH23IQ.js";
39
+ } from "./chunk-UMTG2BN2.js";
40
40
  import {
41
41
  analyzeGraphHealth
42
42
  } from "./chunk-Y56J7CXW.js";
43
43
  import {
44
44
  StorageManager
45
- } from "./chunk-GYITUZFT.js";
45
+ } from "./chunk-A4BTPHIN.js";
46
46
  import {
47
47
  lintWorkspaceFiles
48
48
  } from "./chunk-DM2T26WE.js";
@@ -1336,4 +1336,4 @@ export {
1336
1336
  runBenchmarkRecall,
1337
1337
  runOperatorRepair
1338
1338
  };
1339
- //# sourceMappingURL=chunk-EGBH5IPJ.js.map
1339
+ //# sourceMappingURL=chunk-DC66QVL2.js.map
@@ -0,0 +1,315 @@
1
+ import {
2
+ parseSessionIdentity,
3
+ sessionStoragePaths
4
+ } from "./chunk-S4DDLTPX.js";
5
+ import {
6
+ resolveSafeStoragePath
7
+ } from "./chunk-FF4KLI5W.js";
8
+ import {
9
+ displayErrorDetail
10
+ } from "./chunk-6KYMPV2O.js";
11
+ import {
12
+ log
13
+ } from "./chunk-2ODBA7MQ.js";
14
+
15
+ // src/session-transcript-migration.ts
16
+ import { mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "fs/promises";
17
+ import path from "path";
18
+ var FIRST_CLASS_CHANNEL_TYPE = "session";
19
+ var CANONICAL_SESSION_HASH_RE = /^[0-9a-f]{16}$/;
20
+ function isCanonicalSessionDir(channelType, channelId) {
21
+ return channelType === FIRST_CLASS_CHANNEL_TYPE && CANONICAL_SESSION_HASH_RE.test(channelId);
22
+ }
23
+ var DATE_FILE_RE = /^\d{4}-\d{2}-\d{2}\.jsonl$/;
24
+ function isDateStampedJsonl(name) {
25
+ return DATE_FILE_RE.test(name);
26
+ }
27
+ async function listFallbackSourceFiles(transcriptsDir) {
28
+ const out = [];
29
+ let typeEntries;
30
+ try {
31
+ typeEntries = await readdir(transcriptsDir, { withFileTypes: true });
32
+ } catch {
33
+ return out;
34
+ }
35
+ for (const typeEnt of typeEntries) {
36
+ if (!typeEnt.isDirectory()) continue;
37
+ const typeDir = path.join(transcriptsDir, typeEnt.name);
38
+ let idEntries;
39
+ try {
40
+ idEntries = await readdir(typeDir, { withFileTypes: true });
41
+ } catch {
42
+ continue;
43
+ }
44
+ for (const idEnt of idEntries) {
45
+ if (!idEnt.isDirectory()) continue;
46
+ if (isCanonicalSessionDir(typeEnt.name, idEnt.name)) continue;
47
+ const chanDir = path.join(typeDir, idEnt.name);
48
+ let files;
49
+ try {
50
+ files = (await readdir(chanDir)).filter((f) => f.endsWith(".jsonl")).sort();
51
+ } catch {
52
+ continue;
53
+ }
54
+ for (const fileName of files) {
55
+ out.push({
56
+ relPath: path.join(typeEnt.name, idEnt.name, fileName),
57
+ fileName,
58
+ absPath: path.join(chanDir, fileName)
59
+ });
60
+ }
61
+ }
62
+ }
63
+ return out.sort((a, b) => a.relPath.localeCompare(b.relPath));
64
+ }
65
+ function parseSourceLines(content) {
66
+ const out = [];
67
+ for (const line of content.split("\n")) {
68
+ if (!line.trim()) continue;
69
+ let sessionKey;
70
+ try {
71
+ const parsed = JSON.parse(line);
72
+ if (parsed && typeof parsed === "object") {
73
+ const sk = parsed.sessionKey;
74
+ if (typeof sk === "string" && sk.length > 0) {
75
+ sessionKey = sk;
76
+ }
77
+ }
78
+ } catch {
79
+ }
80
+ out.push({ sessionKey, raw: line });
81
+ }
82
+ return out;
83
+ }
84
+ function planFile(sourceRelPath, fileName, lines) {
85
+ const sourceDir = path.dirname(sourceRelPath);
86
+ const groups = /* @__PURE__ */ new Map();
87
+ let unmovableLines = 0;
88
+ let movableCount = 0;
89
+ for (const line of lines) {
90
+ if (!line.sessionKey) {
91
+ unmovableLines += 1;
92
+ continue;
93
+ }
94
+ const identity = parseSessionIdentity(line.sessionKey);
95
+ const paths = sessionStoragePaths(line.sessionKey);
96
+ if (paths.dir === sourceDir) {
97
+ continue;
98
+ }
99
+ movableCount += 1;
100
+ const existing = groups.get(line.sessionKey);
101
+ if (existing) {
102
+ existing.count += 1;
103
+ } else {
104
+ groups.set(line.sessionKey, {
105
+ legacy: identity.legacy,
106
+ destDir: paths.dir,
107
+ count: 1
108
+ });
109
+ }
110
+ }
111
+ const groupList = [...groups.entries()].map(([sessionKey, info]) => ({
112
+ sessionKey,
113
+ legacy: info.legacy,
114
+ destDir: info.destDir,
115
+ entryCount: info.count
116
+ })).sort((a, b) => a.sessionKey.localeCompare(b.sessionKey));
117
+ return {
118
+ sourceRelPath,
119
+ fileName,
120
+ groups: groupList,
121
+ unmovableLines,
122
+ alreadyHomed: movableCount === 0
123
+ };
124
+ }
125
+ async function planSessionTranscriptMigration(options) {
126
+ const transcriptsDir = path.join(options.memoryDir, "transcripts");
127
+ const sources = await listFallbackSourceFiles(transcriptsDir);
128
+ const files = [];
129
+ const distinctSessions = /* @__PURE__ */ new Set();
130
+ let movedEntries = 0;
131
+ for (const source of sources) {
132
+ if (!isDateStampedJsonl(source.fileName)) continue;
133
+ let content;
134
+ try {
135
+ content = await readFile(source.absPath, "utf-8");
136
+ } catch {
137
+ continue;
138
+ }
139
+ const lines = parseSourceLines(content);
140
+ const filePlan = planFile(source.relPath, source.fileName, lines);
141
+ if (filePlan.alreadyHomed) continue;
142
+ for (const group of filePlan.groups) {
143
+ distinctSessions.add(group.sessionKey);
144
+ movedEntries += group.entryCount;
145
+ }
146
+ files.push(filePlan);
147
+ }
148
+ return {
149
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
150
+ dryRun: options.apply !== true,
151
+ transcriptsDir,
152
+ files,
153
+ distinctSessions: distinctSessions.size,
154
+ movedEntries
155
+ };
156
+ }
157
+ async function mergeAppendLines(transcriptsDir, destDir, fileName, newLines) {
158
+ const destChannelDir = await resolveSafeStoragePath(transcriptsDir, destDir);
159
+ await mkdir(destChannelDir, { recursive: true });
160
+ const destPath = await resolveSafeStoragePath(transcriptsDir, destDir, fileName);
161
+ const existing = [];
162
+ const seen = /* @__PURE__ */ new Set();
163
+ try {
164
+ const raw = await readFile(destPath, "utf-8");
165
+ for (const line of raw.split("\n")) {
166
+ if (!line.trim()) continue;
167
+ existing.push(line);
168
+ seen.add(line);
169
+ }
170
+ } catch {
171
+ }
172
+ const merged = [...existing];
173
+ for (const line of newLines) {
174
+ if (seen.has(line)) continue;
175
+ merged.push(line);
176
+ seen.add(line);
177
+ }
178
+ const body = merged.length > 0 ? `${merged.join("\n")}
179
+ ` : "";
180
+ const tmpPath = await resolveSafeStoragePath(
181
+ transcriptsDir,
182
+ destDir,
183
+ `${fileName}.migrate-${process.pid}-${Date.now()}.tmp`
184
+ );
185
+ await writeFile(tmpPath, body, "utf-8");
186
+ await rename(tmpPath, destPath);
187
+ }
188
+ async function rewriteSourceRetainingUnmoved(transcriptsDir, sourceRelPath, retainedLines) {
189
+ const sourcePath = await resolveSafeStoragePath(transcriptsDir, sourceRelPath);
190
+ if (retainedLines.length === 0) {
191
+ try {
192
+ await unlink(sourcePath);
193
+ } catch (err) {
194
+ const code = err && typeof err === "object" && "code" in err ? String(err.code ?? "") : "";
195
+ if (code !== "ENOENT") throw err;
196
+ }
197
+ return { removed: true };
198
+ }
199
+ const body = `${retainedLines.join("\n")}
200
+ `;
201
+ const sourceDir = path.dirname(sourceRelPath);
202
+ const tmpPath = await resolveSafeStoragePath(
203
+ transcriptsDir,
204
+ sourceDir,
205
+ `${path.basename(sourceRelPath)}.migrate-${process.pid}-${Date.now()}.tmp`
206
+ );
207
+ await writeFile(tmpPath, body, "utf-8");
208
+ await rename(tmpPath, sourcePath);
209
+ return { removed: false };
210
+ }
211
+ async function migrateSessionTranscripts(options) {
212
+ const plan = await planSessionTranscriptMigration(options);
213
+ const apply = options.apply === true;
214
+ if (!apply) {
215
+ return {
216
+ plan,
217
+ applied: false,
218
+ filesRewritten: 0,
219
+ filesRemoved: 0,
220
+ errors: []
221
+ };
222
+ }
223
+ const transcriptsDir = plan.transcriptsDir;
224
+ const errors = [];
225
+ let filesRewritten = 0;
226
+ let filesRemoved = 0;
227
+ for (const filePlan of plan.files) {
228
+ try {
229
+ const sourcePath = await resolveSafeStoragePath(transcriptsDir, filePlan.sourceRelPath);
230
+ const content = await readFile(sourcePath, "utf-8");
231
+ const lines = parseSourceLines(content);
232
+ const sourceDir = path.dirname(filePlan.sourceRelPath);
233
+ const byDest = /* @__PURE__ */ new Map();
234
+ const retained = [];
235
+ for (const line of lines) {
236
+ if (!line.sessionKey) {
237
+ retained.push(line.raw);
238
+ continue;
239
+ }
240
+ const dest = sessionStoragePaths(line.sessionKey).dir;
241
+ if (dest === sourceDir) {
242
+ retained.push(line.raw);
243
+ continue;
244
+ }
245
+ const bucket = byDest.get(dest) ?? [];
246
+ bucket.push(line.raw);
247
+ byDest.set(dest, bucket);
248
+ }
249
+ for (const [destDir, destLines] of byDest.entries()) {
250
+ await mergeAppendLines(transcriptsDir, destDir, filePlan.fileName, destLines);
251
+ }
252
+ const { removed } = await rewriteSourceRetainingUnmoved(transcriptsDir, filePlan.sourceRelPath, retained);
253
+ if (removed) {
254
+ filesRemoved += 1;
255
+ } else {
256
+ filesRewritten += 1;
257
+ }
258
+ } catch (err) {
259
+ const detail = displayErrorDetail(err);
260
+ errors.push(
261
+ `Failed to migrate ${filePlan.sourceRelPath}${detail ? `: ${detail}` : ""}`
262
+ );
263
+ log.error(`session transcript migration failed for ${filePlan.sourceRelPath}:`, err);
264
+ }
265
+ }
266
+ let manifestPath;
267
+ try {
268
+ manifestPath = await writeManifest(options.memoryDir, {
269
+ plan,
270
+ applied: true,
271
+ filesRewritten,
272
+ filesRemoved,
273
+ errors
274
+ });
275
+ } catch (err) {
276
+ log.debug(`failed to write session migration manifest: ${err}`);
277
+ }
278
+ return {
279
+ plan,
280
+ applied: true,
281
+ filesRewritten,
282
+ filesRemoved,
283
+ errors,
284
+ manifestPath
285
+ };
286
+ }
287
+ async function writeManifest(memoryDir, result) {
288
+ const auditDir = path.join(memoryDir, "state", "session-migration");
289
+ await mkdir(auditDir, { recursive: true });
290
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
291
+ const manifestPath = path.join(auditDir, `migrate-transcripts-${stamp}.json`);
292
+ await writeFile(manifestPath, JSON.stringify(result, null, 2), "utf-8");
293
+ return manifestPath;
294
+ }
295
+ async function summarizeMigrationSources(memoryDir) {
296
+ const transcriptsDir = path.join(memoryDir, "transcripts");
297
+ const sources = await listFallbackSourceFiles(transcriptsDir);
298
+ let bytes = 0;
299
+ let files = 0;
300
+ for (const source of sources) {
301
+ const info = await stat(source.absPath).catch(() => null);
302
+ if (info?.isFile()) {
303
+ bytes += info.size;
304
+ files += 1;
305
+ }
306
+ }
307
+ return { files, bytes };
308
+ }
309
+
310
+ export {
311
+ planSessionTranscriptMigration,
312
+ migrateSessionTranscripts,
313
+ summarizeMigrationSources
314
+ };
315
+ //# sourceMappingURL=chunk-EVWIEEKZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session-transcript-migration.ts"],"sourcesContent":["import { mkdir, readFile, readdir, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { log } from \"./logger.js\";\nimport { displayErrorDetail } from \"./runtime/better-sqlite.js\";\nimport { parseSessionIdentity, sessionStoragePaths } from \"./session-identity.js\";\nimport { resolveSafeStoragePath } from \"./storage-paths.js\";\nimport type { TranscriptEntry } from \"./types.js\";\n\n/**\n * Lossless migration of legacy `transcripts/other/default/*.jsonl` files into\n * first-class `transcripts/session/<hash>/*.jsonl` directories (issue #1496).\n *\n * Older builds routed every non-legacy session key (e.g. `pi-geek:abc123`)\n * into the shared `other/default` directory on first write. That conflated\n * distinct standalone-client sessions into one transcript directory. This\n * migration splits those mixed files back out, grouping entries by\n * `entry.sessionKey` and re-homing each distinct session under its own\n * deterministic hashed directory.\n *\n * Safety contract (rules #54, #51, #18, #35):\n * - Dry-run is the default; nothing is written without `apply: true`.\n * - Writes go to a temp file then atomic-rename (never delete-before-write).\n * - JSONL line ordering and byte content are preserved per session.\n * - Idempotent: re-running after apply finds nothing left to migrate.\n * - A manifest is returned (and written under `state/` on apply) as an audit\n * trail.\n */\n\n/**\n * Channel type that NEW writes use for first-class arbitrary keys. Subdirs of\n * this type whose `<id>` is a canonical `storagePathHash` (16 lowercase hex)\n * are already homed — every line there is, by definition, already in its\n * destination directory, so they are never migration sources.\n */\nconst FIRST_CLASS_CHANNEL_TYPE = \"session\";\n\n/**\n * Matches a canonical `session/<hash>` channel id produced by\n * {@link storagePathHash} (16 lowercase hex chars). Used to tell a NEW\n * first-class directory (skip) apart from a LEGACY `session/<name>` directory.\n *\n * Pre-#1496 the OLD `parts.length >= 3` parser stored a key whose 3rd colon\n * segment was literally `session` (e.g. `foo:bar:session:baz`) under\n * `transcripts/session/baz` — a non-hash id. Those legacy dirs MUST still be\n * scanned and split, while genuine `session/<hash>` data is left untouched\n * (codex review on PR #1496 / PR #1504). The hex length is fixed because\n * `storagePathHash` slices the sha256 hex digest to 16 chars.\n */\nconst CANONICAL_SESSION_HASH_RE = /^[0-9a-f]{16}$/;\n\n/**\n * True when `<type>/<id>` is a canonical first-class `session/<hash>` directory\n * (NEW write target) rather than a legacy stranded directory. Defensive: also\n * confirms the id is exactly what `storagePathHash` would produce for some\n * value by shape — a 16-hex string — so we never misclassify a legacy\n * `session/<name>` dir as homed and skip migrating it.\n */\nfunction isCanonicalSessionDir(channelType: string, channelId: string): boolean {\n return channelType === FIRST_CLASS_CHANNEL_TYPE && CANONICAL_SESSION_HASH_RE.test(channelId);\n}\n\nexport interface SessionMigrationEntryGroup {\n /** The distinct session key these lines belong to. */\n sessionKey: string;\n /** Whether this key is a recognized legacy `agent:<id>:...` shape. */\n legacy: boolean;\n /** Destination directory (relative to the transcripts root). */\n destDir: string;\n /** Number of JSONL entries that will move. */\n entryCount: number;\n}\n\nexport interface SessionMigrationFilePlan {\n /** Source file path (relative to transcripts root). */\n sourceRelPath: string;\n /** The date-stamped file name (e.g. `2026-06-29.jsonl`). */\n fileName: string;\n /** Distinct session groups discovered inside this source file. */\n groups: SessionMigrationEntryGroup[];\n /** Lines that could not be parsed as JSON or lacked a sessionKey. */\n unmovableLines: number;\n /**\n * True when EVERY entry in the file already belongs in the source directory\n * (nothing to do). Such files are skipped.\n */\n alreadyHomed: boolean;\n}\n\nexport interface SessionMigrationPlan {\n generatedAt: string;\n dryRun: boolean;\n transcriptsDir: string;\n /** Per-file plans that have at least one entry to move. */\n files: SessionMigrationFilePlan[];\n /** Distinct destination session keys across all files. */\n distinctSessions: number;\n /** Total entries that will be re-homed. */\n movedEntries: number;\n}\n\nexport interface SessionMigrationResult {\n plan: SessionMigrationPlan;\n applied: boolean;\n filesRewritten: number;\n filesRemoved: number;\n errors: string[];\n manifestPath?: string;\n}\n\nexport interface MigrateSessionTranscriptsOptions {\n /** The memory directory root (transcripts live under `<memoryDir>/transcripts`). */\n memoryDir: string;\n /** When true, perform the migration; otherwise produce a dry-run plan only. */\n apply?: boolean;\n}\n\nconst DATE_FILE_RE = /^\\d{4}-\\d{2}-\\d{2}\\.jsonl$/;\n\nfunction isDateStampedJsonl(name: string): boolean {\n return DATE_FILE_RE.test(name);\n}\n\n/**\n * Candidate source directories that older builds may have used to conflate or\n * misroute arbitrary sessions. We scan `transcripts/<type>/<id>/` two levels\n * deep and consider any directory EXCEPT the first-class `session/<hash>` tree\n * (whose contents are already homed). This covers:\n *\n * - the shared `other/default` fallback every arbitrary key once landed in;\n * - the OLD `parts.length >= 3` parser's directories (e.g. `foo:bar:baz` →\n * `baz/default`, `foo:bar:baz:qux` → `baz/qux`), which the pre-#1496 build\n * wrote even for non-`agent:` keys (Thread B / codex review on PR #1504).\n *\n * `planFile` is the actual gate: a line is only moved when its session key now\n * resolves to a DIFFERENT directory than the one it sits in. Legacy\n * `agent:<id>:...` keys still resolve to their original channel directory, so\n * scanning `main/default`, `discord/<chan>`, etc. is a safe no-op for them\n * (rule #39 — identical routing across paths). This keeps the migration\n * lossless and idempotent regardless of which directory data was stranded in.\n */\nasync function listFallbackSourceFiles(\n transcriptsDir: string\n): Promise<Array<{ relPath: string; fileName: string; absPath: string }>> {\n const out: Array<{ relPath: string; fileName: string; absPath: string }> = [];\n let typeEntries: Array<{ name: string; isDirectory(): boolean }>;\n try {\n typeEntries = await readdir(transcriptsDir, { withFileTypes: true });\n } catch {\n return out;\n }\n\n for (const typeEnt of typeEntries) {\n if (!typeEnt.isDirectory()) continue;\n\n const typeDir = path.join(transcriptsDir, typeEnt.name);\n let idEntries: Array<{ name: string; isDirectory(): boolean }>;\n try {\n idEntries = await readdir(typeDir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const idEnt of idEntries) {\n if (!idEnt.isDirectory()) continue;\n\n // Only canonical `session/<hash>` dirs are already homed — skip those.\n // A LEGACY `session/<name>` dir (e.g. `foo:bar:session:baz` →\n // `session/baz` under the OLD parser) is a real migration source and\n // must be scanned/split (codex review on PR #1504). `planFile`'s\n // destination gate still guarantees losslessness/idempotence.\n if (isCanonicalSessionDir(typeEnt.name, idEnt.name)) continue;\n\n const chanDir = path.join(typeDir, idEnt.name);\n let files: string[];\n try {\n files = (await readdir(chanDir)).filter((f) => f.endsWith(\".jsonl\")).sort();\n } catch {\n continue;\n }\n for (const fileName of files) {\n out.push({\n relPath: path.join(typeEnt.name, idEnt.name, fileName),\n fileName,\n absPath: path.join(chanDir, fileName),\n });\n }\n }\n }\n\n return out.sort((a, b) => a.relPath.localeCompare(b.relPath));\n}\n\ninterface ParsedSourceLine {\n sessionKey?: string;\n /** The raw JSONL text (without trailing newline). */\n raw: string;\n}\n\n/**\n * Parse a source JSONL file, preserving line order. Lines that are not valid\n * JSON or lack a string `sessionKey` are tracked as unmovable and left in the\n * source file so no data is lost (rule #18 — validate parse result type).\n */\nfunction parseSourceLines(content: string): ParsedSourceLine[] {\n const out: ParsedSourceLine[] = [];\n for (const line of content.split(\"\\n\")) {\n if (!line.trim()) continue;\n let sessionKey: string | undefined;\n try {\n const parsed: unknown = JSON.parse(line);\n if (parsed && typeof parsed === \"object\") {\n const sk = (parsed as { sessionKey?: unknown }).sessionKey;\n if (typeof sk === \"string\" && sk.length > 0) {\n sessionKey = sk;\n }\n }\n } catch {\n // Unparseable line — keep it in place.\n }\n out.push({ sessionKey, raw: line });\n }\n return out;\n}\n\n/**\n * Build a per-file migration plan. A line is \"movable\" when its session key\n * resolves to a destination directory different from the source directory.\n */\nfunction planFile(sourceRelPath: string, fileName: string, lines: ParsedSourceLine[]): SessionMigrationFilePlan {\n const sourceDir = path.dirname(sourceRelPath);\n const groups = new Map<string, { legacy: boolean; destDir: string; count: number }>();\n let unmovableLines = 0;\n let movableCount = 0;\n\n for (const line of lines) {\n if (!line.sessionKey) {\n unmovableLines += 1;\n continue;\n }\n const identity = parseSessionIdentity(line.sessionKey);\n const paths = sessionStoragePaths(line.sessionKey);\n // Only move when the destination directory differs from the source.\n if (paths.dir === sourceDir) {\n continue;\n }\n movableCount += 1;\n const existing = groups.get(line.sessionKey);\n if (existing) {\n existing.count += 1;\n } else {\n groups.set(line.sessionKey, {\n legacy: identity.legacy,\n destDir: paths.dir,\n count: 1,\n });\n }\n }\n\n const groupList: SessionMigrationEntryGroup[] = [...groups.entries()]\n .map(([sessionKey, info]) => ({\n sessionKey,\n legacy: info.legacy,\n destDir: info.destDir,\n entryCount: info.count,\n }))\n .sort((a, b) => a.sessionKey.localeCompare(b.sessionKey));\n\n return {\n sourceRelPath,\n fileName,\n groups: groupList,\n unmovableLines,\n alreadyHomed: movableCount === 0,\n };\n}\n\n/**\n * Compute the migration plan without modifying anything.\n */\nexport async function planSessionTranscriptMigration(\n options: MigrateSessionTranscriptsOptions\n): Promise<SessionMigrationPlan> {\n const transcriptsDir = path.join(options.memoryDir, \"transcripts\");\n const sources = await listFallbackSourceFiles(transcriptsDir);\n const files: SessionMigrationFilePlan[] = [];\n const distinctSessions = new Set<string>();\n let movedEntries = 0;\n\n for (const source of sources) {\n if (!isDateStampedJsonl(source.fileName)) continue;\n let content: string;\n try {\n content = await readFile(source.absPath, \"utf-8\");\n } catch {\n continue;\n }\n const lines = parseSourceLines(content);\n const filePlan = planFile(source.relPath, source.fileName, lines);\n if (filePlan.alreadyHomed) continue;\n\n for (const group of filePlan.groups) {\n distinctSessions.add(group.sessionKey);\n movedEntries += group.entryCount;\n }\n files.push(filePlan);\n }\n\n return {\n generatedAt: new Date().toISOString(),\n dryRun: options.apply !== true,\n transcriptsDir,\n files,\n distinctSessions: distinctSessions.size,\n movedEntries,\n };\n}\n\n/**\n * Append `lines` (raw JSONL strings) to `<destDir>/<fileName>` using a\n * write-then-rename merge so a crash mid-migration cannot corrupt or truncate\n * the destination (rule #54).\n *\n * Existing destination content is preserved and de-duplicated by raw line so\n * re-running the migration is idempotent.\n */\nasync function mergeAppendLines(\n transcriptsDir: string,\n destDir: string,\n fileName: string,\n newLines: string[]\n): Promise<void> {\n const destChannelDir = await resolveSafeStoragePath(transcriptsDir, destDir);\n await mkdir(destChannelDir, { recursive: true });\n const destPath = await resolveSafeStoragePath(transcriptsDir, destDir, fileName);\n\n const existing: string[] = [];\n const seen = new Set<string>();\n try {\n const raw = await readFile(destPath, \"utf-8\");\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n existing.push(line);\n seen.add(line);\n }\n } catch {\n // No existing destination file — fine.\n }\n\n const merged = [...existing];\n for (const line of newLines) {\n if (seen.has(line)) continue;\n merged.push(line);\n seen.add(line);\n }\n\n const body = merged.length > 0 ? `${merged.join(\"\\n\")}\\n` : \"\";\n const tmpPath = await resolveSafeStoragePath(\n transcriptsDir,\n destDir,\n `${fileName}.migrate-${process.pid}-${Date.now()}.tmp`\n );\n await writeFile(tmpPath, body, \"utf-8\");\n await rename(tmpPath, destPath);\n}\n\n/**\n * Rewrite the source file to retain ONLY the lines that were not moved\n * (unparseable lines and any that already belong in the source dir). Uses\n * write-then-rename; deletes the source only when nothing remains.\n */\nasync function rewriteSourceRetainingUnmoved(\n transcriptsDir: string,\n sourceRelPath: string,\n retainedLines: string[]\n): Promise<{ removed: boolean }> {\n const sourcePath = await resolveSafeStoragePath(transcriptsDir, sourceRelPath);\n if (retainedLines.length === 0) {\n // Atomic-rename into a sibling tombstone then unlink, so a crash leaves the\n // original intact (never delete-before-confirm; rule #54). Simpler and\n // equally safe: unlink only after destinations are confirmed written.\n try {\n await unlink(sourcePath);\n } catch (err) {\n const code =\n err && typeof err === \"object\" && \"code\" in err ? String((err as { code?: unknown }).code ?? \"\") : \"\";\n if (code !== \"ENOENT\") throw err;\n }\n return { removed: true };\n }\n\n const body = `${retainedLines.join(\"\\n\")}\\n`;\n const sourceDir = path.dirname(sourceRelPath);\n const tmpPath = await resolveSafeStoragePath(\n transcriptsDir,\n sourceDir,\n `${path.basename(sourceRelPath)}.migrate-${process.pid}-${Date.now()}.tmp`\n );\n await writeFile(tmpPath, body, \"utf-8\");\n await rename(tmpPath, sourcePath);\n return { removed: false };\n}\n\n/**\n * Run the migration. Dry-run by default; pass `apply: true` to mutate.\n */\nexport async function migrateSessionTranscripts(\n options: MigrateSessionTranscriptsOptions\n): Promise<SessionMigrationResult> {\n const plan = await planSessionTranscriptMigration(options);\n const apply = options.apply === true;\n\n if (!apply) {\n return {\n plan,\n applied: false,\n filesRewritten: 0,\n filesRemoved: 0,\n errors: [],\n };\n }\n\n const transcriptsDir = plan.transcriptsDir;\n const errors: string[] = [];\n let filesRewritten = 0;\n let filesRemoved = 0;\n\n for (const filePlan of plan.files) {\n try {\n const sourcePath = await resolveSafeStoragePath(transcriptsDir, filePlan.sourceRelPath);\n const content = await readFile(sourcePath, \"utf-8\");\n const lines = parseSourceLines(content);\n const sourceDir = path.dirname(filePlan.sourceRelPath);\n\n // Bucket lines by destination, preserving order. Retain unmovable lines\n // and any line already homed in the source dir.\n const byDest = new Map<string, string[]>();\n const retained: string[] = [];\n for (const line of lines) {\n if (!line.sessionKey) {\n retained.push(line.raw);\n continue;\n }\n const dest = sessionStoragePaths(line.sessionKey).dir;\n if (dest === sourceDir) {\n retained.push(line.raw);\n continue;\n }\n const bucket = byDest.get(dest) ?? [];\n bucket.push(line.raw);\n byDest.set(dest, bucket);\n }\n\n // 1. Write destinations FIRST (write-then-rename, idempotent merge).\n for (const [destDir, destLines] of byDest.entries()) {\n await mergeAppendLines(transcriptsDir, destDir, filePlan.fileName, destLines);\n }\n\n // 2. Only after all destinations are confirmed, rewrite/remove source.\n const { removed } = await rewriteSourceRetainingUnmoved(transcriptsDir, filePlan.sourceRelPath, retained);\n if (removed) {\n filesRemoved += 1;\n } else {\n filesRewritten += 1;\n }\n } catch (err) {\n // The `errors` array is surfaced to operators via the CLI\n // (`sessions migrate-transcripts --apply`) AND persisted in the audit\n // manifest. Raw `err.message`/`String(err)` can leak filesystem paths or\n // stack detail, so route operator-facing strings through the shared\n // `displayErrorDetail()` sanitizer (cursor review on PR #1504, rule #51).\n // The full error still goes to the operator-only debug log.\n const detail = displayErrorDetail(err);\n errors.push(\n `Failed to migrate ${filePlan.sourceRelPath}${detail ? `: ${detail}` : \"\"}`,\n );\n log.error(`session transcript migration failed for ${filePlan.sourceRelPath}:`, err);\n }\n }\n\n let manifestPath: string | undefined;\n try {\n manifestPath = await writeManifest(options.memoryDir, {\n plan,\n applied: true,\n filesRewritten,\n filesRemoved,\n errors,\n });\n } catch (err) {\n // Manifest is best-effort audit; do not fail the migration over it.\n log.debug(`failed to write session migration manifest: ${err}`);\n }\n\n return {\n plan,\n applied: true,\n filesRewritten,\n filesRemoved,\n errors,\n manifestPath,\n };\n}\n\nasync function writeManifest(memoryDir: string, result: Omit<SessionMigrationResult, \"manifestPath\">): Promise<string> {\n const auditDir = path.join(memoryDir, \"state\", \"session-migration\");\n await mkdir(auditDir, { recursive: true });\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const manifestPath = path.join(auditDir, `migrate-transcripts-${stamp}.json`);\n await writeFile(manifestPath, JSON.stringify(result, null, 2), \"utf-8\");\n return manifestPath;\n}\n\n/** Best-effort byte-size summary for reporting (used by CLI output). */\nexport async function summarizeMigrationSources(memoryDir: string): Promise<{ files: number; bytes: number }> {\n const transcriptsDir = path.join(memoryDir, \"transcripts\");\n const sources = await listFallbackSourceFiles(transcriptsDir);\n let bytes = 0;\n let files = 0;\n for (const source of sources) {\n const info = await stat(source.absPath).catch(() => null);\n if (info?.isFile()) {\n bytes += info.size;\n files += 1;\n }\n }\n return { files, bytes };\n}\n"],"mappings":"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","names":[]}
@@ -1,14 +1,16 @@
1
1
  import {
2
- ALL_CATEGORY_DIRS,
3
2
  NamespaceStorageRouter
4
- } from "./chunk-RREI7CNM.js";
3
+ } from "./chunk-D6WVJIS3.js";
5
4
  import {
6
5
  namespaceCollectionName
7
- } from "./chunk-HRXCPKN4.js";
6
+ } from "./chunk-APWJRJFW.js";
8
7
  import {
9
8
  namespaceIdentityFromToken,
10
9
  namespaceIdentityToken
11
10
  } from "./chunk-ZFXCQPNO.js";
11
+ import {
12
+ ALL_CATEGORY_DIRS
13
+ } from "./chunk-A4BTPHIN.js";
12
14
  import {
13
15
  isSafeRouteNamespace
14
16
  } from "./chunk-U3PN77QT.js";
@@ -202,4 +204,4 @@ export {
202
204
  verifyNamespaces,
203
205
  runNamespaceMigration
204
206
  };
205
- //# sourceMappingURL=chunk-DM4US77W.js.map
207
+ //# sourceMappingURL=chunk-FAYDM5WD.js.map