claude-recall 0.22.1 → 0.22.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.
|
@@ -8,14 +8,14 @@ source: claude-recall
|
|
|
8
8
|
|
|
9
9
|
# Preferences
|
|
10
10
|
|
|
11
|
-
Auto-generated from 5 memories. Last updated: 2026-04-
|
|
11
|
+
Auto-generated from 5 memories. Last updated: 2026-04-14.
|
|
12
12
|
|
|
13
13
|
## Rules
|
|
14
14
|
|
|
15
|
-
- Session test preference
|
|
16
|
-
- Test preference
|
|
17
|
-
- Test preference
|
|
18
|
-
- Test preference
|
|
15
|
+
- Session test preference 1776172131422
|
|
16
|
+
- Test preference 1776172131359-2
|
|
17
|
+
- Test preference 1776172131359-1
|
|
18
|
+
- Test preference 1776172131359-0
|
|
19
19
|
- Test memory content
|
|
20
20
|
|
|
21
21
|
---
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"topicId": "preferences",
|
|
3
|
-
"sourceHash": "
|
|
3
|
+
"sourceHash": "37e19c67668ff4fdd21019d4837a36dc368138b8e4299bcff235fdaf84b5f028",
|
|
4
4
|
"memoryCount": 5,
|
|
5
|
-
"generatedAt": "2026-04-
|
|
5
|
+
"generatedAt": "2026-04-14T13:08:51.442Z",
|
|
6
6
|
"memoryKeys": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
7
|
+
"memory_1776172131423_zdtbhf2vw",
|
|
8
|
+
"memory_1776172131396_d2s4cuyls",
|
|
9
|
+
"memory_1776172131378_1r9w9r7lq",
|
|
10
|
+
"memory_1776172131363_gqgkc4o6s",
|
|
11
|
+
"memory_1776172131318_dbwsryce8"
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -274,6 +274,10 @@ class MemoryTools {
|
|
|
274
274
|
? metadata.type
|
|
275
275
|
: 'preference';
|
|
276
276
|
const key = `memory_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
277
|
+
const preferenceKey = typeof metadata?.preference_key === 'string' && metadata.preference_key.length > 0
|
|
278
|
+
? metadata.preference_key
|
|
279
|
+
: undefined;
|
|
280
|
+
const isOverride = metadata?.isOverride === true;
|
|
277
281
|
this.memoryService.store({
|
|
278
282
|
key,
|
|
279
283
|
value: {
|
|
@@ -288,8 +292,17 @@ class MemoryTools {
|
|
|
288
292
|
projectId: scope === 'project' ? context.projectId : undefined,
|
|
289
293
|
timestamp: context.timestamp,
|
|
290
294
|
scope: scope || null
|
|
291
|
-
}
|
|
295
|
+
},
|
|
296
|
+
preferenceKey,
|
|
297
|
+
isOverride
|
|
292
298
|
});
|
|
299
|
+
// If this store overrides an existing rule, mark previous active rules with
|
|
300
|
+
// the same preference_key as superseded and surface their keys so the agent
|
|
301
|
+
// knows to ignore the stale text sitting higher up in its context.
|
|
302
|
+
let supersededKeys = [];
|
|
303
|
+
if (isOverride && preferenceKey) {
|
|
304
|
+
supersededKeys = this.memoryService.supersedeByPreferenceKey(preferenceKey, key, { sessionId: context.sessionId, projectId: context.projectId, timestamp: context.timestamp });
|
|
305
|
+
}
|
|
293
306
|
this.logger.info('MemoryTools', 'Memory stored successfully', {
|
|
294
307
|
key,
|
|
295
308
|
type: detectedType,
|
|
@@ -320,6 +333,10 @@ class MemoryTools {
|
|
|
320
333
|
activeRule: `Stored as active rule:\n- ${content}`,
|
|
321
334
|
type: detectedType,
|
|
322
335
|
_directive: 'Apply this rule immediately. No need to call load_rules again.',
|
|
336
|
+
...(supersededKeys.length > 0 && {
|
|
337
|
+
supersededKeys,
|
|
338
|
+
_supersessionNotice: `Superseded ${supersededKeys.length} prior rule(s) for preference_key="${preferenceKey}". Ignore any earlier text from these rules still in your context: ${supersededKeys.join(', ')}`
|
|
339
|
+
}),
|
|
323
340
|
...(skillResults.length > 0 && {
|
|
324
341
|
_skillsGenerated: skillResults
|
|
325
342
|
.filter(r => r.action === 'created' || r.action === 'updated')
|
|
@@ -383,11 +400,21 @@ class MemoryTools {
|
|
|
383
400
|
return `- ${val}`;
|
|
384
401
|
}).join('\n'));
|
|
385
402
|
}
|
|
386
|
-
// Add compliance section for rules loaded frequently but never cited
|
|
403
|
+
// Add compliance section for rules loaded frequently but never cited.
|
|
404
|
+
// Cap at top 10 by load_count so load_rules stays slim — this diagnostic
|
|
405
|
+
// list was previously unbounded and could dominate the payload.
|
|
406
|
+
const RULE_HEALTH_CAP = 10;
|
|
387
407
|
const compliance = this.memoryService.getComplianceReport(projectId || context.projectId);
|
|
388
|
-
const
|
|
408
|
+
const allLowCompliance = compliance.rules.filter(r => r.load_count >= 5 && r.cite_count === 0);
|
|
409
|
+
const lowCompliance = [...allLowCompliance]
|
|
410
|
+
.sort((a, b) => (b.load_count || 0) - (a.load_count || 0))
|
|
411
|
+
.slice(0, RULE_HEALTH_CAP);
|
|
412
|
+
const hiddenCount = allLowCompliance.length - lowCompliance.length;
|
|
389
413
|
if (lowCompliance.length > 0) {
|
|
390
|
-
|
|
414
|
+
const heading = hiddenCount > 0
|
|
415
|
+
? `## Rule Health (top ${RULE_HEALTH_CAP} of ${allLowCompliance.length})\nThese rules are loaded frequently but never cited — consider rewording or removing. ${hiddenCount} more hidden; run \`npx claude-recall outcomes\` to see all.`
|
|
416
|
+
: '## Rule Health\nThese rules are loaded frequently but never cited — consider rewording or removing:';
|
|
417
|
+
sections.push(heading + '\n' +
|
|
391
418
|
lowCompliance.map(r => {
|
|
392
419
|
let val;
|
|
393
420
|
if (typeof r.value === 'string') {
|
package/dist/memory/storage.js
CHANGED
|
@@ -562,6 +562,23 @@ class MemoryStorage {
|
|
|
562
562
|
const stmt = this.db.prepare(`UPDATE memories SET ${setClause} WHERE key = ?`);
|
|
563
563
|
stmt.run(...values, key);
|
|
564
564
|
}
|
|
565
|
+
/**
|
|
566
|
+
* Get active memories (any type) that share the given preference_key.
|
|
567
|
+
* Used by store_memory's override path — a user can override a rule of any
|
|
568
|
+
* type (devops, correction, preference) as long as it was stored with a
|
|
569
|
+
* preference_key.
|
|
570
|
+
*/
|
|
571
|
+
getActiveByPreferenceKeyAnyType(preferenceKey, projectId) {
|
|
572
|
+
let query = 'SELECT * FROM memories WHERE preference_key = ? AND is_active = 1';
|
|
573
|
+
const params = [preferenceKey];
|
|
574
|
+
if (projectId) {
|
|
575
|
+
query += ' AND (project_id = ? OR project_id IS NULL)';
|
|
576
|
+
params.push(projectId);
|
|
577
|
+
}
|
|
578
|
+
query += ' ORDER BY timestamp DESC';
|
|
579
|
+
const rows = this.db.prepare(query).all(...params);
|
|
580
|
+
return rows.map(row => this.rowToMemory(row));
|
|
581
|
+
}
|
|
565
582
|
/**
|
|
566
583
|
* Get preferences by preference key
|
|
567
584
|
*/
|
package/dist/services/memory.js
CHANGED
|
@@ -43,7 +43,9 @@ class MemoryService {
|
|
|
43
43
|
file_path: request.context?.filePath,
|
|
44
44
|
timestamp: request.context?.timestamp || Date.now(),
|
|
45
45
|
relevance_score: request.relevanceScore || 1.0,
|
|
46
|
-
scope: scope
|
|
46
|
+
scope: scope,
|
|
47
|
+
preference_key: request.preferenceKey,
|
|
48
|
+
is_active: true
|
|
47
49
|
};
|
|
48
50
|
this.storage.save(memory);
|
|
49
51
|
this.logger.logMemoryOperation('STORE', {
|
|
@@ -303,6 +305,30 @@ class MemoryService {
|
|
|
303
305
|
throw error;
|
|
304
306
|
}
|
|
305
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Mark all currently-active memories with the given preference_key as superseded by newKey.
|
|
310
|
+
* Returns the list of superseded keys so callers can surface them to the agent — this
|
|
311
|
+
* closes the "I stored an override but the old rule is still in my context" gap.
|
|
312
|
+
*/
|
|
313
|
+
supersedeByPreferenceKey(preferenceKey, newKey, context) {
|
|
314
|
+
if (!preferenceKey || !newKey)
|
|
315
|
+
return [];
|
|
316
|
+
const superseded = [];
|
|
317
|
+
try {
|
|
318
|
+
const pid = context.projectId || this.config.getProjectId();
|
|
319
|
+
const existing = this.storage.getActiveByPreferenceKeyAnyType(preferenceKey, pid);
|
|
320
|
+
for (const prev of existing) {
|
|
321
|
+
if (prev.key !== newKey) {
|
|
322
|
+
this.storage.markSuperseded(prev.key, newKey);
|
|
323
|
+
superseded.push(prev.key);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
this.logger.logServiceError('MemoryService', 'supersedeByPreferenceKey', error, { preferenceKey, newKey });
|
|
329
|
+
}
|
|
330
|
+
return superseded;
|
|
331
|
+
}
|
|
306
332
|
/**
|
|
307
333
|
* Mark existing preferences as superseded
|
|
308
334
|
*/
|
package/package.json
CHANGED