mnemocyte 0.0.7 → 0.1.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/README.md +172 -2
- package/dist/index.d.mts +757 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +816 -30
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -16
package/README.md
CHANGED
|
@@ -52,6 +52,12 @@ explain: true,
|
|
|
52
52
|
await client.close();
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
See also: [Provider resilience](#provider-resilience),
|
|
56
|
+
[Pruning memories](#pruning-memories),
|
|
57
|
+
[Finding duplicates](#finding-duplicates-experimental),
|
|
58
|
+
[Audit log](#audit-log-experimental), and
|
|
59
|
+
[Consolidating duplicates](#consolidating-duplicates-experimental).
|
|
60
|
+
|
|
55
61
|
## Postgres usage
|
|
56
62
|
|
|
57
63
|
Run the included migration against a Postgres database with pgvector enabled, then pass `databaseUrl`:
|
|
@@ -59,12 +65,176 @@ Run the included migration against a Postgres database with pgvector enabled, th
|
|
|
59
65
|
```ts
|
|
60
66
|
const client = createMnemocyte({
|
|
61
67
|
databaseUrl: process.env.DATABASE_URL,
|
|
62
|
-
embedder,
|
|
68
|
+
embedder, // embedder.dimensions MUST be 1536 — see below
|
|
63
69
|
});
|
|
64
70
|
```
|
|
65
71
|
|
|
66
72
|
The published package includes `migrations/0000_initial.sql`.
|
|
67
73
|
|
|
74
|
+
> **Embedding dimensionality is pinned to 1536 on the Postgres backend.**
|
|
75
|
+
> The bundled migration creates `embedding vector(1536)`. If
|
|
76
|
+
> `embedder.dimensions` is anything else, `createMnemocyte` throws a
|
|
77
|
+
> `MnemocyteError` with code `"CONFIG"` *before* opening the connection
|
|
78
|
+
> pool. The in-memory backend has no such constraint.
|
|
79
|
+
|
|
80
|
+
## Provider resilience
|
|
81
|
+
|
|
82
|
+
Mnemocyte applies an optional timeout and retry policy to every outbound
|
|
83
|
+
embedder call, and forwards `AbortSignal` cancellation to in-flight
|
|
84
|
+
attempts. Defaults disable both retries and timeouts so existing setups
|
|
85
|
+
are unaffected.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const client = createMnemocyte({
|
|
89
|
+
embedder,
|
|
90
|
+
provider: {
|
|
91
|
+
timeoutMs: 5_000,
|
|
92
|
+
retries: 2,
|
|
93
|
+
baseDelayMs: 200,
|
|
94
|
+
maxDelayMs: 2_000,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const controller = new AbortController();
|
|
99
|
+
setTimeout(() => controller.abort(), 1_000);
|
|
100
|
+
|
|
101
|
+
await client.remember({
|
|
102
|
+
entityId: "user_123",
|
|
103
|
+
content: "Prefers short answers.",
|
|
104
|
+
signal: controller.signal,
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Failures surface as `MnemocyteError` with stable `code`s:
|
|
109
|
+
|
|
110
|
+
- `"TIMEOUT"` — a single provider attempt exceeded `timeoutMs`.
|
|
111
|
+
- `"ABORTED"` — the operation was cancelled via `signal` (never retried).
|
|
112
|
+
- `"EMBEDDING"` — the embedder failed after all retries are exhausted.
|
|
113
|
+
|
|
114
|
+
## Pruning memories
|
|
115
|
+
|
|
116
|
+
`prune` bulk-deletes memories matching a filter. At least one selector
|
|
117
|
+
is required; `prune({})` is rejected to avoid accidental full deletion.
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// Drop expired memories for one entity.
|
|
121
|
+
await client.prune({ entityId: "user_123", expired: true });
|
|
122
|
+
|
|
123
|
+
// Evict memories not accessed in the last 30 days, but never drop
|
|
124
|
+
// "high" or "critical" memories.
|
|
125
|
+
await client.prune({
|
|
126
|
+
notAccessedSince: new Date(Date.now() - 30 * 24 * 3600 * 1000),
|
|
127
|
+
maxImportance: "normal",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Count what would be deleted without touching the store.
|
|
131
|
+
const preview = await client.prune({
|
|
132
|
+
entityId: "user_123",
|
|
133
|
+
superseded: true,
|
|
134
|
+
dryRun: true,
|
|
135
|
+
});
|
|
136
|
+
console.log(preview.matchedCount);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Available selectors: `entityId`, `expired`, `superseded`, `createdBefore`,
|
|
140
|
+
`notAccessedSince`, `types`, `tags`, `maxImportance`. They AND together.
|
|
141
|
+
|
|
142
|
+
## Finding duplicates (experimental)
|
|
143
|
+
|
|
144
|
+
`findDuplicates` is a **read-only** scan that surfaces near-identical
|
|
145
|
+
memories for one entity using pairwise cosine similarity. It is
|
|
146
|
+
intentionally passive — nothing is deleted or modified — so callers can
|
|
147
|
+
decide their own consolidation policy.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
const pairs = await client.findDuplicates({
|
|
151
|
+
entityId: "user_123",
|
|
152
|
+
threshold: 0.95, // default 0.95
|
|
153
|
+
limit: 50, // default 100, ordered by similarity desc
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
for (const { a, b, similarity } of pairs) {
|
|
157
|
+
console.log(similarity.toFixed(3), a.content, "<>", b.content);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Optional selectors: `types`, `tags` (both required on each pair member),
|
|
162
|
+
`includeSuperseded`, `includeExpired`. Superseded and expired memories
|
|
163
|
+
are excluded by default.
|
|
164
|
+
|
|
165
|
+
> Part of Phase 6 (consolidation tooling). The API surface may change in
|
|
166
|
+
> follow-up releases as conflict detection and active dedup land.
|
|
167
|
+
|
|
168
|
+
## Audit log (experimental)
|
|
169
|
+
|
|
170
|
+
When `audit.enabled` is `true`, Mnemocyte records an entry on every
|
|
171
|
+
state-changing operation (`remember`, `forget`, `forgetAll`, `prune`).
|
|
172
|
+
Read with `client.listAuditLog()`.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
const client = createMnemocyte({
|
|
176
|
+
embedder,
|
|
177
|
+
audit: { enabled: true },
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
await client.remember({ entityId: "user_123", content: "Likes tea." });
|
|
181
|
+
await client.prune({ entityId: "user_123", expired: true });
|
|
182
|
+
|
|
183
|
+
const log = await client.listAuditLog({
|
|
184
|
+
entityId: "user_123",
|
|
185
|
+
limit: 100,
|
|
186
|
+
});
|
|
187
|
+
for (const event of log) {
|
|
188
|
+
console.log(event.timestamp, event.description, event.metadata);
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Recorded `description` slugs:
|
|
193
|
+
|
|
194
|
+
- `"memory.created"` — single insert; metadata: `{ memoryId, type, importance }`
|
|
195
|
+
- `"memory.deleted"` — single `forget`; metadata: `{ memoryId }`
|
|
196
|
+
- `"entity.cleared"` — `forgetAll`; metadata: `{ count }`
|
|
197
|
+
- `"memory.pruned"` — real (non-dryRun) `prune` with an `entityId` selector; metadata: `{ count }`
|
|
198
|
+
|
|
199
|
+
Audit entries are **sticky**: `forgetAll` no longer erases prior log
|
|
200
|
+
entries — wiping an entity is itself a recorded `"entity.cleared"`
|
|
201
|
+
event. Disable audit by leaving `audit.enabled` unset (the default);
|
|
202
|
+
historical entries remain readable.
|
|
203
|
+
|
|
204
|
+
> Part of Phase 6 (consolidation tooling). The API surface may change
|
|
205
|
+
> as conflict detection and consolidation land.
|
|
206
|
+
|
|
207
|
+
## Consolidating duplicates (experimental)
|
|
208
|
+
|
|
209
|
+
`client.experimental.consolidate()` merges likely-duplicate memories
|
|
210
|
+
into a single survivor. Each loser is marked `supersededBy = survivor.id`
|
|
211
|
+
and excluded from `recall` by default. Tags are unioned onto the
|
|
212
|
+
survivor unless `mergeTags: false`. Idempotent — already-superseded
|
|
213
|
+
memories are skipped.
|
|
214
|
+
|
|
215
|
+
Pair it with `findDuplicates` to build a complete dedup pipeline:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
const pairs = await client.findDuplicates({
|
|
219
|
+
entityId: "user_123",
|
|
220
|
+
threshold: 0.97,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
for (const { a, b } of pairs) {
|
|
224
|
+
await client.experimental.consolidate({
|
|
225
|
+
entityId: "user_123",
|
|
226
|
+
survivorId: a.id,
|
|
227
|
+
supersededIds: [b.id],
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
When `audit.enabled` is `true`, each newly superseded memory writes a
|
|
233
|
+
`"memory.superseded"` audit event with metadata `{ memoryId, supersededBy }`.
|
|
234
|
+
|
|
235
|
+
> The `experimental` namespace is intentionally unstable. Members may
|
|
236
|
+
> change or move to the main client surface in future releases.
|
|
237
|
+
|
|
68
238
|
## Retrieval tuning
|
|
69
239
|
|
|
70
240
|
```ts
|
|
@@ -93,7 +263,7 @@ pnpm checktypes
|
|
|
93
263
|
pnpm lint
|
|
94
264
|
pnpm run test:retrieval
|
|
95
265
|
pnpm run test:integration
|
|
96
|
-
pnpm pack
|
|
266
|
+
pnpm run pack:check
|
|
97
267
|
```
|
|
98
268
|
|
|
99
269
|
`test:integration` skips cleanly when `DATABASE_URL` is not set.
|