@melandlabs/memory-store 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.
Files changed (46) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +377 -0
  3. package/dist/config-MlK9f9CM.d.ts +147 -0
  4. package/dist/contracts.d.ts +62 -0
  5. package/dist/contracts.js +27 -0
  6. package/dist/contracts.js.map +1 -0
  7. package/dist/http.d.ts +38 -0
  8. package/dist/http.js +545 -0
  9. package/dist/http.js.map +1 -0
  10. package/dist/index.d.ts +35 -0
  11. package/dist/index.js +442 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/mcp.d.ts +29 -0
  14. package/dist/mcp.js +589 -0
  15. package/dist/mcp.js.map +1 -0
  16. package/dist/policies/memory-graph-correction-policy.d.ts +14 -0
  17. package/dist/policies/memory-graph-correction-policy.js +41 -0
  18. package/dist/policies/memory-graph-correction-policy.js.map +1 -0
  19. package/dist/policies/memory-graph-write-policy.d.ts +35 -0
  20. package/dist/policies/memory-graph-write-policy.js +78 -0
  21. package/dist/policies/memory-graph-write-policy.js.map +1 -0
  22. package/dist/search/unified-search.d.ts +81 -0
  23. package/dist/search/unified-search.js +399 -0
  24. package/dist/search/unified-search.js.map +1 -0
  25. package/dist/server/cli-http.d.ts +1 -0
  26. package/dist/server/cli-http.js +584 -0
  27. package/dist/server/cli-http.js.map +1 -0
  28. package/dist/server/cli-mcp.d.ts +1 -0
  29. package/dist/server/cli-mcp.js +603 -0
  30. package/dist/server/cli-mcp.js.map +1 -0
  31. package/dist/storage/chroma-memory-index.d.ts +76 -0
  32. package/dist/storage/chroma-memory-index.js +249 -0
  33. package/dist/storage/chroma-memory-index.js.map +1 -0
  34. package/dist/storage/postgres-raw-message-factory.d.ts +48 -0
  35. package/dist/storage/postgres-raw-message-factory.js +19 -0
  36. package/dist/storage/postgres-raw-message-factory.js.map +1 -0
  37. package/dist/storage/raw-message-store.d.ts +47 -0
  38. package/dist/storage/raw-message-store.js +140 -0
  39. package/dist/storage/raw-message-store.js.map +1 -0
  40. package/dist/storage/sqlite-raw-message-store.d.ts +19 -0
  41. package/dist/storage/sqlite-raw-message-store.js +68 -0
  42. package/dist/storage/sqlite-raw-message-store.js.map +1 -0
  43. package/dist/storage/sqlite-vector-index.d.ts +42 -0
  44. package/dist/storage/sqlite-vector-index.js +92 -0
  45. package/dist/storage/sqlite-vector-index.js.map +1 -0
  46. package/package.json +87 -0
