brainbank 0.1.3 → 0.2.1

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 (49) hide show
  1. package/README.md +26 -12
  2. package/dist/{types-Da_zLLOl.d.ts → base-9vfWRHCV.d.ts} +131 -31
  3. package/dist/{chunk-TW5NTYYZ.js → chunk-6MFTQV3O.js} +909 -685
  4. package/dist/chunk-6MFTQV3O.js.map +1 -0
  5. package/dist/chunk-7JCEW7LT.js +266 -0
  6. package/dist/chunk-7JCEW7LT.js.map +1 -0
  7. package/dist/{chunk-GOUBW7UA.js → chunk-F6SJ3U4H.js} +98 -34
  8. package/dist/chunk-F6SJ3U4H.js.map +1 -0
  9. package/dist/{chunk-MJ3Y24H6.js → chunk-FJJY4H2Y.js} +11 -11
  10. package/dist/chunk-FJJY4H2Y.js.map +1 -0
  11. package/dist/{chunk-3GAIDXRW.js → chunk-GUT5MSJT.js} +5 -11
  12. package/dist/chunk-GUT5MSJT.js.map +1 -0
  13. package/dist/{chunk-2P3EGY6S.js → chunk-QNHBCOKB.js} +2 -2
  14. package/dist/chunk-QNHBCOKB.js.map +1 -0
  15. package/dist/{chunk-4ZKBQ33J.js → chunk-V4UJKXPK.js} +23 -5
  16. package/dist/chunk-V4UJKXPK.js.map +1 -0
  17. package/dist/{chunk-RAEBYV75.js → chunk-WR4WXKJT.js} +37 -23
  18. package/dist/chunk-WR4WXKJT.js.map +1 -0
  19. package/dist/{chunk-Z5SU54HP.js → chunk-X6645UVR.js} +3 -3
  20. package/dist/chunk-X6645UVR.js.map +1 -0
  21. package/dist/cli.js +122 -102
  22. package/dist/cli.js.map +1 -1
  23. package/dist/code.d.ts +5 -5
  24. package/dist/code.js +1 -1
  25. package/dist/docs.d.ts +4 -6
  26. package/dist/docs.js +1 -1
  27. package/dist/git.d.ts +5 -5
  28. package/dist/git.js +1 -1
  29. package/dist/index.d.ts +54 -90
  30. package/dist/index.js +13 -13
  31. package/dist/memory.d.ts +5 -7
  32. package/dist/memory.js +9 -12
  33. package/dist/memory.js.map +1 -1
  34. package/dist/notes.d.ts +4 -6
  35. package/dist/notes.js +7 -10
  36. package/dist/notes.js.map +1 -1
  37. package/dist/{openai-PCTYLOWI.js → openai-CYDMYX7X.js} +2 -2
  38. package/package.json +2 -2
  39. package/dist/chunk-2P3EGY6S.js.map +0 -1
  40. package/dist/chunk-3GAIDXRW.js.map +0 -1
  41. package/dist/chunk-4ZKBQ33J.js.map +0 -1
  42. package/dist/chunk-GOUBW7UA.js.map +0 -1
  43. package/dist/chunk-MJ3Y24H6.js.map +0 -1
  44. package/dist/chunk-N6ZMBFDE.js +0 -224
  45. package/dist/chunk-N6ZMBFDE.js.map +0 -1
  46. package/dist/chunk-RAEBYV75.js.map +0 -1
  47. package/dist/chunk-TW5NTYYZ.js.map +0 -1
  48. package/dist/chunk-Z5SU54HP.js.map +0 -1
  49. /package/dist/{openai-PCTYLOWI.js.map → openai-CYDMYX7X.js.map} +0 -0
package/README.md CHANGED
@@ -11,7 +11,7 @@ BrainBank gives LLMs a long-term memory that persists between sessions.
11
11
  - **Pluggable embeddings** — local WASM (free) or OpenAI (higher quality)
12
12
  - **Multi-repo** — index multiple repositories into one shared database
13
13
  - **Portable** — single `.brainbank/brainbank.db` file
