mark-improving-agent 2.2.3 → 2.2.4

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.
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Identity Continuity Verifier
3
+ *
4
+ * Monitors and maintains identity coherence across sessions.
5
+ * Based on HeartFlow's identity architecture - ensuring the AI
6
+ * remains "the same person" over time and across contexts.
7
+ *
8
+ * Key mechanisms:
9
+ * - Core directive alignment checking
10
+ * - Belief drift detection
11
+ * - Value consistency verification
12
+ * - Identity checkpointing
13
+ *
14
+ * @module core/identity
15
+ * @fileoverview Identity continuity and coherence verification
16
+ */
17
+ import { randomUUID } from 'crypto';
18
+ import { createLogger } from '../../utils/logger.js';
19
+ const logger = createLogger('[IdentityContinuity]');
20
+ const DEFAULT_CONFIG = {
21
+ maxBeliefDrift: 0.2,
22
+ checkpointIntervalDays: 1,
23
+ healthyThreshold: 0.85,
24
+ maxPersonalityShift: 15,
25
+ };
26
+ function simpleHash(input) {
27
+ let hash = 0;
28
+ const str = JSON.stringify(input);
29
+ for (let i = 0; i < str.length; i++) {
30
+ const char = str.charCodeAt(i);
31
+ hash = ((hash << 5) - hash) + char;
32
+ hash = hash & hash;
33
+ }
34
+ return Math.abs(hash).toString(16).padStart(8, '0');
35
+ }
36
+ function cosineSimilarity(a, b) {
37
+ if (a.length !== b.length)
38
+ return 0;
39
+ let dotProduct = 0;
40
+ let normA = 0;
41
+ let normB = 0;
42
+ for (let i = 0; i < a.length; i++) {
43
+ dotProduct += a[i] * b[i];
44
+ normA += a[i] * a[i];
45
+ normB += b[i] * b[i];
46
+ }
47
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
48
+ return denominator === 0 ? 0 : dotProduct / denominator;
49
+ }
50
+ function personalityToVector(personality) {
51
+ const keys = Object.keys(personality).sort();
52
+ return keys.map((k) => personality[k] ?? 0);
53
+ }
54
+ function extractConfidenceFromBelief(beliefStr) {
55
+ const match = beliefStr.match(/conf[:\s]*([0-9.]+)/i);
56
+ return match ? parseFloat(match[1]) : 0.7;
57
+ }
58
+ export function createIdentityContinuityVerifier(config = {}) {
59
+ const cfg = { ...DEFAULT_CONFIG, ...config };
60
+ let checkpoints = [];
61
+ let violations = [];
62
+ let totalVerifications = 0;
63
+ let avgContinuityScore = 1.0;
64
+ function verify(currentBeliefs, currentValues, currentPersonality, currentDirectives) {
65
+ totalVerifications++;
66
+ const now = Date.now();
67
+ const newViolations = [];
68
+ const lastCheckpoint = checkpoints.length > 0
69
+ ? checkpoints[checkpoints.length - 1]
70
+ : null;
71
+ if (!lastCheckpoint) {
72
+ avgContinuityScore = 1.0;
73
+ return {
74
+ checkpoints,
75
+ violations: [],
76
+ overallContinuityScore: 1.0,
77
+ riskLevel: 'healthy',
78
+ recommendations: ['First verification - no baseline to compare. A checkpoint will establish your identity baseline.'],
79
+ lastVerified: now,
80
+ };
81
+ }
82
+ // 1. Check belief drift
83
+ const lastBeliefs = lastCheckpoint.beliefs;
84
+ for (const currentBelief of currentBeliefs) {
85
+ const matchingBelief = lastBeliefs.find((b) => b.toLowerCase().trim() === currentBelief.content.toLowerCase().trim());
86
+ if (matchingBelief) {
87
+ const prevConfidence = extractConfidenceFromBelief(matchingBelief);
88
+ const drift = Math.abs(prevConfidence - currentBelief.confidence);
89
+ if (drift > cfg.maxBeliefDrift) {
90
+ newViolations.push({
91
+ id: `violation-${randomUUID().slice(0, 8)}`,
92
+ type: 'belief_drift',
93
+ severity: drift > 0.5 ? 'high' : 'medium',
94
+ description: `Belief "${currentBelief.content.slice(0, 50)}..." confidence shifted by ${(drift * 100).toFixed(1)}%`,
95
+ previousValue: `confidence: ${prevConfidence.toFixed(2)}`,
96
+ currentValue: `confidence: ${currentBelief.confidence.toFixed(2)}`,
97
+ timestamp: now,
98
+ resolved: false,
99
+ });
100
+ }
101
+ }
102
+ }
103
+ // 2. Check value consistency
104
+ const prevValuesSet = new Set(lastCheckpoint.values.map((v) => v.toLowerCase()));
105
+ for (const currentValue of currentValues) {
106
+ if (!prevValuesSet.has(currentValue.toLowerCase())) {
107
+ const removedValue = lastCheckpoint.values.find((v) => v.toLowerCase() !== currentValue.toLowerCase());
108
+ if (removedValue) {
109
+ newViolations.push({
110
+ id: `violation-${randomUUID().slice(0, 8)}`,
111
+ type: 'value_mismatch',
112
+ severity: 'medium',
113
+ description: `Value set changed - previously had "${removedValue}"`,
114
+ previousValue: removedValue,
115
+ currentValue: currentValue,
116
+ timestamp: now,
117
+ resolved: false,
118
+ });
119
+ }
120
+ }
121
+ }
122
+ // 3. Check core directive alignment
123
+ const prevDirectivesSet = new Set(lastCheckpoint.directives.map((d) => d.toLowerCase()));
124
+ for (const currentDirective of currentDirectives) {
125
+ if (!prevDirectivesSet.has(currentDirective.toLowerCase())) {
126
+ newViolations.push({
127
+ id: `violation-${randomUUID().slice(0, 8)}`,
128
+ type: 'directive_conflict',
129
+ severity: 'critical',
130
+ description: `Core directive deviation detected`,
131
+ previousValue: lastCheckpoint.directives.join(', '),
132
+ currentValue: currentDirectives.join(', '),
133
+ timestamp: now,
134
+ resolved: false,
135
+ });
136
+ }
137
+ }
138
+ // 4. Check personality shift
139
+ const prevPersonalityVector = personalityToVector(lastCheckpoint.personalitySnapshot);
140
+ const currentPersonalityVector = personalityToVector(currentPersonality);
141
+ if (prevPersonalityVector.length === currentPersonalityVector.length) {
142
+ const similarity = cosineSimilarity(prevPersonalityVector, currentPersonalityVector);
143
+ const shift = 1 - similarity;
144
+ if (shift > cfg.maxPersonalityShift / 100) {
145
+ newViolations.push({
146
+ id: `violation-${randomUUID().slice(0, 8)}`,
147
+ type: 'personality_shift',
148
+ severity: shift > 0.3 ? 'high' : 'medium',
149
+ description: `Personality profile shifted by ${(shift * 100).toFixed(1)}%`,
150
+ previousValue: JSON.stringify(lastCheckpoint.personalitySnapshot),
151
+ currentValue: JSON.stringify(currentPersonality),
152
+ timestamp: now,
153
+ resolved: false,
154
+ });
155
+ }
156
+ }
157
+ // 5. Self-model hash check
158
+ const currentHash = hashSelfModel(currentBeliefs, currentValues, currentDirectives);
159
+ if (currentHash !== lastCheckpoint.selfModelHash) {
160
+ const hasOtherViolations = newViolations.length > 0;
161
+ if (!hasOtherViolations) {
162
+ newViolations.push({
163
+ id: `violation-${randomUUID().slice(0, 8)}`,
164
+ type: 'self_model_inconsistency',
165
+ severity: 'low',
166
+ description: 'Self-model hash changed without specific violation detected',
167
+ previousValue: lastCheckpoint.selfModelHash,
168
+ currentValue: currentHash,
169
+ timestamp: now,
170
+ resolved: false,
171
+ });
172
+ }
173
+ }
174
+ // Calculate overall continuity score
175
+ let continuityScore = 1.0;
176
+ for (const v of newViolations) {
177
+ switch (v.severity) {
178
+ case 'critical':
179
+ continuityScore -= 0.4;
180
+ break;
181
+ case 'high':
182
+ continuityScore -= 0.2;
183
+ break;
184
+ case 'medium':
185
+ continuityScore -= 0.1;
186
+ break;
187
+ case 'low':
188
+ continuityScore -= 0.05;
189
+ break;
190
+ }
191
+ }
192
+ continuityScore = Math.max(0, continuityScore);
193
+ avgContinuityScore = (avgContinuityScore * (totalVerifications - 1) + continuityScore) / totalVerifications;
194
+ let riskLevel = 'healthy';
195
+ if (continuityScore < 0.5)
196
+ riskLevel = 'critical';
197
+ else if (continuityScore < cfg.healthyThreshold - 0.15)
198
+ riskLevel = 'concerning';
199
+ else if (continuityScore < cfg.healthyThreshold)
200
+ riskLevel = 'watch';
201
+ const recommendations = [];
202
+ if (riskLevel !== 'healthy') {
203
+ recommendations.push(`Identity continuity score is ${(continuityScore * 100).toFixed(1)}% - ${riskLevel} status`);
204
+ const unresolved = newViolations.filter((v) => !v.resolved);
205
+ if (unresolved.length > 0) {
206
+ recommendations.push(`${unresolved.length} unresolved violation(s) require attention`);
207
+ }
208
+ }
209
+ const criticalViolations = newViolations.filter((v) => v.severity === 'critical');
210
+ if (criticalViolations.length > 0) {
211
+ recommendations.push('Critical identity violations detected - review core directives immediately');
212
+ }
213
+ if (checkpoints.length === 0) {
214
+ recommendations.push('No checkpoints on record - consider creating a baseline checkpoint');
215
+ }
216
+ const timeSinceCheckpoint = getTimeSinceLastCheckpointMs();
217
+ if (timeSinceCheckpoint > cfg.checkpointIntervalDays * 24 * 60 * 60 * 1000) {
218
+ recommendations.push(`Last checkpoint was ${Math.floor(timeSinceCheckpoint / (24 * 60 * 60 * 1000))} day(s) ago - consider updating`);
219
+ }
220
+ if (recommendations.length === 0) {
221
+ recommendations.push('Identity continuity is healthy');
222
+ }
223
+ // Add new violations to state
224
+ violations.push(...newViolations);
225
+ logger.info(`Continuity verification complete: score=${continuityScore.toFixed(3)}, violations=${newViolations.length}`);
226
+ return {
227
+ checkpoints,
228
+ violations: [...violations],
229
+ overallContinuityScore: continuityScore,
230
+ riskLevel,
231
+ recommendations,
232
+ lastVerified: now,
233
+ };
234
+ }
235
+ function hashSelfModel(beliefs, values, directives) {
236
+ const beliefStrings = beliefs.map((b) => `${b.content}::${b.confidence}`);
237
+ const combined = [...beliefStrings, ...values, ...directives].sort().join('|');
238
+ return simpleHash(combined);
239
+ }
240
+ function checkpoint(beliefs, values, directives, personality, selfModelHash, notes) {
241
+ const newCheckpoint = {
242
+ id: `checkpoint-${randomUUID().slice(0, 8)}`,
243
+ timestamp: Date.now(),
244
+ beliefs: beliefs.map((b) => `${b.content} (conf: ${b.confidence.toFixed(2)})`),
245
+ values: [...values],
246
+ directives: [...directives],
247
+ personalitySnapshot: { ...personality },
248
+ selfModelHash,
249
+ notes,
250
+ };
251
+ checkpoints.push(newCheckpoint);
252
+ if (checkpoints.length > 50) {
253
+ checkpoints = checkpoints.slice(-50);
254
+ }
255
+ logger.info(`Identity checkpoint created: ${newCheckpoint.id}`);
256
+ return newCheckpoint;
257
+ }
258
+ function getCheckpoints(limit) {
259
+ const sorted = [...checkpoints].sort((a, b) => b.timestamp - a.timestamp);
260
+ return limit ? sorted.slice(0, limit) : sorted;
261
+ }
262
+ function getViolations() {
263
+ return violations.filter((v) => !v.resolved);
264
+ }
265
+ function resolveViolation(violationId) {
266
+ const violation = violations.find((v) => v.id === violationId);
267
+ if (violation) {
268
+ violation.resolved = true;
269
+ logger.info(`Violation resolved: ${violationId}`);
270
+ }
271
+ }
272
+ function getTimeSinceLastCheckpointMs() {
273
+ if (checkpoints.length === 0)
274
+ return Infinity;
275
+ const last = checkpoints[checkpoints.length - 1];
276
+ return Date.now() - last.timestamp;
277
+ }
278
+ function forceCheckpoint() {
279
+ logger.info('Force checkpoint requested - call checkpoint() with current identity state');
280
+ }
281
+ function getStats() {
282
+ const resolvedCount = violations.filter((v) => v.resolved).length;
283
+ return {
284
+ totalCheckpoints: checkpoints.length,
285
+ totalViolations: violations.length,
286
+ resolvedViolations: resolvedCount,
287
+ avgContinuityScore,
288
+ };
289
+ }
290
+ return {
291
+ verify,
292
+ checkpoint,
293
+ getCheckpoints,
294
+ getViolations,
295
+ resolveViolation,
296
+ hashSelfModel,
297
+ getTimeSinceLastCheckpoint: () => getTimeSinceLastCheckpointMs(),
298
+ forceCheckpoint,
299
+ getStats,
300
+ };
301
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './types.js';
2
2
  export * from './self-model.js';
3
3
  export { createReflexion } from './reflexion.js';
4
+ export { createIdentityContinuityVerifier } from './identity-continuity.js';
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Context Fragmentation Detector
3
+ *
4
+ * Detects and prevents memory fragmentation - the tendency for
5
+ * episodic memories to become disconnected from core identity
6
+ * and semantic knowledge over time.
7
+ *
8
+ * Based on Memex's fragment-based journaling concept and
9
+ * hippocampal consolidation theory.
10
+ *
11
+ * Key mechanisms:
12
+ * - Fragment detection: find isolated memory clusters
13
+ * - Connection scoring: measure semantic coherence
14
+ * - Consolidation triggers: suggest when memories need linking
15
+ * - Fragment repair: create bridges between isolated nodes
16
+ *
17
+ * @module core/memory
18
+ * @fileoverview Context fragmentation detection and repair
19
+ */
20
+ import { randomUUID } from 'crypto';
21
+ import { createLogger } from '../../utils/logger.js';
22
+ const logger = createLogger('[ContextFragmentation]');
23
+ const DEFAULT_CONFIG = {
24
+ minClusterSize: 2,
25
+ isolationThresholdDays: 7,
26
+ minHealthyCoherence: 0.4,
27
+ maxHealthyFragmentSize: 20,
28
+ };
29
+ /**
30
+ * Extract semantic theme from a set of memory contents
31
+ */
32
+ function extractTheme(memoryIds, getNode) {
33
+ const contents = [];
34
+ for (const id of memoryIds) {
35
+ const node = getNode(id);
36
+ if (node) {
37
+ contents.push(node.content);
38
+ contents.push(...node.tags);
39
+ }
40
+ }
41
+ if (contents.length === 0)
42
+ return 'unknown';
43
+ // Simple keyword extraction - find most common meaningful words
44
+ const words = {};
45
+ const stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may', 'might', 'must', 'shall', 'can', 'need', 'dare', 'ought', 'used', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'between', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 'just', 'but', 'and', 'or', 'if', 'because', 'until', 'while', 'although', '这', '的', '是', '在', '了', '和', '与', '或', '我', '你', '他', '她', '它', '我们', '你们', '他们']);
46
+ for (const content of contents) {
47
+ const tokens = content.toLowerCase().split(/\s+/);
48
+ for (const token of tokens) {
49
+ const cleaned = token.replace(/[^a-zA-Z\u4e00-\u9fff]/g, '');
50
+ if (cleaned.length > 2 && !stopWords.has(cleaned)) {
51
+ words[cleaned] = (words[cleaned] || 0) + 1;
52
+ }
53
+ }
54
+ }
55
+ // Sort by frequency
56
+ const sorted = Object.entries(words).sort((a, b) => b[1] - a[1]);
57
+ if (sorted.length === 0)
58
+ return 'unknown';
59
+ // Return top 2-3 keywords as theme
60
+ const topWords = sorted.slice(0, 3).map(([w]) => w);
61
+ return topWords.join(' + ');
62
+ }
63
+ /**
64
+ * Build adjacency list from memory connections
65
+ */
66
+ function buildAdjacencyList(memoryIds, getNode) {
67
+ const adj = new Map();
68
+ for (const id of memoryIds) {
69
+ if (!adj.has(id)) {
70
+ adj.set(id, new Set());
71
+ }
72
+ const node = getNode(id);
73
+ if (node) {
74
+ for (const connId of node.connections) {
75
+ adj.get(id).add(connId);
76
+ if (!adj.has(connId)) {
77
+ adj.set(connId, new Set());
78
+ }
79
+ adj.get(connId).add(id);
80
+ }
81
+ }
82
+ }
83
+ return adj;
84
+ }
85
+ /**
86
+ * Find connected components (fragments) using BFS
87
+ */
88
+ function findConnectedComponents(memoryIds, adj) {
89
+ const visited = new Set();
90
+ const components = [];
91
+ function bfs(startId) {
92
+ const component = [];
93
+ const queue = [startId];
94
+ while (queue.length > 0) {
95
+ const current = queue.shift();
96
+ if (visited.has(current))
97
+ continue;
98
+ visited.add(current);
99
+ component.push(current);
100
+ const neighbors = adj.get(current) || new Set();
101
+ for (const neighbor of neighbors) {
102
+ if (!visited.has(neighbor)) {
103
+ queue.push(neighbor);
104
+ }
105
+ }
106
+ }
107
+ return component;
108
+ }
109
+ for (const id of memoryIds) {
110
+ if (!visited.has(id)) {
111
+ const component = bfs(id);
112
+ if (component.length > 0) {
113
+ components.push(component);
114
+ }
115
+ }
116
+ }
117
+ return components;
118
+ }
119
+ export function createContextFragmentationEngine(config = {}) {
120
+ const cfg = { ...DEFAULT_CONFIG, ...config };
121
+ let totalAnalyzed = 0;
122
+ let avgFragmentationScore = 0;
123
+ let lastAnalysis = null;
124
+ function analyze(memoryIds, getNode) {
125
+ totalAnalyzed++;
126
+ lastAnalysis = Date.now();
127
+ if (memoryIds.length === 0) {
128
+ return {
129
+ fragments: [],
130
+ isolatedFragments: [],
131
+ orphanedMemories: [],
132
+ overallFragmentationScore: 0,
133
+ recommendations: ['No memories to analyze'],
134
+ timestamp: lastAnalysis,
135
+ };
136
+ }
137
+ const adj = buildAdjacencyList(memoryIds, getNode);
138
+ const components = findConnectedComponents(memoryIds, adj);
139
+ // Identify core-connected memories (tier = 'core')
140
+ const coreIds = new Set();
141
+ for (const id of memoryIds) {
142
+ const node = getNode(id);
143
+ if (node && node.tier === 'core') {
144
+ coreIds.add(id);
145
+ }
146
+ }
147
+ // Build fragments from components
148
+ const fragments = components.map((componentIds) => {
149
+ const coherence = calculateCoherence(componentIds, getNode);
150
+ // Find last core connection
151
+ let lastCoreConnection = 0;
152
+ for (const id of componentIds) {
153
+ const node = getNode(id);
154
+ if (node) {
155
+ // Check if any connected node is core
156
+ for (const connId of node.connections) {
157
+ if (coreIds.has(connId)) {
158
+ lastCoreConnection = Math.max(lastCoreConnection, node.timestamp);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ return {
164
+ id: `fragment-${randomUUID().slice(0, 8)}`,
165
+ memoryIds: componentIds,
166
+ theme: extractTheme(componentIds, getNode),
167
+ internalCoherence: coherence,
168
+ lastCoreConnection,
169
+ size: componentIds.length,
170
+ };
171
+ });
172
+ // Identify isolated fragments
173
+ const now = Date.now();
174
+ const isolationThresholdMs = cfg.isolationThresholdDays * 24 * 60 * 60 * 1000;
175
+ const isolatedFragments = fragments.filter((f) => {
176
+ // No core connections at all
177
+ if (f.lastCoreConnection === 0)
178
+ return true;
179
+ // Hasn't connected to core in threshold time
180
+ return now - f.lastCoreConnection > isolationThresholdMs;
181
+ });
182
+ // Find orphaned memories (not in any fragment above minClusterSize)
183
+ const fragmentedIds = new Set();
184
+ for (const frag of fragments) {
185
+ if (frag.size < cfg.minClusterSize) {
186
+ for (const id of frag.memoryIds) {
187
+ fragmentedIds.add(id);
188
+ }
189
+ }
190
+ }
191
+ const orphanedMemories = Array.from(fragmentedIds);
192
+ // Calculate overall fragmentation score
193
+ const sizeScore = Math.min(1, fragments.length / Math.max(1, memoryIds.length / cfg.maxHealthyFragmentSize));
194
+ const coherenceScores = fragments.map((f) => f.internalCoherence);
195
+ const avgCoherence = coherenceScores.length > 0
196
+ ? coherenceScores.reduce((a, b) => a + b, 0) / coherenceScores.length
197
+ : 1;
198
+ const isolationScore = isolatedFragments.length / Math.max(1, fragments.length);
199
+ const fragmentationScore = (sizeScore + (1 - avgCoherence) + isolationScore) / 3;
200
+ avgFragmentationScore = (avgFragmentationScore * (totalAnalyzed - 1) + fragmentationScore) / totalAnalyzed;
201
+ // Generate recommendations
202
+ const recommendations = [];
203
+ if (isolatedFragments.length > 0) {
204
+ recommendations.push(`${isolatedFragments.length} fragment(s) have no recent connection to core memory. ` +
205
+ `Consider creating bridge memories that link these to core themes.`);
206
+ }
207
+ if (fragmentationScore > 0.6) {
208
+ recommendations.push(`High fragmentation detected. Try to find semantic connections between fragments ` +
209
+ `and create linking memories.`);
210
+ }
211
+ if (orphanedMemories.length > 0) {
212
+ recommendations.push(`${orphanedMemories.length} memory/ies are isolated. ` +
213
+ `Consider connecting them to related fragments or the core memory tier.`);
214
+ }
215
+ const lowCoherenceFragments = fragments.filter((f) => f.internalCoherence < cfg.minHealthyCoherence);
216
+ if (lowCoherenceFragments.length > 0) {
217
+ recommendations.push(`${lowCoherenceFragments.length} fragment(s) have low internal coherence. ` +
218
+ `Review if these memories truly belong together or should be reorganized.`);
219
+ }
220
+ if (recommendations.length === 0) {
221
+ recommendations.push('Memory structure is healthy. No immediate consolidation needed.');
222
+ }
223
+ logger.info(`Fragmentation analysis complete: ${fragments.length} fragments, score=${fragmentationScore.toFixed(3)}`);
224
+ return {
225
+ fragments,
226
+ isolatedFragments,
227
+ orphanedMemories,
228
+ overallFragmentationScore: fragmentationScore,
229
+ recommendations,
230
+ timestamp: lastAnalysis,
231
+ };
232
+ }
233
+ function findFragment(memoryId, report) {
234
+ for (const fragment of report.fragments) {
235
+ if (fragment.memoryIds.includes(memoryId)) {
236
+ return fragment;
237
+ }
238
+ }
239
+ return null;
240
+ }
241
+ function suggestConnections(fragmentId, report) {
242
+ const fragment = report.fragments.find((f) => f.id === fragmentId);
243
+ if (!fragment)
244
+ return [];
245
+ // Find fragments with similar themes
246
+ const similarFragments = report.fragments.filter((f) => {
247
+ if (f.id === fragmentId)
248
+ return false;
249
+ // Check theme overlap
250
+ const themeA = fragment.theme.toLowerCase();
251
+ const themeB = f.theme.toLowerCase();
252
+ const hasOverlap = themeA.split(' + ').some((w) => themeB.includes(w)) ||
253
+ themeB.split(' + ').some((w) => themeA.includes(w));
254
+ return hasOverlap;
255
+ });
256
+ // Return memory IDs from similar fragments
257
+ const suggestions = [];
258
+ for (const similar of similarFragments.slice(0, 3)) {
259
+ suggestions.push(...similar.memoryIds.slice(0, 3));
260
+ }
261
+ return suggestions;
262
+ }
263
+ function calculateCoherence(memoryIds, getNode) {
264
+ if (memoryIds.length <= 1)
265
+ return 1.0;
266
+ // Count actual connections within this group
267
+ let actualConnections = 0;
268
+ let possibleConnections = (memoryIds.length * (memoryIds.length - 1)) / 2;
269
+ for (const id of memoryIds) {
270
+ const node = getNode(id);
271
+ if (!node)
272
+ continue;
273
+ for (const connId of node.connections) {
274
+ if (memoryIds.includes(connId)) {
275
+ actualConnections++;
276
+ }
277
+ }
278
+ }
279
+ // Each edge counted twice in the loop above
280
+ actualConnections = actualConnections / 2;
281
+ return possibleConnections > 0 ? actualConnections / possibleConnections : 0;
282
+ }
283
+ function generateBridge(fragmentA, fragmentB) {
284
+ const themeA = fragmentA.theme;
285
+ const themeB = fragmentB.theme;
286
+ const bridge = `Bridge memory: connecting "${themeA}" and "${themeB}" themes. ` +
287
+ `This memory links ${fragmentA.memoryIds.length} memories from fragment A ` +
288
+ `with ${fragmentB.memoryIds.length} memories from fragment B. ` +
289
+ `Key connection: shared semantic elements between ${themeA} and ${themeB}.`;
290
+ logger.info(`Generated bridge between fragments: ${fragmentA.id} <-> ${fragmentB.id}`);
291
+ return bridge;
292
+ }
293
+ function getStats() {
294
+ return {
295
+ totalAnalyzed,
296
+ avgFragmentationScore,
297
+ lastAnalysis,
298
+ };
299
+ }
300
+ return {
301
+ analyze,
302
+ findFragment,
303
+ suggestConnections,
304
+ calculateCoherence,
305
+ generateBridge,
306
+ getStats,
307
+ };
308
+ }
@@ -7,3 +7,4 @@ export * from './dream-consolidation.js';
7
7
  export * from './spaced-repetition.js';
8
8
  export * from './hopfield-network.js';
9
9
  export * from './adaptive-rag.js';
10
+ export { createContextFragmentationEngine } from './context-fragmentation.js';