@pi-unipi/memory 2.3.0 → 2.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/memory",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "description": "Persistent cross-session memory with MemPalace backend (auto-installed) and SQLite fallback for Pi coding agent",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -43,8 +43,8 @@
43
43
  "better-sqlite3": "^12.9.0",
44
44
  "sqlite-vec": "^0.1.9",
45
45
  "js-yaml": "^4.1.0",
46
- "@pi-unipi/core": "2.3.0",
47
- "@pi-unipi/info-screen": "2.3.0"
46
+ "@pi-unipi/core": "2.4.1",
47
+ "@pi-unipi/info-screen": "2.4.1"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@earendil-works/pi-coding-agent": "^0.80.0",
package/storage.ts CHANGED
@@ -454,24 +454,6 @@ export class MemoryStorage {
454
454
  }
455
455
  }
456
456
 
457
- /**
458
- * Remove corrupted database files (db, wal, shm).
459
- */
460
- private removeCorruptedDb(): void {
461
- const dbPath = path.join(this.scopeDir, MEMORY_DB_NAME);
462
- const files = [dbPath, `${dbPath}-wal`, `${dbPath}-shm`];
463
- for (const file of files) {
464
- try {
465
- if (fs.existsSync(file)) {
466
- fs.unlinkSync(file);
467
- // Removed console.warn — corrupted file cleanup is silent.
468
- }
469
- } catch {
470
- // Ignore removal errors
471
- }
472
- }
473
- }
474
-
475
457
  /**
476
458
  * Check if database is healthy.
477
459
  */
package/tools.ts CHANGED
@@ -15,7 +15,6 @@ import {
15
15
  type MemoryRecord,
16
16
  } from "./storage.js";
17
17
  import { generateEmbedding } from "./embedding.js";
18
- import { hybridSearch } from "./search.js";
19
18
 
20
19
  /** Tool names */
21
20
  export const MEMORY_TOOLS = {
package/search.ts DELETED
@@ -1,134 +0,0 @@
1
- /**
2
- * @unipi/memory — Hybrid search algorithm
3
- *
4
- * Combines vector similarity search with fuzzy text matching
5
- * for best recall across semantic and exact matches.
6
- */
7
-
8
- import type { MemoryStorage, MemoryRecord, SearchResult } from "./storage.js";
9
-
10
- /**
11
- * Perform hybrid search combining vector + fuzzy.
12
- */
13
- export function hybridSearch(
14
- storage: MemoryStorage,
15
- query: string,
16
- limit = 10,
17
- embedding?: Float32Array | null
18
- ): SearchResult[] {
19
- // Delegate to storage's search method which already implements hybrid
20
- return storage.search(query, limit, embedding);
21
- }
22
-
23
- /**
24
- * Calculate fuzzy match score between text and query.
25
- * Returns 0-1 score (1 = perfect match).
26
- */
27
- export function fuzzyMatch(text: string, query: string): number {
28
- const lowerText = text.toLowerCase();
29
- const lowerQuery = query.toLowerCase();
30
-
31
- // Exact match
32
- if (lowerText === lowerQuery) return 1.0;
33
-
34
- // Starts with
35
- if (lowerText.startsWith(lowerQuery)) return 0.9;
36
-
37
- // Contains
38
- if (lowerText.includes(lowerQuery)) return 0.7;
39
-
40
- // Word boundary match
41
- const words = lowerQuery.split(/\s+/);
42
- let matchedWords = 0;
43
- for (const word of words) {
44
- if (lowerText.includes(word)) {
45
- matchedWords++;
46
- }
47
- }
48
- if (matchedWords > 0) {
49
- return 0.3 + (matchedWords / words.length) * 0.4;
50
- }
51
-
52
- // Subsequence match
53
- let textIdx = 0;
54
- let queryIdx = 0;
55
- let subsequenceMatches = 0;
56
-
57
- while (textIdx < lowerText.length && queryIdx < lowerQuery.length) {
58
- if (lowerText[textIdx] === lowerQuery[queryIdx]) {
59
- subsequenceMatches++;
60
- queryIdx++;
61
- }
62
- textIdx++;
63
- }
64
-
65
- if (queryIdx === lowerQuery.length) {
66
- // All query chars found in order
67
- return 0.2 + (subsequenceMatches / lowerQuery.length) * 0.2;
68
- }
69
-
70
- return 0;
71
- }
72
-
73
- /**
74
- * Extract a snippet around the query match.
75
- */
76
- export function extractSnippet(
77
- content: string,
78
- query: string,
79
- chars = 150
80
- ): string {
81
- const lowerContent = content.toLowerCase();
82
- const lowerQuery = query.toLowerCase();
83
-
84
- // Find best match position
85
- let bestIdx = -1;
86
- let bestScore = 0;
87
-
88
- const words = lowerQuery.split(/\s+/);
89
- for (const word of words) {
90
- const idx = lowerContent.indexOf(word);
91
- if (idx !== -1 && (bestIdx === -1 || idx < bestIdx)) {
92
- bestIdx = idx;
93
- bestScore = 0.8;
94
- }
95
- }
96
-
97
- if (bestIdx === -1) {
98
- // No match, return beginning
99
- return content.slice(0, chars) + (content.length > chars ? "..." : "");
100
- }
101
-
102
- const start = Math.max(0, bestIdx - chars / 3);
103
- const end = Math.min(content.length, bestIdx + chars * 2 / 3);
104
- let snippet = content.slice(start, end);
105
-
106
- if (start > 0) snippet = "..." + snippet;
107
- if (end < content.length) snippet = snippet + "...";
108
-
109
- return snippet;
110
- }
111
-
112
- /**
113
- * Merge and deduplicate search results from multiple sources.
114
- */
115
- export function mergeResults(
116
- ...resultSets: SearchResult[][]
117
- ): SearchResult[] {
118
- const merged = new Map<string, SearchResult>();
119
-
120
- for (const results of resultSets) {
121
- for (const result of results) {
122
- const existing = merged.get(result.record.id);
123
- if (existing) {
124
- // Boost score if found in multiple sources
125
- existing.score = Math.min(existing.score + result.score * 0.2, 1);
126
- } else {
127
- merged.set(result.record.id, { ...result });
128
- }
129
- }
130
- }
131
-
132
- return Array.from(merged.values())
133
- .sort((a, b) => b.score - a.score);
134
- }