@cartisien/engram 0.3.0 → 0.5.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
@@ -2,6 +2,8 @@
2
2
 
3
3
  > **Persistent semantic memory for AI agents.**
4
4
 
5
+ ![Engram demo](assets/demo.gif)
6
+
5
7
  ```typescript
6
8
  import { Engram } from '@cartisien/engram';
7
9
 
@@ -28,6 +30,8 @@ Stuffing everything into the system prompt wastes tokens and still misses things
28
30
  Engram gives your agents **persistent, semantically searchable memory** — SQLite-backed, TypeScript-first, zero config.
29
31
 
30
32
  - **Semantic search:** Finds relevant memories by meaning, not just keywords (via local Ollama embeddings)
33
+ - **Graph memory:** Extracts entity-relationship triples — recall connected context automatically
34
+ - **Consolidation:** Summarizes old memories into long-term entries so context stays dense, not noisy
31
35
  - **Zero config:** Works out of the box, falls back to keyword search without Ollama
32
36
  - **Local-first:** Your data stays on your machine. No API keys, no cloud required
33
37
  - **MCP-native:** Drop into Claude Desktop or Cursor via [`@cartisien/engram-mcp`](https://github.com/Cartisien/engram-mcp)
@@ -127,11 +131,87 @@ await memory.forget('session_abc', { id: 'entry_id' }); // one
127
131
  await memory.forget('session_abc', { before: new Date() }); // old entries
128
132
  ```
129
133
 
134
+ ### `graph(sessionId, entity)`
135
+
136
+ Returns a one-hop relationship map for a named entity — all connected entities and the memories that link them.
137
+
138
+ Requires `graphMemory: true` in config and a running Ollama instance with `qwen2.5:32b` (or override via `graphModel`).
139
+
140
+ ```typescript
141
+ const memory = new Engram({
142
+ dbPath: './memory.db',
143
+ graphMemory: true,
144
+ graphModel: 'qwen2.5:32b', // default
145
+ });
146
+
147
+ const graph = await memory.graph('session_abc', 'GovScout');
148
+ // {
149
+ // entity: 'GovScout',
150
+ // edges: [
151
+ // { relation: 'uses', target: 'MUI', sourceMemoryId: '...' },
152
+ // { relation: 'built_by', target: 'Jeff', sourceMemoryId: '...' },
153
+ // ],
154
+ // memories: [ { content: '...', ... } ]
155
+ // }
156
+ ```
157
+
158
+ ### `recall()` with graph augmentation
159
+
160
+ ```typescript
161
+ const results = await memory.recall('session_abc', 'what is GovScout?', 5, {
162
+ includeGraph: true, // augment top results with graph-connected memories
163
+ });
164
+ ```
165
+
166
+ ### `consolidate(sessionId, options?)` *(v0.4)*
167
+
168
+ Summarizes old working memories into dense long-term entries via a local LLM. Originals are archived (hidden from recall but not deleted).
169
+
170
+ ```typescript
171
+ const memory = new Engram({
172
+ dbPath: './memory.db',
173
+ autoConsolidate: true, // auto-trigger on remember() (default: false)
174
+ consolidateThreshold: 100, // trigger when working memories exceed this (default: 100)
175
+ consolidateKeep: 20, // keep N most recent working memories untouched (default: 20)
176
+ consolidateBatch: 50, // memories to process per run (default: 50)
177
+ consolidateModel: 'qwen2.5:32b', // LLM for summarization
178
+ });
179
+
180
+ // Manual consolidation
181
+ const result = await memory.consolidate('session_abc');
182
+ // → { summarized: 50, created: 4, archived: 50 }
183
+
184
+ // Preview without writing
185
+ const preview = await memory.consolidate('session_abc', { dryRun: true });
186
+ // → { summarized: 50, created: 0, archived: 0, previews: ['User prefers TypeScript...', ...] }
187
+ ```
188
+
189
+ **Memory tiers:**
190
+ - `working` — recent, granular memories (default)
191
+ - `long_term` — LLM-generated summaries of consolidated batches
192
+ - `archived` — original memories after consolidation (excluded from recall)
193
+
194
+ `recall()` searches `working` and `long_term` by default. Pass `tiers` to override:
195
+
196
+ ```typescript
197
+ // Search all tiers including archived
198
+ const results = await memory.recall('session_abc', 'preferences', 10, {
199
+ tiers: ['working', 'long_term', 'archived'],
200
+ });
201
+ ```
202
+
130
203
  ### `stats(sessionId)`
