mumpix 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.

Potentially problematic release.


This version of mumpix might be problematic. Click here for more details.

@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * examples/verified-mode.js
5
+ *
6
+ * Demonstrates Mumpix's "Verified" consistency mode:
7
+ * - Immutable audit log (.mumpix.audit)
8
+ * - Every write and recall is logged with hashes
9
+ * - Reproducible recall behavior
10
+ * - Export audit log as NDJSON for compliance hand-off
11
+ *
12
+ * Run:
13
+ * node examples/verified-mode.js
14
+ */
15
+
16
+ const { Mumpix } = require('../src/index');
17
+ const os = require('os');
18
+ const path = require('path');
19
+ const fs = require('fs');
20
+
21
+ const DB_PATH = path.join(os.tmpdir(), 'mumpix-verified-demo.mumpix');
22
+
23
+ ;(async () => {
24
+ // Clean up any previous run
25
+ [DB_PATH, DB_PATH + '.audit', DB_PATH + '.wal'].forEach(f => {
26
+ if (fs.existsSync(f)) fs.unlinkSync(f);
27
+ });
28
+
29
+ console.log('Opening Mumpix in Verified mode...\n');
30
+ const db = Mumpix.open(DB_PATH, { consistency: 'verified' });
31
+
32
+ // Simulate a compliance-relevant workflow
33
+ await db.remember('Loan application #4821 approved — credit score: 742');
34
+ await db.remember('Manual override applied by risk-officer-07 on 2025-02-18');
35
+ await db.remember('Model version: risk-v3.2.1, threshold: 0.72');
36
+ await db.remember('Basel III compliance flag: ACTIVE');
37
+ await db.remember('Customer consent recorded: 2025-02-17T14:32:00Z');
38
+
39
+ console.log(`Stored ${db.size} records.\n`);
40
+
41
+ // Recall queries
42
+ const queries = [
43
+ 'Was there a manual override?',
44
+ 'What model was used?',
45
+ 'Is it Basel III compliant?',
46
+ ];
47
+
48
+ console.log('Recall queries:');
49
+ for (const q of queries) {
50
+ const ans = await db.recall(q);
51
+ console.log(` Q: ${q}`);
52
+ console.log(` A: ${ans ?? '(no match)'}`);
53
+ console.log();
54
+ }
55
+
56
+ // Audit log
57
+ const entries = await db.audit();
58
+ console.log(`Audit log (${entries.length} entries):`);
59
+ entries.forEach(e => {
60
+ const ts = new Date(e.ts).toISOString();
61
+ const type = e._type.padEnd(10);
62
+ const detail =
63
+ e._type === 'write' ? `id=${e.id} hash=${e.hash} len=${e.len}`
64
+ : e._type === 'recall' ? `queryHash=${e.queryHash} resultId=${e.resultId ?? 'null'} ${e.latencyMs}ms`
65
+ : '';
66
+ console.log(` [${ts}] ${type} ${detail}`);
67
+ });
68
+
69
+ // Audit summary
70
+ const summary = await db.auditSummary();
71
+ console.log('\nAudit summary:', JSON.stringify(summary, null, 2));
72
+
73
+ // Export
74
+ const ndjson = await db.exportAudit();
75
+ const exportPath = DB_PATH.replace('.mumpix', '-audit-export.ndjson');
76
+ fs.writeFileSync(exportPath, ndjson, 'utf8');
77
+ console.log(`\nAudit exported to: ${exportPath}`);
78
+
79
+ await db.close();
80
+
81
+ // Cleanup
82
+ [DB_PATH, DB_PATH + '.audit', exportPath].forEach(f => {
83
+ if (fs.existsSync(f)) fs.unlinkSync(f);
84
+ });
85
+ })();
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "mumpix",
3
+ "version": "1.0.0",
4
+ "description": "SQLite for AI — embedded, zero-config memory database for AI agents and LLM applications",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "mumpix": "./bin/mumpix.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "ai",
14
+ "memory",
15
+ "database",
16
+ "embedded",
17
+ "vector",
18
+ "semantic",
19
+ "llm",
20
+ "agent",
21
+ "langchain",
22
+ "local-first",
23
+ "sqlite",
24
+ "storage",
25
+ "retrieval",
26
+ "rag"
27
+ ],
28
+ "author": "Mumpix",
29
+ "license": "MIT",
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "dependencies": {},
34
+ "devDependencies": {},
35
+ "files": [
36
+ "src/",
37
+ "bin/",
38
+ "examples/",
39
+ "README.md",
40
+ "LICENSE"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/mumpix/mumpix.git"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/mumpix/mumpix/issues"
48
+ },
49
+ "homepage": "https://mumpix.dev"
50
+ }
@@ -0,0 +1,306 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MumpixDB — main database class
5
+ *
6
+ * const { Mumpix } = require('mumpix')
7
+ * const db = await Mumpix.open('./agent.mumpix', { consistency: 'strict' })
8
+ *
9
+ * await db.remember('User prefers TypeScript')
10
+ * const ans = await db.recall('what language?')
11
+ * const all = await db.list()
12
+ * await db.clear()
13
+ * await db.close()
14
+ */
15
+
16
+ const { MumpixStore } = require('./store');
17
+ const { MumpixAudit } = require('./audit');
18
+ const { recall, recallMany } = require('./recall');
19
+
20
+ const VALID_MODES = ['eventual', 'strict', 'verified'];
21
+
22
+ class MumpixDB {
23
+ /**
24
+ * @param {MumpixStore} store
25
+ * @param {MumpixAudit|null} audit
26
+ * @param {object} opts
27
+ */
28
+ constructor(store, audit, opts = {}) {
29
+ this._store = store;
30
+ this._audit = audit; // non-null only in 'verified' mode
31
+ this._opts = opts;
32
+ this._closed = false;
33
+ }
34
+
35
+ // ─────────────────────────────────────────
36
+ // Factory
37
+ // ─────────────────────────────────────────
38
+
39
+ /**
40
+ * Open (or create) a Mumpix database.
41
+ *
42
+ * @param {string} filePath Path to .mumpix file
43
+ * @param {object} [opts]
44
+ * @param {string} [opts.consistency] 'eventual' | 'strict' | 'verified' (default: 'eventual')
45
+ * @param {Function} [opts.embedFn] async (texts: string[]) => number[][] — optional custom embeddings
46
+ * @returns {MumpixDB}
47
+ */
48
+ static open(filePath, opts = {}) {
49
+ if (!filePath || typeof filePath !== 'string') {
50
+ throw new TypeError('Mumpix.open() requires a file path string');
51
+ }
52
+
53
+ const consistency = opts.consistency || 'eventual';
54
+ if (!VALID_MODES.includes(consistency)) {
55
+ throw new Error(`Invalid consistency mode "${consistency}". Valid: ${VALID_MODES.join(', ')}`);
56
+ }
57
+
58
+ const store = new MumpixStore(filePath);
59
+ store.open({ consistency });
60
+
61
+ let auditLog = null;
62
+ if (consistency === 'verified') {
63
+ auditLog = new MumpixAudit(filePath);
64
+ auditLog.open();
65
+ }
66
+
67
+ return new MumpixDB(store, auditLog, { ...opts, consistency });
68
+ }
69
+
70
+ // ─────────────────────────────────────────
71
+ // Core API
72
+ // ─────────────────────────────────────────
73
+
74
+ /**
75
+ * Store a memory.
76
+ *
77
+ * @param {string} content
78
+ * @returns {{ id: number, written: boolean, consistency: string }}
79
+ */
80
+ async remember(content) {
81
+ this._assertOpen();
82
+ if (typeof content !== 'string' || !content.trim()) {
83
+ throw new TypeError('remember() requires a non-empty string');
84
+ }
85
+
86
+ const record = this._store.write(content);
87
+
88
+ if (this._audit) {
89
+ this._audit.logWrite(record);
90
+ }
91
+
92
+ return {
93
+ written: true,
94
+ id: record.id,
95
+ consistency: this._store.header.consistency,
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Recall the most semantically relevant memory for a query.
101
+ *
102
+ * @param {string} query
103
+ * @param {object} [opts]
104
+ * @param {object} [opts.filter] Pre-filter records: fn(record) → bool
105
+ * @param {number} [opts.since] Only consider records newer than this timestamp
106
+ * @param {string} [opts.mode] 'hybrid' | 'exact' | 'semantic'
107
+ * @returns {string|null}
108
+ */
109
+ async recall(query, opts = {}) {
110
+ this._assertOpen();
111
+ if (typeof query !== 'string' || !query.trim()) {
112
+ throw new TypeError('recall() requires a non-empty string');
113
+ }
114
+
115
+ const records = this._store.all();
116
+ if (!records.length) return null;
117
+
118
+ const t0 = Date.now();
119
+ const result = await recall(query, records, {
120
+ embedFn: this._opts.embedFn,
121
+ ...opts,
122
+ });
123
+ const latencyMs = Date.now() - t0;
124
+
125
+ if (this._audit) {
126
+ this._audit.logRecall(query, result, latencyMs);
127
+ }
128
+
129
+ return result ? result.content : null;
130
+ }
131
+
132
+ /**
133
+ * Return the top-k most relevant memories.
134
+ *
135
+ * @param {string} query
136
+ * @param {number} [k=5]
137
+ * @param {object} [opts] Same opts as recall()
138
+ * @returns {Array<{ id, content, ts, score }>}
139
+ */
140
+ async recallMany(query, k = 5, opts = {}) {
141
+ this._assertOpen();
142
+ if (typeof query !== 'string' || !query.trim()) {
143
+ throw new TypeError('recallMany() requires a non-empty string');
144
+ }
145
+
146
+ const records = this._store.all();
147
+ if (!records.length) return [];
148
+
149
+ const results = await recallMany(query, records, {
150
+ embedFn: this._opts.embedFn,
151
+ k,
152
+ ...opts,
153
+ });
154
+
155
+ return results.map(r => ({
156
+ id: r.id,
157
+ content: r.content,
158
+ ts: r.ts,
159
+ score: r._score,
160
+ }));
161
+ }
162
+
163
+ /**
164
+ * List all stored memories.
165
+ *
166
+ * @returns {Array<{ id, content, ts }>}
167
+ */
168
+ async list() {
169
+ this._assertOpen();
170
+ return this._store.all().map(r => ({
171
+ id: r.id,
172
+ content: r.content,
173
+ ts: r.ts,
174
+ }));
175
+ }
176
+
177
+ /**
178
+ * Delete all memories.
179
+ *
180
+ * @returns {{ cleared: boolean, count: number }}
181
+ */
182
+ async clear() {
183
+ this._assertOpen();
184
+ const count = this._store.clear();
185
+
186
+ if (this._audit) {
187
+ this._audit.logClear(count);
188
+ }
189
+
190
+ return { cleared: true, count };
191
+ }
192
+
193
+ /**
194
+ * Get audit log entries (Verified mode only).
195
+ *
196
+ * @returns {Array<object>}
197
+ */
198
+ async audit() {
199
+ this._assertOpen();
200
+ if (!this._audit) {
201
+ throw new Error('audit() requires consistency: "verified". Current mode: ' + this._store.header.consistency);
202
+ }
203
+ return this._audit.all();
204
+ }
205
+
206
+ /**
207
+ * Get audit log summary (Verified mode only).
208
+ */
209
+ async auditSummary() {
210
+ this._assertOpen();
211
+ if (!this._audit) {
212
+ throw new Error('auditSummary() requires consistency: "verified"');
213
+ }
214
+ return this._audit.summary();
215
+ }
216
+
217
+ /**
218
+ * Export audit log as NDJSON string (Verified mode only).
219
+ */
220
+ async exportAudit() {
221
+ this._assertOpen();
222
+ if (!this._audit) {
223
+ throw new Error('exportAudit() requires consistency: "verified"');
224
+ }
225
+ return this._audit.export();
226
+ }
227
+
228
+ /**
229
+ * Database metadata and stats.
230
+ */
231
+ async stats() {
232
+ this._assertOpen();
233
+ return this._store.stats();
234
+ }
235
+
236
+ /**
237
+ * Close the database and release file handles.
238
+ */
239
+ async close() {
240
+ if (this._closed) return;
241
+ this._store.close();
242
+ if (this._audit) this._audit.close();
243
+ this._closed = true;
244
+ }
245
+
246
+ // ─────────────────────────────────────────
247
+ // Convenience / fluent helpers
248
+ // ─────────────────────────────────────────
249
+
250
+ /**
251
+ * Add multiple memories at once.
252
+ *
253
+ * @param {string[]} items
254
+ */
255
+ async rememberAll(items) {
256
+ this._assertOpen();
257
+ if (!Array.isArray(items)) throw new TypeError('rememberAll() expects an array');
258
+ const results = [];
259
+ for (const item of items) {
260
+ results.push(await this.remember(item));
261
+ }
262
+ return results;
263
+ }
264
+
265
+ /**
266
+ * Check if any memory semantically matches a query.
267
+ *
268
+ * @param {string} query
269
+ * @returns {boolean}
270
+ */
271
+ async has(query) {
272
+ const result = await this.recall(query);
273
+ return result !== null;
274
+ }
275
+
276
+ /**
277
+ * Return the number of stored memories.
278
+ */
279
+ get size() {
280
+ return this._store.records.length;
281
+ }
282
+
283
+ /**
284
+ * Consistency mode of this instance.
285
+ */
286
+ get consistency() {
287
+ return this._store.header.consistency;
288
+ }
289
+
290
+ /**
291
+ * File path of the underlying .mumpix file.
292
+ */
293
+ get path() {
294
+ return this._store.filePath;
295
+ }
296
+
297
+ // ─────────────────────────────────────────
298
+ // Private
299
+ // ─────────────────────────────────────────
300
+
301
+ _assertOpen() {
302
+ if (this._closed) throw new Error('Database is closed. Call Mumpix.open() again.');
303
+ }
304
+ }
305
+
306
+ module.exports = { MumpixDB };
@@ -0,0 +1,173 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MumpixAudit — immutable append-only audit log for Verified consistency mode
5
+ *
6
+ * Stored alongside the main file: agent.mumpix.audit
7
+ * Each line is a JSON entry — never modified, only appended.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ const AUDIT_SCHEMA_VERSION = 1;
14
+
15
+ class MumpixAudit {
16
+ constructor(dbPath) {
17
+ this.auditPath = path.resolve(dbPath) + '.audit';
18
+ this._entries = [];
19
+ this._fd = null;
20
+ }
21
+
22
+ open() {
23
+ if (fs.existsSync(this.auditPath)) {
24
+ this._load();
25
+ } else {
26
+ // Write header
27
+ const header = {
28
+ _type: 'audit_header',
29
+ version: AUDIT_SCHEMA_VERSION,
30
+ created: Date.now(),
31
+ };
32
+ fs.writeFileSync(this.auditPath, JSON.stringify(header) + '\n', 'utf8');
33
+ }
34
+ this._fd = fs.openSync(this.auditPath, 'a');
35
+ return this;
36
+ }
37
+
38
+ close() {
39
+ if (this._fd !== null) {
40
+ fs.fdatasyncSync(this._fd);
41
+ fs.closeSync(this._fd);
42
+ this._fd = null;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Log a write operation.
48
+ */
49
+ logWrite(record) {
50
+ this._append({
51
+ _type: 'write',
52
+ id: record.id,
53
+ hash: record.h,
54
+ len: record.content.length,
55
+ ts: Date.now(),
56
+ });
57
+ }
58
+
59
+ /**
60
+ * Log a recall operation.
61
+ */
62
+ logRecall(query, result, latencyMs) {
63
+ this._append({
64
+ _type: 'recall',
65
+ queryHash: this._hash(query),
66
+ queryLen: query.length,
67
+ resultId: result ? result.id : null,
68
+ resultHash: result ? result.h : null,
69
+ score: result ? result._score : null,
70
+ latencyMs: Math.round(latencyMs * 100) / 100,
71
+ ts: Date.now(),
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Log a clear operation.
77
+ */
78
+ logClear(count) {
79
+ this._append({
80
+ _type: 'clear',
81
+ count,
82
+ ts: Date.now(),
83
+ });
84
+ }
85
+
86
+ /**
87
+ * Log a consistency mode change.
88
+ */
89
+ logModeChange(from, to) {
90
+ this._append({
91
+ _type: 'mode_change',
92
+ from,
93
+ to,
94
+ ts: Date.now(),
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Return all audit entries (excluding header).
100
+ */
101
+ all() {
102
+ return this._entries.map(e => ({ ...e }));
103
+ }
104
+
105
+ /**
106
+ * Return entries filtered by type.
107
+ */
108
+ filter(type) {
109
+ return this._entries.filter(e => e._type === type).map(e => ({ ...e }));
110
+ }
111
+
112
+ /**
113
+ * Return a compact summary.
114
+ */
115
+ summary() {
116
+ const writes = this._entries.filter(e => e._type === 'write').length;
117
+ const recalls = this._entries.filter(e => e._type === 'recall').length;
118
+ const clears = this._entries.filter(e => e._type === 'clear').length;
119
+ const first = this._entries[0];
120
+ const last = this._entries[this._entries.length - 1];
121
+
122
+ return {
123
+ totalEntries: this._entries.length,
124
+ writes,
125
+ recalls,
126
+ clears,
127
+ firstEventAt: first ? first.ts : null,
128
+ lastEventAt: last ? last.ts : null,
129
+ auditPath: this.auditPath,
130
+ };
131
+ }
132
+
133
+ /**
134
+ * Export as NDJSON string (for compliance hand-off).
135
+ */
136
+ export() {
137
+ return this._entries.map(e => JSON.stringify(e)).join('\n');
138
+ }
139
+
140
+ // ── Private ────────────────────────────────────
141
+
142
+ _append(entry) {
143
+ const line = JSON.stringify(entry) + '\n';
144
+ fs.writeSync(this._fd, line, null, 'utf8');
145
+ fs.fdatasyncSync(this._fd);
146
+ this._entries.push(entry);
147
+ }
148
+
149
+ _load() {
150
+ const raw = fs.readFileSync(this.auditPath, 'utf8');
151
+ const lines = raw.split('\n').filter(l => l.trim());
152
+
153
+ for (const line of lines) {
154
+ try {
155
+ const entry = JSON.parse(line);
156
+ if (entry._type && entry._type !== 'audit_header') {
157
+ this._entries.push(entry);
158
+ }
159
+ } catch (_) { /* skip corrupt lines */ }
160
+ }
161
+ }
162
+
163
+ _hash(s) {
164
+ let h = 0x811c9dc5;
165
+ for (let i = 0; i < s.length; i++) {
166
+ h ^= s.charCodeAt(i);
167
+ h = Math.imul(h, 0x01000193);
168
+ }
169
+ return '0x' + (h >>> 0).toString(16).padStart(8, '0');
170
+ }
171
+ }
172
+
173
+ module.exports = { MumpixAudit };