@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
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Shared session-identity / transcript-pathing layer (issue #1496).
3
+ *
4
+ * This module is the SINGLE source of truth for turning a `sessionKey` into a
5
+ * channel identity and a set of deterministic, collision-resistant storage
6
+ * paths. Every subsystem that previously re-parsed session keys (transcript
7
+ * pathing, tool-usage pathing, hourly summaries, session auditing, conversation
8
+ * indexing) MUST route through `parseSessionIdentity()` / `sessionStoragePaths()`
9
+ * so behavior stays identical across paths (rule #22, #39).
10
+ *
11
+ * Two shapes are recognized:
12
+ *
13
+ * 1. Legacy `agent:<agentId>:<channelType>:...` keys keep their existing
14
+ * readable channel identity:
15
+ * - agent:<id>:main → type="main", id="default"
16
+ * - agent:<id>:discord:channel:<chanId> → type="discord", id="<chanId>"
17
+ * - agent:<id>:slack:channel:<chanId> → type="slack", id="<chanId>"
18
+ * - agent:<id>:cron:<jobId> → type="cron", id="<jobId>"
19
+ * - agent:<id>:<other>[:<id>] → type="<other>", id="<id?>"
20
+ *
21
+ * 2. Arbitrary / non-legacy keys (e.g. `pi-geek:abc123`) become first-class
22
+ * from the FIRST write — they NEVER start life under `other/default`:
23
+ * channelType = "session"
24
+ * channelId = storagePathHash(sessionKey) (collision-resistant)
25
+ * yielding `transcripts/session/<hash>/YYYY-MM-DD.jsonl`.
26
+ *
27
+ * The reserved `"session"` channel type is what makes arbitrary keys isolated
28
+ * and auditable. Legacy data under `other/default` (and any legacy channel
29
+ * directory) remains READABLE via `legacyDir` / `alternateDir` candidates.
30
+ */
31
+ /** Channel type reserved for first-class arbitrary (non-legacy) session keys. */
32
+ declare const SESSION_CHANNEL_TYPE = "session";
33
+ /** Legacy fallback channel identity for un-parseable keys (read-back only). */
34
+ declare const LEGACY_FALLBACK_CHANNEL_TYPE = "other";
35
+ declare const LEGACY_FALLBACK_CHANNEL_ID = "default";
36
+ interface SessionIdentity {
37
+ /** Channel type — legacy channel type for known shapes, else `"session"`. */
38
+ channelType: string;
39
+ /** Channel id — legacy channel id for known shapes, else the key hash. */
40
+ channelId: string;
41
+ /** Human-readable, non-authoritative label derived from the raw key. */
42
+ displayLabel: string;
43
+ /** The original session key, unchanged. */
44
+ canonicalSessionKey: string;
45
+ /** True when the key matched a known legacy `agent:<id>:...` shape. */
46
+ legacy: boolean;
47
+ }
48
+ interface SessionStoragePaths {
49
+ /** Channel type (mirrors {@link SessionIdentity.channelType}). */
50
+ channelType: string;
51
+ /** Channel id (mirrors {@link SessionIdentity.channelId}). */
52
+ channelId: string;
53
+ /** Primary encoded storage subdirectory: `<encodedType>/<encodedId>`. */
54
+ dir: string;
55
+ /**
56
+ * Collision-resistant alternate subdirectory that always embeds the full
57
+ * session-key hash. Used both as a write-time collision escape hatch and as a
58
+ * read-back candidate for data written before this layer existed.
59
+ */
60
+ alternateDir: string;
61
+ /**
62
+ * Un-encoded `<type>/<id>` directory for reading data written by older
63
+ * builds that did not URL-encode path segments. Undefined when it would be
64
+ * identical to `dir` or when either segment is path-unsafe.
65
+ */
66
+ legacyDir?: string;
67
+ /**
68
+ * Ordered, de-duplicated list of READ-BACK-ONLY directories where an older
69
+ * build may have stranded this key's data. NEW writes never target these —
70
+ * they exist purely so pre-#1496 transcripts/tool-usage stay discoverable
71
+ * (and migratable). Populated only for non-legacy keys. Includes:
72
+ * 1. the shared `other/default` fallback (every arbitrary key landed here);
73
+ * 2. the directory the OLD `parts.length >= 3` parser would have chosen
74
+ * (e.g. `foo:bar:baz` → `baz/default`, `foo:bar:baz:qux` → `baz/qux`).
75
+ * See {@link legacyParserReadbackDir}.
76
+ */
77
+ readbackDirs: string[];
78
+ }
79
+ /**
80
+ * Parse a session key into a stable channel identity.
81
+ *
82
+ * Pure and deterministic — no filesystem access. Same input always yields the
83
+ * same identity, which is what makes arbitrary keys collision-resistant from
84
+ * the first write.
85
+ */
86
+ declare function parseSessionIdentity(sessionKey: string): SessionIdentity;
87
+ /**
88
+ * Resolve the storage path pieces for a session key. Wraps
89
+ * {@link parseSessionIdentity} so transcript, tool-usage, and summary writers
90
+ * share one implementation (rule #22).
91
+ */
92
+ declare function sessionStoragePaths(sessionKey: string): SessionStoragePaths;
93
+ /**
94
+ * The shared `other/default` directory every arbitrary key was routed into by
95
+ * builds predating issue #1496. Exposed so transcript/tool-usage/summary read
96
+ * paths and the migration scanner all agree on the same fallback location.
97
+ */
98
+ declare const OTHER_DEFAULT_READBACK_DIR: string;
99
+ /**
100
+ * Reconstruct the transcript/tool-usage directory the OLD `getTranscriptPath`
101
+ * parser (pre-#1496) would have produced for a key the NEW parser reclassifies
102
+ * as a first-class `session/<hash>` identity.
103
+ *
104
+ * The OLD parser treated ANY key with `parts.length >= 3` as legacy — it did
105
+ * NOT require a leading `agent` segment — so an arbitrary key like
106
+ * `foo:bar:baz` was stored under `baz/default` and `foo:bar:baz:qux` under
107
+ * `baz/qux`. Those directories must stay readable (and migratable) for existing
108
+ * installs even though the key now writes to `session/<hash>` (Thread B / codex
109
+ * review on PR #1504). Path segments are encoded exactly as the old `dir` was
110
+ * built (`encodeStoragePathSegment`) so the candidate matches the bytes on disk.
111
+ *
112
+ * Returns `undefined` when the key has fewer than three colon parts (the old
113
+ * parser would have used the `other/default` fallback, already covered), when
114
+ * the channel type is empty, or when the result is unsafe.
115
+ */
116
+ declare function legacyParserReadbackDir(sessionKey: string): string | undefined;
117
+
118
+ export { LEGACY_FALLBACK_CHANNEL_ID, LEGACY_FALLBACK_CHANNEL_TYPE, OTHER_DEFAULT_READBACK_DIR, SESSION_CHANNEL_TYPE, type SessionIdentity, type SessionStoragePaths, legacyParserReadbackDir, parseSessionIdentity, sessionStoragePaths };
@@ -0,0 +1,21 @@
1
+ import {
2
+ LEGACY_FALLBACK_CHANNEL_ID,
3
+ LEGACY_FALLBACK_CHANNEL_TYPE,
4
+ OTHER_DEFAULT_READBACK_DIR,
5
+ SESSION_CHANNEL_TYPE,
6
+ legacyParserReadbackDir,
7
+ parseSessionIdentity,
8
+ sessionStoragePaths
9
+ } from "./chunk-S4DDLTPX.js";
10
+ import "./chunk-FF4KLI5W.js";
11
+ import "./chunk-PZ5AY32C.js";
12
+ export {
13
+ LEGACY_FALLBACK_CHANNEL_ID,
14
+ LEGACY_FALLBACK_CHANNEL_TYPE,
15
+ OTHER_DEFAULT_READBACK_DIR,
16
+ SESSION_CHANNEL_TYPE,
17
+ legacyParserReadbackDir,
18
+ parseSessionIdentity,
19
+ sessionStoragePaths
20
+ };
21
+ //# sourceMappingURL=session-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,65 @@
1
+ interface SessionMigrationEntryGroup {
2
+ /** The distinct session key these lines belong to. */
3
+ sessionKey: string;
4
+ /** Whether this key is a recognized legacy `agent:<id>:...` shape. */
5
+ legacy: boolean;
6
+ /** Destination directory (relative to the transcripts root). */
7
+ destDir: string;
8
+ /** Number of JSONL entries that will move. */
9
+ entryCount: number;
10
+ }
11
+ interface SessionMigrationFilePlan {
12
+ /** Source file path (relative to transcripts root). */
13
+ sourceRelPath: string;
14
+ /** The date-stamped file name (e.g. `2026-06-29.jsonl`). */
15
+ fileName: string;
16
+ /** Distinct session groups discovered inside this source file. */
17
+ groups: SessionMigrationEntryGroup[];
18
+ /** Lines that could not be parsed as JSON or lacked a sessionKey. */
19
+ unmovableLines: number;
20
+ /**
21
+ * True when EVERY entry in the file already belongs in the source directory
22
+ * (nothing to do). Such files are skipped.
23
+ */
24
+ alreadyHomed: boolean;
25
+ }
26
+ interface SessionMigrationPlan {
27
+ generatedAt: string;
28
+ dryRun: boolean;
29
+ transcriptsDir: string;
30
+ /** Per-file plans that have at least one entry to move. */
31
+ files: SessionMigrationFilePlan[];
32
+ /** Distinct destination session keys across all files. */
33
+ distinctSessions: number;
34
+ /** Total entries that will be re-homed. */
35
+ movedEntries: number;
36
+ }
37
+ interface SessionMigrationResult {
38
+ plan: SessionMigrationPlan;
39
+ applied: boolean;
40
+ filesRewritten: number;
41
+ filesRemoved: number;
42
+ errors: string[];
43
+ manifestPath?: string;
44
+ }
45
+ interface MigrateSessionTranscriptsOptions {
46
+ /** The memory directory root (transcripts live under `<memoryDir>/transcripts`). */
47
+ memoryDir: string;
48
+ /** When true, perform the migration; otherwise produce a dry-run plan only. */
49
+ apply?: boolean;
50
+ }
51
+ /**
52
+ * Compute the migration plan without modifying anything.
53
+ */
54
+ declare function planSessionTranscriptMigration(options: MigrateSessionTranscriptsOptions): Promise<SessionMigrationPlan>;
55
+ /**
56
+ * Run the migration. Dry-run by default; pass `apply: true` to mutate.
57
+ */
58
+ declare function migrateSessionTranscripts(options: MigrateSessionTranscriptsOptions): Promise<SessionMigrationResult>;
59
+ /** Best-effort byte-size summary for reporting (used by CLI output). */
60
+ declare function summarizeMigrationSources(memoryDir: string): Promise<{
61
+ files: number;
62
+ bytes: number;
63
+ }>;
64
+
65
+ export { type MigrateSessionTranscriptsOptions, type SessionMigrationEntryGroup, type SessionMigrationFilePlan, type SessionMigrationPlan, type SessionMigrationResult, migrateSessionTranscripts, planSessionTranscriptMigration, summarizeMigrationSources };
@@ -0,0 +1,16 @@
1
+ import {
2
+ migrateSessionTranscripts,
3
+ planSessionTranscriptMigration,
4
+ summarizeMigrationSources
5
+ } from "./chunk-EVWIEEKZ.js";
6
+ import "./chunk-S4DDLTPX.js";
7
+ import "./chunk-FF4KLI5W.js";
8
+ import "./chunk-6KYMPV2O.js";
9
+ import "./chunk-2ODBA7MQ.js";
10
+ import "./chunk-PZ5AY32C.js";
11
+ export {
12
+ migrateSessionTranscripts,
13
+ planSessionTranscriptMigration,
14
+ summarizeMigrationSources
15
+ };
16
+ //# sourceMappingURL=session-transcript-migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/storage.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  parseEntityFile,
10
10
  serializeEntityFile,
11
11
  stripAttributesSuffix
12
- } from "./chunk-GYITUZFT.js";
12
+ } from "./chunk-A4BTPHIN.js";
13
13
  import "./chunk-M7XQSUBB.js";
