claude-memory-layer 1.0.15 → 1.0.16

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.
@@ -1446,28 +1446,33 @@ export class SQLiteEventStore {
1446
1446
  }>> {
1447
1447
  await this.initialize();
1448
1448
 
1449
- const rows = sqliteAll<Record<string, unknown>>(
1450
- this.db,
1451
- `SELECT * FROM retrieval_traces ORDER BY created_at DESC LIMIT ?`,
1452
- [limit]
1453
- );
1449
+ try {
1450
+ const rows = sqliteAll<Record<string, unknown>>(
1451
+ this.db,
1452
+ `SELECT * FROM retrieval_traces ORDER BY created_at DESC LIMIT ?`,
1453
+ [limit]
1454
+ );
1454
1455
 
1455
- return rows.map((row) => ({
1456
- traceId: row.trace_id as string,
1457
- sessionId: (row.session_id as string) || undefined,
1458
- projectHash: (row.project_hash as string) || undefined,
1459
- queryText: row.query_text as string,
1460
- strategy: (row.strategy as string) || undefined,
1461
- candidateEventIds: row.candidate_event_ids ? JSON.parse(row.candidate_event_ids as string) : [],
1462
- selectedEventIds: row.selected_event_ids ? JSON.parse(row.selected_event_ids as string) : [],
1463
- candidateDetails: row.candidate_details_json ? JSON.parse(row.candidate_details_json as string) : [],
1464
- selectedDetails: row.selected_details_json ? JSON.parse(row.selected_details_json as string) : [],
1465
- candidateCount: Number(row.candidate_count || 0),
1466
- selectedCount: Number(row.selected_count || 0),
1467
- confidence: (row.confidence as string) || undefined,
1468
- fallbackTrace: row.fallback_trace ? JSON.parse(row.fallback_trace as string) : [],
1469
- createdAt: toDateFromSQLite(row.created_at),
1470
- }));
1456
+ return rows.map((row) => ({
1457
+ traceId: row.trace_id as string,
1458
+ sessionId: (row.session_id as string) || undefined,
1459
+ projectHash: (row.project_hash as string) || undefined,
1460
+ queryText: row.query_text as string,
1461
+ strategy: (row.strategy as string) || undefined,
1462
+ candidateEventIds: row.candidate_event_ids ? JSON.parse(row.candidate_event_ids as string) : [],
1463
+ selectedEventIds: row.selected_event_ids ? JSON.parse(row.selected_event_ids as string) : [],
1464
+ candidateDetails: row.candidate_details_json ? JSON.parse(row.candidate_details_json as string) : [],
1465
+ selectedDetails: row.selected_details_json ? JSON.parse(row.selected_details_json as string) : [],
1466
+ candidateCount: Number(row.candidate_count || 0),
1467
+ selectedCount: Number(row.selected_count || 0),
1468
+ confidence: (row.confidence as string) || undefined,
1469
+ fallbackTrace: row.fallback_trace ? JSON.parse(row.fallback_trace as string) : [],
1470
+ createdAt: toDateFromSQLite(row.created_at),
1471
+ }));
1472
+ } catch (err: any) {
1473
+ if (err?.message?.includes('no such table')) return [];
1474
+ throw err;
1475
+ }
1471
1476
  }
1472
1477
 
