@dmsdc-ai/aigentry-brain 0.0.1

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 (136) hide show
  1. package/bin/aigentry-brain-mcp.mjs +17 -0
  2. package/bin/aigentry-brain-setup.mjs +17 -0
  3. package/bin/aigentry-brain.mjs +17 -0
  4. package/dist/audit/AuditTrail.d.ts +15 -0
  5. package/dist/audit/AuditTrail.js +43 -0
  6. package/dist/audit/AuditTrail.js.map +1 -0
  7. package/dist/backend/GitBackend.d.ts +27 -0
  8. package/dist/backend/GitBackend.js +201 -0
  9. package/dist/backend/GitBackend.js.map +1 -0
  10. package/dist/backend/SyncBackend.d.ts +13 -0
  11. package/dist/backend/SyncBackend.js +2 -0
  12. package/dist/backend/SyncBackend.js.map +1 -0
  13. package/dist/cli/AutoUpdater.d.ts +7 -0
  14. package/dist/cli/AutoUpdater.js +275 -0
  15. package/dist/cli/AutoUpdater.js.map +1 -0
  16. package/dist/cli/ConfigStore.d.ts +17 -0
  17. package/dist/cli/ConfigStore.js +50 -0
  18. package/dist/cli/ConfigStore.js.map +1 -0
  19. package/dist/cli/GhIntegration.d.ts +55 -0
  20. package/dist/cli/GhIntegration.js +192 -0
  21. package/dist/cli/GhIntegration.js.map +1 -0
  22. package/dist/cli/Prerequisites.d.ts +48 -0
  23. package/dist/cli/Prerequisites.js +182 -0
  24. package/dist/cli/Prerequisites.js.map +1 -0
  25. package/dist/cli/SetupWizard.d.ts +26 -0
  26. package/dist/cli/SetupWizard.js +407 -0
  27. package/dist/cli/SetupWizard.js.map +1 -0
  28. package/dist/cli/braincli.d.ts +2 -0
  29. package/dist/cli/braincli.js +260 -0
  30. package/dist/cli/braincli.js.map +1 -0
  31. package/dist/cli/setup.d.ts +25 -0
  32. package/dist/cli/setup.js +238 -0
  33. package/dist/cli/setup.js.map +1 -0
  34. package/dist/context/ConfidenceGuard.d.ts +11 -0
  35. package/dist/context/ConfidenceGuard.js +32 -0
  36. package/dist/context/ConfidenceGuard.js.map +1 -0
  37. package/dist/context/ContextBudgetPolicy.d.ts +21 -0
  38. package/dist/context/ContextBudgetPolicy.js +136 -0
  39. package/dist/context/ContextBudgetPolicy.js.map +1 -0
  40. package/dist/context/ContextPacker.d.ts +30 -0
  41. package/dist/context/ContextPacker.js +219 -0
  42. package/dist/context/ContextPacker.js.map +1 -0
  43. package/dist/context/ContextRestoreService.d.ts +39 -0
  44. package/dist/context/ContextRestoreService.js +134 -0
  45. package/dist/context/ContextRestoreService.js.map +1 -0
  46. package/dist/contract/BrainContract.d.ts +93 -0
  47. package/dist/contract/BrainContract.js +245 -0
  48. package/dist/contract/BrainContract.js.map +1 -0
  49. package/dist/core/ContentDedup.d.ts +43 -0
  50. package/dist/core/ContentDedup.js +145 -0
  51. package/dist/core/ContentDedup.js.map +1 -0
  52. package/dist/core/EntityGraph.d.ts +66 -0
  53. package/dist/core/EntityGraph.js +196 -0
  54. package/dist/core/EntityGraph.js.map +1 -0
  55. package/dist/core/EntrySchema.d.ts +46 -0
  56. package/dist/core/EntrySchema.js +4 -0
  57. package/dist/core/EntrySchema.js.map +1 -0
  58. package/dist/core/GraphStore.d.ts +16 -0
  59. package/dist/core/GraphStore.js +29 -0
  60. package/dist/core/GraphStore.js.map +1 -0
  61. package/dist/core/MemoryDecay.d.ts +49 -0
  62. package/dist/core/MemoryDecay.js +113 -0
  63. package/dist/core/MemoryDecay.js.map +1 -0
  64. package/dist/core/ProfileFormat.d.ts +38 -0
  65. package/dist/core/ProfileFormat.js +254 -0
  66. package/dist/core/ProfileFormat.js.map +1 -0
  67. package/dist/core/ProfileManager.d.ts +80 -0
  68. package/dist/core/ProfileManager.js +269 -0
  69. package/dist/core/ProfileManager.js.map +1 -0
  70. package/dist/core/SchemaMigration.d.ts +12 -0
  71. package/dist/core/SchemaMigration.js +34 -0
  72. package/dist/core/SchemaMigration.js.map +1 -0
  73. package/dist/crypto/AgeCrypto.d.ts +26 -0
  74. package/dist/crypto/AgeCrypto.js +101 -0
  75. package/dist/crypto/AgeCrypto.js.map +1 -0
  76. package/dist/index.d.ts +38 -0
  77. package/dist/index.js +30 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/mcp/BrainMcpServer.d.ts +13 -0
  80. package/dist/mcp/BrainMcpServer.js +223 -0
  81. package/dist/mcp/BrainMcpServer.js.map +1 -0
  82. package/dist/mcp/cli.d.ts +2 -0
  83. package/dist/mcp/cli.js +107 -0
  84. package/dist/mcp/cli.js.map +1 -0
  85. package/dist/metrics/ResumeMetrics.d.ts +20 -0
  86. package/dist/metrics/ResumeMetrics.js +46 -0
  87. package/dist/metrics/ResumeMetrics.js.map +1 -0
  88. package/dist/peers/PeerStore.d.ts +17 -0
  89. package/dist/peers/PeerStore.js +65 -0
  90. package/dist/peers/PeerStore.js.map +1 -0
  91. package/dist/policy/PolicyEngine.d.ts +16 -0
  92. package/dist/policy/PolicyEngine.js +70 -0
  93. package/dist/policy/PolicyEngine.js.map +1 -0
  94. package/dist/policy/PolicyEnvelope.d.ts +7 -0
  95. package/dist/policy/PolicyEnvelope.js +2 -0
  96. package/dist/policy/PolicyEnvelope.js.map +1 -0
  97. package/dist/policy/RetentionParser.d.ts +6 -0
  98. package/dist/policy/RetentionParser.js +48 -0
  99. package/dist/policy/RetentionParser.js.map +1 -0
  100. package/dist/shared/AtomicWrite.d.ts +1 -0
  101. package/dist/shared/AtomicWrite.js +11 -0
  102. package/dist/shared/AtomicWrite.js.map +1 -0
  103. package/dist/shared/Clock.d.ts +11 -0
  104. package/dist/shared/Clock.js +17 -0
  105. package/dist/shared/Clock.js.map +1 -0
  106. package/dist/shared/Config.d.ts +15 -0
  107. package/dist/shared/Config.js +67 -0
  108. package/dist/shared/Config.js.map +1 -0
  109. package/dist/shared/Encryption.d.ts +19 -0
  110. package/dist/shared/Encryption.js +47 -0
  111. package/dist/shared/Encryption.js.map +1 -0
  112. package/dist/shared/Logger.d.ts +12 -0
  113. package/dist/shared/Logger.js +40 -0
  114. package/dist/shared/Logger.js.map +1 -0
  115. package/dist/shared/Mutex.d.ts +7 -0
  116. package/dist/shared/Mutex.js +34 -0
  117. package/dist/shared/Mutex.js.map +1 -0
  118. package/dist/shared/SetupErrors.d.ts +31 -0
  119. package/dist/shared/SetupErrors.js +155 -0
  120. package/dist/shared/SetupErrors.js.map +1 -0
  121. package/dist/status/SyncStatusStore.d.ts +17 -0
  122. package/dist/status/SyncStatusStore.js +48 -0
  123. package/dist/status/SyncStatusStore.js.map +1 -0
  124. package/dist/sync/AutoCommitter.d.ts +17 -0
  125. package/dist/sync/AutoCommitter.js +68 -0
  126. package/dist/sync/AutoCommitter.js.map +1 -0
  127. package/dist/sync/ConflictResolver.d.ts +7 -0
  128. package/dist/sync/ConflictResolver.js +56 -0
  129. package/dist/sync/ConflictResolver.js.map +1 -0
  130. package/dist/sync/PushPullManager.d.ts +23 -0
  131. package/dist/sync/PushPullManager.js +155 -0
  132. package/dist/sync/PushPullManager.js.map +1 -0
  133. package/dist/sync/SyncEngine.d.ts +43 -0
  134. package/dist/sync/SyncEngine.js +145 -0
  135. package/dist/sync/SyncEngine.js.map +1 -0
  136. package/package.json +65 -0
