@remnic/core 9.3.757 → 9.3.758

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 (165) hide show
  1. package/dist/access-admin-ops-surface.d.ts +1 -1
  2. package/dist/access-admin-ops-surface.js +20 -20
  3. package/dist/access-audit.js +2 -2
  4. package/dist/access-boundary.d.ts +1 -1
  5. package/dist/access-boundary.js +21 -21
  6. package/dist/access-cli.js +68 -68
  7. package/dist/access-http.d.ts +1 -1
  8. package/dist/access-http.js +28 -28
  9. package/dist/access-identity-continuity-surface.d.ts +166 -0
  10. package/dist/access-identity-continuity-surface.js +128 -0
  11. package/dist/access-lcm-surface.d.ts +1 -1
  12. package/dist/access-lcm-surface.js +20 -20
  13. package/dist/access-mcp.d.ts +1 -1
  14. package/dist/access-mcp.js +27 -27
  15. package/dist/access-observe-write-surface.d.ts +1 -1
  16. package/dist/access-observe-write-surface.js +20 -20
  17. package/dist/access-operations-batch.js +24 -24
  18. package/dist/access-operations.d.ts +5 -5
  19. package/dist/access-operations.js +26 -26
  20. package/dist/access-recall-surface.d.ts +1 -1
  21. package/dist/access-recall-surface.js +20 -20
  22. package/dist/access-schema.d.ts +68 -68
  23. package/dist/access-schema.js +5 -5
  24. package/dist/{access-service-CJwcDoQp.d.ts → access-service-Z-8CnmrW.d.ts} +3 -2
  25. package/dist/access-service.d.ts +1 -1
  26. package/dist/access-service.js +20 -20
  27. package/dist/access-surface-catalog.d.ts +1 -1
  28. package/dist/briefing.js +3 -3
  29. package/dist/{capsule-crypto-RAARDY3M.js → capsule-crypto-CZJSLEFG.js} +2 -2
  30. package/dist/causal-behavior.js +2 -2
  31. package/dist/causal-chain.js +2 -2
  32. package/dist/causal-consolidation.js +8 -8
  33. package/dist/causal-retrieval.js +2 -2
  34. package/dist/causal-trajectory-graph.js +2 -2
  35. package/dist/causal-trajectory.js +1 -1
  36. package/dist/{chunk-EEIROWEG.js → chunk-2BFLDH5F.js} +2 -2
  37. package/dist/{chunk-UPSAT4PY.js → chunk-2MR3MFQB.js} +2 -2
  38. package/dist/{chunk-IZP62CEF.js → chunk-3QLPBCFG.js} +4 -4
  39. package/dist/{chunk-LNQUAJNC.js → chunk-437S3G37.js} +1 -1
  40. package/dist/{chunk-4L2KHKLY.js → chunk-6C6BBBCI.js} +271 -197
  41. package/dist/chunk-6C6BBBCI.js.map +1 -0
  42. package/dist/{chunk-6FYRCQ7K.js → chunk-7I2QTC2K.js} +2 -2
  43. package/dist/{chunk-RQXVVENY.js → chunk-7ME6VBF7.js} +69 -69
  44. package/dist/{chunk-AGNBY3VG.js → chunk-APJQ6UEA.js} +4 -4
  45. package/dist/{chunk-GI7PRVMV.js → chunk-CJDGB3EG.js} +2 -2
  46. package/dist/{chunk-IR6ATBUI.js → chunk-CQCCVJZO.js} +2 -2
  47. package/dist/{chunk-5PHPNUZN.js → chunk-DUQHQ3TL.js} +286 -230
  48. package/dist/chunk-DUQHQ3TL.js.map +1 -0
  49. package/dist/{chunk-XROP4ZHB.js → chunk-EHQ5HO5T.js} +4 -4
  50. package/dist/{chunk-AINK3KDE.js → chunk-EOTTKLPJ.js} +2 -2
  51. package/dist/{chunk-KTULX4YO.js → chunk-FLVL4L3L.js} +4 -4
  52. package/dist/{chunk-6A76LSX6.js → chunk-HO3VUUGI.js} +2 -2
  53. package/dist/{chunk-2NTWULEQ.js → chunk-ILB2MFTS.js} +2 -2
  54. package/dist/{chunk-FBS6V6LJ.js → chunk-IWI2ICTF.js} +4 -4
  55. package/dist/{chunk-FFPH47IX.js → chunk-K2DJQOE3.js} +2 -2
  56. package/dist/{chunk-6DMTYBNY.js → chunk-LQDA67TT.js} +2 -2
  57. package/dist/{chunk-7HJ3XBMD.js → chunk-LX4ZWRGD.js} +2 -2
  58. package/dist/{chunk-CYWCKFCL.js → chunk-M5QKGHCR.js} +5 -5
  59. package/dist/{chunk-QVQG7W2H.js → chunk-ODC4HOUZ.js} +2 -2
  60. package/dist/{chunk-ZG2WP425.js → chunk-ORAK3LNZ.js} +2 -2
  61. package/dist/{chunk-LVMVYLNB.js → chunk-PJ5AG6D4.js} +2 -2
  62. package/dist/{chunk-GIJDW2YO.js → chunk-QEAOKI4U.js} +3 -3
  63. package/dist/{chunk-MQQO56LN.js → chunk-QJUZAUAD.js} +30 -30
  64. package/dist/{chunk-SUN6XVNI.js → chunk-QKOTOUVR.js} +3 -3
  65. package/dist/{chunk-XKUYMM34.js → chunk-RF6BZHOD.js} +2 -2
  66. package/dist/{chunk-I65IQD7Y.js → chunk-RQNV75HR.js} +2 -2
  67. package/dist/{chunk-ZE7GJ76H.js → chunk-SQNED75R.js} +1 -1
  68. package/dist/{chunk-PHWBKQHK.js → chunk-TMXTQCJ7.js} +8 -8
  69. package/dist/{chunk-PMBWVQ7D.js → chunk-TQT3USCG.js} +2 -2
  70. package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
  71. package/dist/{chunk-T3UW6NRQ.js → chunk-TVWF3GNF.js} +7 -7
  72. package/dist/{chunk-IF6BTZCB.js → chunk-XQT5GSWU.js} +2 -2
  73. package/dist/{chunk-PXHY2E3S.js → chunk-YQYG24C6.js} +4 -4
  74. package/dist/{chunk-L5DU7THE.js → chunk-ZFYML7C3.js} +2 -2
  75. package/dist/{chunk-ZL3Y3ZTQ.js → chunk-ZLOD23PD.js} +5 -5
  76. package/dist/{chunk-CTGK5VK3.js → chunk-ZQNCHPTZ.js} +2 -2
  77. package/dist/{cli-cZyANcmY.d.ts → cli-C5pCwszN.d.ts} +1 -1
  78. package/dist/cli.d.ts +2 -2
  79. package/dist/cli.js +46 -46
  80. package/dist/compounding/engine.js +3 -3
  81. package/dist/connectors/codex-materialize-runner.js +3 -3
  82. package/dist/connectors/index.js +3 -3
  83. package/dist/contradiction/index.js +4 -4
  84. package/dist/entity-retrieval.js +3 -3
  85. package/dist/{first-start-migration-RYY7PPGF.js → first-start-migration-DY6YCRC2.js} +4 -4
  86. package/dist/{graph-edge-decay-U2GKDAVM.js → graph-edge-decay-RXUCJIN4.js} +4 -4
  87. package/dist/graph-edge-decay-RXUCJIN4.js.map +1 -0
  88. package/dist/graph-snapshot.js +3 -3
  89. package/dist/graph.js +2 -2
  90. package/dist/index.d.ts +2 -2
  91. package/dist/index.js +85 -85
  92. package/dist/lcm/index.js +3 -3
  93. package/dist/maintenance/memory-governance.js +3 -3
  94. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  95. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  96. package/dist/mcp-memory-inspector-app.d.ts +1 -1
  97. package/dist/namespaces/migrate.js +7 -7
  98. package/dist/namespaces/search.js +3 -3
  99. package/dist/namespaces/storage.js +3 -3
  100. package/dist/operator-toolkit.js +12 -12
  101. package/dist/orchestration/maintenance.js +5 -5
  102. package/dist/orchestrator.js +70 -70
  103. package/dist/schemas.d.ts +90 -90
  104. package/dist/search/factory.js +2 -2
  105. package/dist/search/index.js +4 -4
  106. package/dist/semantic-consolidation.js +4 -4
  107. package/dist/semantic-rule-promotion.js +3 -3
  108. package/dist/semantic-rule-verifier.js +3 -3
  109. package/dist/shared-context/manager.d.ts +8 -8
  110. package/dist/storage.d.ts +3 -4
  111. package/dist/storage.js +2 -2
  112. package/dist/transfer/backup.js +3 -3
  113. package/dist/transfer/capsule-export.js +4 -4
  114. package/dist/transfer/capsule-import.js +3 -3
  115. package/dist/transfer/import-sqlite.js +2 -2
  116. package/dist/transfer/types.d.ts +66 -66
  117. package/dist/verified-recall.js +3 -3
  118. package/package.json +2 -2
  119. package/src/access-identity-continuity-surface.test.ts +374 -0
  120. package/src/access-identity-continuity-surface.ts +322 -0
  121. package/src/access-service.ts +23 -171
  122. package/src/storage/identity-continuity-store.ts +223 -0
  123. package/src/storage.ts +25 -142
  124. package/dist/chunk-4L2KHKLY.js.map +0 -1
  125. package/dist/chunk-5PHPNUZN.js.map +0 -1
  126. /package/dist/{capsule-crypto-RAARDY3M.js.map → access-identity-continuity-surface.js.map} +0 -0
  127. /package/dist/{graph-edge-decay-U2GKDAVM.js.map → capsule-crypto-CZJSLEFG.js.map} +0 -0
  128. /package/dist/{chunk-EEIROWEG.js.map → chunk-2BFLDH5F.js.map} +0 -0
  129. /package/dist/{chunk-UPSAT4PY.js.map → chunk-2MR3MFQB.js.map} +0 -0
  130. /package/dist/{chunk-IZP62CEF.js.map → chunk-3QLPBCFG.js.map} +0 -0
  131. /package/dist/{chunk-LNQUAJNC.js.map → chunk-437S3G37.js.map} +0 -0
  132. /package/dist/{chunk-6FYRCQ7K.js.map → chunk-7I2QTC2K.js.map} +0 -0
  133. /package/dist/{chunk-RQXVVENY.js.map → chunk-7ME6VBF7.js.map} +0 -0
  134. /package/dist/{chunk-AGNBY3VG.js.map → chunk-APJQ6UEA.js.map} +0 -0
  135. /package/dist/{chunk-GI7PRVMV.js.map → chunk-CJDGB3EG.js.map} +0 -0
  136. /package/dist/{chunk-IR6ATBUI.js.map → chunk-CQCCVJZO.js.map} +0 -0
  137. /package/dist/{chunk-XROP4ZHB.js.map → chunk-EHQ5HO5T.js.map} +0 -0
  138. /package/dist/{chunk-AINK3KDE.js.map → chunk-EOTTKLPJ.js.map} +0 -0
  139. /package/dist/{chunk-KTULX4YO.js.map → chunk-FLVL4L3L.js.map} +0 -0
  140. /package/dist/{chunk-6A76LSX6.js.map → chunk-HO3VUUGI.js.map} +0 -0
  141. /package/dist/{chunk-2NTWULEQ.js.map → chunk-ILB2MFTS.js.map} +0 -0
  142. /package/dist/{chunk-FBS6V6LJ.js.map → chunk-IWI2ICTF.js.map} +0 -0
  143. /package/dist/{chunk-FFPH47IX.js.map → chunk-K2DJQOE3.js.map} +0 -0
  144. /package/dist/{chunk-6DMTYBNY.js.map → chunk-LQDA67TT.js.map} +0 -0
  145. /package/dist/{chunk-7HJ3XBMD.js.map → chunk-LX4ZWRGD.js.map} +0 -0
  146. /package/dist/{chunk-CYWCKFCL.js.map → chunk-M5QKGHCR.js.map} +0 -0
  147. /package/dist/{chunk-QVQG7W2H.js.map → chunk-ODC4HOUZ.js.map} +0 -0
  148. /package/dist/{chunk-ZG2WP425.js.map → chunk-ORAK3LNZ.js.map} +0 -0
  149. /package/dist/{chunk-LVMVYLNB.js.map → chunk-PJ5AG6D4.js.map} +0 -0
  150. /package/dist/{chunk-GIJDW2YO.js.map → chunk-QEAOKI4U.js.map} +0 -0
  151. /package/dist/{chunk-MQQO56LN.js.map → chunk-QJUZAUAD.js.map} +0 -0
  152. /package/dist/{chunk-SUN6XVNI.js.map → chunk-QKOTOUVR.js.map} +0 -0
  153. /package/dist/{chunk-XKUYMM34.js.map → chunk-RF6BZHOD.js.map} +0 -0
  154. /package/dist/{chunk-I65IQD7Y.js.map → chunk-RQNV75HR.js.map} +0 -0
  155. /package/dist/{chunk-ZE7GJ76H.js.map → chunk-SQNED75R.js.map} +0 -0
  156. /package/dist/{chunk-PHWBKQHK.js.map → chunk-TMXTQCJ7.js.map} +0 -0
  157. /package/dist/{chunk-PMBWVQ7D.js.map → chunk-TQT3USCG.js.map} +0 -0
  158. /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
  159. /package/dist/{chunk-T3UW6NRQ.js.map → chunk-TVWF3GNF.js.map} +0 -0
  160. /package/dist/{chunk-IF6BTZCB.js.map → chunk-XQT5GSWU.js.map} +0 -0
  161. /package/dist/{chunk-PXHY2E3S.js.map → chunk-YQYG24C6.js.map} +0 -0
  162. /package/dist/{chunk-L5DU7THE.js.map → chunk-ZFYML7C3.js.map} +0 -0
  163. /package/dist/{chunk-ZL3Y3ZTQ.js.map → chunk-ZLOD23PD.js.map} +0 -0
  164. /package/dist/{chunk-CTGK5VK3.js.map → chunk-ZQNCHPTZ.js.map} +0 -0
  165. /package/dist/{first-start-migration-RYY7PPGF.js.map → first-start-migration-DY6YCRC2.js.map} +0 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Identity-continuity persistence extracted from StorageManager.