package/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 Meland Labs
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,377 @@
1
+ # memory-store (workspace)
2
+
3
+ > **Workspace package.** Internal monorepo build artifact; not published to npm.
4
+ > End users install [`@melandlabs/opencontext`](https://www.npmjs.com/package/@melandlabs/opencontext)
5
+ > (the facade) instead. Monorepo contributors depend on this package via
6
+ > the workspace protocol.
7
+
8
+
9
+ OpenContext memory storage + search SDK with optional HTTP and MCP server
10
+ entry points. The package is intentionally decoupled from the opencontext
11
+ web app's database and env layers — every consumer wires up its own
12
+ implementation via `MemoryStoreConfig`.
13
+
14
+ - **SDK** (`memory-store`) — embed in a Node.js host
15
+ - **HTTP** (`memory-store/http`) — REST daemon
16
+ - **MCP** (`memory-store/mcp`) — stdio tools for Claude Desktop / Cursor
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pnpm add @melandlabs/opencontext
22
+ ```
23
+
24
+ ## Quick start
25
+
26
+ ```ts
27
+ import { createMemoryStore } from "memory-store";
28
+
29
+ const store = await createMemoryStore({
30
+ db: { getDb: () => drizzleDb() },
31
+ env: { isTauriMode: () => false },
32
+ unified: {
33
+ embedQuery: async ({ query }) => myEmbed(query),
34
+ },
35
+ });
36
+
37
+ await store.getRawMessageManager();
38
+ const hits = await store.searchUnifiedMemory({ userId, query });
39
+ ```
40
+
41
+ ## Entry points
42
+
43
+ | Import | What you get |
44
+ | -------------------------------- | ----------------------------------------------- |
45
+ | `memory-store` | `createMemoryStore(config)` facade |
46
+ | `memory-store/http` | `startHttpServer(options)` — Hono app on a port |
47
+ | `memory-store/mcp` | `startMcpServer()` — stdio MCP server |
48
+
49
+ CLI bins ship with the package:
50
+
51
+ ```bash
52
+ opencontext-memory-http --port 7421 --host 127.0.0.1
53
+ opencontext-memory-mcp
54
+ ```
55
+
56
+ ## Configuration matrix
57
+
58
+ | Key | Required | Description |
59
+ | ----------------------------------------------------- | ------------------------------- | --------------------------------------------------------- |
60
+ | `db.getDb()` | yes for persistence | Drizzle DB handle factory |
61
+ | `db.tables.rawMessages` / `db.tables.memorySummaries` | yes for postgres backend | Drizzle table references owned by the host |
62
+ | `env.isTauriMode()` | yes | selects sqlite vs postgres backend |
63
+ | `env.getTauriDbPath()` / `env.getTauriDataDir()` | for Tauri | where to place the SQLite files |
64
+ | `vector.backend` | one of `sqlite-vec` or `chroma` | vector index backend (auto-detected if omitted) |
65
+ | `vector.sqliteVec.dbPath` | when backend = sqlite-vec | sqlite-vec DB path |
66
+ | `vector.chroma.url` | when backend = chroma | chroma server URL |
67
+ | `unified.embedQuery` | yes for unified search | query embedder |
68
+ | `unified.searchKnowledge` | optional | RAG over uploaded documents |
69
+ | `unified.searchInsights` | optional | semantic search over extracted insights |
70
+ | `unified.searchRawMessagesAnn` | optional | postgres-side ANN over `raw_messages` (database fallback) |
71
+ | `logger` | optional | `console`-shaped logger; defaults to `console` |
72
+
73
+ If any of `unified.embedQuery`, `unified.searchKnowledge`, `unified.searchInsights`,
74
+ or `unified.searchRawMessagesAnn` are absent, the corresponding source in
75
+ `searchUnifiedMemory` returns empty results with a warning. The SDK still
76
+ works — fine for a read-only memory daemon or a chroma-only deployment.
77
+
78
+ See `src/config.ts` for the full type surface.
79
+
80
+ ## Recipes
81
+
82
+ ### 1. Postgres backend (server / agent daemon)
83
+
84
+ ```ts
85
+ import { drizzle } from "drizzle-orm/postgres-js";
86
+ import {
87
+ createMemoryStore,
88
+ registerPostgresFactory,
89
+ type PostgresRawMessageManagerLike,
90
+ } from "memory-store";
91
+ import { myPostgresRawMessageManager } from "./postgres-raw-message-store";
92
+ import * as schema from "./schema";
93
+
94
+ registerPostgresFactory<typeof myPostgresRawMessageManager>(
95
+ async () =>
96
+ myPostgresRawMessageManager as unknown as PostgresRawMessageManagerLike,
97
+ );
98
+
99
+ const db = drizzle(process.env.DATABASE_URL!, { schema });
100
+
101
+ const store = await createMemoryStore({
102
+ db: {
103
+ getDb: () => db,
104
+ tables: {
105
+ rawMessages: schema.rawMessages,
106
+ memorySummaries: schema.memorySummaries,
107
+ },
108
+ },
109
+ env: { isTauriMode: () => false },
110
+ unified: {
111
+ embedQuery: async ({ query }) => myEmbedder.embedQuery(query),
112
+ searchInsights: mySearchInsights,
113
+ searchKnowledge: mySearchKnowledge,
114
+ searchRawMessagesAnn: async ({
115
+ userId,
116
+ queryEmbedding,
117
+ limit,
118
+ threshold,
119
+ botId,
120
+ }) => myAnnSearch({ userId, queryEmbedding, limit, threshold, botId }),
121
+ },
122
+ });
123
+ ```
124
+
125
+ ### 2. Tauri / SQLite-vec backend (desktop)
126
+
127
+ ```ts
128
+ const store = await createMemoryStore({
129
+ env: {
130
+ isTauriMode: () => true,
131
+ getTauriDataDir: () => appDataDir(),
132
+ getTauriDbPath: () => `${appDataDir()}/memory.sqlite`,
133
+ },
134
+ vector: {
135
+ backend: "sqlite-vec",
136
+ sqliteVec: { dbPath: `${appDataDir()}/vectors.sqlite` },
137
+ },
138
+ unified: {
139
+ embedQuery: async ({ query }) => localOnnxEmbedder.embed(query),
140
+ },
141
+ });
142
+ ```
143
+
144
+ ### 3. Chroma backend (managed vector store)
145
+
146
+ ```ts
147
+ const store = await createMemoryStore({
148
+ env: { isTauriMode: () => false },
149
+ vector: {
150
+ backend: "chroma",
151
+ chroma: {
152
+ url: process.env.CHROMA_URL ?? "http://127.0.0.1:8000",
153
+ rawMessagesCollection: "opencontext_raw_messages",
154
+ insightsCollection: "opencontext_insights",
155
+ },
156
+ },
157
+ unified: { embedQuery: myEmbedder },
158
+ });
159
+ ```
160
+
161
+ When chroma is reachable and the host's postgres manager exposes
162
+ `searchMessagesSemantically`, chroma wins and the database path becomes
163
+ the fallback — same semantics as the web app.
164
+
165
+ ### 4. Cross-source search wiring
166
+
167
+ `createUnifiedSearch(deps)` accepts the per-source searchers independently.
168
+ You don't need to wire all of them; the ones you omit just emit a warning:
169
+
170
+ ```ts
171
+ import { createUnifiedSearch } from "memory-store/unified-search";
172
+
173
+ const search = createUnifiedSearch({
174
+ embedQuery: async ({ userId, query, authToken }) =>
175
+ embedder.embed(query, { userId, authToken }),
176
+
177
+ searchRawMessagesAnn: async ({
178
+ userId,
179
+ queryEmbedding,
180
+ limit,
181
+ threshold,
182
+ botId,
183
+ }) =>
184
+ db.rawMessages.searchAnn({
185
+ userId,
186
+ embedding: queryEmbedding,
187
+ limit,
188
+ threshold,
189
+ botId,
190
+ }),
191
+
192
+ searchInsights: async ({ userId, query, limit, threshold, botIds }) =>
193
+ insightIndex.search({ userId, query, limit, threshold, botIds }),
194
+
195
+ searchKnowledge: async ({ userId, query, options, authToken }) =>
196
+ ragIndex.search({ userId, query, ...options, authToken }),
197
+ });
198
+
199
+ const result = await search.searchUnifiedMemory({
200
+ userId: "u-1",
201
+ query: "what changed since yesterday?",
202
+ sources: ["memory", "insights", "knowledge"],
203
+ limit: 10,
204
+ threshold: 0.7,
205
+ botIds: ["bot-42"],
206
+ });
207
+
208
+ // result.results: UnifiedMemorySearchResult[]
209
+ // result.warnings: UnifiedMemorySearchWarning[]
210
+ // e.g. { source: "memory", code: "raw_message_storage_unavailable", ... }
211
+ ```
212
+
213
+ ### 5. Postgres manager factory (lazy registration)
214
+
215
+ If the host application owns the postgres raw-message manager (e.g. an
216
+ existing Drizzle-based repo), register it via the factory pattern. The
217
+ memory-store package will resolve it on first use:
218
+
219
+ ```ts
220
+ import { registerPostgresFactory } from "memory-store/postgres-raw-message-factory";
221
+ import { myPostgresManager } from "./managers/postgres-raw-message";
222
+
223
+ registerPostgresFactory(async () => myPostgresManager);
224
+ // Later — anywhere in the codebase:
225
+ import { getRawMessageManager } from "memory-store";
226
+ const manager = await getRawMessageManager();
227
+ ```
228
+
229
+ If you don't register a factory, the package falls back to the legacy
230
+ sqlite path (`env.isTauriMode()` must return true). For non-Tauri hosts,
231
+ registering the factory is mandatory.
232
+
233
+ ### 6. HTTP daemon
234
+
235
+ ```ts
236
+ import { startHttpServer } from "memory-store/http";
237
+
238
+ const { url, port, stop } = await startHttpServer({
239
+ port: 7421,
240
+ host: "127.0.0.1",
241
+ // MemoryStoreConfig passthrough:
242
+ env: { isTauriMode: () => false },
243
+ vector: { backend: "chroma", chroma: { url: process.env.CHROMA_URL! } },
244
+ unified: { embedQuery: myEmbedder },
245
+ });
246
+
247
+ console.log(`listening at ${url}`);
248
+ // later: await stop();
249
+ ```
250
+
251
+ Or run the CLI:
252
+
253
+ ```bash
254
+ MEMORY_HTTP_HOST=0.0.0.0 MEMORY_HTTP_PORT=7421 opencontext-memory-http
255
+ ```
256
+
257
+ Endpoints:
258
+
259
+ | Method + path | Body |
260
+ | -------------------------------------- | ------------------------------------------------------------------------ |
261
+ | `GET /health` | — |
262
+ | `POST /v1/search` | `{ userId, query, limit?, threshold?, sources?, botIds?, documentIds? }` |
263
+ | `POST /v1/raw-messages` | `{ userId, messages: RawMessage[] }` |
264
+ | `GET /v1/raw-messages/:id?userId=...` | — |
265
+
266
+ For container / LAN deployment, put it behind a reverse proxy:
267
+
268
+ ```nginx
269
+ location /memory/ {
270
+ proxy_pass http://127.0.0.1:7421/;
271
+ proxy_set_header X-Forwarded-User $remote_user;
272
+ proxy_read_timeout 60s;
273
+ }
274
+ ```
275
+
276
+ ### 7. MCP daemon
277
+
278
+ ```bash
279
+ opencontext-memory-mcp
280
+ ```
281
+
282
+ Tools exposed over stdio:
283
+
284
+ | Tool | Required args |
285
+ | ------------------------ | ------------------------------------------------------------------------------------ |
286
+ | `memory.health` | — |
287
+ | `memory.searchUnified` | `userId`, `query`, optional `limit`, `threshold`, `sources`, `botIds`, `documentIds` |
288
+ | `memory.writeRawMessage` | `userId`, `message: { role, content, platform?, botId?, ... }` |
289
+ | `memory.getRawMessage` | `userId`, `messageId` |
290
+
291
+ #### Claude Desktop (`claude_desktop_config.json`)
292
+
293
+ ```json
294
+ {
295
+ "mcpServers": {
296
+ "opencontext-memory": {
297
+ "command": "npx",
298
+ "args": ["-y", "memory-store", "memory-mcp"],
299
+ "env": {
300
+ "DATABASE_URL": "postgres://user:pass@host:5432/opencontext"
301
+ }
302
+ }
303
+ }
304
+ }
305
+ ```
306
+
307
+ For local dev against this monorepo, point at the built CLI directly:
308
+
309
+ ```json
310
+ {
311
+ "mcpServers": {
312
+ "opencontext-memory": {
313
+ "command": "node",
314
+ "args": [
315
+ "/path/to/opencontext/packages/memory-store/dist/server/cli-mcp.js"
316
+ ]
317
+ }
318
+ }
319
+ }
320
+ ```
321
+
322
+ #### Cursor
323
+
324
+ Same shape — Cursor reads `mcpServers` from its MCP settings
325
+ (Cursor → Settings → MCP → Add new global MCP server):
326
+
327
+ ```json
328
+ {
329
+ "mcpServers": {
330
+ "opencontext-memory": {
331
+ "command": "opencontext-memory-mcp"
332
+ }
333
+ }
334
+ }
335
+ ```
336
+
337
+ After registering, the four `memory.*` tools become available inside the
338
+ editor. Tool calls appear in the chat like any other MCP tool.
339
+
340
+ ## Subpath exports
341
+
342
+ | Subpath | Contents |
343
+ | ---------------------------------------------------------- | ------------------------------------------------------------------------------- |
344
+ | `memory-store` | `createMemoryStore`, top-level types |
345
+ | `memory-store/http` | `startHttpServer`, `StartedHttpServer` |
346
+ | `memory-store/mcp` | `startMcpServer` |
347
+ | `memory-store/unified-search` | `createUnifiedSearch(deps)` factory + result types |
348
+ | `memory-store/raw-message-store` | `createRawMessageStore`, `getRawMessageManager`, `isRawMessageStorageAvailable` |
349
+ | `memory-store/sqlite-raw-message-store` | SQLite-vec raw-message manager (Tauri / desktop) |
350
+ | `memory-store/postgres-raw-message-factory` | `registerPostgresFactory`, `resolvePostgresFactory` |
351
+ | `memory-store/sqlite-vector-index` | Direct sqlite-vec insight index helpers |
352
+ | `memory-store/chroma-memory-index` | Direct chroma upsert / search helpers |
353
+ | `memory-store/memory-graph-write-policy` | `resolveMemoryGraphWritePolicy`, allowlist gating |
354
+ | `memory-store/memory-graph-correction-policy` | `resolveMemoryGraphCorrectionPolicy` |
355
+
356
+ ## Behaviour notes
357
+
358
+ - An empty / whitespace `query` short-circuits and returns
359
+ `{ query, sources, results: [], count: 0, warnings: [] }` without
360
+ touching any backend.
361
+ - If `isRawMessageStorageAvailable()` returns `false`, the memory source
362
+ emits a `raw_message_storage_unavailable` warning instead of running
363
+ Chroma / postgres search.
364
+ - When `botIds` is provided, the memory source fans out across each
365
+ `botId` filter in parallel (`Promise.all(filters.map(...))`) and
366
+ flattens the results. If `botIds` is empty, a single unfiltered query
367
+ is sent.
368
+ - `limit` is passed straight through to each underlying backend — no
369
+ silent inflation. `threshold` is clamped to `[-1, 1]`.
370
+ - Chroma failures fall back to `unified.searchRawMessagesAnn` when both
371
+ are configured; otherwise the memory source returns empty.
372
+
373
+ ## See also
374
+
375
+ - [`docs/architecture.md`](../../docs/architecture.md) — the overall data
376
+ model, lifecycle, and transport surfaces that the memory primitives
377
+ plug into.
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Memory store configuration — injected by the host application.
3
+ *
4
+ * The memory-store package is intentionally decoupled from the
5
+ * opencontext web app's database and env layers. Each consumer wires up
6
+ * its own implementation of these contracts.
7
+ *
8
+ * Required for raw-message persistence:
9
+ * - `db.getDb()` — returns a Drizzle DB handle
10
+ * - `env.isTauriMode()` — for sqlite vs postgres selection
11
+ *
12
+ * Required for vector indexing:
13
+ * - one of `vector.sqlite-vec.dbPath` or `vector.chroma.url`
14
+ *
15
+ * Required for unified semantic search:
16
+ * - `unified.embedQuery` — the embedding provider's query embedder
17
+ *
18
+ * Optional for cross-source unified search:
19
+ * - `unified.searchKnowledge` — RAG over uploaded documents
20
+ * - `unified.searchInsights` — over extracted insights
21
+ *
22
+ * If `unified.*` are absent, the SDK still works but the corresponding
23
+ * sources in `searchUnifiedMemory` will return empty results with a
24
+ * warning — fine for a standalone memory daemon.
25
+ */
26
+
27
+ interface MemoryStoreDb {
28
+ /** Resolve the active Drizzle DB handle. Must be server-side. */
29
+ getDb(): unknown;
30
+ /** Whether the DB has finished initializing (optional, for startup gating). */
31
+ isDbInitialized?(): boolean;
32
+ /** Initialize the DB (optional, called by `createMemoryStore` if present). */
33
+ initDb?(): unknown;
34
+ /**
35
+ * Drizzle table references for the postgres backend. Required when
36
+ * running outside Tauri mode without a registered factory. The host
37
+ * application owns the actual schema.
38
+ */
39
+ tables?: {
40
+ rawMessages?: unknown;
41
+ memorySummaries?: unknown;
42
+ };
43
+ }
44
+ interface MemoryStoreEnv {
45
+ /** True when running under Tauri (sqlite local). */
46
+ isTauriMode(): boolean;
47
+ /** Where to place the SQLite DB file (Tauri mode only). */
48
+ getTauriDbPath?(): string;
49
+ /**
50
+ * Tauri data dir for mkdirSync. Defaults to dirname(getTauriDbPath()).
51
+ * Provided so callers that use a custom layout don't need to expose
52
+ * their internal helpers.
53
+ */
54
+ getTauriDataDir?(): string;
55
+ }
56
+ type VectorBackend = "sqlite-vec" | "chroma";
57
+ interface MemoryStoreVectorConfig {
58
+ /** Active backend selector. Defaults to env-based detection. */
59
+ backend?: VectorBackend;
60
+ /** SQLite-vec options (used when backend === "sqlite-vec"). */
61
+ sqliteVec?: {
62
+ dbPath?: string;
63
+ insightsCollection?: string;
64
+ };
65
+ /** Chroma options (used when backend === "chroma"). */
66
+ chroma?: {
67
+ url?: string;
68
+ rawMessagesCollection?: string;
69
+ insightsCollection?: string;
70
+ };
71
+ }
72
+ type EmbedQueryFn = (input: {
73
+ userId: string;
74
+ query: string;
75
+ authToken?: string;
76
+ }) => Promise<number[]>;
77
+ interface UnifiedSearchKnowledgeResult {
78
+ chunkId: string;
79
+ documentId: string;
80
+ documentName: string;
81
+ content: string;
82
+ similarity: number;
83
+ chunkIndex: number;
84
+ }
85
+ interface UnifiedSearchInsightsResult {
86
+ id: string;
87
+ content: string;
88
+ similarity: number;
89
+ metadata: Record<string, unknown>;
90
+ }
91
+ interface UnifiedSearchDeps {
92
+ /** Embed a query string using the active user's provider. */
93
+ embedQuery?: EmbedQueryFn;
94
+ /**
95
+ * Postgres-side ANN search over `raw_messages`. Used when chroma is
96
+ * not enabled but the host has a postgres-backed manager. If both
97
+ * chroma and this are absent, the memory source will return empty
98
+ * (with a warning) in standalone mode.
99
+ */
100
+ searchRawMessagesAnn?: (input: {
101
+ userId: string;
102
+ queryEmbedding: number[];
103
+ limit: number;
104
+ threshold: number;
105
+ botId?: string;
106
+ }) => Promise<Array<{
107
+ id: string;
108
+ content: string;
109
+ similarity: number;
110
+ metadata: Record<string, unknown>;
111
+ }>>;
112
+ /** RAG over uploaded knowledge documents. Returns a list of chunk hits. */
113
+ searchKnowledge?: (input: {
114
+ userId: string;
115
+ query: string;
116
+ options: {
117
+ limit: number;
118
+ threshold: number;
119
+ documentIds?: string[];
120
+ };
121
+ authToken?: string;
122
+ }) => Promise<UnifiedSearchKnowledgeResult[]>;
123
+ /** Semantic search over extracted insights. */
124
+ searchInsights?: (input: {
125
+ userId: string;
126
+ query: string;
127
+ limit: number;
128
+ threshold: number;
129
+ botIds?: string[];
130
+ includeArchived?: boolean;
131
+ authToken?: string;
132
+ }) => Promise<UnifiedSearchInsightsResult[]>;
133
+ }
134
+ interface MemoryStoreConfig {
135
+ /** Database handle factory. Required for any persistence. */
136
+ db?: MemoryStoreDb;
137
+ /** Environment helpers. Defaults to a process.env-only stub if omitted. */
138
+ env?: MemoryStoreEnv;
139
+ /** Vector backend config. Defaults to env-based detection. */
140
+ vector?: MemoryStoreVectorConfig;
141
+ /** Optional cross-source search providers. */
142
+ unified?: UnifiedSearchDeps;
143
+ /** Optional logger. Defaults to console. */
144
+ logger?: Pick<Console, "log" | "warn" | "error">;
145
+ }
146
+
147
+ export type { EmbedQueryFn as E, MemoryStoreConfig as M, UnifiedSearchInsightsResult as U, VectorBackend as V, MemoryStoreDb as a, MemoryStoreEnv as b, UnifiedSearchKnowledgeResult as c, UnifiedSearchDeps as d };
@@ -0,0 +1,62 @@
1
+ import { MemoryRecord } from '@melandlabs/ai/memory';
2
+
3
+ /**
4
+ * Memory-store contracts — types and constants that were originally re-exported
5
+ * from `@melandlabs/indexeddb` but pulled in browser-only code (IndexedDB
6
+ * globals) at module evaluation time, breaking the standalone HTTP/MCP
7
+ * daemons.
8
+ *
9
+ * Anything defined here can be safely imported on the server without any
10
+ * browser globals being resolved. The `@melandlabs/indexeddb` peer dependency
11
+ * is optional in `@melandlabs/memory-store/package.json`; consumers can
12
+ * depend on it directly for IndexedDB-backed storage.
13
+ *
14
+ * Re-export from this module is stable. New domain types belong in
15
+ * dedicated files; this module only hosts the boundary contracts.
16
+ */
17
+
18
+ /** Prefix used to reserve chat-memory evidence IDs that must not collide. */
19
+ declare const CHAT_MEMORY_EVIDENCE_ID_PREFIX = "opencontext-chat:";
20
+ /** A locally-defined re-export of the raw-message shape used by the SDK.
21
+ * Kept as a structural type so consumers (SQLite, postgres factories)
22
+ * can pass either the IndexedDB `RawMessage` or this shape. */
23
+ interface RawMessage {
24
+ id?: number;
25
+ messageId: string;
26
+ platform: string;
27
+ botId: string;
28
+ userId: string;
29
+ channel?: string;
30
+ person?: string;
31
+ timestamp: number;
32
+ content: string;
33
+ attachments?: Array<{
34
+ name: string;
35
+ url: string;
36
+ contentType?: string;
37
+ sizeBytes?: number;
38
+ }>;
39
+ embedding?: number[];
40
+ embeddingModel?: string;
41
+ embeddingContentHash?: string;
42
+ embeddingDimensions?: number;
43
+ embeddingUpdatedAt?: number;
44
+ metadata?: Record<string, any>;
45
+ createdAt: number;
46
+ memoryStage?: "short" | "mid" | "long";
47
+ accessCount?: number;
48
+ lastAccessAt?: number;
49
+ importanceScore?: number;
50
+ archivedAt?: number;
51
+ isPinned?: boolean;
52
+ summaryRefId?: string;
53
+ deprecatedAt?: number;
54
+ deprecationReason?: string;
55
+ supersededBySummaryId?: string;
56
+ }
57
+ /** Convert a RawMessage to a MemoryRecord for embedding. Local copy of the
58
+ * helper that previously lived in `@melandlabs/indexeddb/embedding` and pulled
59
+ * in browser-only modules. */
60
+ declare function rawMessageToMemoryRecord(message: RawMessage): MemoryRecord;
61
+
62
+ export { CHAT_MEMORY_EVIDENCE_ID_PREFIX, type RawMessage, rawMessageToMemoryRecord };
@@ -0,0 +1,27 @@
1
+ // src/contracts.ts
2
+ var CHAT_MEMORY_EVIDENCE_ID_PREFIX = "opencontext-chat:";
3
+ function normalizeTimestampToMs(value) {
4
+ if (!Number.isFinite(value)) {
5
+ return 0;
6
+ }
7
+ if (value < 1e11) {
8
+ return Math.floor(value * 1e3);
9
+ }
10
+ return Math.floor(value);
11
+ }
12
+ function rawMessageToMemoryRecord(message) {
13
+ return {
14
+ id: message.messageId,
15
+ userId: message.userId,
16
+ timestamp: normalizeTimestampToMs(message.timestamp),
17
+ text: message.archivedAt ? void 0 : message.content,
18
+ mediaRefs: message.attachments?.map((item) => item.url).filter(Boolean),
19
+ embedding: message.embedding,
20
+ embeddingModel: message.embeddingModel,
21
+ embeddingContentHash: message.embeddingContentHash
22
+ };
23
+ }
24
+
25
+ export { CHAT_MEMORY_EVIDENCE_ID_PREFIX, rawMessageToMemoryRecord };
26
+ //# sourceMappingURL=contracts.js.map
27
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/contracts.ts"],"names":[],"mappings":";AAkBO,IAAM,8BAAA,GAAiC;AA0C9C,SAAS,uBAAuB,KAAA,EAAmC;AAClE,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAG;AAC5B,IAAA,OAAO,CAAA;AAAA,EACR;AACA,EAAA,IAAK,QAAmB,IAAA,EAAM;AAC7B,IAAA,OAAO,IAAA,CAAK,KAAA,CAAO,KAAA,GAAmB,GAAI,CAAA;AAAA,EAC3C;AACA,EAAA,OAAO,IAAA,CAAK,MAAM,KAAe,CAAA;AAClC;AAKO,SAAS,yBAAyB,OAAA,EAAmC;AAC3E,EAAA,OAAO;AAAA,IACN,IAAI,OAAA,CAAQ,SAAA;AAAA,IACZ,QAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,SAAA,EAAW,sBAAA,CAAuB,OAAA,CAAQ,SAAS,CAAA;AAAA,IACnD,IAAA,EAAM,OAAA,CAAQ,UAAA,GAAa,MAAA,GAAY,OAAA,CAAQ,OAAA;AAAA,IAC/C,SAAA,EAAW,OAAA,CAAQ,WAAA,EAAa,GAAA,CAAI,CAAC,SAAS,IAAA,CAAK,GAAG,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA;AAAA,IACtE,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,gBAAgB,OAAA,CAAQ,cAAA;AAAA,IACxB,sBAAsB,OAAA,CAAQ;AAAA,GAC/B;AACD","file":"contracts.js","sourcesContent":["/**\n * Memory-store contracts — types and constants that were originally re-exported\n * from `@melandlabs/indexeddb` but pulled in browser-only code (IndexedDB\n * globals) at module evaluation time, breaking the standalone HTTP/MCP\n * daemons.\n *\n * Anything defined here can be safely imported on the server without any\n * browser globals being resolved. The `@melandlabs/indexeddb` peer dependency\n * is optional in `@melandlabs/memory-store/package.json`; consumers can\n * depend on it directly for IndexedDB-backed storage.\n *\n * Re-export from this module is stable. New domain types belong in\n * dedicated files; this module only hosts the boundary contracts.\n */\n\nimport type { MemoryRecord } from \"@melandlabs/ai/memory\";\n\n/** Prefix used to reserve chat-memory evidence IDs that must not collide. */\nexport const CHAT_MEMORY_EVIDENCE_ID_PREFIX = \"opencontext-chat:\";\n\n/** A locally-defined re-export of the raw-message shape used by the SDK.\n * Kept as a structural type so consumers (SQLite, postgres factories)\n * can pass either the IndexedDB `RawMessage` or this shape. */\n// biome-ignore lint/suspicious/noExplicitAny: structural mirror of `@melandlabs/indexeddb/storage`'s `RawMessage`.\nexport interface RawMessage {\n\tid?: number;\n\tmessageId: string;\n\tplatform: string;\n\tbotId: string;\n\tuserId: string;\n\tchannel?: string;\n\tperson?: string;\n\ttimestamp: number;\n\tcontent: string;\n\tattachments?: Array<{\n\t\tname: string;\n\t\turl: string;\n\t\tcontentType?: string;\n\t\tsizeBytes?: number;\n\t}>;\n\tembedding?: number[];\n\tembeddingModel?: string;\n\tembeddingContentHash?: string;\n\tembeddingDimensions?: number;\n\tembeddingUpdatedAt?: number;\n\t// biome-ignore lint/suspicious/noExplicitAny: Preserve the public raw metadata contract.\n\tmetadata?: Record<string, any>;\n\tcreatedAt: number;\n\tmemoryStage?: \"short\" | \"mid\" | \"long\";\n\taccessCount?: number;\n\tlastAccessAt?: number;\n\timportanceScore?: number;\n\tarchivedAt?: number;\n\tisPinned?: boolean;\n\tsummaryRefId?: string;\n\tdeprecatedAt?: number;\n\tdeprecationReason?: string;\n\tsupersededBySummaryId?: string;\n}\n\nfunction normalizeTimestampToMs(value: number | undefined): number {\n\tif (!Number.isFinite(value)) {\n\t\treturn 0;\n\t}\n\tif ((value as number) < 1e11) {\n\t\treturn Math.floor((value as number) * 1000);\n\t}\n\treturn Math.floor(value as number);\n}\n\n/** Convert a RawMessage to a MemoryRecord for embedding. Local copy of the\n * helper that previously lived in `@melandlabs/indexeddb/embedding` and pulled\n * in browser-only modules. */\nexport function rawMessageToMemoryRecord(message: RawMessage): MemoryRecord {\n\treturn {\n\t\tid: message.messageId,\n\t\tuserId: message.userId,\n\t\ttimestamp: normalizeTimestampToMs(message.timestamp),\n\t\ttext: message.archivedAt ? undefined : message.content,\n\t\tmediaRefs: message.attachments?.map((item) => item.url).filter(Boolean),\n\t\tembedding: message.embedding,\n\t\tembeddingModel: message.embeddingModel,\n\t\tembeddingContentHash: message.embeddingContentHash,\n\t} as unknown as MemoryRecord;\n}\n"]}
package/dist/http.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { M as MemoryStoreConfig } from './config-MlK9f9CM.js';
2
+
3
+ /**
4
+ * @melandlabs/memory-store/http — HTTP server entry.
5
+ *
6
+ * Usage:
7
+ * import { startHttpServer } from "@melandlabs/memory-store/http";
8
+ * const { url, stop } = await startHttpServer({
9
+ * port: 7421,
10
+ * db: { getDb: () => drizzleDb() },
11
+ * env: { isTauriMode: () => false },
12
+ * });
13
+ *
14
+ * Endpoints (all POST, JSON in/out):
15
+ * GET /health → { ok: true }
16
+ * POST /v1/search → UnifiedMemorySearchOutput
17
+ * POST /v1/raw-messages → upsert raw messages (returns count)
18
+ * GET /v1/raw-messages/:id → single raw message
19
+ *
20
+ * The HTTP server speaks only to the raw-message + vector layers; it does
21
+ * not expose RAG/insights cross-source search (callers wire those up
22
+ * server-side or use the MCP server with a fully-wired store).
23
+ */
24
+
25
+ interface StartHttpServerOptions extends MemoryStoreConfig {
26
+ /** Port to bind. Defaults to 7421. */
27
+ port?: number;
28
+ /** Bind address. Defaults to 127.0.0.1 (loopback only — change to 0.0.0.0 for LAN). */
29
+ host?: string;
30
+ }
31
+ interface StartedHttpServer {
32
+ url: string;
33
+ port: number;
34
+ stop(): Promise<void>;
35
+ }
36
+ declare function startHttpServer(options?: StartHttpServerOptions): Promise<StartedHttpServer>;
37
+
38
+ export { MemoryStoreConfig, type StartHttpServerOptions, type StartedHttpServer, startHttpServer };