1473
1478
  async getRetrievalTraceStats(): Promise<{
@@ -1478,26 +1483,33 @@ export class SQLiteEventStore {
1478
1483
  }> {
1479
1484
  await this.initialize();
1480
1485
 
1481
- const row = sqliteGet<Record<string, unknown>>(
1482
- this.db,
1483
- `SELECT
1484
- COUNT(*) as total_queries,
1485
- AVG(candidate_count) as avg_candidate_count,
1486
- AVG(selected_count) as avg_selected_count,
1487
- CASE
1488
- WHEN SUM(candidate_count) > 0 THEN (SUM(selected_count) * 1.0 / SUM(candidate_count))
1489
- ELSE 0
1490
- END as selection_rate
1491
- FROM retrieval_traces`,
1492
- []
1493
- );
1486
+ try {
1487
+ const row = sqliteGet<Record<string, unknown>>(
1488
+ this.db,
1489
+ `SELECT
1490
+ COUNT(*) as total_queries,
1491
+ AVG(candidate_count) as avg_candidate_count,
1492
+ AVG(selected_count) as avg_selected_count,
1493
+ CASE
1494
+ WHEN SUM(candidate_count) > 0 THEN (SUM(selected_count) * 1.0 / SUM(candidate_count))
1495
+ ELSE 0
1496
+ END as selection_rate
1497
+ FROM retrieval_traces`,
1498
+ []
1499
+ );
1494
1500
 
1495
- return {
1496
- totalQueries: Number(row?.total_queries || 0),
1497
- avgCandidateCount: Number(row?.avg_candidate_count || 0),
1498
- avgSelectedCount: Number(row?.avg_selected_count || 0),
1499
- selectionRate: Number(row?.selection_rate || 0),
1500
- };
1501
+ return {
1502
+ totalQueries: Number(row?.total_queries || 0),
1503
+ avgCandidateCount: Number(row?.avg_candidate_count || 0),
1504
+ avgSelectedCount: Number(row?.avg_selected_count || 0),
1505
+ selectionRate: Number(row?.selection_rate || 0),
1506
+ };
1507
+ } catch (err: any) {
1508
+ if (err?.message?.includes('no such table')) {
1509
+ return { totalQueries: 0, avgCandidateCount: 0, avgSelectedCount: 0, selectionRate: 0 };
1510
+ }
1511
+ throw err;
1512
+ }
1501
1513
  }
1502
1514
 
1503
1515
  /**
@@ -17,7 +17,9 @@ import type { UserPromptSubmitInput, UserPromptSubmitOutput } from '../core/type
17
17
 
18
18
  // Configuration
19
19
  const MAX_MEMORIES = parseInt(process.env.CLAUDE_MEMORY_MAX_COUNT || '5');
20
- const MIN_SCORE = parseFloat(process.env.CLAUDE_MEMORY_MIN_SCORE || '0.3');
20
+ // Tuned default for noise/recall balance on shopping_assistant-like corpus
21
+ const BASE_MIN_SCORE = parseFloat(process.env.CLAUDE_MEMORY_MIN_SCORE || '0.4');
22
+ const FALLBACK_MIN_SCORE = parseFloat(process.env.CLAUDE_MEMORY_FALLBACK_MIN_SCORE || '0.3');
21
23
  const ENABLE_SEARCH = process.env.CLAUDE_MEMORY_SEARCH !== 'false';
22
24
 
23
25
  /**
@@ -32,6 +34,14 @@ function shouldStorePrompt(prompt: string): boolean {
32
34
  return true;
33
35
  }
34
36
 
37
+
38
+ function getDynamicMinScore(prompt: string): number {
39
+ const len = prompt.trim().length;
40
+ if (len <= 20) return Math.min(0.55, BASE_MIN_SCORE + 0.1); // short query → stricter
41
+ if (len >= 80) return Math.max(0.3, BASE_MIN_SCORE - 0.05); // long query → slightly looser
42
+ return BASE_MIN_SCORE;
43
+ }
44
+
35
45
  async function main(): Promise<void> {
36
46
  // Read input from stdin
37
47
  const inputData = await readStdin();
@@ -61,11 +71,20 @@ async function main(): Promise<void> {
61
71
 
62
72
  // Fast keyword search if enabled
63
73
  if (ENABLE_SEARCH && input.prompt.length > 10) {
64
- const results = await memoryService.keywordSearch(input.prompt, {
74
+ const minScore = getDynamicMinScore(input.prompt);
75
+ let results = await memoryService.keywordSearch(input.prompt, {
65
76
  topK: MAX_MEMORIES,
66
- minScore: MIN_SCORE
77
+ minScore
67
78
  });
68
79
 
80
+ // recall rescue: if nothing found at tuned threshold, retry with fallback floor
81
+ if (results.length === 0 && FALLBACK_MIN_SCORE < minScore) {
82
+ results = await memoryService.keywordSearch(input.prompt, {
83
+ topK: MAX_MEMORIES,
84
+ minScore: FALLBACK_MIN_SCORE
85
+ });
86
+ }
87
+
69
88
  if (results.length > 0) {
70
89
  // Increment access count for found memories
71
90
  const eventIds = results.map(r => r.event.id);