3
+ *
4
+ * The host keeps the public API while this store owns anchors, incidents,
5
+ * audits, and improvement-loop files. Dependencies stay live through
6
+ * `selfDeps`, preserving secure-store and catalog-write behavior.
7
+ */
8
+
9
+ import { readdir } from "node:fs/promises";
10
+ import path from "node:path";
11
+ import {
12
+ closeContinuityIncidentRecord,
13
+ createContinuityIncidentRecord,
14
+ parseContinuityImprovementLoops,
15
+ parseContinuityIncident,
16
+ reviewContinuityLoopInMarkdown,
17
+ serializeContinuityIncident,
18
+ upsertContinuityLoopInMarkdown,
19
+ } from "../identity-continuity.js";
20
+ import { SecureStoreLockedError } from "../secure-store/secure-fs.js";
21
+ import type {
22
+ ContinuityImprovementLoop,
23
+ ContinuityIncidentCloseInput,
24
+ ContinuityIncidentOpenInput,
25
+ ContinuityIncidentRecord,
26
+ ContinuityLoopReviewInput,
27
+ ContinuityLoopUpsertInput,
28
+ } from "../types.js";
29
+ import { isErrnoCode } from "../utils/errno.js";
30
+
31
+ export interface IdentityContinuityStoreDeps {
32
+ ensureDirectories(): Promise<void>;
33
+ generateId(prefix?: string): string;
34
+ readonly identityAnchorPath: string;
35
+ readonly identityIncidentsDir: string;
36
+ readonly identityAuditsWeeklyDir: string;
37
+ readonly identityAuditsMonthlyDir: string;
38
+ readonly identityImprovementLoopsPath: string;
39
+ readStorageSecureFile(filePath: string): Promise<string>;
40
+ writeStorageSecureFile(filePath: string, content: string | Buffer): Promise<void>;
41
+ }
42
+
43
+ export class IdentityContinuityStore {
44
+ constructor(private readonly deps: IdentityContinuityStoreDeps) {}
45
+
46
+ async writeIdentityAnchor(content: string): Promise<void> {
47
+ await this.deps.ensureDirectories();
48
+ await this.deps.writeStorageSecureFile(this.deps.identityAnchorPath, content);
49
+ }
50
+
51
+ async readIdentityAnchor(): Promise<string | null> {
52
+ try {
53
+ return await this.deps.readStorageSecureFile(this.deps.identityAnchorPath);
54
+ } catch (err) {
55
+ if (!isErrnoCode(err, "ENOENT")) throw err;
56
+ return null;
57
+ }
58
+ }
59
+
60
+ async appendContinuityIncident(input: ContinuityIncidentOpenInput): Promise<ContinuityIncidentRecord> {
61
+ await this.deps.ensureDirectories();
62
+ const now = new Date();
63
+ const nowIso = now.toISOString();
64
+ const date = nowIso.slice(0, 10);
65
+ const id = this.deps.generateId("incident");
66
+ const incident = createContinuityIncidentRecord(id, input, nowIso);
67
+ const filePath = path.join(this.deps.identityIncidentsDir, `${date}-${id}.md`);
68
+ await this.deps.writeStorageSecureFile(filePath, serializeContinuityIncident(incident));
69
+ return { ...incident, filePath };
70
+ }
71
+
72
+ async readContinuityIncidents(
73
+ limit = 200,
74
+ state: "open" | "closed" | "all" = "all"
75
+ ): Promise<ContinuityIncidentRecord[]> {
76
+ const normalizedLimit = Number.isFinite(limit) ? Math.floor(limit) : 0;
77
+ const cappedLimit = Math.max(0, normalizedLimit);
78
+ if (cappedLimit === 0) return [];
79
+
80
+ try {
81
+ const candidates = await this.readContinuityIncidentFileNames();
82
+ const incidents: ContinuityIncidentRecord[] = [];
83
+
84
+ for (const file of candidates) {
85
+ if (incidents.length >= cappedLimit) break;
86
+ const filePath = path.join(this.deps.identityIncidentsDir, file);
87
+ try {
88
+ const raw = await this.deps.readStorageSecureFile(filePath);
89
+ const parsed = parseContinuityIncident(raw);
90
+ if (!parsed) continue;
91
+ if (state !== "all" && parsed.state !== state) continue;
92
+ incidents.push({ ...parsed, filePath });
93
+ } catch (err) {
94
+ if (err instanceof SecureStoreLockedError) throw err;
95
+ // Fail-open on malformed/missing files.
96
+ }
97
+ }
98
+ return incidents;
99
+ } catch (err) {
100
+ if (err instanceof SecureStoreLockedError) throw err;
101
+ if (!isErrnoCode(err, "ENOENT")) throw err;
102
+ return [];
103
+ }
104
+ }
105
+
106
+ async closeContinuityIncident(
107
+ id: string,
108
+ closure: ContinuityIncidentCloseInput
109
+ ): Promise<ContinuityIncidentRecord | null> {
110
+ const directFilePath = await this.findContinuityIncidentFilePathById(id);
111
+ const target = directFilePath ? await this.readContinuityIncidentFile(directFilePath) : null;
112
+ if (!target || !directFilePath) return null;
113
+ if (target.state === "closed") return target;
114
+
115
+ const closed = closeContinuityIncidentRecord(target, closure, new Date().toISOString());
116
+ await this.deps.writeStorageSecureFile(directFilePath, serializeContinuityIncident(closed));
117
+ return { ...closed, filePath: directFilePath };
118
+ }
119
+
120
+ async writeIdentityAudit(period: "weekly" | "monthly", key: string, content: string): Promise<string> {
121
+ await this.deps.ensureDirectories();
122
+ const safeKey = this.sanitizeIdentityAuditKey(key);
123
+ const dir = period === "weekly" ? this.deps.identityAuditsWeeklyDir : this.deps.identityAuditsMonthlyDir;
124
+ const filePath = path.join(dir, `${safeKey}.md`);
125
+ await this.deps.writeStorageSecureFile(filePath, content);
126
+ return filePath;
127
+ }
128
+
129
+ async readIdentityAudit(period: "weekly" | "monthly", key: string): Promise<string | null> {
130
+ try {
131
+ const safeKey = this.sanitizeIdentityAuditKey(key);
132
+ const dir = period === "weekly" ? this.deps.identityAuditsWeeklyDir : this.deps.identityAuditsMonthlyDir;
133
+ return await this.deps.readStorageSecureFile(path.join(dir, `${safeKey}.md`));
134
+ } catch (err) {
135
+ if (err instanceof Error && err.message === "Invalid identity audit key") return null;
136
+ if (!isErrnoCode(err, "ENOENT")) throw err;
137
+ return null;
138
+ }
139
+ }
140
+
141
+ async writeIdentityImprovementLoops(content: string): Promise<void> {
142
+ await this.deps.ensureDirectories();
143
+ await this.deps.writeStorageSecureFile(this.deps.identityImprovementLoopsPath, content);
144
+ }
145
+
146
+ async readIdentityImprovementLoops(): Promise<string | null> {
147
+ try {
148
+ return await this.deps.readStorageSecureFile(this.deps.identityImprovementLoopsPath);
149
+ } catch (err) {
150
+ if (!isErrnoCode(err, "ENOENT")) throw err;
151
+ return null;
152
+ }
153
+ }
154
+
155
+ async readIdentityImprovementLoopRegister(): Promise<ContinuityImprovementLoop[]> {
156
+ const raw = await this.readIdentityImprovementLoops();
157
+ if (!raw) return [];
158
+ return parseContinuityImprovementLoops(raw);
159
+ }
160
+
161
+ async upsertIdentityImprovementLoop(input: ContinuityLoopUpsertInput): Promise<ContinuityImprovementLoop> {
162
+ const nowIso = new Date().toISOString();
163
+ const raw = await this.readIdentityImprovementLoops();
164
+ const { markdown, loop } = upsertContinuityLoopInMarkdown(raw, input, nowIso);
165
+ await this.writeIdentityImprovementLoops(markdown);
166
+ return loop;
167
+ }
168
+
169
+ async reviewIdentityImprovementLoop(
170
+ id: string,
171
+ input: ContinuityLoopReviewInput
172
+ ): Promise<ContinuityImprovementLoop | null> {
173
+ const raw = await this.readIdentityImprovementLoops();
174
+ const { markdown, loop } = reviewContinuityLoopInMarkdown(raw, id, input, new Date().toISOString());
175
+ if (!loop) return null;
176
+ await this.writeIdentityImprovementLoops(markdown);
177
+ return loop;
178
+ }
179
+
180
+ private async readContinuityIncidentFileNames(): Promise<string[]> {
181
+ const files = await readdir(this.deps.identityIncidentsDir);
182
+ return files
183
+ .filter((file) => file.endsWith(".md"))
184
+ .sort()
185
+ .reverse();
186
+ }
187
+
188
+ private async readContinuityIncidentFile(filePath: string): Promise<ContinuityIncidentRecord | null> {
189
+ try {
190
+ const raw = await this.deps.readStorageSecureFile(filePath);
191
+ const parsed = parseContinuityIncident(raw);
192
+ return parsed ? { ...parsed, filePath } : null;
193
+ } catch (err) {
194
+ if (err instanceof SecureStoreLockedError) throw err;
195
+ return null;
196
+ }
197
+ }
198
+
199
+ private async findContinuityIncidentFilePathById(id: string): Promise<string | null> {
200
+ const fileNames = await this.readContinuityIncidentFileNames();
201
+ const directMatch = fileNames.find((name) => name.endsWith(`-${id}.md`));
202
+ if (directMatch) {
203
+ const directPath = path.join(this.deps.identityIncidentsDir, directMatch);
204
+ const parsed = await this.readContinuityIncidentFile(directPath);
205
+ if (parsed?.id === id) return directPath;
206
+ }
207
+
208
+ for (const fileName of fileNames) {
209
+ const filePath = path.join(this.deps.identityIncidentsDir, fileName);
210
+ const parsed = await this.readContinuityIncidentFile(filePath);
211
+ if (parsed?.id === id) return filePath;
212
+ }
213
+ return null;
214
+ }
215
+
216
+ private sanitizeIdentityAuditKey(key: string): string {
217
+ const trimmed = key.trim();
218
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(trimmed) || trimmed.includes("..")) {
219
+ throw new Error("Invalid identity audit key");
220
+ }
221
+ return trimmed;
222
+ }
223
+ }
package/src/storage.ts CHANGED
@@ -7,6 +7,7 @@ import { log } from "./logger.js";
7
7
  import { MemoryReadStore } from "./storage/memory-read-store.js";
