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 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 --dry-run
266
+ pnpm run pack:check
97
267
  ```
98
268
 
99
269
  `test:integration` skips cleanly when `DATABASE_URL` is not set.