cozo-memory 1.2.0 → 1.2.2

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,295 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EmotionalSalienceService = void 0;
4
+ /**
5
+ * Emotional Salience Keywords
6
+ *
7
+ * Organized by intensity and category based on psychological research
8
+ * and biological memory enhancement patterns.
9
+ */
10
+ const SALIENCE_KEYWORDS = {
11
+ // High Intensity (0.8-1.0) - Critical, urgent, never forget
12
+ critical: {
13
+ weight: 1.0,
14
+ keywords: [
15
+ 'critical', 'crucial', 'vital', 'essential', 'must', 'never forget',
16
+ 'always remember', 'extremely important', 'life-changing', 'breakthrough',
17
+ 'emergency', 'urgent', 'immediate', 'asap', 'priority one'
18
+ ]
19
+ },
20
+ // High Importance (0.6-0.8) - Important, significant
21
+ important: {
22
+ weight: 0.8,
23
+ keywords: [
24
+ 'important', 'significant', 'key', 'major', 'primary', 'fundamental',
25
+ 'noteworthy', 'remarkable', 'substantial', 'considerable', 'priority',
26
+ 'high priority', 'top priority', 'remember this', 'don\'t forget'
27
+ ]
28
+ },
29
+ // Emotional Arousal (0.5-0.7) - Surprise, excitement, concern
30
+ emotional: {
31
+ weight: 0.7,
32
+ keywords: [
33
+ 'surprising', 'unexpected', 'shocking', 'amazing', 'incredible',
34
+ 'exciting', 'thrilling', 'alarming', 'concerning', 'worrying',
35
+ 'breakthrough', 'discovery', 'revelation', 'game-changer'
36
+ ]
37
+ },
38
+ // Temporal Urgency (0.5-0.7) - Time-sensitive
39
+ temporal: {
40
+ weight: 0.6,
41
+ keywords: [
42
+ 'deadline', 'due', 'expires', 'time-sensitive', 'limited time',
43
+ 'soon', 'quickly', 'immediately', 'now', 'today', 'this week',
44
+ 'before', 'until', 'by'
45
+ ]
46
+ },
47
+ // Negative Salience (0.4-0.6) - Problems, risks, warnings
48
+ negative: {
49
+ weight: 0.5,
50
+ keywords: [
51
+ 'problem', 'issue', 'bug', 'error', 'failure', 'risk', 'danger',
52
+ 'warning', 'alert', 'caution', 'concern', 'threat', 'vulnerability',
53
+ 'critical bug', 'security issue', 'data loss'
54
+ ]
55
+ },
56
+ // Positive Salience (0.4-0.6) - Success, achievement
57
+ positive: {
58
+ weight: 0.5,
59
+ keywords: [
60
+ 'success', 'achievement', 'milestone', 'completed', 'solved',
61
+ 'accomplished', 'victory', 'win', 'breakthrough', 'innovation',
62
+ 'excellent', 'outstanding', 'exceptional'
63
+ ]
64
+ },
65
+ // Moderate Importance (0.3-0.5) - Notable, relevant
66
+ moderate: {
67
+ weight: 0.4,
68
+ keywords: [
69
+ 'notable', 'relevant', 'useful', 'helpful', 'valuable', 'worth noting',
70
+ 'interesting', 'attention', 'note', 'reminder', 'tip', 'advice'
71
+ ]
72
+ }
73
+ };
74
+ class EmotionalSalienceService {
75
+ db;
76
+ config;
77
+ constructor(db, config = {}) {
78
+ this.db = db;
79
+ this.config = {
80
+ enableSalience: true,
81
+ salienceBoostFactor: 2.0,
82
+ decaySlowdownFactor: 0.5,
83
+ minSalienceThreshold: 0.3,
84
+ updateExistingObservations: false,
85
+ ...config
86
+ };
87
+ }
88
+ /**
89
+ * Calculate emotional salience score for a text
90
+ */
91
+ calculateSalienceScore(text) {
92
+ const lowerText = text.toLowerCase();
93
+ const detectedKeywords = [];
94
+ // Scan for keywords in each category
95
+ for (const [categoryName, categoryData] of Object.entries(SALIENCE_KEYWORDS)) {
96
+ for (const keyword of categoryData.keywords) {
97
+ // Use word boundaries for accurate matching
98
+ const regex = new RegExp(`\\b${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i');
99
+ if (regex.test(text)) {
100
+ detectedKeywords.push({
101
+ keyword,
102
+ weight: categoryData.weight,
103
+ category: categoryName
104
+ });
105
+ }
106
+ }
107
+ }
108
+ // Calculate weighted score
109
+ let totalScore = 0;
110
+ let maxWeight = 0;
111
+ for (const detected of detectedKeywords) {
112
+ totalScore += detected.weight;
113
+ maxWeight = Math.max(maxWeight, detected.weight);
114
+ }
115
+ // Normalize: Use max weight if multiple keywords, with diminishing returns
116
+ const score = detectedKeywords.length === 0
117
+ ? 0
118
+ : Math.min(1.0, maxWeight + (totalScore - maxWeight) * 0.2);
119
+ // Categorize
120
+ let category;
121
+ if (score >= 0.7)
122
+ category = 'high';
123
+ else if (score >= 0.4)
124
+ category = 'medium';
125
+ else if (score >= 0.3)
126
+ category = 'low';
127
+ else
128
+ category = 'neutral';
129
+ // Generate reason
130
+ const uniqueKeywords = [...new Set(detectedKeywords.map(d => d.keyword))];
131
+ const reason = detectedKeywords.length === 0
132
+ ? 'No emotional salience keywords detected'
133
+ : `Detected ${detectedKeywords.length} salience keyword(s): ${uniqueKeywords.slice(0, 3).join(', ')}${uniqueKeywords.length > 3 ? '...' : ''}`;
134
+ return {
135
+ score,
136
+ keywords: uniqueKeywords,
137
+ category,
138
+ reason
139
+ };
140
+ }
141
+ /**
142
+ * Calculate salience boost for memory activation
143
+ */
144
+ calculateBoost(salienceScore) {
145
+ if (salienceScore < this.config.minSalienceThreshold) {
146
+ return { strengthMultiplier: 1.0, decayReduction: 0.0 };
147
+ }
148
+ // Strength multiplier: 1.0 to salienceBoostFactor (default: 2.0)
149
+ const strengthMultiplier = 1.0 + (salienceScore * (this.config.salienceBoostFactor - 1.0));
150
+ // Decay reduction: 0.0 to decaySlowdownFactor (default: 0.5)
151
+ // Higher salience = slower decay
152
+ const decayReduction = salienceScore * this.config.decaySlowdownFactor;
153
+ return { strengthMultiplier, decayReduction };
154
+ }
155
+ /**
156
+ * Score all observations for emotional salience
157
+ */
158
+ async scoreAllObservations() {
159
+ if (!this.config.enableSalience) {
160
+ return [];
161
+ }
162
+ try {
163
+ // Fetch all observations (without time travel if not supported)
164
+ const result = await this.db.run(`
165
+ ?[id, text, entity_id] := *observation{id, text, entity_id}
166
+ `);
167
+ const scores = [];
168
+ for (const row of result.rows) {
169
+ const [id, text, entityId] = row;
170
+ const { score, keywords, category, reason } = this.calculateSalienceScore(text);
171
+ const boost = this.calculateBoost(score);
172
+ scores.push({
173
+ observationId: id,
174
+ text: text,
175
+ salienceScore: score,
176
+ detectedKeywords: keywords,
177
+ category,
178
+ boost,
179
+ reason
180
+ });
181
+ }
182
+ return scores.sort((a, b) => b.salienceScore - a.salienceScore);
183
+ }
184
+ catch (error) {
185
+ console.error('[EmotionalSalience] Error scoring observations:', error);
186
+ return [];
187
+ }
188
+ }
189
+ /**
190
+ * Apply salience metadata to observations
191
+ */
192
+ async applySalienceMetadata(dryRun = false) {
193
+ const scores = await this.scoreAllObservations();
194
+ const toUpdate = scores.filter(s => s.salienceScore >= this.config.minSalienceThreshold);
195
+ if (dryRun) {
196
+ return { updated: 0, scores: toUpdate, dryRun: true };
197
+ }
198
+ let updated = 0;
199
+ for (const score of toUpdate) {
200
+ try {
201
+ // Update observation metadata with salience information
202
+ await this.db.run(`
203
+ ?[id, entity_id, text, metadata] :=
204
+ *observation{id, entity_id, text, metadata},
205
+ id = $id,
206
+ new_metadata = {
207
+ "emotional_salience": $salience_score,
208
+ "salience_category": $category,
209
+ "salience_keywords": $keywords,
210
+ "salience_boost_strength": $strength_multiplier,
211
+ "salience_boost_decay": $decay_reduction
212
+ },
213
+ metadata = if(is_null(metadata), new_metadata, concat(metadata, new_metadata))
214
+
215
+ :put observation {id, entity_id, text, metadata}
216
+ `, {
217
+ id: score.observationId,
218
+ salience_score: score.salienceScore,
219
+ category: score.category,
220
+ keywords: JSON.stringify(score.detectedKeywords),
221
+ strength_multiplier: score.boost.strengthMultiplier,
222
+ decay_reduction: score.boost.decayReduction
223
+ });
224
+ updated++;
225
+ }
226
+ catch (error) {
227
+ console.error(`[EmotionalSalience] Error updating observation ${score.observationId}:`, error);
228
+ }
229
+ }
230
+ return { updated, scores: toUpdate, dryRun: false };
231
+ }
232
+ /**
233
+ * Get salience statistics
234
+ */
235
+ async getSalienceStats() {
236
+ const scores = await this.scoreAllObservations();
237
+ const distribution = {
238
+ high: scores.filter(s => s.category === 'high').length,
239
+ medium: scores.filter(s => s.category === 'medium').length,
240
+ low: scores.filter(s => s.category === 'low').length,
241
+ neutral: scores.filter(s => s.category === 'neutral').length
242
+ };
243
+ const averageSalience = scores.length > 0
244
+ ? scores.reduce((sum, s) => sum + s.salienceScore, 0) / scores.length
245
+ : 0;
246
+ // Count keyword frequencies
247
+ const keywordCounts = new Map();
248
+ for (const score of scores) {
249
+ for (const keyword of score.detectedKeywords) {
250
+ keywordCounts.set(keyword, (keywordCounts.get(keyword) || 0) + 1);
251
+ }
252
+ }
253
+ const topKeywords = Array.from(keywordCounts.entries())
254
+ .map(([keyword, count]) => ({ keyword, count }))
255
+ .sort((a, b) => b.count - a.count)
256
+ .slice(0, 10);
257
+ return {
258
+ totalObservations: scores.length,
259
+ withSalience: scores.filter(s => s.salienceScore >= this.config.minSalienceThreshold).length,
260
+ distribution,
261
+ averageSalience,
262
+ topKeywords
263
+ };
264
+ }
265
+ /**
266
+ * Get salience score for a specific observation
267
+ */
268
+ async getObservationSalience(observationId) {
269
+ try {
270
+ const result = await this.db.run(`
271
+ ?[id, text] := *observation{id, text}, id = $id
272
+ `, { id: observationId });
273
+ if (result.rows.length === 0) {
274
+ return null;
275
+ }
276
+ const [id, text] = result.rows[0];
277
+ const { score, keywords, category, reason } = this.calculateSalienceScore(text);
278
+ const boost = this.calculateBoost(score);
279
+ return {
280
+ observationId: id,
281
+ text: text,
282
+ salienceScore: score,
283
+ detectedKeywords: keywords,
284
+ category,
285
+ boost,
286
+ reason
287
+ };
288
+ }
289
+ catch (error) {
290
+ console.error('[EmotionalSalience] Error getting observation salience:', error);
291
+ return null;
292
+ }
293
+ }
294
+ }
295
+ exports.EmotionalSalienceService = EmotionalSalienceService;