brainbank 0.1.3 → 0.2.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 +26 -12
- package/dist/{types-Da_zLLOl.d.ts → base-9vfWRHCV.d.ts} +131 -31
- package/dist/{chunk-TW5NTYYZ.js → chunk-6MFTQV3O.js} +909 -685
- package/dist/chunk-6MFTQV3O.js.map +1 -0
- package/dist/chunk-7JCEW7LT.js +266 -0
- package/dist/chunk-7JCEW7LT.js.map +1 -0
- package/dist/{chunk-GOUBW7UA.js → chunk-F6SJ3U4H.js} +98 -34
- package/dist/chunk-F6SJ3U4H.js.map +1 -0
- package/dist/{chunk-MJ3Y24H6.js → chunk-FJJY4H2Y.js} +11 -11
- package/dist/chunk-FJJY4H2Y.js.map +1 -0
- package/dist/{chunk-3GAIDXRW.js → chunk-GUT5MSJT.js} +5 -11
- package/dist/chunk-GUT5MSJT.js.map +1 -0
- package/dist/{chunk-2P3EGY6S.js → chunk-QNHBCOKB.js} +2 -2
- package/dist/chunk-QNHBCOKB.js.map +1 -0
- package/dist/{chunk-4ZKBQ33J.js → chunk-V4UJKXPK.js} +23 -5
- package/dist/chunk-V4UJKXPK.js.map +1 -0
- package/dist/{chunk-RAEBYV75.js → chunk-WR4WXKJT.js} +37 -23
- package/dist/chunk-WR4WXKJT.js.map +1 -0
- package/dist/{chunk-Z5SU54HP.js → chunk-X6645UVR.js} +3 -3
- package/dist/chunk-X6645UVR.js.map +1 -0
- package/dist/cli.js +122 -102
- package/dist/cli.js.map +1 -1
- package/dist/code.d.ts +5 -5
- package/dist/code.js +1 -1
- package/dist/docs.d.ts +4 -6
- package/dist/docs.js +1 -1
- package/dist/git.d.ts +5 -5
- package/dist/git.js +1 -1
- package/dist/index.d.ts +54 -90
- package/dist/index.js +13 -13
- package/dist/memory.d.ts +5 -7
- package/dist/memory.js +9 -12
- package/dist/memory.js.map +1 -1
- package/dist/notes.d.ts +4 -6
- package/dist/notes.js +7 -10
- package/dist/notes.js.map +1 -1
- package/dist/{openai-PCTYLOWI.js → openai-CYDMYX7X.js} +2 -2
- package/package.json +3 -3
- package/dist/chunk-2P3EGY6S.js.map +0 -1
- package/dist/chunk-3GAIDXRW.js.map +0 -1
- package/dist/chunk-4ZKBQ33J.js.map +0 -1
- package/dist/chunk-GOUBW7UA.js.map +0 -1
- package/dist/chunk-MJ3Y24H6.js.map +0 -1
- package/dist/chunk-N6ZMBFDE.js +0 -224
- package/dist/chunk-N6ZMBFDE.js.map +0 -1
- package/dist/chunk-RAEBYV75.js.map +0 -1
- package/dist/chunk-TW5NTYYZ.js.map +0 -1
- package/dist/chunk-Z5SU54HP.js.map +0 -1
- /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) (
|
|
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
|

|
|
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
|
|
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
|
|
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
|
|
786
|
-
|
|
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
|
|
790
|
-
await memory.process(userMessage, assistantResponse);
|
|
791
|
-
// →
|
|
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
|
-
//
|
|
807
|
+
// System prompt with memories + entities
|
|
794
808
|
const context = memory.buildContext();
|
|
795
|
-
// → "## Memories\n- User's name is Berna\n-
|
|
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
|
|
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
|
|
170
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
523
|
+
/** Index content (code, git plugins). */
|
|
438
524
|
index?(options?: any): Promise<any>;
|
|
439
|
-
/** Search indexed content
|
|
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
|
|
533
|
+
/** Index collections (docs plugin). */
|
|
448
534
|
indexCollections?(options?: any): Promise<any>;
|
|
449
|
-
/** Add context
|
|
535
|
+
/** Add context for a collection path (docs plugin). */
|
|
450
536
|
addContext?(collection: string, path: string, context: string): void;
|
|
451
|
-
/** Remove context
|
|
537
|
+
/** Remove context (docs plugin). */
|
|
452
538
|
removeContext?(collection: string, path: string): void;
|
|
453
|
-
/** List
|
|
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
|
-
|
|
472
|
-
|
|
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
|
|
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 };
|