14
- - **Optional packages** — [`@brainbank/memory`](#memory) (deterministic fact extraction), [`@brainbank/reranker`](#reranker) (Qwen3 cross-encoder), [`@brainbank/mcp`](#mcp-server) (MCP server)
14
+ - **Optional packages** — [`@brainbank/memory`](#memory) (fact extraction + entity graph), [`@brainbank/reranker`](#reranker) (Qwen3 cross-encoder), [`@brainbank/mcp`](#mcp-server) (MCP server)
15
15
 
16
16
  ![BrainBank Architecture](assets/architecture.png)
17
17
 
@@ -29,17 +29,19 @@ Most AI memory solutions (mem0, Zep, LangMem) require cloud services, external d
29
29
  | LLM required to write | **No**¹ | Yes | Yes | Yes |
30
30
  | Code-aware | **19 AST-parsed languages (tree-sitter), git, co-edits** | ✗ | ✗ | ✗ |
31
31
  | Custom indexers | **`.use()` plugin system** | ✗ | ✗ | ✗ |
32
- | Search | **Vector + BM25 + RRF** | Vector only | Vector + graph | Vector only |
32
+ | Search | **Vector + BM25 + RRF** | Vector + graph² | Vector + BM25 + graph | Vector only |
33
33
  | Framework lock-in | **None** | Optional | Zep cloud | LangChain |
34
34
  | Portable | **Copy one file** | Tied to DB | Tied to cloud | Tied to platform |
35
35
 
36
36
  > ¹ mem0 and Zep use LLMs to auto-extract memories from raw text. BrainBank is explicit — you decide what gets stored. Less magic, more control.
37
+ >
38
+ > ² mem0's graph store (mem0g) is available in the paid platform version.
37
39
 
38
40
  **In short:**
39
41
  - **Code-first** — the only memory layer that understands code structure, git history, and file co-edit relationships
42
+ - **Framework-agnostic** — plain TypeScript, works with any agent framework (LangChain, Vercel AI SDK, custom) or none at all. Unopinionated — doesn't force you into a specific pattern
40
43
  - **$0 memory bill** — no LLM calls to extract/consolidate. You store what you want, BrainBank embeds deterministically
41
44
  - **Truly portable** — `.brainbank/brainbank.db` is a normal file. Copy it, back it up, `git lfs` it
42
- - **No vendor lock-in** — plain TypeScript, works with any agent framework or none at all
43
45
 
44
46
  ### Table of Contents
45
47
 
@@ -84,7 +86,7 @@ npm install brainbank
84
86
 
85
87
  | Package | When to install |
86
88
  |---------|----------------|
87
- | `@brainbank/memory` | Deterministic memory extraction for LLM conversations (mem0-style pipeline) |
89
+ | `@brainbank/memory` | Deterministic memory extraction + entity graph for LLM conversations |
88
90
  | `@brainbank/reranker` | Cross-encoder reranker (Qwen3-0.6B, ~640MB model) |
89
91
  | `@brainbank/mcp` | MCP server for AI tool integration |
90
92
 
@@ -769,6 +771,8 @@ Without a reranker, BrainBank uses pure RRF fusion — which is already producti
769
771
 
770
772
  `@brainbank/memory` adds **deterministic memory extraction** to any LLM conversation. After every turn, it automatically extracts facts, deduplicates against existing memories, and decides `ADD` / `UPDATE` / `NONE` — no function calling needed.
771
773
 
774
+ Optionally extracts **entities and relationships** (knowledge graph) from the same LLM call — no extra cost. Includes **LLM-powered entity resolution** to merge aliases (e.g. "TS" → "TypeScript").
775
+
772
776
  Inspired by [mem0](https://github.com/mem0ai/mem0)'s pipeline, but framework-agnostic and built on BrainBank collections.
773
777
 
774
778
  ```bash
@@ -777,22 +781,32 @@ npm install @brainbank/memory
777
781
 
778
782
  ```typescript
779
783
  import { BrainBank } from 'brainbank';
780
- import { Memory, OpenAIProvider } from '@brainbank/memory';
784
+ import { Memory, EntityStore, OpenAIProvider } from '@brainbank/memory';
781
785
 
782
786
  const brain = new BrainBank({ dbPath: './memory.db' });
783
787
  await brain.initialize();
784
788
 
785
- const memory = new Memory(brain.collection('memories'), {
786
- llm: new OpenAIProvider({ model: 'gpt-4.1-nano' }),
789
+ const llm = new OpenAIProvider({ model: 'gpt-4.1-nano' });
790
+
791
+ // Opt-in entity extraction (knowledge graph)
792
+ const entityStore = new EntityStore(brain, {
793
+ onEntity: (op) => console.log(`${op.action}: ${op.name}`),
794
+ });
795
+
796
+ const memory = new Memory(brain, {
797
+ llm, // auto-shared with EntityStore
798
+ entityStore, // optional — omit for facts-only mode
799
+ onOperation: (op) => console.log(`${op.action}: ${op.fact}`),
787
800
  });
788
801
 
789
- // After every conversation turn (deterministic, automatic)
790
- await memory.process(userMessage, assistantResponse);
791
- // → extracts facts, deduplicates, executes ADD/UPDATE/NONE
802
+ // After every conversation turn
803
+ const result = await memory.process(userMessage, assistantResponse);
804
+ // result.operations [{ fact, action: "ADD", reason }]
805
+ // result.entities → { entitiesProcessed: 2, relationshipsProcessed: 1 }
792
806
 
793
- // For the system prompt
807
+ // System prompt with memories + entities
794
808
  const context = memory.buildContext();
795
- // → "## Memories\n- User's name is Berna\n- Prefers TypeScript"
809
+ // → "## Memories\n- User's name is Berna\n\n## Known Entities\n- Berna (person, 3x)\n..."
796
810
  ```
797
811
 
798
812
  The `LLMProvider` interface works with any framework:
@@ -101,10 +101,14 @@ interface SearchHit {
101
101
  interface VectorIndex {
102
102
  /** Initialize the index. Must be called before add/search. */
103
103
  init(): Promise<this>;
104
- /** Add a vector with an integer ID. */
104
+ /** Add a vector with an integer ID. Idempotent: duplicate IDs are skipped. */
105
105
  add(vector: Float32Array, id: number): void;
106
+ /** Mark a vector as deleted so it no longer appears in searches. */
107
+ remove(id: number): void;
106
108
  /** Search for k nearest neighbors. */
107
109
  search(query: Float32Array, k: number): SearchHit[];
110
+ /** Clear all vectors and reset to empty state. */
111
+ reinit(): void;
108
112
  /** Number of vectors in the index. */
109
113
  readonly size: number;
110
114
  }
@@ -140,7 +144,7 @@ interface GitCommitRecord {
140
144
  deletions: number;
141
145
  isMerge: boolean;
142
146
  }
143
- interface MemoryPattern {
147
+ interface LearningPattern {
144
148
  id?: number;
145
149
  /** Category (e.g. 'api', 'refactor', 'debug') */
146
150
  taskType: string;
@@ -166,25 +170,88 @@ interface DistilledStrategy {
166
170
  updatedAt: number;
167
171
  }
168
172
  type SearchResultType = 'code' | 'commit' | 'pattern' | 'document' | 'collection';
169
- interface SearchResult {
170
- type: SearchResultType;
173
+ interface CodeResultMetadata {
174
+ chunkType: string;
175
+ name?: string;
176
+ startLine: number;
177
+ endLine: number;
178
+ language: string;
179
+ searchType?: string;
180
+ }
181
+ interface CommitResultMetadata {
182
+ hash: string;
183
+ shortHash: string;
184
+ author: string;
185
+ date: string;
186
+ files: string[];
187
+ additions?: number;
188
+ deletions?: number;
189
+ diff?: string;
190
+ searchType?: string;
191
+ }
192
+ interface PatternResultMetadata {
193
+ taskType: string;
194
+ task: string;
195
+ outcome?: string;
196
+ successRate: number;
197
+ critique?: string;
198
+ searchType?: string;
199
+ }
200
+ interface DocumentResultMetadata {
201
+ collection?: string;
202
+ title?: string;
203
+ seq?: number;
204
+ path?: string;
205
+ searchType?: string;
206
+ }
207
+ interface CodeResult {
208
+ type: 'code';
209
+ score: number;
210
+ filePath: string;
211
+ content: string;
212
+ context?: string;
213
+ metadata: CodeResultMetadata;
214
+ }
215
+ interface CommitResult {
216
+ type: 'commit';
217
+ score: number;
218
+ filePath?: string;
219
+ content: string;
220
+ context?: string;
221
+ metadata: CommitResultMetadata;
222
+ }
223
+ interface PatternResult {
224
+ type: 'pattern';
225
+ score: number;
226
+ filePath?: string;
227
+ content: string;
228
+ context?: string;
229
+ metadata: PatternResultMetadata;
230
+ }
231
+ interface DocumentResult {
232
+ type: 'document';
233
+ score: number;
234
+ filePath?: string;
235
+ content: string;
236
+ context?: string;
237
+ metadata: DocumentResultMetadata;
238
+ }
239
+ interface CollectionResult {
240
+ type: 'collection';
171
241
  score: number;
172
- /** File path (for code results) or document path */
173
242
  filePath?: string;
174
- /** Content / text */
175
243
  content: string;
176
- /** Context description (for document results) */
177
244
  context?: string;
178
- /** Extra metadata depending on type */
179
245
  metadata: Record<string, any>;
180
246
  }
247
+ type SearchResult = CodeResult | CommitResult | PatternResult | DocumentResult | CollectionResult;
181
248
  interface ContextOptions {
182
249
  /** Max code chunks to include. Default: 6 */
183
250
  codeResults?: number;
184
251
  /** Max git commits to include. Default: 5 */
185
252
  gitResults?: number;
186
253
  /** Max memory patterns to include. Default: 4 */
187
- memoryResults?: number;
254
+ patternResults?: number;
188
255
  /** Files the agent is about to modify (improves co-edit suggestions) */
189
256
  affectedFiles?: string[];
190
257
  /** Minimum similarity score threshold. Default: 0.25 */
@@ -252,7 +319,10 @@ interface IndexStats {
252
319
  long: number;
253
320
  };
254
321
  }
322
+ /** File-level progress (used by indexers). */
255
323
  type ProgressCallback = (file: string, current: number, total: number) => void;
324
+ /** Stage-level progress (used by BrainBank.index() orchestrator). */
325
+ type StageProgressCallback = (stage: string, message: string) => void;
256
326
  interface IndexResult {
257
327
  indexed: number;
258
328
  skipped: number;
@@ -278,18 +348,34 @@ declare class HNSWIndex implements VectorIndex {
278
348
  private _efConstruction;
279
349
  private _efSearch;
280
350
  private _index;
281
- private _count;
351
+ private _lib;
352
+ private _ids;
282
353
  constructor(_dims: number, _maxElements?: number, _M?: number, _efConstruction?: number, _efSearch?: number);
283
354
  /**
284
355
  * Initialize the HNSW index.
285
356
  * Must be called before add/search.
286
357
  */
287
358
  init(): Promise<this>;
359
+ /**
360
+ * Reinitialize the index in-place, clearing all vectors.
361
+ * Required after reembed or full re-index to avoid duplicate IDs.
362
+ * init() must have been called first.
363
+ */
364
+ reinit(): void;
365
+ private _createIndex;
366
+ /** Maximum capacity of this index. */
367
+ get maxElements(): number;
288
368
  /**
289
369
  * Add a vector with an integer ID.
290
370
  * The vector should be pre-normalized for cosine distance.
291
371
  */
292
372
  add(vector: Float32Array, id: number): void;
373
+ /**
374
+ * Mark a vector as deleted so it no longer appears in searches.
375
+ * Uses hnswlib-node markDelete under the hood.
376
+ * Safe to call with an ID that doesn't exist.
377
+ */
378
+ remove(id: number): void;
293
379
  /**
294
380
  * Search for the k nearest neighbors.
295
381
  * Returns results sorted by score (highest first).
@@ -434,41 +520,55 @@ interface Indexer {
434
520
  readonly name: string;
435
521
  /** Initialize the indexer (create HNSW, load vectors, etc.). */
436
522
  initialize(ctx: IndexerContext): Promise<void>;
437
- /** Index content. Implemented by code and git indexers. */
523
+ /** Index content (code, git plugins). */
438
524
  index?(options?: any): Promise<any>;
439
- /** Search indexed content. Implemented by docs indexer. */
525
+ /** Search indexed content (docs plugin). */
440
526
  search?(query: string, options?: any): Promise<any[]>;
441
- /** Register a document collection. */
527
+ /** Register a document collection (docs plugin). */
442
528
  addCollection?(collection: any): void;
443
- /** Remove a collection. */
529
+ /** Remove a collection (docs plugin). */
444
530
  removeCollection?(name: string): void;
445
- /** List registered collections. */
531
+ /** List registered collections (docs plugin). */
446
532
  listCollections?(): any[];
447
- /** Index all or specific collections. */
533
+ /** Index collections (docs plugin). */
448
534
  indexCollections?(options?: any): Promise<any>;
449
- /** Add context description for a collection path. */
535
+ /** Add context for a collection path (docs plugin). */
450
536
  addContext?(collection: string, path: string, context: string): void;
451
- /** Remove context for a collection path. */
537
+ /** Remove context (docs plugin). */
452
538
  removeContext?(collection: string, path: string): void;
453
- /** List all context entries. */
539
+ /** List context entries (docs plugin). */
454
540
  listContexts?(): any[];
455
- /**
456
- * Called by watch mode when a file changes.
457
- * Return true if this indexer handled the change.
458
- * If not implemented, watch will fall back to brain.index().
459
- */
541
+ /** Watch mode: handle file change (returns true if handled). */
460
542
  onFileChange?(filePath: string, event: 'create' | 'update' | 'delete'): Promise<boolean>;
461
- /**
462
- * Glob patterns this indexer watches.
463
- * If not set, defaults to all supported code extensions.
464
- */
543
+ /** Glob patterns for watch mode. */
465
544
  watchPatterns?(): string[];
466
545
  /** Return stats for this indexer. */
467
546
  stats?(): Record<string, any>;
468
547
  /** Clean up resources. */
469
548
  close?(): void;
470
549
  }
471
- type BrainBankModule = Indexer;
472
- type ModuleContext = IndexerContext;
550
+ /** Indexers that can scan and index content. */
551
+ interface IndexablePlugin extends Indexer {
552
+ index(options?: any): Promise<any>;
553
+ }
554
+ /** Indexers that can search indexed content. */
555
+ interface SearchablePlugin extends Indexer {
556
+ search(query: string, options?: any): Promise<any[]>;
557
+ }
558
+ /** Indexers that support file watch mode. */
559
+ interface WatchablePlugin extends Indexer {
560
+ onFileChange(filePath: string, event: 'create' | 'update' | 'delete'): Promise<boolean>;
561
+ watchPatterns(): string[];
562
+ }
563
+ /** Indexers that manage document collections. */
564
+ interface CollectionPlugin extends Indexer {
565
+ addCollection(collection: any): void;
566
+ removeCollection(name: string): void;
567
+ listCollections(): any[];
568
+ indexCollections(options?: any): Promise<any>;
569
+ addContext?(collection: string, path: string, context: string): void;
570
+ removeContext?(collection: string, path: string): void;
571
+ listContexts?(): any[];
572
+ }
473
573
 
474
- export { type BrainBankModule as B, Collection as C, type DocumentCollection as D, type EmbeddingProvider as E, type GitCommitRecord as G, HNSWIndex as H, type Indexer as I, type MemoryPattern as M, type ProgressCallback as P, type ResolvedConfig as R, type SearchResult as S, type VectorIndex as V, type BrainBankConfig as a, type IndexResult as b, type ContextOptions as c, type CoEditSuggestion as d, type IndexStats as e, type SearchHit as f, type CodeChunk as g, Database as h, type Reranker as i, type CollectionAddOptions as j, type CollectionItem as k, type CollectionSearchOptions as l, type DistilledStrategy as m, type DocChunk as n, type IndexerContext as o, type ModuleContext as p, type SearchResultType as q };
574
+ export { type SearchResultType as A, type BrainBankConfig as B, Collection as C, type DocumentCollection as D, type EmbeddingProvider as E, type SearchablePlugin as F, type GitCommitRecord as G, HNSWIndex as H, type Indexer as I, type LearningPattern as L, type ProgressCallback as P, type ResolvedConfig as R, type StageProgressCallback as S, type VectorIndex as V, type WatchablePlugin as W, type IndexResult as a, type SearchResult as b, type ContextOptions as c, type CoEditSuggestion as d, type IndexStats as e, type SearchHit as f, type CodeChunk as g, Database as h, type Reranker as i, type CodeResult as j, type CodeResultMetadata as k, type CollectionAddOptions as l, type CollectionItem as m, type CollectionPlugin as n, type CollectionResult as o, type CollectionSearchOptions as p, type CommitResult as q, type CommitResultMetadata as r, type DistilledStrategy as s, type DocChunk as t, type DocumentResult as u, type DocumentResultMetadata as v, type IndexablePlugin as w, type IndexerContext as x, type PatternResult as y, type PatternResultMetadata as z };