8
8
  import { selfDeps } from "./orchestration/self-deps.js";
9
9
  import { EntityStore } from "./storage/entity-store.js";
10
+ import { IdentityContinuityStore } from "./storage/identity-continuity-store.js";
10
11
  import { isErrnoCode } from "./utils/errno.js";
11
12
  import { RECALL_FALLBACK_DIRS, getCategoryDir, categoryDirName } from "./utils/category-dir.js";
12
13
  import { assertPathInsideRoot } from "./utils/path-containment.js";
@@ -113,15 +114,6 @@ import {
113
114
  normalizeProjectionPreview,
114
115
  normalizeProjectionTags,
115
116
  } from "./memory-projection-format.js";
116
- import {
117
- closeContinuityIncidentRecord,
118
- createContinuityIncidentRecord,
119
- parseContinuityIncident,
120
- parseContinuityImprovementLoops,
121
- reviewContinuityLoopInMarkdown,
122
- serializeContinuityIncident,
123
- upsertContinuityLoopInMarkdown,
124
- } from "./identity-continuity.js";
125
117
  import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
126
118
  // stripCitation import removed: legacy rebuild fallback was replaced by a
127
119
  // skip-with-warning strategy (Finding 1 — Uhol). See ensureFactHashIndexAuthoritative.