14
14
  import "./chunk-5UZXUTVO.js";
15
15
  import "./chunk-J6A3CX5N.js";
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  HourlySummarizer
3
- } from "./chunk-Y2XKJJEI.js";
3
+ } from "./chunk-IMWFHBG2.js";
4
4
  import "./chunk-XJNBEDFE.js";
5
5
  import "./chunk-XZ4WBBB5.js";
6
6
  import "./chunk-H3HDXD3U.js";
7
+ import "./chunk-S4DDLTPX.js";
7
8
  import "./chunk-A3BS64GV.js";
8
9
  import "./chunk-B5XMS73R.js";
9
10
  import "./chunk-7SI52C65.js";
@@ -26,13 +26,17 @@ declare class TranscriptManager {
26
26
  private static readonly CHARS_PER_TOKEN;
27
27
  constructor(config: PluginConfig);
28
28
  /**
29
- * Parse a sessionKey to extract channel type and ID.
29
+ * Resolve the storage path pieces for a sessionKey via the shared
30
+ * {@link sessionStoragePaths} helper (issue #1496, rule #22).
30
31
  *
31
- * SessionKey patterns:
32
- * - agent:<agent-id>:main type="main", id="default"
33
- * - agent:<agent-id>:discord:channel:<channel-id> type="discord", id="<channel-id>"
34
- * - agent:<agent-id>:cron:<job-id> type="cron", id="<job-id>"
35
- * - agent:<agent-id>:slack:channel:<channel-id> type="slack", id="<channel-id>"
32
+ * Legacy `agent:<id>:...` shapes keep their existing readable channel paths.
33
+ * Arbitrary / non-legacy keys (e.g. `pi-geek:abc123`) route to
34
+ * `transcripts/session/<hash>/YYYY-MM-DD.jsonl` from the FIRST write — they
35
+ * never start life in `other/default`. For those keys a list of
36
+ * read-back-only `readbackDirs` is returned so data written by older builds
37
+ * (under `other/default`, or under an old `parts.length >= 3` directory such
38
+ * as `baz/default`) stays discoverable until migrated. New writes never
39
+ * target read-back dirs. See `session-identity.ts`.
36
40
  *
37
41
  * @returns Object with raw channel identifiers and encoded storage path pieces.
38
42
  */
@@ -43,6 +47,7 @@ declare class TranscriptManager {
43
47
  channelId: string;
44
48
  alternateDir: string;
45
49
  legacyDir?: string;
50
+ readbackDirs: string[];
46
51
  };
47
52
  /**
48
53
  * Initialize the transcript manager by ensuring directories exist.
@@ -59,6 +64,7 @@ declare class TranscriptManager {
59
64
  file: string;
60
65
  alternateDir: string;
61
66
  legacyDir?: string;
67
+ readbackDirs: string[];
62
68
  };
63
69
  private selectStorageDirForWrite;
64
70
  private directorySessionStatus;
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  TranscriptManager
3
- } from "./chunk-IHG27THY.js";
3
+ } from "./chunk-JLOJ5RJ7.js";
4
+ import "./chunk-S4DDLTPX.js";
4
5
  import "./chunk-KFY3SGN7.js";
5
6
  import "./chunk-FF4KLI5W.js";
6
7
  import "./chunk-2ODBA7MQ.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  backupMemoryDir
3
- } from "../chunk-BXLOS5AJ.js";
3
+ } from "../chunk-OWHERGF2.js";
4
4
  import "../chunk-765K3SAT.js";
5
- import "../chunk-DQEMWVMT.js";
5
+ import "../chunk-UVYI6VIX.js";
6
6
  import "../chunk-J4EB7DNW.js";
7
7
  import "../chunk-BJMBJZ2Y.js";
8
8
  import "../chunk-UKJAGEXH.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  exportCapsule,
3
3
  isValidCapsuleSince
4
- } from "../chunk-VDX2J7OX.js";
4
+ } from "../chunk-BUKK5SWA.js";
5
5
  import "../chunk-WEHSQBFR.js";
6
- import "../chunk-DQEMWVMT.js";
6
+ import "../chunk-UVYI6VIX.js";
7
7
  import "../chunk-J4EB7DNW.js";
8
8
  import "../chunk-BJMBJZ2Y.js";
9
9
  import "../chunk-UKJAGEXH.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  importCapsule
3
- } from "../chunk-DGNQRNLL.js";
3
+ } from "../chunk-NT5TINK5.js";
4
4
  import "../chunk-ALEPI75L.js";
5
5
  import "../chunk-WEHSQBFR.js";
6
- import "../chunk-DQEMWVMT.js";
6
+ import "../chunk-UVYI6VIX.js";
7
7
  import "../chunk-BJMBJZ2Y.js";
8
8
  import "../chunk-UKJAGEXH.js";
9
9
  import "../chunk-FP2373TW.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  importSqlite
3
- } from "../chunk-AGNBY3VG.js";
4
- import "../chunk-Z734BLO3.js";
3
+ } from "../chunk-APJQ6UEA.js";
5
4
  import "../chunk-G4IAEX6D.js";
5
+ import "../chunk-Z734BLO3.js";
6
6
  import "../chunk-6KYMPV2O.js";
7
7
  import "../chunk-GCGJW34D.js";
8
8
  import "../chunk-PZ5AY32C.js";
@@ -313,13 +313,13 @@ declare const CapsuleBlockSchema: z.ZodObject<{
313
313
  peerProfiles: boolean;
314
314
  }>;
315
315
  }, "strip", z.ZodTypeAny, {
316
+ schemaVersion: string;
316
317
  includes: {
317
318
  procedural: boolean;
318
319
  taxonomy: boolean;
319
320
  identityAnchors: boolean;
320
321
  peerProfiles: boolean;
321
322
  };
322
- schemaVersion: string;
323
323
  id: string;
324
324
  description: string;
325
325
  version: string;
@@ -334,13 +334,13 @@ declare const CapsuleBlockSchema: z.ZodObject<{
334
334
  directAnswerEnabled: boolean;
335
335
  };
336
336
  }, {
337
+ schemaVersion: string;
337
338
  includes: {
338
339
  procedural: boolean;
339
340
  taxonomy: boolean;
340
341
  identityAnchors: boolean;
341
342
  peerProfiles: boolean;
342
343
  };
343
- schemaVersion: string;
344
344
  id: string;
345
345
  description: string;
346
346
  version: string;
@@ -464,13 +464,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
464
464
  peerProfiles: boolean;
465
465
  }>;
466
466
  }, "strip", z.ZodTypeAny, {
467
+ schemaVersion: string;
467
468
  includes: {
468
469
  procedural: boolean;
469
470
  taxonomy: boolean;
470
471
  identityAnchors: boolean;
471
472
  peerProfiles: boolean;
472
473
  };
473
- schemaVersion: string;
474
474
  id: string;
475
475
  description: string;
476
476
  version: string;
@@ -485,13 +485,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
485
485
  directAnswerEnabled: boolean;
486
486
  };
487
487
  }, {
488
+ schemaVersion: string;
488
489
  includes: {
489
490
  procedural: boolean;
490
491
  taxonomy: boolean;
491
492
  identityAnchors: boolean;
492
493
  peerProfiles: boolean;
493
494
  };
494
- schemaVersion: string;
495
495
  id: string;
496
496
  description: string;
497
497
  version: string;
@@ -518,13 +518,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
518
518
  pluginVersion: string;
519
519
  includesTranscripts: boolean;
520
520
  capsule: {
521
+ schemaVersion: string;
521
522
  includes: {
522
523
  procedural: boolean;
523
524
  taxonomy: boolean;
524
525
  identityAnchors: boolean;
525
526
  peerProfiles: boolean;
526
527
  };
527
- schemaVersion: string;
528
528
  id: string;
529
529
  description: string;
530
530
  version: string;
@@ -551,13 +551,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
551
551
  pluginVersion: string;
552
552
  includesTranscripts: boolean;
553
553
  capsule: {
554
+ schemaVersion: string;
554
555
  includes: {
555
556
  procedural: boolean;
556
557
  taxonomy: boolean;
557
558
  identityAnchors: boolean;
558
559
  peerProfiles: boolean;
559
560
  };
560
- schemaVersion: string;
561
561
  id: string;
562
562
  description: string;
563
563
  version: string;
@@ -683,13 +683,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
683
683
  peerProfiles: boolean;
684
684
  }>;
685
685
  }, "strip", z.ZodTypeAny, {
686
+ schemaVersion: string;
686
687
  includes: {
687
688
  procedural: boolean;
688
689
  taxonomy: boolean;
689
690
  identityAnchors: boolean;
690
691
  peerProfiles: boolean;
691
692
  };
692
- schemaVersion: string;
693
693
  id: string;
694
694
  description: string;
695
695
  version: string;
@@ -704,13 +704,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
704
704
  directAnswerEnabled: boolean;
705
705
  };
706
706
  }, {
707
+ schemaVersion: string;
707
708
  includes: {
708
709
  procedural: boolean;
709
710
  taxonomy: boolean;
710
711
  identityAnchors: boolean;
711
712
  peerProfiles: boolean;
712
713
  };
713
- schemaVersion: string;
714
714
  id: string;
715
715
  description: string;
716
716
  version: string;
@@ -737,13 +737,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
737
737
  pluginVersion: string;
738
738
  includesTranscripts: boolean;
739
739
  capsule: {
740
+ schemaVersion: string;
740
741
  includes: {
741
742
  procedural: boolean;
742
743
  taxonomy: boolean;
743
744
  identityAnchors: boolean;
744
745
  peerProfiles: boolean;
745
746
  };
746
- schemaVersion: string;
747
747
  id: string;
748
748
  description: string;
749
749
  version: string;
@@ -770,13 +770,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
770
770
  pluginVersion: string;
771
771
  includesTranscripts: boolean;
772
772
  capsule: {
773
+ schemaVersion: string;
773
774
  includes: {
774
775
  procedural: boolean;
775
776
  taxonomy: boolean;
776
777
  identityAnchors: boolean;
777
778
  peerProfiles: boolean;
778
779
  };
779
- schemaVersion: string;
780
780
  id: string;
781
781
  description: string;
782
782
  version: string;
@@ -815,13 +815,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
815
815
  pluginVersion: string;
816
816
  includesTranscripts: boolean;
817
817
  capsule: {
818
+ schemaVersion: string;
818
819
  includes: {
819
820
  procedural: boolean;
820
821
  taxonomy: boolean;
821
822
  identityAnchors: boolean;
822
823
  peerProfiles: boolean;
823
824
  };
824
- schemaVersion: string;
825
825
  id: string;
826
826
  description: string;
827
827
  version: string;
@@ -854,13 +854,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
854
854
  pluginVersion: string;
855
855
  includesTranscripts: boolean;
856
856
  capsule: {
857
+ schemaVersion: string;
857
858
  includes: {
858
859
  procedural: boolean;
859
860
  taxonomy: boolean;
860
861
  identityAnchors: boolean;
861
862
  peerProfiles: boolean;
862
863
  };
863
- schemaVersion: string;
864
864
  id: string;
865
865
  description: string;
866
866
  version: string;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  searchVerifiedEpisodes
3
- } from "./chunk-4HYLRROV.js";
3
+ } from "./chunk-UMKPSD35.js";
4
4
  import "./chunk-HQ6NIBL6.js";
5
- import "./chunk-GYITUZFT.js";
5
+ import "./chunk-A4BTPHIN.js";
6
6
  import "./chunk-M7XQSUBB.js";
7
7
  import "./chunk-5UZXUTVO.js";
8
8
  import "./chunk-J6A3CX5N.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.3.644",
3
+ "version": "9.3.646",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/cli.ts CHANGED
@@ -202,6 +202,10 @@ import {
202
202
  type SessionRepairApplyResult,
203
203
  type SessionRepairPlan,
204
204
  } from "./session-integrity.js";
205
+ import {
206
+ migrateSessionTranscripts,
207
+ planSessionTranscriptMigration,
208
+ } from "./session-transcript-migration.js";
205
209
  import type { TierMigrationCycleSummary, TierMigrationStatusSnapshot } from "./recall-state.js";
206
210
  import {
207
211
  readRuntimePolicySnapshot as readPolicyRuntimeSnapshot,
@@ -8650,6 +8654,81 @@ export function registerCli(
8650
8654
  }
8651
8655
  });
8652
8656
 
8657
+ // ── Sessions subcommand (issue #1496) ───────────────────────────────
8658
+ const sessionsCmd = cmd
8659
+ .command("sessions")
8660
+ .description("Inspect and migrate session transcript storage");
8661
+
8662
+ sessionsCmd
8663
+ .command("migrate-transcripts")
8664
+ .description(
8665
+ "Split conflated other/default transcripts into first-class session/<hash> dirs",
8666
+ )
8667
+ .option("--dry-run", "Report the migration plan without moving files (default)")
8668
+ .option("--apply", "Apply the migration (move files; default is dry-run)")
8669
+ .action(async (...args: unknown[]) => {
8670
+ const options = (args[0] ?? {}) as Record<string, unknown>;
8671
+ const dryRunFlag = options.dryRun === true;
8672
+ const applyFlag = options.apply === true;
8673
+
8674
+ // Rule #51: reject contradictory flags rather than silently picking one.
8675
+ if (dryRunFlag && applyFlag) {
8676
+ console.error("Cannot pass both --dry-run and --apply. Choose one.");
8677
+ process.exit(1);
8678
+ }
8679
+
8680
+ // Safe default: dry-run when neither flag is given.
8681
+ const apply = applyFlag;
8682
+ const memoryDir = orchestrator.config.memoryDir;
8683
+
8684
+ if (!apply) {
8685
+ const plan = await planSessionTranscriptMigration({ memoryDir });
8686
+ console.log("=== Session Transcript Migration (DRY RUN) ===\n");
8687
+ if (!dryRunFlag && !applyFlag) {
8688
+ console.log("No flag given — defaulting to --dry-run. Pass --apply to migrate.\n");
8689
+ }
8690
+ console.log(`transcripts dir: ${plan.transcriptsDir}`);
8691
+ console.log(`files to migrate: ${plan.files.length}`);
8692
+ console.log(`distinct sessions: ${plan.distinctSessions}`);
8693
+ console.log(`entries to move: ${plan.movedEntries}`);
8694
+ if (plan.files.length > 0) {
8695
+ console.log("\nPlanned splits:");
8696
+ for (const file of plan.files) {
8697
+ console.log(`- ${file.sourceRelPath}`);
8698
+ for (const group of file.groups) {
8699
+ console.log(
8700
+ ` ${group.entryCount} entr${group.entryCount === 1 ? "y" : "ies"} → ${group.destDir} (${group.legacy ? "legacy" : "session"})`,
8701
+ );
8702
+ }
8703
+ if (file.unmovableLines > 0) {
8704
+ console.log(` ${file.unmovableLines} unmovable line(s) retained in source`);
8705
+ }
8706
+ }
8707
+ }
8708
+ console.log("\nDRY RUN — no files changed.");
8709
+ return;
8710
+ }
8711
+
8712
+ const result = await migrateSessionTranscripts({ memoryDir, apply: true });
8713
+ console.log("=== Session Transcript Migration (APPLY) ===\n");
8714
+ console.log(`files rewritten: ${result.filesRewritten}`);
8715
+ console.log(`files removed: ${result.filesRemoved}`);
8716
+ console.log(`distinct sessions: ${result.plan.distinctSessions}`);
8717
+ console.log(`entries moved: ${result.plan.movedEntries}`);
8718
+ if (result.manifestPath) {
8719
+ console.log(`manifest: ${result.manifestPath}`);
8720
+ }
8721
+ if (result.errors.length > 0) {
8722
+ console.log("\nErrors:");
8723
+ for (const err of result.errors) {
8724
+ console.log(`- ${err}`);
8725
+ }
8726
+ process.exitCode = 1;
8727
+ return;
8728
+ }
8729
+ console.log("\nOK");
8730
+ });
8731
+
8653
8732
  // Checkpoint command
8654
8733
  cmd
8655
8734
  .command("checkpoint")
package/src/index.ts CHANGED
@@ -324,6 +324,24 @@ export { MeilisearchBackend } from "./search/meilisearch-backend.js";
324
324
  export { buildEntityRecallSection } from "./entity-retrieval.js";
325
325
  export { resolvePrincipal } from "./namespaces/principal.js";
326
326
 
327
+ // ---------------------------------------------------------------------------
328
+ // Session identity / transcript pathing (issue #1496)
329
+ // ---------------------------------------------------------------------------
330
+
331
+ // Shared, deterministic session-key → storage-path resolution. Exported so
332
+ // downstream consumers (the @remnic/bench adapter + its tests, host adapters)
333
+ // resolve transcript directories through the SAME layer the core writer uses
334
+ // instead of hard-coding `other/default` (rule #20, #22, #26).
335
+ export {
336
+ SESSION_CHANNEL_TYPE,
337
+ parseSessionIdentity,
338
+ sessionStoragePaths,
339
+ legacyParserReadbackDir,
340
+ type SessionIdentity,
341
+ type SessionStoragePaths,
342
+ } from "./session-identity.js";
343
+ export { storagePathHash } from "./storage-paths.js";
344
+
327
345
  // ---------------------------------------------------------------------------
328
346
  // Trust zones
329
347
  // ---------------------------------------------------------------------------