@@ -0,0 +1,254 @@
1
+ import yaml from 'js-yaml';
2
+ import { readFile, rename } from 'node:fs/promises';
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { atomicWrite } from '../shared/AtomicWrite.js';
5
+ import { migrateProfile, CURRENT_SCHEMA_VERSION } from './SchemaMigration.js';
6
+ import { getEncryptionMode, EncryptionMode } from '../shared/Encryption.js';
7
+ import { createLogger } from '../shared/Logger.js';
8
+ import { encryptBlock, decryptBlock, readKeyFile, AgeDecryptError, AgeKeyError, } from '../crypto/AgeCrypto.js';
9
+ const logger = createLogger('ProfileFormat');
10
+ const DEFAULT_PROFILE = {
11
+ version: CURRENT_SCHEMA_VERSION,
12
+ device_id: '',
13
+ last_sync: new Date().toISOString(),
14
+ entries: [],
15
+ };
16
+ function detectFormat(filePath) {
17
+ if (filePath.endsWith('.enc.json')) {
18
+ try {
19
+ const content = JSON.parse(readFileSync(filePath, 'utf-8'));
20
+ if (content._aigentry?.format === 'age-block')
21
+ return 'age-block-json';
22
+ return 'plaintext-json';
23
+ }
24
+ catch {
25
+ return 'plaintext-json';
26
+ }
27
+ }
28
+ // Legacy YAML
29
+ try {
30
+ const content = readFileSync(filePath, 'utf-8');
31
+ if (content.includes('sops:') && content.includes('ENC['))
32
+ return 'sops-yaml';
33
+ return 'plaintext-yaml';
34
+ }
35
+ catch {
36
+ return 'plaintext-yaml';
37
+ }
38
+ }
39
+ /** Convert MemoryProfile entries to a flat uuid→JSON map. */
40
+ function entriesToMap(entries) {
41
+ const map = {};
42
+ for (const entry of entries) {
43
+ map[entry.id] = JSON.stringify(entry);
44
+ }
45
+ return map;
46
+ }
47
+ /** Convert flat uuid→JSON map back to Entry array. */
48
+ function mapToEntries(map) {
49
+ const entries = [];
50
+ for (const json of Object.values(map)) {
51
+ try {
52
+ entries.push(JSON.parse(json));
53
+ }
54
+ catch {
55
+ logger.warn('Failed to parse entry JSON, skipping');
56
+ }
57
+ }
58
+ return entries;
59
+ }
60
+ export class ProfileFormat {
61
+ static serialize(profile) {
62
+ return yaml.dump(profile, {
63
+ indent: 2,
64
+ lineWidth: -1,
65
+ noRefs: true,
66
+ sortKeys: false,
67
+ });
68
+ }
69
+ static deserialize(content) {
70
+ const parsed = yaml.load(content);
71
+ if (!parsed || typeof parsed !== 'object') {
72
+ return { ...DEFAULT_PROFILE };
73
+ }
74
+ const profile = {
75
+ version: parsed.version ?? 1,
76
+ device_id: parsed.device_id ?? '',
77
+ last_sync: parsed.last_sync ?? new Date().toISOString(),
78
+ entries: Array.isArray(parsed.entries) ? parsed.entries : [],
79
+ };
80
+ return migrateProfile(profile);
81
+ }
82
+ /**
83
+ * Read profile from file, handling all formats:
84
+ * - age-block JSON (v2 encrypted)
85
+ * - plaintext JSON (v2 fallback)
86
+ * - plaintext YAML (legacy v1/v2)
87
+ * - sops YAML (legacy, read as plaintext — sops binary no longer used)
88
+ */
89
+ static async readFromFile(filePath) {
90
+ // Try new .enc.json format first
91
+ const encJsonPath = filePath.replace(/\.yaml$/, '.enc.json');
92
+ if (existsSync(encJsonPath)) {
93
+ return ProfileFormat.readEncryptedJson(encJsonPath);
94
+ }
95
+ // Fall back to legacy YAML
96
+ if (!existsSync(filePath)) {
97
+ return { ...DEFAULT_PROFILE, entries: [] };
98
+ }
99
+ const format = detectFormat(filePath);
100
+ if (format === 'sops-yaml') {
101
+ // Legacy sops-encrypted: read as plaintext (fields will have ENC[] markers)
102
+ logger.warn('Found sops-encrypted YAML — reading plaintext fields only. Re-save to migrate to age-block format.');
103
+ }
104
+ const content = await readFile(filePath, 'utf-8');
105
+ return ProfileFormat.deserialize(content);
106
+ }
107
+ /**
108
+ * Write profile to encrypted JSON format.
109
+ * Falls back to plaintext JSON if encryption fails (Invisible Security).
110
+ */
111
+ static async writeToFile(filePath, profile) {
112
+ const encJsonPath = filePath.replace(/\.yaml$/, '.enc.json');
113
+ const mode = getEncryptionMode();
114
+ if (mode === EncryptionMode.DISABLED) {
115
+ // Write plaintext JSON
116
+ const encProfile = {
117
+ _aigentry: {
118
+ version: 2,
119
+ format: 'plaintext-fallback',
120
+ recipients: [],
121
+ modified: new Date().toISOString(),
122
+ device_id: profile.device_id,
123
+ },
124
+ entries: entriesToMap(profile.entries),
125
+ };
126
+ await atomicWrite(encJsonPath, JSON.stringify(encProfile, null, 2));
127
+ return;
128
+ }
129
+ // ACTIVE mode: encrypt with age
130
+ try {
131
+ const keyPair = readKeyFile();
132
+ const encProfile = await ProfileFormat.encryptProfile(profile, [keyPair.recipient], encJsonPath);
133
+ await atomicWrite(encJsonPath, JSON.stringify(encProfile, null, 2));
134
+ }
135
+ catch (err) {
136
+ // Graceful fallback: save plaintext with warning
137
+ logger.warn('Encryption failed, saving as plaintext fallback', {
138
+ error: err instanceof Error ? err.message : String(err),
139
+ });
140
+ const encProfile = {
141
+ _aigentry: {
142
+ version: 2,
143
+ format: 'plaintext-fallback',
144
+ recipients: [],
145
+ modified: new Date().toISOString(),
146
+ device_id: profile.device_id,
147
+ },
148
+ entries: entriesToMap(profile.entries),
149
+ };
150
+ await atomicWrite(encJsonPath, JSON.stringify(encProfile, null, 2));
151
+ }
152
+ }
153
+ /** Read and decrypt an age-block JSON profile. */
154
+ static async readEncryptedJson(encJsonPath) {
155
+ const raw = await readFile(encJsonPath, 'utf-8');
156
+ const encProfile = JSON.parse(raw);
157
+ if (encProfile._aigentry.format === 'plaintext-fallback') {
158
+ // Plaintext entries — just parse JSON
159
+ return {
160
+ version: CURRENT_SCHEMA_VERSION,
161
+ device_id: encProfile._aigentry.device_id ?? '',
162
+ last_sync: encProfile._aigentry.modified,
163
+ entries: mapToEntries(encProfile.entries),
164
+ };
165
+ }
166
+ // Decrypt age-block entries
167
+ const mode = getEncryptionMode();
168
+ if (mode === EncryptionMode.DISABLED) {
169
+ // Cannot decrypt in disabled mode — return empty
170
+ logger.warn('Encryption disabled but found encrypted profile. Returning empty profile.');
171
+ return { ...DEFAULT_PROFILE, entries: [] };
172
+ }
173
+ try {
174
+ const keyPair = readKeyFile();
175
+ const decryptedMap = {};
176
+ for (const [id, ct] of Object.entries(encProfile.entries)) {
177
+ decryptedMap[id] = await decryptBlock(ct, keyPair.identity);
178
+ }
179
+ return {
180
+ version: CURRENT_SCHEMA_VERSION,
181
+ device_id: encProfile._aigentry.device_id ?? '',
182
+ last_sync: encProfile._aigentry.modified,
183
+ entries: mapToEntries(decryptedMap),
184
+ };
185
+ }
186
+ catch (err) {
187
+ if (err instanceof AgeDecryptError) {
188
+ logger.warn('Cannot decrypt profile — this device may need re-encryption from another device');
189
+ }
190
+ else if (err instanceof AgeKeyError) {
191
+ logger.warn('No age key found — returning empty profile');
192
+ }
193
+ else {
194
+ logger.warn('Profile decryption failed', {
195
+ error: err instanceof Error ? err.message : String(err),
196
+ });
197
+ }
198
+ return { ...DEFAULT_PROFILE, entries: [] };
199
+ }
200
+ }
201
+ /** Encrypt a MemoryProfile into EncryptedProfile format. Preserves existing recipients from file. */
202
+ static async encryptProfile(profile, localRecipients, encJsonPath) {
203
+ // Read existing recipients from file if it exists
204
+ let existingRecipients = [];
205
+ try {
206
+ const existing = JSON.parse(readFileSync(encJsonPath, 'utf-8'));
207
+ existingRecipients = existing._aigentry?.recipients ?? [];
208
+ }
209
+ catch { /* file doesn't exist yet */ }
210
+ // Merge recipients (deduplicate)
211
+ const allRecipients = [...new Set([...existingRecipients, ...localRecipients])];
212
+ const entryMap = entriesToMap(profile.entries);
213
+ const encryptedEntries = {};
214
+ for (const [id, plaintext] of Object.entries(entryMap)) {
215
+ encryptedEntries[id] = await encryptBlock(plaintext, allRecipients);
216
+ }
217
+ return {
218
+ _aigentry: {
219
+ version: 2,
220
+ format: 'age-block',
221
+ recipients: allRecipients,
222
+ modified: new Date().toISOString(),
223
+ device_id: profile.device_id,
224
+ },
225
+ entries: encryptedEntries,
226
+ };
227
+ }
228
+ /**
229
+ * Migrate legacy YAML to new enc.json format.
230
+ * Called after reading legacy format — backs up .yaml to .yaml.bak.
231
+ */
232
+ static async migrateLegacyYaml(yamlPath) {
233
+ if (!existsSync(yamlPath))
234
+ return;
235
+ const encJsonPath = yamlPath.replace(/\.yaml$/, '.enc.json');
236
+ if (existsSync(encJsonPath))
237
+ return; // Already migrated
238
+ try {
239
+ const content = await readFile(yamlPath, 'utf-8');
240
+ const profile = ProfileFormat.deserialize(content);
241
+ await ProfileFormat.writeToFile(yamlPath, profile);
242
+ // Backup original
243
+ const bakPath = yamlPath + '.bak';
244
+ await rename(yamlPath, bakPath);
245
+ logger.info('Migrated legacy YAML to enc.json', { backup: bakPath });
246
+ }
247
+ catch (err) {
248
+ logger.warn('Legacy migration failed — keeping YAML', {
249
+ error: err instanceof Error ? err.message : String(err),
250
+ });
251
+ }
252
+ }
253
+ }
254
+ //# sourceMappingURL=ProfileFormat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileFormat.js","sourceRoot":"","sources":["../../src/core/ProfileFormat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import type { Entry, MemoryProfile } from './EntrySchema.js';
2
+ import type { Clock } from '../shared/Clock.js';
3
+ import { type MemoryConfig } from './MemoryDecay.js';
4
+ import { type DedupConfig } from './ContentDedup.js';
5
+ /** Categories whose entries are permanently protected from auto-pruning. */
6
+ export declare const PROTECTED_CATEGORIES: Set<string>;
7
+ /** Maximum number of unprotected active entries before compact prunes oldest. */
8
+ export declare const MAX_UNPROTECTED_ENTRIES = 500;
9
+ export interface CompactResult {
10
+ removed: number;
11
+ remaining: number;
12
+ decayed: number;
13
+ deduped: number;
14
+ graphNodes: number;
15
+ graphEdges: number;
16
+ }
17
+ export declare class ProfileManager {
18
+ private mutex;
19
+ private clock;
20
+ private profilePath;
21
+ private deviceId;
22
+ private cachedProfile;
23
+ private memoryConfig;
24
+ private dedupConfig;
25
+ private graphPath;
26
+ constructor(profilePath: string, clock: Clock, deviceId?: string, options?: {
27
+ memoryConfig?: MemoryConfig;
28
+ dedupConfig?: DedupConfig;
29
+ graphPath?: string | null;
30
+ });
31
+ /** Force read from disk (internal). */
32
+ private loadFromDisk;
33
+ load(): Promise<MemoryProfile>;
34
+ save(entries: Entry[]): Promise<void>;
35
+ getAll(): Promise<Entry[]>;
36
+ getById(id: string): Promise<Entry | null>;
37
+ upsert(partial: Partial<Entry> & {
38
+ id?: string;
39
+ category: string;
40
+ content: string;
41
+ source: string;
42
+ }): Promise<Entry>;
43
+ /** Batch upsert — single read + N mutations + single write. */
44
+ upsertBatch(items: Array<Partial<Entry> & {
45
+ id?: string;
46
+ category: string;
47
+ content: string;
48
+ source: string;
49
+ }>): Promise<Entry[]>;
50
+ softDelete(id: string): Promise<boolean>;
51
+ /** Invalidate in-memory cache (call after external file changes, e.g. git pull). */
52
+ invalidateCache(): void;
53
+ /**
54
+ * Compact: 3-tier memory management pipeline.
55
+ *
56
+ * Stage 1 — Confidence decay (Tier 1): soft-delete entries whose effective
57
+ * confidence has dropped below the threshold.
58
+ * Stage 2 — Content dedup (Tier 2): soft-delete near-duplicate entries
59
+ * within each category using TF-IDF cosine similarity.
60
+ * Stage 3 — Entity graph scoring (Tier 3): boost hub-entry confidence
61
+ * based on cross-entry entity connectedness.
62
+ * Stage 4 — Enforce max entries (Tier 1 second pass): hard cap via
63
+ * soft-delete of lowest-scored entries.
64
+ * Stage 5 — Final cleanup: strip all tombstones.
65
+ *
66
+ * Protected categories (preference/identity/skill/habit) are NEVER auto-removed.
67
+ */
68
+ compact(): Promise<CompactResult>;
69
+ private pruneExpiredTombstones;
70
+ /**
71
+ * Prune unprotected active entries beyond MAX_UNPROTECTED_ENTRIES.
72
+ * Sort by (updated ASC, confidence ASC) — oldest + lowest confidence removed first.
73
+ * Protected category entries and tombstones are never touched.
74
+ */
75
+ private pruneExcessUnprotected;
76
+ getProfilePath(): string;
77
+ getDeviceId(): string;
78
+ }
79
+ export type { MemoryConfig, DecayConfig } from './MemoryDecay.js';
80
+ export type { DedupConfig } from './ContentDedup.js';
@@ -0,0 +1,269 @@
1
+ import { createEntryId } from './EntrySchema.js';
2
+ import { ProfileFormat } from './ProfileFormat.js';
3
+ import { Mutex } from '../shared/Mutex.js';
4
+ import { hostname } from 'node:os';
5
+ import { applyDecay, enforceMaxEntries, DEFAULT_CONFIG } from './MemoryDecay.js';
6
+ import { deduplicateEntries, DEFAULT_DEDUP_CONFIG } from './ContentDedup.js';
7
+ import { buildEntityGraph, scoreEntries, connectednessBoost, serializeGraph } from './EntityGraph.js';
8
+ import { atomicWrite } from '../shared/AtomicWrite.js';
9
+ const TOMBSTONE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days (sufficient for cross-machine sync)
10
+ /** Categories whose entries are permanently protected from auto-pruning. */
11
+ export const PROTECTED_CATEGORIES = new Set(['preference', 'identity', 'skill', 'habit']);
12
+ /** Maximum number of unprotected active entries before compact prunes oldest. */
13
+ export const MAX_UNPROTECTED_ENTRIES = 500;
14
+ export class ProfileManager {
15
+ mutex;
16
+ clock;
17
+ profilePath;
18
+ deviceId;
19
+ cachedProfile = null;
20
+ memoryConfig;
21
+ dedupConfig;
22
+ graphPath;
23
+ constructor(profilePath, clock, deviceId, options) {
24
+ this.profilePath = profilePath;
25
+ this.clock = clock;
26
+ this.mutex = new Mutex();
27
+ this.deviceId = deviceId ?? hostname();
28
+ this.memoryConfig = options?.memoryConfig ?? { ...DEFAULT_CONFIG };
29
+ this.dedupConfig = options?.dedupConfig ?? { ...DEFAULT_DEDUP_CONFIG };
30
+ this.graphPath = options?.graphPath !== undefined
31
+ ? options.graphPath
32
+ : profilePath.replace(/\.enc\.json$|\.yaml$/, '.graph.json');
33
+ }
34
+ /** Force read from disk (internal). */
35
+ async loadFromDisk() {
36
+ const profile = await ProfileFormat.readFromFile(this.profilePath);
37
+ profile.entries = this.pruneExpiredTombstones(profile.entries);
38
+ profile.entries = this.pruneExcessUnprotected(profile.entries);
39
+ profile.device_id = this.deviceId;
40
+ return profile;
41
+ }
42
+ async load() {
43
+ return this.mutex.runExclusive(async () => {
44
+ if (this.cachedProfile)
45
+ return this.cachedProfile;
46
+ const profile = await this.loadFromDisk();
47
+ this.cachedProfile = profile;
48
+ return profile;
49
+ });
50
+ }
51
+ async save(entries) {
52
+ return this.mutex.runExclusive(async () => {
53
+ const profile = this.cachedProfile ?? await this.loadFromDisk();
54
+ const pruned = this.pruneExpiredTombstones(entries);
55
+ profile.entries = pruned;
56
+ profile.device_id = this.deviceId;
57
+ profile.last_sync = this.clock.now().toISOString();
58
+ await ProfileFormat.writeToFile(this.profilePath, profile);
59
+ this.cachedProfile = profile;
60
+ });
61
+ }
62
+ async getAll() {
63
+ const profile = await this.load();
64
+ return profile.entries.filter(e => !e.deleted);
65
+ }
66
+ async getById(id) {
67
+ const profile = await this.load();
68
+ return profile.entries.find(e => e.id === id && !e.deleted) ?? null;
69
+ }
70
+ async upsert(partial) {
71
+ return this.mutex.runExclusive(async () => {
72
+ const profile = this.cachedProfile ?? await this.loadFromDisk();
73
+ const now = this.clock.now().toISOString();
74
+ const existing = partial.id ? profile.entries.find(e => e.id === partial.id) : null;
75
+ if (existing) {
76
+ Object.assign(existing, {
77
+ ...partial,
78
+ updated: now,
79
+ device_id: this.deviceId,
80
+ });
81
+ if (existing.deleted) {
82
+ existing.deleted = false;
83
+ delete existing.deleted_at;
84
+ }
85
+ profile.last_sync = now;
86
+ await ProfileFormat.writeToFile(this.profilePath, profile);
87
+ this.cachedProfile = profile;
88
+ return existing;
89
+ }
90
+ else {
91
+ const entry = {
92
+ id: partial.id ?? createEntryId(),
93
+ created: now,
94
+ updated: now,
95
+ category: partial.category,
96
+ content: partial.content,
97
+ source: partial.source,
98
+ confidence: partial.confidence ?? 0.5,
99
+ device_id: this.deviceId,
100
+ deleted: false,
101
+ ...(partial.tags !== undefined && { tags: partial.tags }),
102
+ ...(partial.media_pointers !== undefined && { media_pointers: partial.media_pointers }),
103
+ ...(partial.pinned !== undefined && { pinned: partial.pinned }),
104
+ };
105
+ profile.entries.push(entry);
106
+ profile.last_sync = now;
107
+ await ProfileFormat.writeToFile(this.profilePath, profile);
108
+ this.cachedProfile = profile;
109
+ return entry;
110
+ }
111
+ });
112
+ }
113
+ /** Batch upsert — single read + N mutations + single write. */
114
+ async upsertBatch(items) {
115
+ return this.mutex.runExclusive(async () => {
116
+ const profile = this.cachedProfile ?? await this.loadFromDisk();
117
+ const now = this.clock.now().toISOString();
118
+ const results = [];
119
+ for (const partial of items) {
120
+ const existing = partial.id ? profile.entries.find(e => e.id === partial.id) : null;
121
+ if (existing) {
122
+ Object.assign(existing, { ...partial, updated: now, device_id: this.deviceId });
123
+ if (existing.deleted) {
124
+ existing.deleted = false;
125
+ delete existing.deleted_at;
126
+ }
127
+ results.push(existing);
128
+ }
129
+ else {
130
+ const entry = {
131
+ id: partial.id ?? createEntryId(),
132
+ created: now,
133
+ updated: now,
134
+ category: partial.category,
135
+ content: partial.content,
136
+ source: partial.source,
137
+ confidence: partial.confidence ?? 0.5,
138
+ device_id: this.deviceId,
139
+ deleted: false,
140
+ ...(partial.tags !== undefined && { tags: partial.tags }),
141
+ ...(partial.media_pointers !== undefined && { media_pointers: partial.media_pointers }),
142
+ ...(partial.pinned !== undefined && { pinned: partial.pinned }),
143
+ };
144
+ profile.entries.push(entry);
145
+ results.push(entry);
146
+ }
147
+ }
148
+ profile.last_sync = now;
149
+ await ProfileFormat.writeToFile(this.profilePath, profile);
150
+ this.cachedProfile = profile;
151
+ return results;
152
+ });
153
+ }
154
+ async softDelete(id) {
155
+ return this.mutex.runExclusive(async () => {
156
+ const profile = this.cachedProfile ?? await this.loadFromDisk();
157
+ const entry = profile.entries.find(e => e.id === id);
158
+ if (!entry || entry.deleted)
159
+ return false;
160
+ const now = this.clock.now().toISOString();
161
+ entry.deleted = true;
162
+ entry.deleted_at = now;
163
+ entry.updated = now;
164
+ entry.device_id = this.deviceId;
165
+ profile.last_sync = now;
166
+ await ProfileFormat.writeToFile(this.profilePath, profile);
167
+ this.cachedProfile = profile;
168
+ return true;
169
+ });
170
+ }
171
+ /** Invalidate in-memory cache (call after external file changes, e.g. git pull). */
172
+ invalidateCache() {
173
+ this.cachedProfile = null;
174
+ }
175
+ /**
176
+ * Compact: 3-tier memory management pipeline.
177
+ *
178
+ * Stage 1 — Confidence decay (Tier 1): soft-delete entries whose effective
179
+ * confidence has dropped below the threshold.
180
+ * Stage 2 — Content dedup (Tier 2): soft-delete near-duplicate entries
181
+ * within each category using TF-IDF cosine similarity.
182
+ * Stage 3 — Entity graph scoring (Tier 3): boost hub-entry confidence
183
+ * based on cross-entry entity connectedness.
184
+ * Stage 4 — Enforce max entries (Tier 1 second pass): hard cap via
185
+ * soft-delete of lowest-scored entries.
186
+ * Stage 5 — Final cleanup: strip all tombstones.
187
+ *
188
+ * Protected categories (preference/identity/skill/habit) are NEVER auto-removed.
189
+ */
190
+ async compact() {
191
+ return this.mutex.runExclusive(async () => {
192
+ const profile = await this.loadFromDisk();
193
+ const before = profile.entries.length;
194
+ const now = this.clock.now();
195
+ // Stage 1: Confidence decay (Tier 1) — soft-delete low-confidence old entries
196
+ const decayResult = applyDecay(profile.entries, now, this.memoryConfig);
197
+ profile.entries = decayResult.entries;
198
+ // Stage 2: Content deduplication (Tier 2) — soft-delete near-duplicate entries
199
+ const dedupResult = deduplicateEntries(profile.entries, now, this.memoryConfig.decay, this.dedupConfig);
200
+ profile.entries = dedupResult.entries;
201
+ // Stage 3: Entity graph scoring (Tier 3) — boost hub entry confidence
202
+ const activeForGraph = profile.entries.filter(e => !e.deleted);
203
+ const graph = buildEntityGraph(activeForGraph);
204
+ const scores = scoreEntries(activeForGraph, graph);
205
+ // Apply connectedness boost to active entries (mutate in place before enforceMax)
206
+ for (const score of scores) {
207
+ const boost = connectednessBoost(score);
208
+ if (boost > 0) {
209
+ const entry = profile.entries.find(e => e.id === score.entryId);
210
+ if (entry && !entry.deleted && !entry.pinned) {
211
+ entry.confidence = Math.min(1.0, entry.confidence + boost);
212
+ }
213
+ }
214
+ }
215
+ // Stage 4: Enforce max entries (Tier 1 second pass)
216
+ const maxResult = enforceMaxEntries(profile.entries, now, this.memoryConfig);
217
+ profile.entries = maxResult.entries;
218
+ // Stage 5: Remove all tombstones (final cleanup)
219
+ profile.entries = profile.entries.filter(e => !e.deleted);
220
+ // Write graph.json if path configured
221
+ if (this.graphPath) {
222
+ await atomicWrite(this.graphPath, serializeGraph(graph));
223
+ }
224
+ // Write profile
225
+ await ProfileFormat.writeToFile(this.profilePath, profile);
226
+ this.cachedProfile = profile;
227
+ return {
228
+ removed: before - profile.entries.length,
229
+ remaining: profile.entries.length,
230
+ decayed: decayResult.softDeleted,
231
+ deduped: dedupResult.merged,
232
+ graphNodes: graph.metadata.nodeCount,
233
+ graphEdges: graph.metadata.edgeCount,
234
+ };
235
+ });
236
+ }
237
+ pruneExpiredTombstones(entries) {
238
+ const cutoff = this.clock.now().getTime() - TOMBSTONE_TTL_MS;
239
+ return entries.filter(e => !e.deleted || new Date(e.updated).getTime() > cutoff);
240
+ }
241
+ /**
242
+ * Prune unprotected active entries beyond MAX_UNPROTECTED_ENTRIES.
243
+ * Sort by (updated ASC, confidence ASC) — oldest + lowest confidence removed first.
244
+ * Protected category entries and tombstones are never touched.
245
+ */
246
+ pruneExcessUnprotected(entries) {
247
+ const unprotectedActive = entries.filter(e => !e.deleted && !e.pinned && !PROTECTED_CATEGORIES.has(e.category));
248
+ if (unprotectedActive.length <= MAX_UNPROTECTED_ENTRIES)
249
+ return entries;
250
+ // Sort: oldest updated + lowest confidence first (these get pruned)
251
+ const sorted = [...unprotectedActive].sort((a, b) => {
252
+ const timeA = new Date(a.updated).getTime();
253
+ const timeB = new Date(b.updated).getTime();
254
+ if (timeA !== timeB)
255
+ return timeA - timeB;
256
+ return (a.confidence ?? 0.5) - (b.confidence ?? 0.5);
257
+ });
258
+ const excess = unprotectedActive.length - MAX_UNPROTECTED_ENTRIES;
259
+ const toRemove = new Set(sorted.slice(0, excess).map(e => e.id));
260
+ return entries.filter(e => !toRemove.has(e.id));
261
+ }
262
+ getProfilePath() {
263
+ return this.profilePath;
264
+ }
265
+ getDeviceId() {
266
+ return this.deviceId;
267
+ }
268
+ }
269
+ //# sourceMappingURL=ProfileManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileManager.js","sourceRoot":"","sources":["../../src/core/ProfileManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { MemoryProfile } from './EntrySchema.js';
2
+ export interface SchemaMigration {
3
+ from: number;
4
+ to: number;
5
+ migrate(profile: MemoryProfile): MemoryProfile;
6
+ }
7
+ export declare const CURRENT_SCHEMA_VERSION = 2;
8
+ /**
9
+ * Apply all necessary migrations to bring a profile up to the current version.
10
+ * Returns the migrated profile. If already current, returns as-is.
11
+ */
12
+ export declare function migrateProfile(profile: MemoryProfile): MemoryProfile;