@@ -2560,6 +2552,18 @@ export class StorageManager {
2560
2552
  return this._entityStore;
2561
2553
  }
2562
2554
 
2555
+ /** IdentityContinuityStore (storage.ts decomposition). Lazy; selfDeps live wiring. */
2556
+ private _identityContinuityStore: IdentityContinuityStore | undefined;
2557
+
2558
+ private get identityContinuityStore(): IdentityContinuityStore {
2559
+ if (!this._identityContinuityStore) {
2560
+ this._identityContinuityStore = new IdentityContinuityStore(
2561
+ selfDeps<ConstructorParameters<typeof IdentityContinuityStore>[0]>(this),
2562
+ );
2563
+ }
2564
+ return this._identityContinuityStore;
2565
+ }
2566
+
2563
2567
  get dir(): string {
2564
2568
  return this.baseDir;
2565
2569
  }
@@ -5355,137 +5359,60 @@ export class StorageManager {
5355
5359
  }
5356
5360
 
5357
5361
  async writeIdentityAnchor(content: string): Promise<void> {
5358
- await this.ensureDirectories();
5359
- await this.writeStorageSecureFile(this.identityAnchorPath, content);
5362
+ return this.identityContinuityStore.writeIdentityAnchor(content);
5360
5363
  }
5361
5364
 
