opencode-mem 2.11.3 → 2.11.4

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
@@ -22,6 +22,24 @@ A persistent memory system for AI coding agents that enables long-term context r
22
22
 
23
23
  Local vector database with SQLite + HNSW (hnswlib-wasm), persistent project memories, automatic user profile learning, unified memory-prompt timeline, full-featured web UI, intelligent prompt-based memory extraction, multi-provider AI support (OpenAI, Anthropic), 12+ local embedding models, smart deduplication, and built-in privacy protection.
24
24
 
25
+ ## Prerequisites
26
+
27
+ This plugin uses `hnswlib-node` for fast vector similarity search, which requires native compilation. Ensure you have:
28
+
29
+ **All platforms:**
30
+
31
+ - Python 3.x
32
+ - A C++ compiler (gcc, clang, or MSVC)
33
+ - `make` or CMake
34
+
35
+ **Platform-specific setup:**
36
+
37
+ | Platform | Requirements |
38
+ | ----------- | ------------------------------------------------------------------------------------------------------------------------- |
39
+ | **macOS** | Xcode Command Line Tools: `xcode-select --install` |
40
+ | **Linux** | Build essentials: `sudo apt install build-essential python3` (Debian/Ubuntu) or `sudo pacman -S base-devel python` (Arch) |
41
+ | **Windows** | Visual Studio Build Tools with C++ workload, or Windows Build Tools: `npm install -g windows-build-tools` |
42
+
25
43
  ## Getting Started
26
44
 
27
45
  Add to your OpenCode configuration at `~/.config/opencode/opencode.json`:
@@ -32,7 +50,7 @@ Add to your OpenCode configuration at `~/.config/opencode/opencode.json`:
32
50
  }
