@memoryrelay/plugin-memoryrelay-ai 0.16.3 → 0.17.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
@@ -5,7 +5,7 @@
5
5
  Persistent memory, architectural decisions, reusable patterns, and project orchestration for AI agents.
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@memoryrelay/plugin-memoryrelay-ai.svg)](https://www.npmjs.com/package/@memoryrelay/plugin-memoryrelay-ai)
8
- [![OpenClaw Compatible](https://img.shields.io/badge/OpenClaw-2026.2.0+-blue.svg)](https://openclaw.ai)
8
+ [![OpenClaw Compatible](https://img.shields.io/badge/OpenClaw-2026.3.28+-blue.svg)](https://openclaw.ai)
9
9
 
10
10
  ## Why MemoryRelay?
11
11
 
@@ -226,6 +226,7 @@ openclaw config set plugins.entries.plugin-memoryrelay-ai.config '{
226
226
  | `excludeChannels` | string[] | `[]` | Channel IDs to skip auto-recall |
227
227
  | `sessionTimeoutMinutes` | number | `120` | Idle time before session auto-close (10-1440) |
228
228
  | `sessionCleanupIntervalMinutes` | number | `30` | Stale session check interval (5-360) |
229
+ | `localCache` | object | see below | Local SQLite cache configuration (v0.17.0+) |
229
230
  | `debug` | boolean | `false` | Enable debug logging of API calls |
230
231
  | `verbose` | boolean | `false` | Include request/response bodies in logs |
231
232
  | `maxLogEntries` | number | `100` | Circular buffer size for in-memory logs (10-10000) |
@@ -239,6 +240,33 @@ openclaw config set plugins.entries.plugin-memoryrelay-ai.config '{
239
240
  | `MEMORYRELAY_API_URL` | `apiUrl` |
240
241
  | `MEMORYRELAY_DEFAULT_PROJECT` | `defaultProject` |
241
242
 
243
+ ### Local Cache Configuration (v0.17.0+)
244
+
245
+ ```json
246
+ {
247
+ "localCache": {
248
+ "enabled": true,
249
+ "dbPath": "~/.openclaw/memoryrelay-cache.db",
250
+ "syncIntervalMinutes": 5,
251
+ "maxLocalMemories": 10000,
252
+ "vectorSearch": { "enabled": false, "provider": "sqlite-vec" },
253
+ "ttl": { "hot": 72, "warm": 168, "cold": 720 }
254
+ }
255
+ }
256
+ ```
257
+
258
+ | Key | Type | Default | Description |
259
+ |-----|------|---------|-------------|
260
+ | `localCache.enabled` | boolean | `true` | Enable local SQLite cache |
261
+ | `localCache.dbPath` | string | `~/.openclaw/memoryrelay-cache.db` | Path to SQLite database |
262
+ | `localCache.syncIntervalMinutes` | number | `5` | Background sync interval (1-60) |
263
+ | `localCache.maxLocalMemories` | number | `10000` | Max memories stored locally |
264
+ | `localCache.vectorSearch.enabled` | boolean | `false` | Enable sqlite-vec vector search |
265
+ | `localCache.vectorSearch.provider` | string | `sqlite-vec` | Vector extension provider |
266
+ | `localCache.ttl.hot` | number | `72` | Hot tier TTL in hours (3 days) |
267
+ | `localCache.ttl.warm` | number | `168` | Warm tier TTL in hours (7 days) |
268
+ | `localCache.ttl.cold` | number | `720` | Cold tier TTL in hours (30 days) |
269
+
242
270
  ### Auto-Capture Tiers
243
271
 
244
272
  | Tier | Behavior | Use When |
@@ -267,6 +295,22 @@ The `confirmFirst` setting (default: `5`) prompts for confirmation on the first
267
295
  }
268
296
  ```
269
297
 
298
+ ## Performance (v0.17.0+)
299
+
300
+ With local cache enabled (default in v0.17.0), most operations skip API round-trips entirely:
301
+
302
+ | Operation | API-only (v0.16) | Local cache (v0.17) | Improvement |
303
+ |-----------|------------------|---------------------|-------------|
304
+ | Recall | ~200–500ms | <5ms | 40–100× |
305
+ | Capture | ~150–300ms | <2ms | 75–150× |
306
+ | Status probe | ~100ms | <1ms | 100× |
307
+
308
+ The local cache uses SQLite (better-sqlite3) with FTS5 for full-text search. An optional sqlite-vec extension enables local vector similarity search without API round-trips.
309
+
310
+ SyncDaemon runs in the background, pushing buffered writes and pulling remote changes on a configurable interval (default: 5 minutes).
311
+
312
+ > **Note:** v0.17.0 also fixes the `· unavailable` status display in `openclaw status` — the plugin now returns real memory counts from the local cache.
313
+
270
314
  ## Architecture & Privacy
271
315
 
272
316
  ### Data Flow
@@ -383,6 +427,92 @@ Then inspect with `/memory-logs` or `/memory-metrics` to identify slow or failin
383
427
 
384
428
  - `memory_batch_store`: May return 500 errors on large batches (use individual `memory_store` as workaround)
385
429
 
430
+ ## VPS Setup
431
+
432
+ Complete guide for running Claude Code with MemoryRelay on a VPS (Ubuntu).
433
+
434
+ ### Prerequisites
435
+
436
+ - Node.js 20+
437
+ - [OpenClaw](https://openclaw.ai) installed and configured
438
+ - [Claude Code](https://claude.ai/code) CLI installed
439
+
440
+ ### 1. Install the MCP server
441
+
442
+ ```bash
443
+ npm install -g @memoryrelay/mcp-server
444
+ ```
445
+
446
+ ### 2. Configure Claude Code settings
447
+
448
+ Add the MemoryRelay MCP server to `~/.claude/settings.json`:
449
+
450
+ ```json
451
+ {
452
+ "mcpServers": {
453
+ "MemoryRelay": {
454
+ "command": "memoryrelay-mcp",
455
+ "args": ["--agent-id", "YOUR_AGENT_UUID"],
456
+ "env": {
457
+ "MEMORYRELAY_API_KEY": "mem_prod_your_key_here"
458
+ }
459
+ }
460
+ }
461
+ }
462
+ ```
463
+
464
+ > **Important:** `agentId` must be a UUID obtained from `GET /v1/agents` — not a name string. Using a name string will cause authentication failures.
465
+
466
+ ### 3. Install the OpenClaw plugin
467
+
468
+ ```bash
469
+ openclaw plugins install @memoryrelay/plugin-memoryrelay-ai
470
+ ```
471
+
472
+ ### 4. Add `.mcp.json` to each project
473
+
474
+ Create `.mcp.json` in each project worktree root. This is **required** for MCP tools to be available in Claude sessions (including `claude --print`):
475
+
476
+ ```json
477
+ {
478
+ "mcpServers": {
479
+ "MemoryRelay": {
480
+ "command": "memoryrelay-mcp",
481
+ "args": ["--agent-id", "YOUR_AGENT_UUID"],
482
+ "env": {
483
+ "MEMORYRELAY_API_KEY": "mem_prod_your_key_here"
484
+ }
485
+ }
486
+ }
487
+ }
488
+ ```
489
+
490
+ ### 5. Install Alteriom Claude Skills (optional)
491
+
492
+ ```bash
493
+ git clone git@github.com:Alteriom/alteriom-claude-skills.git ~/.alteriom-claude-skills
494
+ ```
495
+
496
+ These provide curated skill files for common workflows across projects.
497
+
498
+ ## Known Issues
499
+
500
+ | Issue | Status | Workaround |
501
+ |-------|--------|------------|
502
+ | `openclaw status` shows `· unavailable` on OpenClaw 2026.3.28 | Cosmetic — plugin is functional | Fix planned in v0.17.0 (local cache with MemorySearchManager-compatible schema) |
503
+ | `plugins update --all` doesn't reliably update extensions | OpenClaw CLI bug | `rm -rf ~/.openclaw/extensions/plugin-memoryrelay-ai && openclaw plugins install @memoryrelay/plugin-memoryrelay-ai` |
504
+ | `agentId` must be a UUID from `GET /v1/agents` | By design | Do not use agent name strings — retrieve the UUID from the API |
505
+
506
+ ## Roadmap
507
+
508
+ **v0.17.0** — Local SQLite cache layer ([Epic #62](https://github.com/memoryrelay/openclaw-plugin/issues/62))
509
+
510
+ - Local SQLite cache for offline-first memory access
511
+ - SyncDaemon for background API synchronization
512
+ - Local vector search via `sqlite-vec`
513
+ - `MemorySearchManager`-compatible schema (fixes `openclaw status` display)
514
+ - Issues [#63](https://github.com/memoryrelay/openclaw-plugin/issues/63)–[#72](https://github.com/memoryrelay/openclaw-plugin/issues/72)
515
+
386
516
  ## Development
387
517
 
388
518
  ```bash
package/index.ts CHANGED
@@ -14,7 +14,8 @@
14
14
  * Docs: https://memoryrelay.ai
15
15
  */
16
16
 
17
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
17
+ import { mkdirSync } from "node:fs";
18
+ import { homedir } from "node:os";
18
19
  import { join } from "node:path";
19
20
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
20
21
 
@@ -31,6 +32,10 @@ import {
31
32
  VALID_HEALTH_STATUSES,
32
33
  } from "./src/client/memoryrelay-client.js";
33
34
  import { SessionResolver } from "./src/context/session-resolver.js";
35
+ import { LocalCache } from "./src/cache/local-cache.js";
36
+ import { SyncDaemon } from "./src/cache/sync-daemon.js";
37
+ import { PluginMemoryManager } from "./src/cache/memory-manager.js";
38
+ import type { LocalCacheConfig } from "./src/cache/types.js";
34
39
 
35
40
  // --- Hooks ---
36
41
  import { registerBeforeAgentStart } from "./src/hooks/before-agent-start.js";
@@ -98,6 +103,7 @@ interface MemoryRelayConfig {
98
103
  maxLogEntries?: number;
99
104
  sessionTimeoutMinutes?: number;
100
105
  sessionCleanupIntervalMinutes?: number;
106
+ localCache?: Partial<LocalCacheConfig>;
101
107
  }
102
108
 
103
109
  // ============================================================================
@@ -352,23 +358,64 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
352
358
  api.logger.error(`memory-memoryrelay: health check failed: ${String(err)}`);
353
359
  }
354
360
 
355
- // --- Create stub store file so OpenClaw's existsSync checks pass ---
356
- // OpenClaw 2026.3.28 scanner proceeds for all memory plugins but then checks
357
- // existsSync(store.path) for a local SQLite file. Create a minimal empty file
358
- // at the expected path so the scanner doesn't bail with "unavailable".
359
- try {
360
- const openclawHome = process.env.OPENCLAW_HOME || join(process.env.HOME || "/tmp", ".openclaw");
361
- const storeDir = join(openclawHome, "memory");
362
- // OpenClaw resolves store path as: ~/.openclaw/memory/{agentId}.sqlite
363
- const resolvedAgentId = agentId || "main";
364
- const storePath = join(storeDir, `${resolvedAgentId}.sqlite`);
365
- if (!existsSync(storePath)) {
361
+ // --- Local Cache + SyncDaemon (v0.17.0) ---
362
+ // Replaces the stub file hack from v0.16.x. LocalCache creates a real SQLite
363
+ // database at the expected path, satisfying OpenClaw's existsSync scanner and
364
+ // enabling local-first recall/capture pipelines.
365
+ const DEFAULT_CACHE_CONFIG: LocalCacheConfig = {
366
+ enabled: true,
367
+ dbPath: "",
368
+ syncIntervalMinutes: 5,
369
+ maxLocalMemories: 1000,
370
+ vectorSearch: { enabled: false, provider: "none" },
371
+ ttl: { hot: 72, warm: 168, cold: 720 },
372
+ };
373
+
374
+ const localCacheConfig: LocalCacheConfig = {
375
+ ...DEFAULT_CACHE_CONFIG,
376
+ ...cfg?.localCache,
377
+ vectorSearch: { ...DEFAULT_CACHE_CONFIG.vectorSearch, ...cfg?.localCache?.vectorSearch },
378
+ ttl: { ...DEFAULT_CACHE_CONFIG.ttl, ...cfg?.localCache?.ttl },
379
+ };
380
+
381
+ let localCache: LocalCache | null = null;
382
+ let syncDaemon: SyncDaemon | null = null;
383
+ let memoryManager: PluginMemoryManager | null = null;
384
+
385
+ if (localCacheConfig.enabled) {
386
+ try {
387
+ const openclawHome = process.env.OPENCLAW_HOME || join(homedir(), ".openclaw");
388
+ const resolvedAgentId = agentId || "main";
389
+ const storeDir = join(openclawHome, "memory");
366
390
  mkdirSync(storeDir, { recursive: true });
367
- writeFileSync(storePath, Buffer.alloc(0));
368
- api.logger.info?.(`memory-memoryrelay: created stub store file at ${storePath}`);
391
+ const dbPath = join(storeDir, `${resolvedAgentId}.sqlite`);
392
+ localCacheConfig.dbPath = dbPath;
393
+
394
+ localCache = new LocalCache(dbPath, localCacheConfig);
395
+ syncDaemon = new SyncDaemon(localCache, client, localCacheConfig);
396
+ syncDaemon.start();
397
+
398
+ const vectorAvailable = localCacheConfig.vectorSearch.enabled;
399
+ memoryManager = new PluginMemoryManager(
400
+ localCache,
401
+ syncDaemon,
402
+ localCacheConfig,
403
+ vectorAvailable,
404
+ agentId || "main",
405
+ );
406
+
407
+ // Initial pull on startup (non-blocking)
408
+ syncDaemon.pull().catch((err) =>
409
+ api.logger.warn?.(`memory-memoryrelay: initial sync failed: ${String(err)}`),
410
+ );
411
+
412
+ api.logger.info?.(`memory-memoryrelay: local cache initialized at ${dbPath}`);
413
+ } catch (err) {
414
+ api.logger.warn?.(`memory-memoryrelay: local cache init failed, falling back to API-only: ${String(err)}`);
415
+ localCache = null;
416
+ syncDaemon = null;
417
+ memoryManager = null;
369
418
  }
370
- } catch (err) {
371
- api.logger.warn?.(`memory-memoryrelay: failed to create stub store file: ${String(err)}`);
372
419
  }
373
420
 
374
421
  // --- Tool enablement filter ---
@@ -398,8 +445,8 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
398
445
  // ========================================================================
399
446
 
400
447
  registerBeforeAgentStart(api, pluginConfig, isToolEnabled, defaultProject);
401
- registerBeforePromptBuild(api, pluginConfig, client, sessionResolver);
402
- registerAgentEnd(api, pluginConfig, client, sessionResolver);
448
+ registerBeforePromptBuild(api, pluginConfig, client, sessionResolver, localCache, syncDaemon);
449
+ registerAgentEnd(api, pluginConfig, client, sessionResolver, localCache, syncDaemon);
403
450
  registerSessionLifecycle(api, pluginConfig, client, agentId, defaultProject, sessionResolver);
404
451
  registerSubagentHooks(api, pluginConfig, client, agentId, autoCaptureConfig, isBlocklisted);
405
452
  registerCompactionHooks(api, client, agentId, blocklist, extractRescueContent);
@@ -425,7 +472,7 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
425
472
  // ========================================================================
426
473
 
427
474
  api.logger.info?.(
428
- `memory-memoryrelay: plugin v0.16.3 loaded (${Object.values(TOOL_GROUPS).flat().length} tools, autoRecall: ${pluginConfig.autoRecall}, autoCapture: ${autoCaptureConfig.enabled ? autoCaptureConfig.tier : "off"}, debug: ${debugEnabled})`,
475
+ `memory-memoryrelay: plugin v0.17.0 loaded (${Object.values(TOOL_GROUPS).flat().length} tools, autoRecall: ${pluginConfig.autoRecall}, autoCapture: ${autoCaptureConfig.enabled ? autoCaptureConfig.tier : "off"}, debug: ${debugEnabled})`,
429
476
  );
430
477
 
431
478
  // ========================================================================
@@ -467,6 +514,29 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
467
514
  // `openclaw status` shows memory count, vector info, and provider details
468
515
  // instead of "unavailable". OpenClaw 2026.3.28+ calls this for all memory plugins.
469
516
  api.registerGatewayMethod?.("memory.probe", async ({ respond }) => {
517
+ // Use local PluginMemoryManager when available (v0.17.0+)
518
+ if (memoryManager) {
519
+ try {
520
+ const stats = memoryManager.cacheStats();
521
+ const daemonInfo = memoryManager.getSyncDaemonInfo();
522
+ respond(true, {
523
+ available: true,
524
+ provider: "memoryrelay",
525
+ memoryCount: stats.totalMemories,
526
+ tierBreakdown: stats.tierBreakdown,
527
+ bufferDepth: stats.bufferDepth,
528
+ syncActive: daemonInfo.running,
529
+ lastSync: stats.lastSync,
530
+ vector: { enabled: localCacheConfig.vectorSearch.enabled, dims: 768 },
531
+ fts: { enabled: true },
532
+ consecutiveErrors: daemonInfo.errors,
533
+ });
534
+ return;
535
+ } catch (err) {
536
+ api.logger.warn?.(`memory-memoryrelay: memory.probe local cache failed, falling back to API: ${String(err)}`);
537
+ }
538
+ }
539
+
470
540
  try {
471
541
  const health = await client.health() as { status: string; embedding_info?: { dimension?: number } };
472
542
  const healthStatus = String(health.status).toLowerCase();
@@ -509,6 +579,16 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
509
579
  }
510
580
  });
511
581
 
582
+ // getMemorySearchManager — exposes the PluginMemoryManager so OpenClaw's
583
+ // status scanner can call status(), probeVectorAvailability(), and close().
584
+ api.registerGatewayMethod?.("getMemorySearchManager", async ({ respond }) => {
585
+ if (memoryManager) {
586
+ respond(true, { manager: memoryManager });
587
+ } else {
588
+ respond(false, { error: "Local cache not initialized" });
589
+ }
590
+ });
591
+
512
592
  api.registerGatewayMethod?.("memory.status", async ({ respond }) => {
513
593
  try {
514
594
  const startTime = Date.now();
@@ -833,7 +913,43 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
833
913
 
834
914
  if (statusReporter) {
835
915
  const report = statusReporter.buildReport(connectionStatus, reportConfig, { total_memories: memoryCount }, TOOL_GROUPS);
836
- return { text: StatusReporter.formatReport(report) };
916
+ let text = StatusReporter.formatReport(report);
917
+
918
+ // Append local cache section when available
919
+ if (memoryManager) {
920
+ try {
921
+ const cacheStats = memoryManager.cacheStats();
922
+ const daemonInfo = memoryManager.getSyncDaemonInfo();
923
+ const cacheLines: string[] = [];
924
+ cacheLines.push("LOCAL CACHE");
925
+ const { hot, warm, cold } = cacheStats.tierBreakdown;
926
+ cacheLines.push(` Total: ${cacheStats.totalMemories} memories (hot: ${hot}, warm: ${warm}, cold: ${cold})`);
927
+ cacheLines.push(` Buffer: ${cacheStats.bufferDepth} entries pending sync`);
928
+ if (cacheStats.lastSync) {
929
+ const ago = StatusReporter.formatTimeAgo(new Date(cacheStats.lastSync));
930
+ cacheLines.push(` Last sync: ${ago}`);
931
+ } else {
932
+ cacheLines.push(" Last sync: never");
933
+ }
934
+ const vecLabel = localCacheConfig.vectorSearch.enabled ? "ready (sqlite-vec, 768 dims)" : "disabled";
935
+ cacheLines.push(` Vector: ${vecLabel}`);
936
+ cacheLines.push(" FTS: ready");
937
+ cacheLines.push("");
938
+ const daemonStatus = daemonInfo.running ? "running" : "stopped";
939
+ cacheLines.push(`SYNC DAEMON: ${daemonStatus}`);
940
+ cacheLines.push(` Interval: ${daemonInfo.intervalMinutes} minutes`);
941
+ cacheLines.push(` Errors: ${daemonInfo.errors} consecutive`);
942
+ if (daemonInfo.lastError) {
943
+ cacheLines.push(` Last error: ${daemonInfo.lastError}`);
944
+ }
945
+ cacheLines.push("");
946
+ text += cacheLines.join("\n");
947
+ } catch {
948
+ // cache stats unavailable — skip section
949
+ }
950
+ }
951
+
952
+ return { text };
837
953
  }
838
954
  return { text: `MemoryRelay: ${isConnected ? "connected" : "disconnected"} | Endpoint: ${apiUrl} | Memories: ${memoryCount} | Agent: ${agentId}` };
839
955
  } catch (err) {
@@ -1336,6 +1452,20 @@ export default async function plugin(api: OpenClawPluginApi): Promise<void> {
1336
1452
  },
1337
1453
  });
1338
1454
 
1455
+ // ========================================================================
1456
+ // Local Cache Cleanup Service
1457
+ // ========================================================================
1458
+
1459
+ if (localCache || syncDaemon) {
1460
+ api.registerService({
1461
+ id: "memoryrelay-cache-cleanup",
1462
+ stop: async () => {
1463
+ syncDaemon?.stop();
1464
+ localCache?.close();
1465
+ },
1466
+ });
1467
+ }
1468
+
1339
1469
  // ========================================================================
1340
1470
  // Stale Session Cleanup Service
1341
1471
  // ========================================================================
@@ -2,8 +2,8 @@
2
2
  "id": "plugin-memoryrelay-ai",
3
3
  "kind": "memory",
4
4
  "name": "MemoryRelay AI",
5
- "description": "MemoryRelay v0.16.3 - Long-term memory with pipeline architecture, 42 tools, 17 commands, V2 async, sessions, decisions, patterns & projects (api.memoryrelay.net)",
6
- "version": "0.16.3",
5
+ "description": "MemoryRelay v0.17.0 - Long-term memory with pipeline architecture, 42 tools, 17 commands, V2 async, sessions, decisions, patterns & projects (api.memoryrelay.net)",
6
+ "version": "0.17.0",
7
7
  "uiHints": {
8
8
  "apiKey": {
9
9
  "label": "MemoryRelay API Key",
@@ -225,6 +225,47 @@
225
225
  }
226
226
  }
227
227
  },
228
+ "localCache": {
229
+ "type": "object",
230
+ "description": "Local SQLite cache for fast, offline-capable memory access",
231
+ "properties": {
232
+ "enabled": {
233
+ "type": "boolean",
234
+ "default": true,
235
+ "description": "Enable local SQLite cache layer"
236
+ },
237
+ "syncIntervalMinutes": {
238
+ "type": "number",
239
+ "default": 5,
240
+ "minimum": 1,
241
+ "maximum": 60,
242
+ "description": "How often to sync with the API (minutes)"
243
+ },
244
+ "maxLocalMemories": {
245
+ "type": "number",
246
+ "default": 1000,
247
+ "minimum": 100,
248
+ "maximum": 10000,
249
+ "description": "Maximum number of memories to keep locally"
250
+ },
251
+ "vectorSearch": {
252
+ "type": "object",
253
+ "properties": {
254
+ "enabled": { "type": "boolean", "default": true },
255
+ "provider": { "type": "string", "default": "sqlite-vec" }
256
+ }
257
+ },
258
+ "ttl": {
259
+ "type": "object",
260
+ "description": "Time-to-live per tier (hours)",
261
+ "properties": {
262
+ "hot": { "type": "number", "default": 72 },
263
+ "warm": { "type": "number", "default": 168 },
264
+ "cold": { "type": "number", "default": 720 }
265
+ }
266
+ }
267
+ }
268
+ },
228
269
  "ranking": {
229
270
  "type": "object",
230
271
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoryrelay/plugin-memoryrelay-ai",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "description": "OpenClaw memory plugin for MemoryRelay API - 42 tools, 17 commands, V2 async, sessions, decisions, patterns, projects & semantic search",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -33,6 +33,7 @@
33
33
  "openclaw": ">=2026.2.0"
34
34
  },
35
35
  "devDependencies": {
36
+ "@types/better-sqlite3": "^7.6.13",
36
37
  "@types/node": "^25.3.5",
37
38
  "@vitest/coverage-v8": "^1.2.0",
38
39
  "typescript": "^5.9.3",
@@ -53,5 +54,11 @@
53
54
  ],
54
55
  "engines": {
55
56
  "node": ">=20.0.0"
57
+ },
58
+ "dependencies": {
59
+ "better-sqlite3": "^12.8.0"
60
+ },
61
+ "optionalDependencies": {
62
+ "sqlite-vec": "^0.1.0"
56
63
  }
57
64
  }