5362
5365
  async readIdentityAnchor(): Promise<string | null> {
5363
- try {
5364
- return await this.readStorageSecureFile(this.identityAnchorPath);
5365
- } catch (err) {
5366
- if (!isErrnoCode(err, "ENOENT")) throw err;
5367
- return null;
5368
- }
5366
+ return this.identityContinuityStore.readIdentityAnchor();
5369
5367
  }
5370
5368
 
5371
5369
  async appendContinuityIncident(input: ContinuityIncidentOpenInput): Promise<ContinuityIncidentRecord> {
5372
- await this.ensureDirectories();
5373
- const now = new Date();
5374
- const nowIso = now.toISOString();
5375
- const date = nowIso.slice(0, 10);
5376
- const id = this.generateId("incident");
5377
- const incident = createContinuityIncidentRecord(id, input, nowIso);
5378
- const filePath = path.join(this.identityIncidentsDir, `${date}-${id}.md`);
5379
- await this.writeStorageSecureFile(filePath, serializeContinuityIncident(incident));
5380
- return { ...incident, filePath };
5370
+ return this.identityContinuityStore.appendContinuityIncident(input);
5381
5371
  }
5382
5372
 
5383
5373
  async readContinuityIncidents(
5384
5374
  limit: number = 200,
5385
5375
  state: "open" | "closed" | "all" = "all",
5386
5376
  ): Promise<ContinuityIncidentRecord[]> {
5387
- const normalizedLimit = Number.isFinite(limit) ? Math.floor(limit) : 0;
5388
- const cappedLimit = Math.max(0, normalizedLimit);
5389
- if (cappedLimit === 0) return [];
5390
-
5391
- try {
5392
- const candidates = await this.readContinuityIncidentFileNames();
5393
- const incidents: ContinuityIncidentRecord[] = [];
5394
-
5395
- for (const file of candidates) {
5396
- if (incidents.length >= cappedLimit) break;
5397
- const filePath = path.join(this.identityIncidentsDir, file);
5398
- try {
5399
- const raw = await this.readStorageSecureFile(filePath);
5400
- const parsed = parseContinuityIncident(raw);
5401
- if (!parsed) continue;
5402
- if (state !== "all" && parsed.state !== state) continue;
5403
- incidents.push({ ...parsed, filePath });
5404
- } catch (err) {
5405
- if (err instanceof SecureStoreLockedError) throw err;
5406
- // Fail-open on malformed/missing files.
5407
- }
5408
- }
5409
- return incidents;
5410
- } catch (err) {
5411
- if (err instanceof SecureStoreLockedError) throw err;
5412
- if (!isErrnoCode(err, "ENOENT")) throw err;
5413
- return [];
5414
- }
5377
+ return this.identityContinuityStore.readContinuityIncidents(limit, state);
5415
5378
  }