33
51
  ```
34
52
 
35
- The plugin downloads automatically on next startup. No additional dependencies required - vector search is powered by pure WASM for cross-platform compatibility.
53
+ The plugin downloads automatically on next startup.
36
54
 
37
55
  ## Usage Examples
38
56
 
@@ -13,7 +13,6 @@ export declare class HNSWIndex {
13
13
  private initialized;
14
14
  constructor(dimensions: number, indexPath: string);
15
15
  private ensureInitialized;
16
- private vectorToArray;
17
16
  insert(id: string, vector: Float32Array): Promise<void>;
18
17
  insertBatch(items: HNSWIndexData[]): Promise<void>;
19
18
  search(queryVector: Float32Array, k: number): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"hnsw-index.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/hnsw-index.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,WAAW,CAAkB;gBAEzB,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;YAKnC,iBAAiB;IAyC/B,OAAO,CAAC,aAAa;IAIf,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBvD,WAAW,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBlD,MAAM,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAkBzF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,QAAQ,IAAI,MAAM;CAGnB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM;IAO3B,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS;IAWzE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS;IAWvE,gBAAgB,CACpB,EAAE,EAAE,GAAG,EACP,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAoCV,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBhF,sBAAsB,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CA0BpE"}
1
+ {"version":3,"file":"hnsw-index.d.ts","sourceRoot":"","sources":["../../../src/services/sqlite/hnsw-index.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,WAAW,CAAkB;gBAEzB,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;YAKnC,iBAAiB;IAyCzB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBvD,WAAW,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBlD,MAAM,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAkBzF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,QAAQ,IAAI,MAAM;CAGnB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM;IAO3B,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS;IAWzE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS;IAWvE,gBAAgB,CACpB,EAAE,EAAE,GAAG,EACP,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAoCV,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBhF,sBAAsB,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CA0BpE"}
@@ -2,7 +2,14 @@ import { mkdirSync, existsSync, writeFileSync, readFileSync, unlinkSync, readdir
2
2
  import { join, dirname, basename } from "node:path";
3
3
  import { log } from "../logger.js";
4
4
  import { CONFIG } from "../../config.js";
5
- import { HierarchicalNSW } from "hnswlib-node";
5
+ let HNSWLib = null;
6
+ async function loadHNSWLib() {
7
+ if (!HNSWLib) {
8
+ const module = await import("hnswlib-wasm");
9
+ HNSWLib = module;
10
+ }
11
+ return HNSWLib;
12
+ }
6
13
  export class HNSWIndex {
7
14
  index = null;
8
15
  idMap = new Map();
@@ -19,14 +26,15 @@ export class HNSWIndex {
19
26
  async ensureInitialized() {
20
27
  if (this.initialized)
21
28
  return;
29
+ const hnsw = await loadHNSWLib();
22
30
  const dir = dirname(this.indexPath);
23
31
  if (!existsSync(dir)) {
24
32
  mkdirSync(dir, { recursive: true });
25
33
  }
26
34
  if (existsSync(this.indexPath)) {
27
35
  try {
28
- this.index = new HierarchicalNSW("cosine", this.dimensions);
29
- await this.index.readIndex(this.indexPath);
36
+ this.index = new hnsw.HierarchicalNSW("cosine", this.dimensions);
37
+ this.index.readIndex(this.indexPath);
30
38
  const metaPath = this.indexPath + ".meta";
31
39
  if (existsSync(metaPath)) {
32
40
  const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
@@ -41,20 +49,15 @@ export class HNSWIndex {
41
49
  path: this.indexPath,
42
50
  error: String(error),
43
51
  });
44
- this.index = new HierarchicalNSW("cosine", this.dimensions);
45
- await this.index.initIndex(this.maxElements);
52
+ this.index = new hnsw.HierarchicalNSW("cosine", this.dimensions, this.maxElements);
46
53
  }
47
54
  }
48
55
  else {
49
- this.index = new HierarchicalNSW("cosine", this.dimensions);
50
- await this.index.initIndex(this.maxElements);
56
+ this.index = new hnsw.HierarchicalNSW("cosine", this.dimensions, this.maxElements);
51
57
  log("HNSW index created", { path: this.indexPath, dimensions: this.dimensions });
52
58
  }
53
59
  this.initialized = true;
54
60
  }
55
- vectorToArray(vector) {
56
- return Array.from(vector);
57
- }
58
61
  async insert(id, vector) {
59
62
  await this.ensureInitialized();
60
63
  if (this.reverseMap.has(id)) {
@@ -62,7 +65,7 @@ export class HNSWIndex {
62
65
  this.index.markDelete(internalId);
63
66
  }
64
67
  const internalId = this.nextId++;
65
- await this.index.addPoint(this.vectorToArray(vector), internalId);
68
+ this.index.addPoint(vector, internalId);
66
69
  this.idMap.set(internalId, id);
67
70
  this.reverseMap.set(id, internalId);
68
71
  await this.save();
@@ -75,7 +78,7 @@ export class HNSWIndex {
75
78
  this.index.markDelete(internalId);
76
79
  }
77
80
  const internalId = this.nextId++;
78
- await this.index.addPoint(this.vectorToArray(item.vector), internalId);
81
+ this.index.addPoint(item.vector, internalId);
79
82
  this.idMap.set(internalId, item.id);
80
83
  this.reverseMap.set(item.id, internalId);
81
84
  }
@@ -84,7 +87,7 @@ export class HNSWIndex {
84
87
  async search(queryVector, k) {
85
88
  await this.ensureInitialized();
86
89
  try {
87
- const results = await this.index.searchKnn(this.vectorToArray(queryVector), k);
90
+ const results = this.index.searchKnn(queryVector, k);
88
91
  return results.neighbors
89
92
  .map((internalId, idx) => ({
90
93
  id: this.idMap.get(internalId) || "",
@@ -114,7 +117,7 @@ export class HNSWIndex {
114
117
  if (!existsSync(dir)) {
115
118
  mkdirSync(dir, { recursive: true });
116
119
  }
117
- await this.index.writeIndex(this.indexPath);
120
+ this.index.writeIndex(this.indexPath);
118
121
  const metaPath = this.indexPath + ".meta";
119
122
  const meta = {
120
123
  nextId: this.nextId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-mem",
3
- "version": "2.11.3",
3
+ "version": "2.11.4",
4
4
  "description": "OpenCode plugin that gives coding agents persistent memory using local vector database",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
@@ -36,7 +36,7 @@
36
36
  "@opencode-ai/plugin": "^1.0.162",
37
37
  "@xenova/transformers": "^2.17.2",
38
38
  "franc-min": "^6.2.0",
39
- "hnswlib-node": "^3.0.0",
39
+ "hnswlib-wasm": "^0.8.2",
40
40
  "iso-639-3": "^3.0.1"
41
41
  },
42
42
  "devDependencies": {