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.
- package/LICENSE +21 -0
- package/README.md +258 -0
- package/bin/mumpix.js +305 -0
- package/examples/agent-memory.js +63 -0
- package/examples/basic.js +44 -0
- package/examples/verified-mode.js +85 -0
- package/package.json +50 -0
- package/src/core/MumpixDB.js +306 -0
- package/src/core/audit.js +173 -0
- package/src/core/recall.js +176 -0
- package/src/core/store.js +230 -0
- package/src/index.js +38 -0
- package/src/integrations/langchain.js +131 -0
- package/src/integrations/llamaindex.js +86 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mumpix
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Mumpix
|
|
2
|
+
|
|
3
|
+
**SQLite for AI** — embedded, zero-config memory database for AI agents and LLM applications.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install mumpix
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Why Mumpix?
|
|
12
|
+
|
|
13
|
+
If you've ever watched an agent "forget" what it just learned, or had to explain why the same prompt produced a different result a day later — Mumpix is your fix.
|
|
14
|
+
|
|
15
|
+
- **Zero config** — one file, zero external dependencies, works offline
|
|
16
|
+
- **Portable** — the `.mumpix` file is the API. Copy it, move it, back it up
|
|
17
|
+
- **Crash-safe** — WAL-based atomicity: records are either fully written or fully absent
|
|
18
|
+
- **Hybrid recall** — exact match + TF-IDF semantic similarity out of the box
|
|
19
|
+
- **Verified consistency** — immutable audit log, snapshot replay, compliance artifacts
|
|
20
|
+
- **LangChain / LlamaIndex adapters** — drop-in integrations
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const { Mumpix } = require('mumpix')
|
|
28
|
+
|
|
29
|
+
// Open (or create) a local database
|
|
30
|
+
const db = Mumpix.open('./agent.mumpix', { consistency: 'strict' })
|
|
31
|
+
|
|
32
|
+
// Store memories
|
|
33
|
+
await db.remember('User prefers TypeScript over JavaScript')
|
|
34
|
+
await db.remember('Project uses pnpm workspaces')
|
|
35
|
+
|
|
36
|
+
// Semantic recall — no API key, no cloud
|
|
37
|
+
const answer = await db.recall('what language does the user prefer?')
|
|
38
|
+
console.log(answer)
|
|
39
|
+
// → 'User prefers TypeScript over JavaScript'
|
|
40
|
+
|
|
41
|
+
await db.close()
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## API
|
|
47
|
+
|
|
48
|
+
### `Mumpix.open(filePath, [opts])` → `MumpixDB`
|
|
49
|
+
|
|
50
|
+
Opens (or creates) a Mumpix database.
|
|
51
|
+
|
|
52
|
+
| Option | Type | Default | Description |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| `consistency` | `string` | `'eventual'` | `'eventual'` / `'strict'` / `'verified'` |
|
|
55
|
+
| `embedFn` | `async fn(texts[]) → number[][]` | — | Custom embedding function (OpenAI, Cohere, etc.) |
|
|
56
|
+
|
|
57
|
+
### Consistency modes
|
|
58
|
+
|
|
59
|
+
| Mode | Write behaviour | Audit log | Use case |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| `eventual` | Fast append, OS-buffered | No | Prototypes, local tools |
|
|
62
|
+
| `strict` | Synced to disk (`fdatasync`) | No | Production agents |
|
|
63
|
+
| `verified` | Synced + immutable audit log | Yes | Fintech, healthcare, regulated AI |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
### Core methods
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
// Store a memory
|
|
71
|
+
await db.remember(text)
|
|
72
|
+
// → { written: true, id: 1, consistency: 'strict' }
|
|
73
|
+
|
|
74
|
+
// Recall the best semantic match
|
|
75
|
+
await db.recall(query)
|
|
76
|
+
// → 'User prefers TypeScript over JavaScript' or null
|
|
77
|
+
|
|
78
|
+
// Recall top-k matches with scores
|
|
79
|
+
await db.recallMany(query, k = 5)
|
|
80
|
+
// → [{ id, content, ts, score }, ...]
|
|
81
|
+
|
|
82
|
+
// Store multiple memories
|
|
83
|
+
await db.rememberAll(['pref 1', 'pref 2', 'pref 3'])
|
|
84
|
+
|
|
85
|
+
// Check if any memory matches
|
|
86
|
+
await db.has(query)
|
|
87
|
+
// → true | false
|
|
88
|
+
|
|
89
|
+
// List all memories
|
|
90
|
+
await db.list()
|
|
91
|
+
// → [{ id, content, ts }, ...]
|
|
92
|
+
|
|
93
|
+
// Delete all memories
|
|
94
|
+
await db.clear()
|
|
95
|
+
// → { cleared: true, count: 3 }
|
|
96
|
+
|
|
97
|
+
// Stats
|
|
98
|
+
await db.stats()
|
|
99
|
+
// → { path, consistency, records, sizeBytes, created, version }
|
|
100
|
+
|
|
101
|
+
// Close (releases file handles)
|
|
102
|
+
await db.close()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Verified-mode methods
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
const db = Mumpix.open('./compliance.mumpix', { consistency: 'verified' })
|
|
109
|
+
|
|
110
|
+
// Full audit log
|
|
111
|
+
await db.audit()
|
|
112
|
+
// → [{ _type, id, hash, ts, ... }, ...]
|
|
113
|
+
|
|
114
|
+
// Summary for dashboards
|
|
115
|
+
await db.auditSummary()
|
|
116
|
+
// → { totalEntries, writes, recalls, clears, firstEventAt, lastEventAt, auditPath }
|
|
117
|
+
|
|
118
|
+
// Export as NDJSON (for compliance hand-off)
|
|
119
|
+
await db.exportAudit()
|
|
120
|
+
// → NDJSON string
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Recall options
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
await db.recall(query, {
|
|
127
|
+
mode: 'hybrid', // 'hybrid' | 'exact' | 'semantic'
|
|
128
|
+
filter: r => r.ts > Date.now() - 3600_000, // only last hour
|
|
129
|
+
since: Date.now() - 86400_000, // shorthand for time filter
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
await db.recallMany(query, 5, { mode: 'semantic' })
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Custom embeddings (OpenAI, Cohere, etc.)
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
const { Mumpix } = require('mumpix')
|
|
139
|
+
const OpenAI = require('openai')
|
|
140
|
+
|
|
141
|
+
const client = new OpenAI()
|
|
142
|
+
|
|
143
|
+
async function embedFn(texts) {
|
|
144
|
+
const res = await client.embeddings.create({ model: 'text-embedding-3-small', input: texts })
|
|
145
|
+
return res.data.map(d => d.embedding)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const db = Mumpix.open('./agent.mumpix', { consistency: 'strict', embedFn })
|
|
149
|
+
// db.recall() and db.recallMany() now use OpenAI embeddings automatically
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## LangChain integration
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
const { Mumpix } = require('mumpix')
|
|
158
|
+
const { MumpixVectorStore, MumpixChatMemory, MumpixRetriever } = require('mumpix/src/integrations/langchain')
|
|
159
|
+
|
|
160
|
+
const db = Mumpix.open('./agent.mumpix', { consistency: 'strict' })
|
|
161
|
+
|
|
162
|
+
// As a VectorStore
|
|
163
|
+
const store = new MumpixVectorStore(db)
|
|
164
|
+
await store.addTexts(['preference 1', 'preference 2'])
|
|
165
|
+
const docs = await store.similaritySearch('query', 4)
|
|
166
|
+
|
|
167
|
+
// As chat memory (stores Human/AI turns, recalls semantically)
|
|
168
|
+
const memory = new MumpixChatMemory({ db, k: 4 })
|
|
169
|
+
|
|
170
|
+
// As a retriever
|
|
171
|
+
const retriever = new MumpixRetriever(db, { k: 5 })
|
|
172
|
+
const results = await retriever.getRelevantDocuments('query')
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## LlamaIndex integration
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
const { Mumpix } = require('mumpix')
|
|
179
|
+
const { MumpixIndex, MumpixReader } = require('mumpix/src/integrations/llamaindex')
|
|
180
|
+
|
|
181
|
+
const db = Mumpix.open('./agent.mumpix', { consistency: 'strict' })
|
|
182
|
+
|
|
183
|
+
const index = new MumpixIndex(db)
|
|
184
|
+
const retriever = index.asRetriever({ topK: 5 })
|
|
185
|
+
|
|
186
|
+
await index.insert('User prefers dark mode')
|
|
187
|
+
const nodes = await retriever.retrieve('interface preferences')
|
|
188
|
+
|
|
189
|
+
// Load all memories as documents
|
|
190
|
+
const reader = new MumpixReader(db)
|
|
191
|
+
const docs = await reader.loadData()
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## CLI
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Global install
|
|
200
|
+
npm install -g mumpix
|
|
201
|
+
|
|
202
|
+
mumpix remember ./agent.mumpix "User prefers TypeScript"
|
|
203
|
+
mumpix recall ./agent.mumpix "what language?"
|
|
204
|
+
mumpix search ./agent.mumpix "language" 3
|
|
205
|
+
mumpix list ./agent.mumpix
|
|
206
|
+
mumpix clear ./agent.mumpix
|
|
207
|
+
mumpix stats ./agent.mumpix
|
|
208
|
+
mumpix audit ./agent.mumpix # verified mode only
|
|
209
|
+
mumpix shell ./agent.mumpix # interactive REPL
|
|
210
|
+
|
|
211
|
+
# Options
|
|
212
|
+
mumpix recall ./agent.mumpix "query" --consistency=strict
|
|
213
|
+
mumpix shell ./compliance.mumpix --consistency=verified
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## File format
|
|
219
|
+
|
|
220
|
+
The `.mumpix` file is plain NDJSON — human-readable, portable, no binary parser required:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
{"v":1,"consistency":"strict","created":1740000000000,"path":"agent.mumpix"}
|
|
224
|
+
{"id":1,"content":"User prefers TypeScript","ts":1740000001000,"h":"0x4a2b1c3d"}
|
|
225
|
+
{"id":2,"content":"Use pnpm workspaces","ts":1740000002000,"h":"0x9f8e7d6c"}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Crash safety**: Mumpix uses a WAL (`.mumpix.wal`). On write:
|
|
229
|
+
1. Write to WAL
|
|
230
|
+
2. Append to main file and `fdatasync` (strict/verified)
|
|
231
|
+
3. Delete WAL
|
|
232
|
+
|
|
233
|
+
On open, any leftover WAL is replayed before accepting new writes. A record is either fully present or fully absent — no partial writes.
|
|
234
|
+
|
|
235
|
+
**Audit log**: In `verified` mode, an additional `.mumpix.audit` file is created. It is append-only, never modified, and `fdatasync`'d after every entry.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Examples
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
node examples/basic.js # Zero-config 5-line demo
|
|
243
|
+
node examples/agent-memory.js # Persistent agent preferences (run twice)
|
|
244
|
+
node examples/verified-mode.js # Compliance audit log export
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Requirements
|
|
250
|
+
|
|
251
|
+
- Node.js >= 18
|
|
252
|
+
- Zero runtime dependencies
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
MIT © Mumpix
|
package/bin/mumpix.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* mumpix CLI
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* mumpix remember ./agent.mumpix "User prefers TypeScript"
|
|
9
|
+
* mumpix recall ./agent.mumpix "what language?"
|
|
10
|
+
* mumpix list ./agent.mumpix
|
|
11
|
+
* mumpix clear ./agent.mumpix
|
|
12
|
+
* mumpix stats ./agent.mumpix
|
|
13
|
+
* mumpix audit ./agent.mumpix
|
|
14
|
+
* mumpix shell ./agent.mumpix # interactive REPL
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const readline = require('readline');
|
|
19
|
+
const { Mumpix } = require('../src/index');
|
|
20
|
+
|
|
21
|
+
const USAGE = `
|
|
22
|
+
mumpix — SQLite for AI
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
mumpix <command> <file> [args...]
|
|
26
|
+
|
|
27
|
+
Commands:
|
|
28
|
+
remember <file> "<text>" Store a memory
|
|
29
|
+
recall <file> "<query>" Recall the best match
|
|
30
|
+
search <file> "<query>" [k] Recall top-k matches
|
|
31
|
+
list <file> List all memories
|
|
32
|
+
clear <file> Clear all memories
|
|
33
|
+
stats <file> Show database stats
|
|
34
|
+
audit <file> Show audit log (verified mode)
|
|
35
|
+
shell <file> Start interactive REPL
|
|
36
|
+
help Show this message
|
|
37
|
+
|
|
38
|
+
Options (append to any command):
|
|
39
|
+
--consistency=eventual|strict|verified (default: eventual)
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
mumpix remember ./agent.mumpix "User prefers TypeScript"
|
|
43
|
+
mumpix recall ./agent.mumpix "what language?" --consistency=strict
|
|
44
|
+
mumpix search ./agent.mumpix "TypeScript" 3
|
|
45
|
+
mumpix shell ./agent.mumpix --consistency=verified
|
|
46
|
+
`.trim();
|
|
47
|
+
|
|
48
|
+
function parseArgs(argv) {
|
|
49
|
+
const args = [];
|
|
50
|
+
const flags = {};
|
|
51
|
+
for (const arg of argv) {
|
|
52
|
+
if (arg.startsWith('--')) {
|
|
53
|
+
const [k, v] = arg.slice(2).split('=');
|
|
54
|
+
flags[k] = v !== undefined ? v : true;
|
|
55
|
+
} else {
|
|
56
|
+
args.push(arg);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { args, flags };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function fmt(val) {
|
|
63
|
+
if (val === null) return 'null';
|
|
64
|
+
if (typeof val === 'string') return val;
|
|
65
|
+
return JSON.stringify(val, null, 2);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function colorize(text, code) {
|
|
69
|
+
if (!process.stdout.isTTY) return text;
|
|
70
|
+
return `\x1b[${code}m${text}\x1b[0m`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const dim = t => colorize(t, '2');
|
|
74
|
+
const cyan = t => colorize(t, '36');
|
|
75
|
+
const green = t => colorize(t, '32');
|
|
76
|
+
const yellow = t => colorize(t, '33');
|
|
77
|
+
const red = t => colorize(t, '31');
|
|
78
|
+
const bold = t => colorize(t, '1');
|
|
79
|
+
|
|
80
|
+
async function main() {
|
|
81
|
+
const { args, flags } = parseArgs(process.argv.slice(2));
|
|
82
|
+
const [command, filePath, ...rest] = args;
|
|
83
|
+
|
|
84
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
85
|
+
console.log(USAGE);
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!filePath && command !== 'help') {
|
|
90
|
+
console.error(red('Error: file path required'));
|
|
91
|
+
console.log(USAGE);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const consistency = flags.consistency || 'eventual';
|
|
96
|
+
let db;
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
db = Mumpix.open(filePath, { consistency });
|
|
100
|
+
} catch (err) {
|
|
101
|
+
console.error(red(`Error opening ${filePath}: ${err.message}`));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
switch (command) {
|
|
107
|
+
|
|
108
|
+
case 'remember': {
|
|
109
|
+
const text = rest.join(' ').replace(/^["']|["']$/g, '');
|
|
110
|
+
if (!text) { console.error(red('Error: provide text to remember')); process.exit(1); }
|
|
111
|
+
const result = await db.remember(text);
|
|
112
|
+
console.log(green('✓') + ` Stored as #${result.id} (${result.consistency})`);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case 'recall': {
|
|
117
|
+
const query = rest.join(' ').replace(/^["']|["']$/g, '');
|
|
118
|
+
if (!query) { console.error(red('Error: provide a query')); process.exit(1); }
|
|
119
|
+
const result = await db.recall(query);
|
|
120
|
+
if (result === null) {
|
|
121
|
+
console.log(dim('No memories found.'));
|
|
122
|
+
} else {
|
|
123
|
+
console.log(green('→') + ' ' + result);
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case 'search': {
|
|
129
|
+
const k = parseInt(rest[rest.length - 1], 10);
|
|
130
|
+
const limit = Number.isInteger(k) && k > 0 ? k : 5;
|
|
131
|
+
const queryParts = Number.isInteger(k) ? rest.slice(0, -1) : rest;
|
|
132
|
+
const query = queryParts.join(' ').replace(/^["']|["']$/g, '');
|
|
133
|
+
if (!query) { console.error(red('Error: provide a query')); process.exit(1); }
|
|
134
|
+
const results = await db.recallMany(query, limit);
|
|
135
|
+
if (!results.length) {
|
|
136
|
+
console.log(dim('No memories found.'));
|
|
137
|
+
} else {
|
|
138
|
+
results.forEach((r, i) => {
|
|
139
|
+
const score = r.score !== undefined ? dim(` (${(r.score * 100).toFixed(1)}%)`) : '';
|
|
140
|
+
console.log(`${cyan(`#${r.id}`)} ${r.content}${score}`);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
case 'list': {
|
|
147
|
+
const memories = await db.list();
|
|
148
|
+
if (!memories.length) {
|
|
149
|
+
console.log(dim('No memories stored.'));
|
|
150
|
+
} else {
|
|
151
|
+
memories.forEach(m => {
|
|
152
|
+
const ts = new Date(m.ts).toLocaleString();
|
|
153
|
+
console.log(`${cyan(`#${m.id}`)} ${m.content} ${dim(ts)}`);
|
|
154
|
+
});
|
|
155
|
+
console.log(dim(`\n${memories.length} total`));
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
case 'clear': {
|
|
161
|
+
const { count } = await db.clear();
|
|
162
|
+
console.log(yellow('⚠') + ` Cleared ${count} ${count === 1 ? 'memory' : 'memories'}`);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
case 'stats': {
|
|
167
|
+
const s = await db.stats();
|
|
168
|
+
console.log(bold('Mumpix stats'));
|
|
169
|
+
console.log(` path: ${s.path}`);
|
|
170
|
+
console.log(` consistency: ${cyan(s.consistency)}`);
|
|
171
|
+
console.log(` records: ${s.records}`);
|
|
172
|
+
console.log(` size: ${(s.sizeBytes / 1024).toFixed(2)} KB`);
|
|
173
|
+
console.log(` created: ${new Date(s.created).toLocaleString()}`);
|
|
174
|
+
console.log(` version: v${s.version}`);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'audit': {
|
|
179
|
+
let entries;
|
|
180
|
+
try {
|
|
181
|
+
entries = await db.audit();
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.error(red('Error: ') + err.message);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
if (!entries.length) {
|
|
187
|
+
console.log(dim('Audit log is empty.'));
|
|
188
|
+
} else {
|
|
189
|
+
entries.forEach(e => {
|
|
190
|
+
const ts = new Date(e.ts).toISOString();
|
|
191
|
+
const type = e._type.padEnd(12);
|
|
192
|
+
const detail = e._type === 'write' ? `id=${e.id} hash=${e.hash}`
|
|
193
|
+
: e._type === 'recall' ? `queryHash=${e.queryHash} resultId=${e.resultId ?? 'null'} latency=${e.latencyMs}ms`
|
|
194
|
+
: e._type === 'clear' ? `count=${e.count}`
|
|
195
|
+
: '';
|
|
196
|
+
console.log(`${dim(ts)} ${yellow(type)} ${detail}`);
|
|
197
|
+
});
|
|
198
|
+
console.log(dim(`\n${entries.length} entries`));
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
case 'shell': {
|
|
204
|
+
await runShell(db, filePath, consistency);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
default:
|
|
209
|
+
console.error(red(`Unknown command: ${command}`));
|
|
210
|
+
console.log(USAGE);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
} finally {
|
|
214
|
+
if (command !== 'shell') await db.close();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function runShell(db, filePath, consistency) {
|
|
219
|
+
const rl = readline.createInterface({
|
|
220
|
+
input: process.stdin,
|
|
221
|
+
output: process.stdout,
|
|
222
|
+
prompt: cyan('mumpix') + dim(' › '),
|
|
223
|
+
historySize: 100,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
console.log(bold('Mumpix interactive shell'));
|
|
227
|
+
console.log(dim(` file: ${filePath} consistency: ${consistency}`));
|
|
228
|
+
console.log(dim(' Commands: remember("…"), recall("…"), list(), clear(), stats(), audit(), exit\n'));
|
|
229
|
+
|
|
230
|
+
rl.prompt();
|
|
231
|
+
|
|
232
|
+
rl.on('line', async (line) => {
|
|
233
|
+
const raw = line.trim();
|
|
234
|
+
if (!raw) { rl.prompt(); return; }
|
|
235
|
+
if (raw === 'exit' || raw === 'quit' || raw === '.exit') {
|
|
236
|
+
console.log(dim('bye'));
|
|
237
|
+
rl.close();
|
|
238
|
+
await db.close();
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const match = raw.match(/^(\w+)\s*\((.*)?\)$/s);
|
|
243
|
+
if (!match) {
|
|
244
|
+
console.log(red('Syntax error') + dim(' — try: remember("…") or recall("…")'));
|
|
245
|
+
rl.prompt();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const [, fn, argsRaw] = match;
|
|
250
|
+
const strArg = (s => {
|
|
251
|
+
const m = (s || '').trim().match(/^["'`]([\s\S]*)["'`]$/);
|
|
252
|
+
return m ? m[1] : null;
|
|
253
|
+
})(argsRaw);
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
let result;
|
|
257
|
+
switch (fn) {
|
|
258
|
+
case 'remember':
|
|
259
|
+
if (strArg === null) throw new Error('remember() expects a string');
|
|
260
|
+
result = await db.remember(strArg);
|
|
261
|
+
console.log(green('←') + ' ' + JSON.stringify(result));
|
|
262
|
+
break;
|
|
263
|
+
case 'recall':
|
|
264
|
+
if (strArg === null) throw new Error('recall() expects a string');
|
|
265
|
+
result = await db.recall(strArg);
|
|
266
|
+
console.log(green('←') + ' ' + (result === null ? dim('null') : JSON.stringify(result)));
|
|
267
|
+
break;
|
|
268
|
+
case 'list':
|
|
269
|
+
result = await db.list();
|
|
270
|
+
if (!result.length) console.log(dim('[]'));
|
|
271
|
+
else result.forEach(m => console.log(` ${cyan(`#${m.id}`)} ${m.content}`));
|
|
272
|
+
break;
|
|
273
|
+
case 'clear':
|
|
274
|
+
result = await db.clear();
|
|
275
|
+
console.log(green('←') + ' ' + JSON.stringify(result));
|
|
276
|
+
break;
|
|
277
|
+
case 'stats':
|
|
278
|
+
result = await db.stats();
|
|
279
|
+
console.log(green('←') + ' ' + JSON.stringify(result, null, 2));
|
|
280
|
+
break;
|
|
281
|
+
case 'audit':
|
|
282
|
+
result = await db.audit();
|
|
283
|
+
if (!result.length) console.log(dim('[]'));
|
|
284
|
+
else result.forEach(e => console.log(' ' + JSON.stringify(e)));
|
|
285
|
+
break;
|
|
286
|
+
default:
|
|
287
|
+
console.log(red(`Unknown: db.${fn}`));
|
|
288
|
+
}
|
|
289
|
+
} catch (err) {
|
|
290
|
+
console.log(red('Error: ') + err.message);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
rl.prompt();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
rl.on('close', async () => {
|
|
297
|
+
await db.close();
|
|
298
|
+
process.exit(0);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
main().catch(err => {
|
|
303
|
+
console.error(err.message);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* examples/agent-memory.js
|
|
5
|
+
*
|
|
6
|
+
* Demonstrates Mumpix as persistent memory for an AI agent.
|
|
7
|
+
* The agent "remembers" across restarts — the .mumpix file persists.
|
|
8
|
+
*
|
|
9
|
+
* Run:
|
|
10
|
+
* node examples/agent-memory.js
|
|
11
|
+
* node examples/agent-memory.js ← run again; memory persists
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { Mumpix } = require('../src/index');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const DB_PATH = path.join(__dirname, 'agent-memory.mumpix');
|
|
18
|
+
|
|
19
|
+
;(async () => {
|
|
20
|
+
const db = Mumpix.open(DB_PATH, { consistency: 'strict' });
|
|
21
|
+
const { records } = await db.stats();
|
|
22
|
+
|
|
23
|
+
if (records === 0) {
|
|
24
|
+
console.log('First run — seeding agent preferences...\n');
|
|
25
|
+
|
|
26
|
+
await db.rememberAll([
|
|
27
|
+
'User timezone is America/New_York',
|
|
28
|
+
'User prefers concise responses, no bullet points',
|
|
29
|
+
'User is building a B2B SaaS product called "Locaify"',
|
|
30
|
+
'User always uses TypeScript, never plain JavaScript',
|
|
31
|
+
'User runs pnpm workspaces',
|
|
32
|
+
'Preferred output format: JSON when structured, plain prose otherwise',
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
console.log(`Seeded ${db.size} preferences.\n`);
|
|
36
|
+
} else {
|
|
37
|
+
console.log(`Loaded ${records} existing memories from ${DB_PATH}\n`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Simulate agent planning queries
|
|
41
|
+
const queries = [
|
|
42
|
+
'What is the user\'s timezone?',
|
|
43
|
+
'How should I format my response?',
|
|
44
|
+
'What are they building?',
|
|
45
|
+
'Which package manager?',
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
console.log('Agent recall test:');
|
|
49
|
+
for (const q of queries) {
|
|
50
|
+
const answer = await db.recall(q);
|
|
51
|
+
console.log(` Q: ${q}`);
|
|
52
|
+
console.log(` A: ${answer ?? '(no match)'}`);
|
|
53
|
+
console.log();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Top-3 recall
|
|
57
|
+
console.log('Top-3 for "output format and style":');
|
|
58
|
+
const top = await db.recallMany('output format and style', 3);
|
|
59
|
+
top.forEach(r => console.log(` [${(r.score * 100).toFixed(1)}%] ${r.content}`));
|
|
60
|
+
|
|
61
|
+
await db.close();
|
|
62
|
+
console.log('\nRun this file again — the memories persist.');
|
|
63
|
+
})();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* examples/basic.js — zero-config, 5-line demo
|
|
5
|
+
*
|
|
6
|
+
* Run:
|
|
7
|
+
* node examples/basic.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { Mumpix } = require('../src/index');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
;(async () => {
|
|
15
|
+
const dbPath = path.join(os.tmpdir(), 'mumpix-basic-demo.mumpix');
|
|
16
|
+
const db = Mumpix.open(dbPath, { consistency: 'strict' });
|
|
17
|
+
|
|
18
|
+
// Store memories
|
|
19
|
+
await db.remember('User prefers TypeScript over JavaScript');
|
|
20
|
+
await db.remember('Always use async/await, never raw Promises');
|
|
21
|
+
await db.remember('Project uses pnpm, not npm');
|
|
22
|
+
|
|
23
|
+
// Semantic recall
|
|
24
|
+
const lang = await db.recall('What language does the user prefer?');
|
|
25
|
+
console.log('Recalled:', lang);
|
|
26
|
+
// → 'User prefers TypeScript over JavaScript'
|
|
27
|
+
|
|
28
|
+
const pkgMgr = await db.recall('which package manager?');
|
|
29
|
+
console.log('Recalled:', pkgMgr);
|
|
30
|
+
// → 'Project uses pnpm, not npm'
|
|
31
|
+
|
|
32
|
+
// List all
|
|
33
|
+
const all = await db.list();
|
|
34
|
+
console.log(`\nAll ${all.length} memories:`);
|
|
35
|
+
all.forEach(m => console.log(` #${m.id} ${m.content}`));
|
|
36
|
+
|
|
37
|
+
// Stats
|
|
38
|
+
const stats = await db.stats();
|
|
39
|
+
console.log(`\nDB at ${stats.path} — ${stats.records} records, ${stats.sizeBytes} bytes`);
|
|
40
|
+
|
|
41
|
+
await db.close();
|
|
42
|
+
require('fs').unlinkSync(dbPath);
|
|
43
|
+
console.log('\nDone.');
|
|
44
|
+
})();
|