5416
5379
 
5417
5380
  async closeContinuityIncident(
5418
5381
  id: string,
5419
5382
  closure: ContinuityIncidentCloseInput,
5420
5383
  ): Promise<ContinuityIncidentRecord | null> {
5421
- const directFilePath = await this.findContinuityIncidentFilePathById(id);
5422
- const target = directFilePath ? await this.readContinuityIncidentFile(directFilePath) : null;
5423
- if (!target || !directFilePath) return null;
5424
- if (target.state === "closed") return target;
5425
-
5426
- const closed = closeContinuityIncidentRecord(target, closure, new Date().toISOString());
5427
- await this.writeStorageSecureFile(directFilePath, serializeContinuityIncident(closed));
5428
- return { ...closed, filePath: directFilePath };
5384
+ return this.identityContinuityStore.closeContinuityIncident(id, closure);
5429
5385
  }
5430
5386
 
5431
5387
  async writeIdentityAudit(period: "weekly" | "monthly", key: string, content: string): Promise<string> {
5432
- await this.ensureDirectories();
5433
- const safeKey = this.sanitizeIdentityAuditKey(key);
5434
- const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
5435
- const filePath = path.join(dir, `${safeKey}.md`);
5436
- await this.writeStorageSecureFile(filePath, content);
5437
- return filePath;
5388
+ return this.identityContinuityStore.writeIdentityAudit(period, key, content);
5438
5389
  }
