@mnemo-mcp/core 1.0.0
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/LICENSE +21 -0
- package/README.md +123 -0
- package/dist/duration.d.ts +8 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +31 -0
- package/dist/duration.js.map +1 -0
- package/dist/embedder.d.ts +19 -0
- package/dist/embedder.d.ts.map +1 -0
- package/dist/embedder.js +33 -0
- package/dist/embedder.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/mnemo.d.ts +22 -0
- package/dist/mnemo.d.ts.map +1 -0
- package/dist/mnemo.js +266 -0
- package/dist/mnemo.js.map +1 -0
- package/dist/onnx-embedder.d.ts +10 -0
- package/dist/onnx-embedder.d.ts.map +1 -0
- package/dist/onnx-embedder.js +30 -0
- package/dist/onnx-embedder.js.map +1 -0
- package/dist/paths.d.ts +12 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +24 -0
- package/dist/paths.js.map +1 -0
- package/dist/ranker.d.ts +9 -0
- package/dist/ranker.d.ts.map +1 -0
- package/dist/ranker.js +16 -0
- package/dist/ranker.js.map +1 -0
- package/dist/store.d.ts +32 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +223 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/vector-index.d.ts +31 -0
- package/dist/vector-index.d.ts.map +1 -0
- package/dist/vector-index.js +98 -0
- package/dist/vector-index.js.map +1 -0
- package/package.json +53 -0
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function resolveDataDir(override?: string): string;
|
|
2
|
+
export type Paths = {
|
|
3
|
+
dataDir: string;
|
|
4
|
+
dbFile: string;
|
|
5
|
+
indexFile: string;
|
|
6
|
+
modelDir: string;
|
|
7
|
+
configFile: string;
|
|
8
|
+
logFile: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function paths(dataDir: string): Paths;
|
|
11
|
+
export declare function projectHashOf(absolutePath: string): string;
|
|
12
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED,MAAM,MAAM,KAAK,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAS5C;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE1D"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
export function resolveDataDir(override) {
|
|
5
|
+
if (override)
|
|
6
|
+
return override;
|
|
7
|
+
if (process.env.MNEMO_DATA_DIR)
|
|
8
|
+
return process.env.MNEMO_DATA_DIR;
|
|
9
|
+
return join(homedir(), '.mnemo');
|
|
10
|
+
}
|
|
11
|
+
export function paths(dataDir) {
|
|
12
|
+
return {
|
|
13
|
+
dataDir,
|
|
14
|
+
dbFile: join(dataDir, 'memory.db'),
|
|
15
|
+
indexFile: join(dataDir, 'hnsw.bin'),
|
|
16
|
+
modelDir: join(dataDir, 'model'),
|
|
17
|
+
configFile: join(dataDir, 'config.json'),
|
|
18
|
+
logFile: join(dataDir, 'mnemo.log'),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function projectHashOf(absolutePath) {
|
|
22
|
+
return createHash('sha256').update(absolutePath).digest('hex').slice(0, 16);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,UAAU,cAAc,CAAC,QAAiB;IAC9C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAClE,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAWD,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,OAAO;QACL,OAAO;QACP,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;QAClC,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC;QACpC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC;QAChC,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QACxC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,YAAoB;IAChD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC9E,CAAC"}
|
package/dist/ranker.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MemoryRecord } from './types.js';
|
|
2
|
+
export type RankerWeights = {
|
|
3
|
+
similarity: number;
|
|
4
|
+
recency: number;
|
|
5
|
+
access: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const DEFAULT_WEIGHTS: RankerWeights;
|
|
8
|
+
export declare function score(similarity: number, rec: MemoryRecord, weights?: RankerWeights, now?: number): number;
|
|
9
|
+
//# sourceMappingURL=ranker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ranker.d.ts","sourceRoot":"","sources":["../src/ranker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,aAI7B,CAAC;AAKF,wBAAgB,KAAK,CACnB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,YAAY,EACjB,OAAO,GAAE,aAA+B,EACxC,GAAG,GAAE,MAAmB,GACvB,MAAM,CASR"}
|
package/dist/ranker.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const DEFAULT_WEIGHTS = {
|
|
2
|
+
similarity: 0.7,
|
|
3
|
+
recency: 0.2,
|
|
4
|
+
access: 0.1,
|
|
5
|
+
};
|
|
6
|
+
const RECENCY_HALF_LIFE_DAYS = 30;
|
|
7
|
+
const ACCESS_SATURATION = 20;
|
|
8
|
+
export function score(similarity, rec, weights = DEFAULT_WEIGHTS, now = Date.now()) {
|
|
9
|
+
const ageDays = Math.max(0, (now - rec.lastAccessedAt) / 86400_000);
|
|
10
|
+
const recency = Math.exp(-ageDays / RECENCY_HALF_LIFE_DAYS);
|
|
11
|
+
const accessBoost = Math.min(1, rec.accessCount / ACCESS_SATURATION);
|
|
12
|
+
return (weights.similarity * similarity +
|
|
13
|
+
weights.recency * recency +
|
|
14
|
+
weights.access * accessBoost);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=ranker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ranker.js","sourceRoot":"","sources":["../src/ranker.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,eAAe,GAAkB;IAC5C,UAAU,EAAE,GAAG;IACf,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE,GAAG;CACZ,CAAC;AAEF,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,MAAM,UAAU,KAAK,CACnB,UAAkB,EAClB,GAAiB,EACjB,UAAyB,eAAe,EACxC,MAAc,IAAI,CAAC,GAAG,EAAE;IAExB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,GAAG,sBAAsB,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,WAAW,GAAG,iBAAiB,CAAC,CAAC;IACrE,OAAO,CACL,OAAO,CAAC,UAAU,GAAG,UAAU;QAC/B,OAAO,CAAC,OAAO,GAAG,OAAO;QACzB,OAAO,CAAC,MAAM,GAAG,WAAW,CAC7B,CAAC;AACJ,CAAC"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { MemoryRecord, MemoryScope, MemorySource, UpdateInput } from './types.js';
|
|
2
|
+
export type StoreCounts = {
|
|
3
|
+
total: number;
|
|
4
|
+
byScope: Record<MemoryScope, number>;
|
|
5
|
+
expired: number;
|
|
6
|
+
};
|
|
7
|
+
export type StoreListFilter = {
|
|
8
|
+
scope?: MemoryScope;
|
|
9
|
+
projectHash?: string | null;
|
|
10
|
+
source?: MemorySource | MemorySource[];
|
|
11
|
+
tags?: string[];
|
|
12
|
+
limit?: number;
|
|
13
|
+
since?: number;
|
|
14
|
+
includeExpired?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export declare class Store {
|
|
17
|
+
private db;
|
|
18
|
+
private path;
|
|
19
|
+
private constructor();
|
|
20
|
+
static open(path: string): Promise<Store>;
|
|
21
|
+
upsert(rec: MemoryRecord): Promise<void>;
|
|
22
|
+
update(id: string, fields: UpdateInput): Promise<MemoryRecord | null>;
|
|
23
|
+
get(id: string): Promise<MemoryRecord | null>;
|
|
24
|
+
delete(id: string): Promise<void>;
|
|
25
|
+
list(filter?: StoreListFilter): Promise<MemoryRecord[]>;
|
|
26
|
+
count(): Promise<StoreCounts>;
|
|
27
|
+
bumpAccess(id: string): Promise<void>;
|
|
28
|
+
flush(): Promise<void>;
|
|
29
|
+
close(): void;
|
|
30
|
+
private fromRow;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAqBvF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAC;IACvC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAsBF,qBAAa,KAAK;IAEd,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,IAAI;IAFd,OAAO;WAKM,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAwBzC,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BxC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAgBrE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAY7C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,IAAI,CAAC,MAAM,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyC3D,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IAkB7B,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,OAAO;CAehB"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import initSqlJs from 'sql.js';
|
|
2
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const SCHEMA = `
|
|
7
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
8
|
+
id TEXT PRIMARY KEY,
|
|
9
|
+
scope TEXT NOT NULL,
|
|
10
|
+
project_hash TEXT,
|
|
11
|
+
source TEXT NOT NULL,
|
|
12
|
+
content TEXT NOT NULL,
|
|
13
|
+
tags TEXT NOT NULL,
|
|
14
|
+
created_at INTEGER NOT NULL,
|
|
15
|
+
updated_at INTEGER NOT NULL,
|
|
16
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
17
|
+
last_accessed_at INTEGER NOT NULL,
|
|
18
|
+
expires_at INTEGER
|
|
19
|
+
);
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_scope_project ON memories(scope, project_hash);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_updated ON memories(updated_at);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_expires ON memories(expires_at);
|
|
23
|
+
`;
|
|
24
|
+
let cachedSqlPromise = null;
|
|
25
|
+
function loadSql() {
|
|
26
|
+
if (!cachedSqlPromise) {
|
|
27
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
cachedSqlPromise = initSqlJs({
|
|
29
|
+
locateFile: (file) => {
|
|
30
|
+
const candidates = [
|
|
31
|
+
join(here, '..', '..', '..', 'node_modules', 'sql.js', 'dist', file),
|
|
32
|
+
join(here, '..', '..', 'node_modules', 'sql.js', 'dist', file),
|
|
33
|
+
join(here, '..', 'node_modules', 'sql.js', 'dist', file),
|
|
34
|
+
];
|
|
35
|
+
for (const c of candidates)
|
|
36
|
+
if (existsSync(c))
|
|
37
|
+
return c;
|
|
38
|
+
return file;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return cachedSqlPromise;
|
|
43
|
+
}
|
|
44
|
+
export class Store {
|
|
45
|
+
db;
|
|
46
|
+
path;
|
|
47
|
+
constructor(db, path) {
|
|
48
|
+
this.db = db;
|
|
49
|
+
this.path = path;
|
|
50
|
+
}
|
|
51
|
+
static async open(path) {
|
|
52
|
+
const SQL = (await loadSql());
|
|
53
|
+
let db;
|
|
54
|
+
let isFresh = false;
|
|
55
|
+
if (existsSync(path)) {
|
|
56
|
+
const bytes = await readFile(path);
|
|
57
|
+
db = new SQL.Database(new Uint8Array(bytes));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
await mkdir(dirname(path), { recursive: true });
|
|
61
|
+
db = new SQL.Database();
|
|
62
|
+
isFresh = true;
|
|
63
|
+
}
|
|
64
|
+
db.exec(SCHEMA);
|
|
65
|
+
// Best-effort migration for legacy dbs that predate expires_at:
|
|
66
|
+
try {
|
|
67
|
+
db.exec('ALTER TABLE memories ADD COLUMN expires_at INTEGER');
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// column already exists — fine
|
|
71
|
+
}
|
|
72
|
+
const store = new Store(db, path);
|
|
73
|
+
if (isFresh)
|
|
74
|
+
await store.flush();
|
|
75
|
+
return store;
|
|
76
|
+
}
|
|
77
|
+
async upsert(rec) {
|
|
78
|
+
this.db.run(`INSERT INTO memories
|
|
79
|
+
(id, scope, project_hash, source, content, tags, created_at, updated_at, access_count, last_accessed_at, expires_at)
|
|
80
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
81
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
82
|
+
scope=excluded.scope,
|
|
83
|
+
project_hash=excluded.project_hash,
|
|
84
|
+
source=excluded.source,
|
|
85
|
+
content=excluded.content,
|
|
86
|
+
tags=excluded.tags,
|
|
87
|
+
updated_at=excluded.updated_at,
|
|
88
|
+
expires_at=excluded.expires_at`, [
|
|
89
|
+
rec.id,
|
|
90
|
+
rec.scope,
|
|
91
|
+
rec.projectHash,
|
|
92
|
+
rec.source,
|
|
93
|
+
rec.content,
|
|
94
|
+
JSON.stringify(rec.tags),
|
|
95
|
+
rec.createdAt,
|
|
96
|
+
rec.updatedAt,
|
|
97
|
+
rec.accessCount,
|
|
98
|
+
rec.lastAccessedAt,
|
|
99
|
+
rec.expiresAt,
|
|
100
|
+
]);
|
|
101
|
+
await this.flush();
|
|
102
|
+
}
|
|
103
|
+
async update(id, fields) {
|
|
104
|
+
const existing = await this.get(id);
|
|
105
|
+
if (!existing)
|
|
106
|
+
return null;
|
|
107
|
+
const next = {
|
|
108
|
+
...existing,
|
|
109
|
+
content: fields.content ?? existing.content,
|
|
110
|
+
tags: fields.tags ?? existing.tags,
|
|
111
|
+
scope: fields.scope ?? existing.scope,
|
|
112
|
+
projectHash: fields.projectHash !== undefined ? fields.projectHash : existing.projectHash,
|
|
113
|
+
expiresAt: fields.expiresAt !== undefined ? fields.expiresAt : existing.expiresAt,
|
|
114
|
+
updatedAt: Date.now(),
|
|
115
|
+
};
|
|
116
|
+
await this.upsert(next);
|
|
117
|
+
return next;
|
|
118
|
+
}
|
|
119
|
+
async get(id) {
|
|
120
|
+
const stmt = this.db.prepare('SELECT * FROM memories WHERE id = ?');
|
|
121
|
+
stmt.bind([id]);
|
|
122
|
+
if (!stmt.step()) {
|
|
123
|
+
stmt.free();
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const row = stmt.getAsObject();
|
|
127
|
+
stmt.free();
|
|
128
|
+
return this.fromRow(row);
|
|
129
|
+
}
|
|
130
|
+
async delete(id) {
|
|
131
|
+
this.db.run('DELETE FROM memories WHERE id = ?', [id]);
|
|
132
|
+
await this.flush();
|
|
133
|
+
}
|
|
134
|
+
async list(filter = {}) {
|
|
135
|
+
const where = [];
|
|
136
|
+
const args = [];
|
|
137
|
+
if (filter.scope) {
|
|
138
|
+
where.push('scope = ?');
|
|
139
|
+
args.push(filter.scope);
|
|
140
|
+
}
|
|
141
|
+
if (filter.projectHash !== undefined) {
|
|
142
|
+
if (filter.projectHash === null) {
|
|
143
|
+
where.push('project_hash IS NULL');
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
where.push('project_hash = ?');
|
|
147
|
+
args.push(filter.projectHash);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (filter.since) {
|
|
151
|
+
where.push('updated_at >= ?');
|
|
152
|
+
args.push(filter.since);
|
|
153
|
+
}
|
|
154
|
+
if (filter.source) {
|
|
155
|
+
const sources = Array.isArray(filter.source) ? filter.source : [filter.source];
|
|
156
|
+
where.push(`source IN (${sources.map(() => '?').join(',')})`);
|
|
157
|
+
args.push(...sources);
|
|
158
|
+
}
|
|
159
|
+
if (!filter.includeExpired) {
|
|
160
|
+
where.push('(expires_at IS NULL OR expires_at > ?)');
|
|
161
|
+
args.push(Date.now());
|
|
162
|
+
}
|
|
163
|
+
const sql = `SELECT * FROM memories ${where.length ? 'WHERE ' + where.join(' AND ') : ''} ORDER BY updated_at DESC ${filter.limit ? 'LIMIT ' + Number(filter.limit) : ''}`;
|
|
164
|
+
const stmt = this.db.prepare(sql);
|
|
165
|
+
if (args.length)
|
|
166
|
+
stmt.bind(args);
|
|
167
|
+
let rows = [];
|
|
168
|
+
while (stmt.step())
|
|
169
|
+
rows.push(this.fromRow(stmt.getAsObject()));
|
|
170
|
+
stmt.free();
|
|
171
|
+
if (filter.tags && filter.tags.length) {
|
|
172
|
+
const required = filter.tags;
|
|
173
|
+
rows = rows.filter(r => required.every(t => r.tags.includes(t)));
|
|
174
|
+
}
|
|
175
|
+
return rows;
|
|
176
|
+
}
|
|
177
|
+
async count() {
|
|
178
|
+
const stmt = this.db.prepare('SELECT scope, COUNT(*) as n FROM memories GROUP BY scope');
|
|
179
|
+
const byScope = { project: 0, global: 0 };
|
|
180
|
+
let total = 0;
|
|
181
|
+
while (stmt.step()) {
|
|
182
|
+
const row = stmt.getAsObject();
|
|
183
|
+
byScope[row.scope] = row.n;
|
|
184
|
+
total += row.n;
|
|
185
|
+
}
|
|
186
|
+
stmt.free();
|
|
187
|
+
const expStmt = this.db.prepare('SELECT COUNT(*) as n FROM memories WHERE expires_at IS NOT NULL AND expires_at <= ?');
|
|
188
|
+
expStmt.bind([Date.now()]);
|
|
189
|
+
let expired = 0;
|
|
190
|
+
if (expStmt.step())
|
|
191
|
+
expired = Number(expStmt.getAsObject().n);
|
|
192
|
+
expStmt.free();
|
|
193
|
+
return { total, byScope, expired };
|
|
194
|
+
}
|
|
195
|
+
async bumpAccess(id) {
|
|
196
|
+
const now = Date.now();
|
|
197
|
+
this.db.run('UPDATE memories SET access_count = access_count + 1, last_accessed_at = ? WHERE id = ?', [now, id]);
|
|
198
|
+
await this.flush();
|
|
199
|
+
}
|
|
200
|
+
async flush() {
|
|
201
|
+
const data = this.db.export();
|
|
202
|
+
await writeFile(this.path, Buffer.from(data));
|
|
203
|
+
}
|
|
204
|
+
close() {
|
|
205
|
+
this.db.close();
|
|
206
|
+
}
|
|
207
|
+
fromRow(row) {
|
|
208
|
+
return {
|
|
209
|
+
id: String(row.id),
|
|
210
|
+
scope: row.scope,
|
|
211
|
+
projectHash: row.project_hash ?? null,
|
|
212
|
+
source: row.source,
|
|
213
|
+
content: String(row.content),
|
|
214
|
+
tags: JSON.parse(String(row.tags ?? '[]')),
|
|
215
|
+
createdAt: Number(row.created_at),
|
|
216
|
+
updatedAt: Number(row.updated_at),
|
|
217
|
+
accessCount: Number(row.access_count),
|
|
218
|
+
lastAccessedAt: Number(row.last_accessed_at),
|
|
219
|
+
expiresAt: row.expires_at == null ? null : Number(row.expires_at),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export type MemoryScope = 'project' | 'global';
|
|
2
|
+
export type MemorySource = 'manual' | 'auto-edit' | 'auto-task' | 'imported';
|
|
3
|
+
export type MemoryRecord = {
|
|
4
|
+
id: string;
|
|
5
|
+
scope: MemoryScope;
|
|
6
|
+
projectHash: string | null;
|
|
7
|
+
source: MemorySource;
|
|
8
|
+
content: string;
|
|
9
|
+
tags: string[];
|
|
10
|
+
createdAt: number;
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
accessCount: number;
|
|
13
|
+
lastAccessedAt: number;
|
|
14
|
+
/** Unix ms after which the memory should be considered expired and skipped from recall. null = never. */
|
|
15
|
+
expiresAt: number | null;
|
|
16
|
+
};
|
|
17
|
+
export type CaptureInput = {
|
|
18
|
+
content: string;
|
|
19
|
+
scope?: MemoryScope;
|
|
20
|
+
projectHash?: string | null;
|
|
21
|
+
source?: MemorySource;
|
|
22
|
+
tags?: string[];
|
|
23
|
+
/** Unix ms or null. Convenience helper: pass `Date.now() + ms` for relative TTL. */
|
|
24
|
+
expiresAt?: number | null;
|
|
25
|
+
/**
|
|
26
|
+
* Cosine-similarity threshold (0–1). If a candidate already exists with similarity ≥ this,
|
|
27
|
+
* Mnemo updates the existing record instead of inserting a duplicate. Default 0.95.
|
|
28
|
+
* Pass `0` to always insert.
|
|
29
|
+
*/
|
|
30
|
+
dedupThreshold?: number;
|
|
31
|
+
};
|
|
32
|
+
export type RecallOpts = {
|
|
33
|
+
k?: number;
|
|
34
|
+
scope?: MemoryScope | 'all';
|
|
35
|
+
projectHash?: string | null;
|
|
36
|
+
minScore?: number;
|
|
37
|
+
/** Filter results to memories that have ALL of these tags. */
|
|
38
|
+
tags?: string[];
|
|
39
|
+
/** Filter results by source. */
|
|
40
|
+
source?: MemorySource | MemorySource[];
|
|
41
|
+
/** Only consider memories with `updatedAt >= since` (unix ms). */
|
|
42
|
+
since?: number;
|
|
43
|
+
/** Include expired memories in results. Default false. */
|
|
44
|
+
includeExpired?: boolean;
|
|
45
|
+
};
|
|
46
|
+
export type MemoryHit = {
|
|
47
|
+
record: MemoryRecord;
|
|
48
|
+
score: number;
|
|
49
|
+
similarity: number;
|
|
50
|
+
};
|
|
51
|
+
export type ListFilter = {
|
|
52
|
+
scope?: MemoryScope;
|
|
53
|
+
projectHash?: string | null;
|
|
54
|
+
tags?: string[];
|
|
55
|
+
source?: MemorySource | MemorySource[];
|
|
56
|
+
limit?: number;
|
|
57
|
+
since?: number;
|
|
58
|
+
includeExpired?: boolean;
|
|
59
|
+
};
|
|
60
|
+
export type UpdateInput = {
|
|
61
|
+
content?: string;
|
|
62
|
+
tags?: string[];
|
|
63
|
+
scope?: MemoryScope;
|
|
64
|
+
projectHash?: string | null;
|
|
65
|
+
expiresAt?: number | null;
|
|
66
|
+
};
|
|
67
|
+
export type PruneOpts = {
|
|
68
|
+
/** Drop memories whose `expiresAt` is in the past. Default true. */
|
|
69
|
+
expired?: boolean;
|
|
70
|
+
/** Drop memories with similarity ≥ this to a *more recently accessed* peer. Default 0.97. Set 0 to disable. */
|
|
71
|
+
duplicateThreshold?: number;
|
|
72
|
+
/** Drop memories with `accessCount` < this AND older than `staleAfterDays`. Default disabled (0). */
|
|
73
|
+
minAccessCount?: number;
|
|
74
|
+
staleAfterDays?: number;
|
|
75
|
+
/** When true, only return what would be pruned without deleting. */
|
|
76
|
+
dryRun?: boolean;
|
|
77
|
+
};
|
|
78
|
+
export type PruneResult = {
|
|
79
|
+
expired: MemoryRecord[];
|
|
80
|
+
duplicates: {
|
|
81
|
+
kept: MemoryRecord;
|
|
82
|
+
dropped: MemoryRecord;
|
|
83
|
+
}[];
|
|
84
|
+
stale: MemoryRecord[];
|
|
85
|
+
totalDeleted: number;
|
|
86
|
+
};
|
|
87
|
+
export type MnemoStats = {
|
|
88
|
+
totalMemories: number;
|
|
89
|
+
byScope: Record<MemoryScope, number>;
|
|
90
|
+
indexSize: number;
|
|
91
|
+
embeddingDimension: number;
|
|
92
|
+
storageBytes: number;
|
|
93
|
+
expired: number;
|
|
94
|
+
};
|
|
95
|
+
export type MnemoOpts = {
|
|
96
|
+
dataDir?: string;
|
|
97
|
+
embedderType?: 'onnx' | 'hash';
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type VectorIndexOpts = {
|
|
2
|
+
path: string;
|
|
3
|
+
dimension: number;
|
|
4
|
+
maxElements?: number;
|
|
5
|
+
m?: number;
|
|
6
|
+
efConstruction?: number;
|
|
7
|
+
};
|
|
8
|
+
export type VectorHit = {
|
|
9
|
+
id: string;
|
|
10
|
+
similarity: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* HNSW vector index with id-based lookup. Stores a label↔id mapping
|
|
14
|
+
* separately because hnswlib uses numeric labels.
|
|
15
|
+
*/
|
|
16
|
+
export declare class VectorIndex {
|
|
17
|
+
private hnsw;
|
|
18
|
+
private opts;
|
|
19
|
+
private nextLabel;
|
|
20
|
+
private idToLabel;
|
|
21
|
+
private labelToId;
|
|
22
|
+
private constructor();
|
|
23
|
+
private static mapPath;
|
|
24
|
+
static open(opts: VectorIndexOpts): Promise<VectorIndex>;
|
|
25
|
+
add(id: string, vector: Float32Array): Promise<void>;
|
|
26
|
+
remove(id: string): Promise<void>;
|
|
27
|
+
query(vector: Float32Array, k: number): Promise<VectorHit[]>;
|
|
28
|
+
size(): number;
|
|
29
|
+
save(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=vector-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-index.d.ts","sourceRoot":"","sources":["../src/vector-index.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAOF;;;GAGG;AACH,qBAAa,WAAW;IAMpB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,IAAI;IANd,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,SAAS,CAA6B;IAE9C,OAAO;IAKP,OAAO,CAAC,MAAM,CAAC,OAAO;WAIT,IAAI,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IA6BxD,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAWpD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjC,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAelE,IAAI,IAAI,MAAM;IAIR,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ5B"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import hnswlib from 'hnswlib-node';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
const { HierarchicalNSW } = hnswlib;
|
|
4
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { dirname } from 'node:path';
|
|
6
|
+
/**
|
|
7
|
+
* HNSW vector index with id-based lookup. Stores a label↔id mapping
|
|
8
|
+
* separately because hnswlib uses numeric labels.
|
|
9
|
+
*/
|
|
10
|
+
export class VectorIndex {
|
|
11
|
+
hnsw;
|
|
12
|
+
opts;
|
|
13
|
+
nextLabel = 1;
|
|
14
|
+
idToLabel = new Map();
|
|
15
|
+
labelToId = new Map();
|
|
16
|
+
constructor(hnsw, opts) {
|
|
17
|
+
this.hnsw = hnsw;
|
|
18
|
+
this.opts = opts;
|
|
19
|
+
}
|
|
20
|
+
static mapPath(p) {
|
|
21
|
+
return `${p}.map.json`;
|
|
22
|
+
}
|
|
23
|
+
static async open(opts) {
|
|
24
|
+
const full = {
|
|
25
|
+
path: opts.path,
|
|
26
|
+
dimension: opts.dimension,
|
|
27
|
+
maxElements: opts.maxElements ?? 100_000,
|
|
28
|
+
m: opts.m ?? 16,
|
|
29
|
+
efConstruction: opts.efConstruction ?? 200,
|
|
30
|
+
};
|
|
31
|
+
const hnsw = new HierarchicalNSW('cosine', full.dimension);
|
|
32
|
+
if (existsSync(full.path)) {
|
|
33
|
+
hnsw.readIndexSync(full.path);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
await mkdir(dirname(full.path), { recursive: true });
|
|
37
|
+
hnsw.initIndex(full.maxElements, full.m, full.efConstruction);
|
|
38
|
+
}
|
|
39
|
+
hnsw.setEf(Math.max(50, full.m * 4));
|
|
40
|
+
const idx = new VectorIndex(hnsw, full);
|
|
41
|
+
if (existsSync(VectorIndex.mapPath(full.path))) {
|
|
42
|
+
const raw = await readFile(VectorIndex.mapPath(full.path), 'utf8');
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
idx.nextLabel = parsed.nextLabel;
|
|
45
|
+
for (const [id, label] of parsed.entries) {
|
|
46
|
+
idx.idToLabel.set(id, label);
|
|
47
|
+
idx.labelToId.set(label, id);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return idx;
|
|
51
|
+
}
|
|
52
|
+
async add(id, vector) {
|
|
53
|
+
if (vector.length !== this.opts.dimension) {
|
|
54
|
+
throw new Error(`vector dim ${vector.length} != index dim ${this.opts.dimension}`);
|
|
55
|
+
}
|
|
56
|
+
const existing = this.idToLabel.get(id);
|
|
57
|
+
const label = existing ?? this.nextLabel++;
|
|
58
|
+
this.hnsw.addPoint(Array.from(vector), label);
|
|
59
|
+
this.idToLabel.set(id, label);
|
|
60
|
+
this.labelToId.set(label, id);
|
|
61
|
+
}
|
|
62
|
+
async remove(id) {
|
|
63
|
+
const label = this.idToLabel.get(id);
|
|
64
|
+
if (label === undefined)
|
|
65
|
+
return;
|
|
66
|
+
this.hnsw.markDelete(label);
|
|
67
|
+
this.idToLabel.delete(id);
|
|
68
|
+
this.labelToId.delete(label);
|
|
69
|
+
}
|
|
70
|
+
async query(vector, k) {
|
|
71
|
+
if (this.size() === 0)
|
|
72
|
+
return [];
|
|
73
|
+
const got = this.hnsw.searchKnn(Array.from(vector), Math.min(k, this.size()));
|
|
74
|
+
const out = [];
|
|
75
|
+
for (let i = 0; i < got.neighbors.length; i++) {
|
|
76
|
+
const label = got.neighbors[i];
|
|
77
|
+
const id = this.labelToId.get(label);
|
|
78
|
+
if (!id)
|
|
79
|
+
continue;
|
|
80
|
+
// hnswlib returns cosine *distance* (1 - sim) for 'cosine' space.
|
|
81
|
+
const distance = got.distances[i];
|
|
82
|
+
out.push({ id, similarity: 1 - distance });
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
size() {
|
|
87
|
+
return this.idToLabel.size;
|
|
88
|
+
}
|
|
89
|
+
async save() {
|
|
90
|
+
this.hnsw.writeIndexSync(this.opts.path);
|
|
91
|
+
const map = {
|
|
92
|
+
entries: [...this.idToLabel.entries()],
|
|
93
|
+
nextLabel: this.nextLabel,
|
|
94
|
+
};
|
|
95
|
+
await writeFile(VectorIndex.mapPath(this.opts.path), JSON.stringify(map));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=vector-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-index.js","sourceRoot":"","sources":["../src/vector-index.ts"],"names":[],"mappings":"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"}
|