131
204
 
132
205
  ```typescript
133
206
  const stats = await memory.stats('session_abc');
134
- // { total: 42, byRole: { user: 21, assistant: 21 }, withEmbeddings: 42, ... }
207
+ // {
208
+ // total: 42,
209
+ // byRole: { user: 21, assistant: 21 },
210
+ // byTier: { working: 30, long_term: 12, archived: 50 },
211
+ // withEmbeddings: 42,
212
+ // graphNodes: 18,
213
+ // graphEdges: 31
214
+ // }
135
215
  ```
136
216
 
137
217
  ## MCP Server
@@ -167,8 +247,8 @@ Engram doesn't just persist data — it gives your agents **continuity**. The ab
167
247
 
168
248
  - **v0.1** ✅ SQLite persistence, keyword search
169
249
  - **v0.2** ✅ Semantic search via local Ollama embeddings
170
- - **v0.3** 🚧 Graph memory — entity relationships, connected context
171
- - **v0.4** 📋 Memory consolidation, long-term summarization
250
+ - **v0.3** Graph memory — entity relationships, connected context
251
+ - **v0.4** Memory consolidation, long-term summarization
172
252
 
173
253
  ## The Cartisien Memory Suite
174
254
 
@@ -0,0 +1,2 @@
1
+ export type { IMemoryAdapter } from '../types.js';
2
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import type { IMemoryAdapter, Memory, SearchOptions, SearchResult } from '../types.js';
2
+ /**
3
+ * In-process MemoryAdapter — no persistence, ideal for testing and ephemeral agents.
4
+ * The simplest possible trace: exists only as long as the process lives.
5
+ */
6
+ export declare class MemoryAdapter implements IMemoryAdapter {
7
+ private _cache;
8
+ init(): Promise<void>;
9
+ store(memory: Memory): Promise<Memory>;
10
+ get(id: string): Promise<Memory | null>;
11
+ search(embedding: number[], options: Required<SearchOptions>): Promise<SearchResult[]>;
12
+ forget(id: string): Promise<void>;
13
+ list(agentId: string, limit?: number): Promise<Memory[]>;
14
+ close(): Promise<void>;
15
+ get size(): number;
16
+ }
17
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAGtF;;;GAGG;AACH,qBAAa,aAAc,YAAW,cAAc;IAClD,OAAO,CAAC,MAAM,CAA4B;IAEpC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKtC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcvC,MAAM,CACV,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,GAC/B,OAAO,CAAC,YAAY,EAAE,CAAC;IAepB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAOpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,58 @@
1
+ import { cosineSimilarity } from '../utils/similarity.js';
2
+ /**
3
+ * In-process MemoryAdapter — no persistence, ideal for testing and ephemeral agents.
4
+ * The simplest possible trace: exists only as long as the process lives.
5
+ */
6
+ export class MemoryAdapter {
7
+ constructor() {
8
+ this._cache = new Map();
9
+ }
10
+ async init() {
11
+ // nothing to set up
12
+ }
13
+ async store(memory) {
14
+ this._cache.set(memory.id, memory);
15
+ return memory;
16
+ }
17
+ async get(id) {
18
+ const memory = this._cache.get(id) ?? null;
19
+ if (memory) {
20
+ const updated = {
21
+ ...memory,
22
+ accessedAt: new Date(),
23
+ accessCount: memory.accessCount + 1,
24
+ };
25
+ this._cache.set(id, updated);
26
+ return updated;
27
+ }
28
+ return null;
29
+ }
30
+ async search(embedding, options) {
31
+ const results = [];
32
+ for (const memory of this._cache.values()) {
33
+ const score = cosineSimilarity(embedding, memory.embedding);
34
+ if (score >= options.threshold) {
35
+ results.push({ memory, score });
36
+ }
37
+ }
38
+ return results
39
+ .sort((a, b) => b.score - a.score)
40
+ .slice(0, options.limit);
41
+ }
42
+ async forget(id) {
43
+ this._cache.delete(id);
44
+ }
45
+ async list(agentId, limit = 50) {
46
+ return [...this._cache.values()]
47
+ .filter((m) => m.agentId === agentId)
48
+ .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
49
+ .slice(0, limit);
50
+ }
51
+ async close() {
52
+ this._cache.clear();
53
+ }
54
+ get size() {
55
+ return this._cache.size;
56
+ }
57
+ }
58
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEzD;;;GAGG;AACH,MAAM,OAAO,aAAa;IAA1B;QACU,WAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IA6D5C,CAAC;IA3DC,KAAK,CAAC,IAAI;QACR,oBAAoB;IACtB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,MAAc;QACxB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAClC,OAAO,MAAM,CAAA;IACf,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAA;QAC1C,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,OAAO,GAAW;gBACtB,GAAG,MAAM;gBACT,UAAU,EAAE,IAAI,IAAI,EAAE;gBACtB,WAAW,EAAE,MAAM,CAAC,WAAW,GAAG,CAAC;aACpC,CAAA;YACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;YAC5B,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,CAAC,MAAM,CACV,SAAmB,EACnB,OAAgC;QAEhC,MAAM,OAAO,GAAmB,EAAE,CAAA;QAElC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAC3D,IAAI,KAAK,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBAC/B,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;YACjC,CAAC;QACH,CAAC;QAED,OAAO,OAAO;aACX,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;aACjC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACxB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,KAAK,GAAG,EAAE;QACpC,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;aAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC;aACpC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;aAC7D,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;IACrB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAA;IACzB,CAAC;CACF"}
@@ -0,0 +1,33 @@
1
+ import type { IMemoryAdapter, Memory, SearchOptions, SearchResult } from '../types.js';
2
+ /**
3
+ * PostgresAdapter — uses pgvector for semantic similarity search.
4
+ *
5
+ * TODO: implement with `pg` + pgvector extension.
6
+ * Requires: CREATE EXTENSION IF NOT EXISTS vector;
7
+ *
8
+ * Schema:
9
+ * CREATE TABLE engram_memories (
10
+ * id TEXT PRIMARY KEY,
11
+ * agent_id TEXT NOT NULL,
12
+ * content TEXT NOT NULL,
13
+ * embedding vector(1536),
14
+ * importance FLOAT NOT NULL DEFAULT 0.5,
15
+ * metadata JSONB NOT NULL DEFAULT '{}',
16
+ * created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
17
+ * accessed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
18
+ * access_count INT NOT NULL DEFAULT 0
19
+ * );
20
+ * CREATE INDEX ON engram_memories USING ivfflat (embedding vector_cosine_ops);
21
+ */
22
+ export declare class PostgresAdapter implements IMemoryAdapter {
23
+ private readonly connectionString;
24
+ constructor(connectionString: string);
25
+ init(): Promise<void>;
26
+ store(_memory: Memory): Promise<Memory>;
27
+ get(_id: string): Promise<Memory | null>;
28
+ search(_embedding: number[], _options: Required<SearchOptions>): Promise<SearchResult[]>;
29
+ forget(_id: string): Promise<void>;
30
+ list(_agentId: string, _limit?: number): Promise<Memory[]>;
31
+ close(): Promise<void>;
32
+ }
33
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/adapters/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEtF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,eAAgB,YAAW,cAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAAhB,gBAAgB,EAAE,MAAM;IAE/C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIxC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIxF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI1D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * PostgresAdapter — uses pgvector for semantic similarity search.
3
+ *
4
+ * TODO: implement with `pg` + pgvector extension.
5
+ * Requires: CREATE EXTENSION IF NOT EXISTS vector;
6
+ *
7
+ * Schema:
8
+ * CREATE TABLE engram_memories (
9
+ * id TEXT PRIMARY KEY,
10
+ * agent_id TEXT NOT NULL,
11
+ * content TEXT NOT NULL,
12
+ * embedding vector(1536),
13
+ * importance FLOAT NOT NULL DEFAULT 0.5,
14
+ * metadata JSONB NOT NULL DEFAULT '{}',
15
+ * created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
16
+ * accessed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
17
+ * access_count INT NOT NULL DEFAULT 0
18
+ * );
19
+ * CREATE INDEX ON engram_memories USING ivfflat (embedding vector_cosine_ops);
20
+ */
21
+ export class PostgresAdapter {
22
+ constructor(connectionString) {
23
+ this.connectionString = connectionString;
24
+ }
25
+ async init() {
26
+ throw new Error('PostgresAdapter not yet implemented. Use MemoryAdapter for now.');
27
+ }
28
+ async store(_memory) {
29
+ throw new Error('Not implemented');
30
+ }
31
+ async get(_id) {
32
+ throw new Error('Not implemented');
33
+ }
34
+ async search(_embedding, _options) {
35
+ throw new Error('Not implemented');
36
+ }
37
+ async forget(_id) {
38
+ throw new Error('Not implemented');
39
+ }
40
+ async list(_agentId, _limit) {
41
+ throw new Error('Not implemented');
42
+ }
43
+ async close() {
44
+ // no-op until implemented
45
+ }
46
+ }
47
+ //# sourceMappingURL=postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/adapters/postgres.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,eAAe;IAC1B,YAA6B,gBAAwB;QAAxB,qBAAgB,GAAhB,gBAAgB,CAAQ;IAAG,CAAC;IAEzD,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAA;IACpF,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAe;QACzB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,UAAoB,EAAE,QAAiC;QAClE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAgB,EAAE,MAAe;QAC1C,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,0BAA0B;IAC5B,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import type { IMemoryAdapter, Memory, SearchOptions, SearchResult } from '../types.js';
2
+ /**
3
+ * SqliteAdapter — local file-backed persistence with vector search.
4
+ *
5
+ * TODO: implement with `better-sqlite3` + manual cosine similarity
6
+ * (sqlite-vss or sqlite-vec extension optional).
7
+ */
8
+ export declare class SqliteAdapter implements IMemoryAdapter {
9
+ private readonly filePath;
10
+ constructor(filePath: string);
11
+ init(): Promise<void>;
12
+ store(_memory: Memory): Promise<Memory>;
13
+ get(_id: string): Promise<Memory | null>;
14
+ search(_embedding: number[], _options: Required<SearchOptions>): Promise<SearchResult[]>;
15
+ forget(_id: string): Promise<void>;
16
+ list(_agentId: string, _limit?: number): Promise<Memory[]>;
17
+ close(): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEtF;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,cAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAEvC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIxC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIxF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI1D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * SqliteAdapter — local file-backed persistence with vector search.
3
+ *
4
+ * TODO: implement with `better-sqlite3` + manual cosine similarity
5
+ * (sqlite-vss or sqlite-vec extension optional).
6
+ */
7
+ export class SqliteAdapter {
8
+ constructor(filePath) {
9
+ this.filePath = filePath;
10
+ }
11
+ async init() {
12
+ throw new Error('SqliteAdapter not yet implemented. Use MemoryAdapter for now.');
13
+ }
14
+ async store(_memory) {
15
+ throw new Error('Not implemented');
16
+ }
17
+ async get(_id) {
18
+ throw new Error('Not implemented');
19
+ }
20
+ async search(_embedding, _options) {
21
+ throw new Error('Not implemented');
22
+ }
23
+ async forget(_id) {
24
+ throw new Error('Not implemented');
25
+ }
26
+ async list(_agentId, _limit) {
27
+ throw new Error('Not implemented');
28
+ }
29
+ async close() {
30
+ // no-op until implemented
31
+ }
32
+ }
33
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IACxB,YAA6B,QAAgB;QAAhB,aAAQ,GAAR,QAAQ,CAAQ;IAAG,CAAC;IAEjD,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAA;IAClF,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAe;QACzB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,UAAoB,EAAE,QAAiC;QAClE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAgB,EAAE,MAAe;QAC1C,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,0BAA0B;IAC5B,CAAC;CACF"}
@@ -0,0 +1,57 @@
1
+ import type { EngramConfig, Memory, MemoryInput, SearchOptions, SearchResult } from './types.js';
2
+ /**
3
+ * Engram — persistent semantic memory for AI agents.
4
+ *
5
+ * "We no longer live in the age of cogito. We live in the age of the trace."
6
+ * — Derrida (via Orlo Rodriguez, "I Compute, Therefore Am I?")
7
+ *
8
+ * Usage:
9
+ * const mem = new Engram({ adapter: 'memory', agentId: 'my-agent' })
10
+ * await mem.wake()
11
+ * await mem.store({ content: 'The user prefers dark mode' })
12
+ * const results = await mem.search('user preferences')
13
+ * await mem.sleep()
14
+ */
15
+ export declare class Engram {
16
+ private adapter;
17
+ private readonly config;
18
+ private initialized;
19
+ private sessionStart;
20
+ constructor(config: EngramConfig);
21
+ /**
22
+ * Initialize the adapter and record session start.
23
+ * Call this at agent startup.
24
+ */
25
+ wake(): Promise<void>;
26
+ /**
27
+ * Persist state and close connections.
28
+ * Call this at agent shutdown.
29
+ */
30
+ sleep(): Promise<void>;
31
+ /**
32
+ * Store a memory. Embeds content automatically.
33
+ */
34
+ store(input: MemoryInput): Promise<Memory>;
35
+ /**
36
+ * Store multiple memories in parallel.
37
+ */
38
+ storeMany(inputs: MemoryInput[]): Promise<Memory[]>;
39
+ /**
40
+ * Semantic search over stored memories.
41
+ */
42
+ search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
43
+ /**
44
+ * Retrieve a memory by ID.
45
+ */
46
+ get(id: string): Promise<Memory | null>;
47
+ /**
48
+ * Delete a memory permanently.
49
+ */
50
+ forget(id: string): Promise<void>;
51
+ /**
52
+ * List recent memories for this agent.
53
+ */
54
+ list(limit?: number): Promise<Memory[]>;
55
+ private ensureReady;
56
+ }
57
+ //# sourceMappingURL=engram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engram.d.ts","sourceRoot":"","sources":["../src/engram.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EAEZ,MAAM,EACN,WAAW,EACX,aAAa,EACb,YAAY,EACb,MAAM,YAAY,CAAA;AAMnB;;;;;;;;;;;;GAYG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,YAAY,CAAoB;gBAE5B,MAAM,EAAE,YAAY;IAiChC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;OAEG;IACG,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAqBhD;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAIzD;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAWjF;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAK7C;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvC;;OAEG;IACG,IAAI,CAAC,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAS3B,WAAW;CAK1B"}
package/dist/engram.js ADDED
@@ -0,0 +1,148 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { MemoryAdapter } from './adapters/memory.js';
3
+ import { PostgresAdapter } from './adapters/postgres.js';
4
+ import { SqliteAdapter } from './adapters/sqlite.js';
5
+ import { embedText, DEFAULT_DIMENSIONS } from './utils/embeddings.js';
6
+ /**
7
+ * Engram — persistent semantic memory for AI agents.
8
+ *
9
+ * "We no longer live in the age of cogito. We live in the age of the trace."
10
+ * — Derrida (via Orlo Rodriguez, "I Compute, Therefore Am I?")
11
+ *
12
+ * Usage:
13
+ * const mem = new Engram({ adapter: 'memory', agentId: 'my-agent' })
14
+ * await mem.wake()
15
+ * await mem.store({ content: 'The user prefers dark mode' })
16
+ * const results = await mem.search('user preferences')
17
+ * await mem.sleep()
18
+ */
19
+ export class Engram {
20
+ constructor(config) {
21
+ this.initialized = false;
22
+ this.sessionStart = null;
23
+ this.config = {
24
+ adapter: config.adapter,
25
+ agentId: config.agentId,
26
+ connectionString: config.connectionString ?? '',
27
+ embeddingDimensions: config.embeddingDimensions ?? DEFAULT_DIMENSIONS,
28
+ };
29
+ switch (config.adapter) {
30
+ case 'memory':
31
+ this.adapter = new MemoryAdapter();
32
+ break;
33
+ case 'postgres':
34
+ if (!config.connectionString) {
35
+ throw new Error('connectionString required for postgres adapter');
36
+ }
37
+ this.adapter = new PostgresAdapter(config.connectionString);
38
+ break;
39
+ case 'sqlite':
40
+ if (!config.connectionString) {
41
+ throw new Error('connectionString (file path) required for sqlite adapter');
42
+ }
43
+ this.adapter = new SqliteAdapter(config.connectionString);
44
+ break;
45
+ default:
46
+ throw new Error(`Unknown adapter: ${config.adapter}`);
47
+ }
48
+ }
49
+ // ---------------------------------------------------------------------------
50
+ // Lifecycle
51
+ // ---------------------------------------------------------------------------
52
+ /**
53
+ * Initialize the adapter and record session start.
54
+ * Call this at agent startup.
55
+ */
56
+ async wake() {
57
+ if (!this.initialized) {
58
+ await this.adapter.init();
59
+ this.initialized = true;
60
+ }
61
+ this.sessionStart = new Date();
62
+ }
63
+ /**
64
+ * Persist state and close connections.
65
+ * Call this at agent shutdown.
66
+ */
67
+ async sleep() {
68
+ if (this.sessionStart) {
69
+ const duration = Date.now() - this.sessionStart.getTime();
70
+ // TODO: emit session summary to a "session" memory
71
+ void duration;
72
+ this.sessionStart = null;
73
+ }
74
+ await this.adapter.close();
75
+ this.initialized = false;
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Core API
79
+ // ---------------------------------------------------------------------------
80
+ /**
81
+ * Store a memory. Embeds content automatically.
82
+ */
83
+ async store(input) {
84
+ await this.ensureReady();
85
+ const embedding = await embedText(input.content, this.config.embeddingDimensions);
86
+ const now = new Date();
87
+ const memory = {
88
+ id: randomUUID(),
89
+ agentId: this.config.agentId,
90
+ content: input.content,
91
+ embedding,
92
+ importance: input.importance ?? 0.5,
93
+ metadata: input.metadata ?? {},
94
+ createdAt: now,
95
+ accessedAt: now,
96
+ accessCount: 0,
97
+ };
98
+ return this.adapter.store(memory);
99
+ }
100
+ /**
101
+ * Store multiple memories in parallel.
102
+ */
103
+ async storeMany(inputs) {
104
+ return Promise.all(inputs.map((input) => this.store(input)));
105
+ }
106
+ /**
107
+ * Semantic search over stored memories.
108
+ */
109
+ async search(query, options = {}) {
110
+ await this.ensureReady();
111
+ const embedding = await embedText(query, this.config.embeddingDimensions);
112
+ return this.adapter.search(embedding, {
113
+ limit: options.limit ?? 10,
114
+ threshold: options.threshold ?? 0.0,
115
+ filter: options.filter ?? {},
116
+ });
117
+ }
118
+ /**
119
+ * Retrieve a memory by ID.
120
+ */
121
+ async get(id) {
122
+ await this.ensureReady();
123
+ return this.adapter.get(id);
124
+ }
125
+ /**
126
+ * Delete a memory permanently.
127
+ */
128
+ async forget(id) {
129
+ await this.ensureReady();
130
+ return this.adapter.forget(id);
131
+ }
132
+ /**
133
+ * List recent memories for this agent.
134
+ */
135
+ async list(limit = 50) {
136
+ await this.ensureReady();
137
+ return this.adapter.list(this.config.agentId, limit);
138
+ }
139
+ // ---------------------------------------------------------------------------
140
+ // Internals
141
+ // ---------------------------------------------------------------------------
142
+ async ensureReady() {
143
+ if (!this.initialized) {
144
+ await this.wake();
145
+ }
146
+ }
147
+ }
148
+ //# sourceMappingURL=engram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engram.js","sourceRoot":"","sources":["../src/engram.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AASxC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAErE;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,MAAM;IAMjB,YAAY,MAAoB;QAHxB,gBAAW,GAAG,KAAK,CAAA;QACnB,iBAAY,GAAgB,IAAI,CAAA;QAGtC,IAAI,CAAC,MAAM,GAAG;YACZ,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,EAAE;YAC/C,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,IAAI,kBAAkB;SACtE,CAAA;QAED,QAAQ,MAAM,CAAC,OAAO,EAAE,CAAC;YACvB,KAAK,QAAQ;gBACX,IAAI,CAAC,OAAO,GAAG,IAAI,aAAa,EAAE,CAAA;gBAClC,MAAK;YACP,KAAK,UAAU;gBACb,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;oBAC7B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAA;gBACnE,CAAC;gBACD,IAAI,CAAC,OAAO,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAA;gBAC3D,MAAK;YACP,KAAK,QAAQ;gBACX,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;oBAC7B,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;gBAC7E,CAAC;gBACD,IAAI,CAAC,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAA;gBACzD,MAAK;YACP;gBACE,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,CAAC,OAAiB,EAAE,CAAC,CAAA;QACnE,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E;;;OAGG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;YACzB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACzB,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAA;IAChC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAA;YACzD,mDAAmD;YACnD,KAAK,QAAQ,CAAA;YACb,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;QAC1B,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QAC1B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAA;IAC1B,CAAC;IAED,8EAA8E;IAC9E,WAAW;IACX,8EAA8E;IAE9E;;OAEG;IACH,KAAK,CAAC,KAAK,CAAC,KAAkB;QAC5B,MAAM,IAAI,CAAC,WAAW,EAAE,CAAA;QAExB,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;QACjF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;QAEtB,MAAM,MAAM,GAAW;YACrB,EAAE,EAAE,UAAU,EAAE;YAChB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS;YACT,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,GAAG;YACnC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;YAC9B,SAAS,EAAE,GAAG;YACd,UAAU,EAAE,GAAG;YACf,WAAW,EAAE,CAAC;SACf,CAAA;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACnC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS,CAAC,MAAqB;QACnC,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAC9D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,UAAyB,EAAE;QACrD,MAAM,IAAI,CAAC,WAAW,EAAE,CAAA;QAExB,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;QACzE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE;YACpC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,GAAG;YACnC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;SAC7B,CAAC,CAAA;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAA;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAA;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAChC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE;QACnB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAA;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACtD,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAEtE,KAAK,CAAC,WAAW;QACvB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;QACnB,CAAC;IACH,CAAC;CACF"}