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.
- package/dist/emotional-salience.js +295 -0
- package/dist/index.js +726 -9
- package/dist/memory-activation.js +64 -30
- package/dist/memory-service.js +68 -0
- package/dist/pre-storage-reasoning.js +351 -0
- package/dist/temporal-conflict-resolution.js +10 -6
- package/dist/test-activation-mcp.js +118 -0
- package/dist/test-advanced-search-mcp.js +204 -0
- package/dist/test-conflicts-mcp.js +173 -0
- package/dist/test-emotional-salience.js +177 -0
- package/dist/test-hierarchical-mcp.js +135 -0
- package/dist/test-logical-edges-mcp.js +215 -0
- package/dist/test-metadata-check.js +69 -0
- package/dist/test-metadata-update.js +92 -0
- package/dist/test-pre-storage-reasoning.js +149 -0
- package/dist/test-salience-mcp.js +94 -0
- package/dist/test-spreading-mcp.js +155 -0
- package/dist/test-suggest-connections-mcp.js +172 -0
- package/dist/test-zettelkasten-evolution.js +255 -0
- package/dist/test-zettelkasten-fixed.js +74 -0
- package/dist/test-zettelkasten-live.js +117 -0
- package/dist/test-zettelkasten-mcp.js +96 -0
- package/dist/zettelkasten-evolution.js +342 -0
- package/package.json +1 -1
|
@@ -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;
|