5439
5390
 
5440
5391
  async readIdentityAudit(period: "weekly" | "monthly", key: string): Promise<string | null> {
5441
- try {
5442
- const safeKey = this.sanitizeIdentityAuditKey(key);
5443
- const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
5444
- return await this.readStorageSecureFile(path.join(dir, `${safeKey}.md`));
5445
- } catch (err) {
5446
- if (err instanceof Error && err.message === "Invalid identity audit key") return null;
5447
- if (!isErrnoCode(err, "ENOENT")) throw err;
5448
- return null;
5449
- }
5392
+ return this.identityContinuityStore.readIdentityAudit(period, key);
5450
5393
  }
5451
5394
 
5452
5395
  async writeIdentityImprovementLoops(content: string): Promise<void> {
5453
- await this.ensureDirectories();
5454
- await this.writeStorageSecureFile(this.identityImprovementLoopsPath, content);
5396
+ return this.identityContinuityStore.writeIdentityImprovementLoops(content);
5455
5397
  }
5456
5398
 
5457
5399
  async readIdentityImprovementLoops(): Promise<string | null> {
5458
- try {
5459
- return await this.readStorageSecureFile(this.identityImprovementLoopsPath);
5460
- } catch (err) {
5461
- if (!isErrnoCode(err, "ENOENT")) throw err;
5462
- return null;
5463
- }
5400
+ return this.identityContinuityStore.readIdentityImprovementLoops();
5464
5401
  }
5465
5402
 
5466
5403
  async readIdentityImprovementLoopRegister(): Promise<ContinuityImprovementLoop[]> {
5467
- const raw = await this.readIdentityImprovementLoops();
5468
- if (!raw) return [];
5469
- return parseContinuityImprovementLoops(raw);
5404
+ return this.identityContinuityStore.readIdentityImprovementLoopRegister();
5470
5405
  }
5471
5406
 
5472
5407
  async upsertIdentityImprovementLoop(input: ContinuityLoopUpsertInput): Promise<ContinuityImprovementLoop> {
5473
- const nowIso = new Date().toISOString();
5474
- const raw = await this.readIdentityImprovementLoops();
5475
- const { markdown, loop } = upsertContinuityLoopInMarkdown(raw, input, nowIso);
5476
- await this.writeIdentityImprovementLoops(markdown);
5477
- return loop;
5408
+ return this.identityContinuityStore.upsertIdentityImprovementLoop(input);
5478
5409
  }
5479
5410
 
5480
5411
  async reviewIdentityImprovementLoop(
5481
5412
  id: string,
5482
5413
  input: ContinuityLoopReviewInput,
5483
5414
  ): Promise<ContinuityImprovementLoop | null> {
5484
- const raw = await this.readIdentityImprovementLoops();
5485
- const { markdown, loop } = reviewContinuityLoopInMarkdown(raw, id, input, new Date().toISOString());
5486
- if (!loop) return null;
5487
- await this.writeIdentityImprovementLoops(markdown);
5488
- return loop;
5415
+ return this.identityContinuityStore.reviewIdentityImprovementLoop(id, input);
5489
5416
  }
5490
5417
 
5491
5418
  // ---------------------------------------------------------------------------
@@ -5498,50 +5425,6 @@ export class StorageManager {
5498
5425
  return `${prefix}-${ts}-${rand}`;
5499
5426
  }
5500
5427
 
5501
- private async readContinuityIncidentFileNames(): Promise<string[]> {
5502
- const files = await readdir(this.identityIncidentsDir);
5503
- return files
5504
- .filter((file) => file.endsWith(".md"))
5505
- .sort()
5506
- .reverse();
5507
- }
5508
-
5509
- private async readContinuityIncidentFile(filePath: string): Promise<ContinuityIncidentRecord | null> {
5510
- try {
5511
- const raw = await this.readStorageSecureFile(filePath);
5512
- const parsed = parseContinuityIncident(raw);
5513
- return parsed ? { ...parsed, filePath } : null;
5514
- } catch (err) {
5515
- if (err instanceof SecureStoreLockedError) throw err;
5516
- return null;
5517
- }
5518
- }
5519
-
5520
- private async findContinuityIncidentFilePathById(id: string): Promise<string | null> {
5521
- const fileNames = await this.readContinuityIncidentFileNames();
5522
- const directMatch = fileNames.find((name) => name.endsWith(`-${id}.md`));
5523
- if (directMatch) {
5524
- const directPath = path.join(this.identityIncidentsDir, directMatch);
5525
- const parsed = await this.readContinuityIncidentFile(directPath);
5526
- if (parsed?.id === id) return directPath;
5527
- }
5528
-
5529
- for (const fileName of fileNames) {
5530
- const filePath = path.join(this.identityIncidentsDir, fileName);
5531
- const parsed = await this.readContinuityIncidentFile(filePath);
5532
- if (parsed?.id === id) return filePath;
5533
- }
5534
- return null;
5535
- }
5536
-
5537
- private sanitizeIdentityAuditKey(key: string): string {
5538
- const trimmed = key.trim();
5539
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(trimmed) || trimmed.includes("..")) {
5540
- throw new Error("Invalid identity audit key");
5541
- }
5542
- return trimmed;
5543
- }
5544
-
5545
5428
  async writeQuestion(
5546
5429
  question: string,
5547
5430
  context: string,