@cartisien/engram 0.2.0 → 0.4.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 +104 -203
- package/dist/adapters/base.d.ts +2 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +2 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/memory.d.ts +17 -0
- package/dist/adapters/memory.d.ts.map +1 -0
- package/dist/adapters/memory.js +58 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/postgres.d.ts +33 -0
- package/dist/adapters/postgres.d.ts.map +1 -0
- package/dist/adapters/postgres.js +47 -0
- package/dist/adapters/postgres.js.map +1 -0
- package/dist/adapters/sqlite.d.ts +19 -0
- package/dist/adapters/sqlite.d.ts.map +1 -0
- package/dist/adapters/sqlite.js +33 -0
- package/dist/adapters/sqlite.js.map +1 -0
- package/dist/engram.d.ts +57 -0
- package/dist/engram.d.ts.map +1 -0
- package/dist/engram.js +148 -0
- package/dist/engram.js.map +1 -0
- package/dist/index.d.ts +99 -122
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +458 -468
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +48 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/embeddings.d.ts +20 -0
- package/dist/utils/embeddings.d.ts.map +1 -0
- package/dist/utils/embeddings.js +28 -0
- package/dist/utils/embeddings.js.map +1 -0
- package/dist/utils/similarity.d.ts +10 -0
- package/dist/utils/similarity.d.ts.map +1 -0
- package/dist/utils/similarity.js +31 -0
- package/dist/utils/similarity.js.map +1 -0
- package/package.json +4 -3
- package/LICENSE +0 -21
- package/dist/example/temporal-demo.js +0 -91
|
@@ -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"}
|
package/dist/engram.d.ts
ADDED
|
@@ -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":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,73 +1,89 @@
|
|
|
1
|
+
export type MemoryTier = 'working' | 'long_term' | 'archived';
|
|
1
2
|
export interface MemoryEntry {
|
|
2
3
|
id: string;
|
|
3
4
|
sessionId: string;
|
|
4
5
|
content: string;
|
|
5
6
|
role: 'user' | 'assistant' | 'system';
|
|
6
7
|
timestamp: Date;
|
|
8
|
+
tier: MemoryTier;
|
|
9
|
+
consolidatedFrom?: string[];
|
|
7
10
|
metadata?: Record<string, unknown>;
|
|
11
|
+
similarity?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface GraphNode {
|
|
14
|
+
entity: string;
|
|
15
|
+
type?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface GraphEdge {
|
|
18
|
+
from: string;
|
|
19
|
+
relation: string;
|
|
20
|
+
to: string;
|
|
21
|
+
confidence?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface GraphResult {
|
|
24
|
+
entity: string;
|
|
25
|
+
relationships: Array<{
|
|
26
|
+
type: 'outgoing' | 'incoming';
|
|
27
|
+
relation: string;
|
|
28
|
+
target: string;
|
|
29
|
+
confidence?: number;
|
|
30
|
+
}>;
|
|
31
|
+
relatedMemories: MemoryEntry[];
|
|
8
32
|
}
|
|
9
33
|
export interface RecallOptions {
|
|
10
34
|
limit?: number;
|
|
11
35
|
before?: Date;
|
|
12
36
|
after?: Date;
|
|
13
37
|
role?: 'user' | 'assistant' | 'system';
|
|
38
|
+
includeGraph?: boolean;
|
|
39
|
+
tiers?: MemoryTier[];
|
|
14
40
|
}
|
|
15
|
-
export interface
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
41
|
+
export interface ConsolidateOptions {
|
|
42
|
+
batch?: number;
|
|
43
|
+
keep?: number;
|
|
44
|
+
model?: string;
|
|
45
|
+
dryRun?: boolean;
|
|
20
46
|
}
|
|
21
|
-
export interface
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
47
|
+
export interface ConsolidationResult {
|
|
48
|
+
summarized: number;
|
|
49
|
+
created: number;
|
|
50
|
+
archived: number;
|
|
51
|
+
previews?: string[];
|
|
25
52
|
}
|
|
26
53
|
export interface EngramConfig {
|
|
27
54
|
dbPath?: string;
|
|
28
55
|
maxContextLength?: number;
|
|
56
|
+
embeddingUrl?: string;
|
|
57
|
+
embeddingModel?: string;
|
|
58
|
+
semanticSearch?: boolean;
|
|
59
|
+
graphMemory?: boolean;
|
|
60
|
+
graphModel?: string;
|
|
61
|
+
autoConsolidate?: boolean;
|
|
62
|
+
consolidateThreshold?: number;
|
|
63
|
+
consolidateKeep?: number;
|
|
64
|
+
consolidateBatch?: number;
|
|
65
|
+
consolidateModel?: string;
|
|
29
66
|
}
|
|
30
67
|
/**
|
|
31
|
-
*
|
|
32
|
-
*/
|
|
33
|
-
export declare class TemporalQuery {
|
|
34
|
-
private referenceDate;
|
|
35
|
-
private timezoneOffset;
|
|
36
|
-
constructor(referenceDate?: Date, timezoneOffset?: number);
|
|
37
|
-
/**
|
|
38
|
-
* Parse a natural language time expression into a concrete time range
|
|
39
|
-
*
|
|
40
|
-
* Supports:
|
|
41
|
-
* - Relative: 'today', 'yesterday', 'tomorrow'
|
|
42
|
-
* - Days ago: '3 days ago', 'a week ago', '2 weeks ago'
|
|
43
|
-
* - Last: 'last monday', 'last week', 'last month'
|
|
44
|
-
* - This: 'this week', 'this month', 'this year'
|
|
45
|
-
* - Recent: 'recent', 'lately', 'recently' (last 7 days)
|
|
46
|
-
* - Between: 'january 15 to january 20', '3/1 to 3/15'
|
|
47
|
-
*/
|
|
48
|
-
parse(expression: string): TimeRange | null;
|
|
49
|
-
private parseDate;
|
|
50
|
-
private startOfDay;
|
|
51
|
-
private endOfDay;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Engram - Persistent memory for AI assistants
|
|
68
|
+
* Engram - Persistent semantic memory for AI agents
|
|
55
69
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
70
|
+
* v0.4 adds memory consolidation — working memories are periodically
|
|
71
|
+
* summarized into long-term memories by a local LLM, keeping context
|
|
72
|
+
* dense and relevant as conversations grow.
|
|
58
73
|
*
|
|
59
74
|
* @example
|
|
60
75
|
* ```typescript
|
|
61
76
|
* import { Engram } from '@cartisien/engram';
|
|
62
77
|
*
|
|
63
|
-
* const memory = new Engram({
|
|
78
|
+
* const memory = new Engram({
|
|
79
|
+
* dbPath: './memory.db',
|
|
80
|
+
* autoConsolidate: true,
|
|
81
|
+
* consolidateThreshold: 100,
|
|
82
|
+
* });
|
|
64
83
|
*
|
|
65
|
-
* //
|
|
66
|
-
* await memory.
|
|
67
|
-
*
|
|
68
|
-
* // Retrieve with temporal query
|
|
69
|
-
* const yesterday = await memory.recallByTime('user_123', 'yesterday');
|
|
70
|
-
* const lastWeek = await memory.recallByTime('user_123', 'last week');
|
|
84
|
+
* // Manual consolidation
|
|
85
|
+
* const result = await memory.consolidate('session_1');
|
|
86
|
+
* // → { summarized: 50, created: 4, archived: 50 }
|
|
71
87
|
* ```
|
|
72
88
|
*/
|
|
73
89
|
export declare class Engram {
|
|
@@ -75,113 +91,74 @@ export declare class Engram {
|
|
|
75
91
|
private maxContextLength;
|
|
76
92
|
private dbPath;
|
|
77
93
|
private initialized;
|
|
94
|
+
private embeddingUrl;
|
|
95
|
+
private embeddingModel;
|
|
96
|
+
private semanticSearch;
|
|
97
|
+
private graphMemory;
|
|
98
|
+
private graphModel;
|
|
99
|
+
private autoConsolidate;
|
|
100
|
+
private consolidateThreshold;
|
|
101
|
+
private consolidateKeep;
|
|
102
|
+
private consolidateBatch;
|
|
103
|
+
private consolidateModel;
|
|
78
104
|
constructor(config?: EngramConfig);
|
|
79
105
|
private init;
|
|
106
|
+
private embed;
|
|
107
|
+
private extractGraph;
|
|
108
|
+
private upsertNode;
|
|
109
|
+
private storeEdge;
|
|
110
|
+
private cosineSimilarity;
|
|
80
111
|
/**
|
|
81
|
-
*
|
|
112
|
+
* Call LLM to summarize a batch of memories into consolidated entries.
|
|
113
|
+
* Returns an array of summary strings (typically 2-5 per batch).
|
|
82
114
|
*/
|
|
83
|
-
|
|
115
|
+
private summarizeMemories;
|
|
84
116
|
/**
|
|
85
|
-
*
|
|
117
|
+
* v0.4: Consolidate working memories into long-term summaries.
|
|
86
118
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* - 'last monday', 'last week', 'last month'
|
|
91
|
-
* - 'this week', 'this month'
|
|
92
|
-
* - 'last 3 days', 'last week'
|
|
93
|
-
* - 'january 15', '3/15', '2024-01-15'
|
|
94
|
-
* - 'jan 15 to jan 20', '3/1 to 3/15'
|
|
95
|
-
*/
|
|
96
|
-
recall(sessionId: string, query?: string, limit?: number, options?: TemporalRecallOptions): Promise<MemoryEntry[]>;
|
|
97
|
-
/**
|
|
98
|
-
* Recall memories by natural language time expression
|
|
119
|
+
* Takes the oldest `batch` working memories (excluding the `keep` most recent),
|
|
120
|
+
* summarizes them via LLM, stores summaries as `long_term` tier, and archives
|
|
121
|
+
* the originals.
|
|
99
122
|
*
|
|
100
123
|
* @example
|
|
101
124
|
* ```typescript
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* // Get last week's memories
|
|
106
|
-
* const lastWeek = await memory.recallByTime('session_123', 'last week');
|
|
125
|
+
* const result = await memory.consolidate('session_1');
|
|
126
|
+
* // → { summarized: 50, created: 4, archived: 50 }
|
|
107
127
|
*
|
|
108
|
-
* //
|
|
109
|
-
* const
|
|
128
|
+
* // Preview without writing
|
|
129
|
+
* const preview = await memory.consolidate('session_1', { dryRun: true });
|
|
130
|
+
* // → { summarized: 50, created: 0, archived: 0, previews: ['...', '...'] }
|
|
110
131
|
* ```
|
|
111
132
|
*/
|
|
112
|
-
|
|
113
|
-
entries: MemoryEntry[];
|
|
114
|
-
range: TimeRange;
|
|
115
|
-
}>;
|
|
116
|
-
/**
|
|
117
|
-
* Get memories from the last N days
|
|
118
|
-
*/
|
|
119
|
-
recallRecent(sessionId: string, days?: number, query?: string, limit?: number, options?: Omit<TemporalRecallOptions, 'after' | 'before'>): Promise<{
|
|
120
|
-
entries: MemoryEntry[];
|
|
121
|
-
days: number;
|
|
122
|
-
since: Date;
|
|
123
|
-
}>;
|
|
133
|
+
consolidate(sessionId: string, options?: ConsolidateOptions): Promise<ConsolidationResult>;
|
|
124
134
|
/**
|
|
125
|
-
*
|
|
135
|
+
* Store a memory entry. With autoConsolidate enabled, triggers consolidation
|
|
136
|
+
* when working memory count exceeds the configured threshold.
|
|
126
137
|
*/
|
|
127
|
-
|
|
128
|
-
entries: MemoryEntry[];
|
|
129
|
-
since: Date;
|
|
130
|
-
count: number;
|
|
131
|
-
}>;
|
|
132
|
-
/**
|
|
133
|
-
* Get memories between two dates
|
|
134
|
-
*/
|
|
135
|
-
recallBetween(sessionId: string, start: Date, end: Date, query?: string, limit?: number, options?: Omit<TemporalRecallOptions, 'after' | 'before'>): Promise<{
|
|
136
|
-
entries: MemoryEntry[];
|
|
137
|
-
start: Date;
|
|
138
|
-
end: Date;
|
|
139
|
-
count: number;
|
|
140
|
-
}>;
|
|
141
|
-
/**
|
|
142
|
-
* Get a daily summary of memories
|
|
143
|
-
*
|
|
144
|
-
* Returns memories grouped by day, useful for "what happened each day" views
|
|
145
|
-
*/
|
|
146
|
-
dailySummary(sessionId: string, days?: number): Promise<{
|
|
147
|
-
date: Date;
|
|
148
|
-
entries: MemoryEntry[];
|
|
149
|
-
count: number;
|
|
150
|
-
}[]>;
|
|
138
|
+
remember(sessionId: string, content: string, role?: 'user' | 'assistant' | 'system', metadata?: Record<string, unknown>): Promise<MemoryEntry>;
|
|
151
139
|
/**
|
|
152
|
-
*
|
|
140
|
+
* Recall memories. Searches working and long_term tiers by default.
|
|
141
|
+
* Archived memories (consolidated originals) are excluded unless explicitly requested.
|
|
153
142
|
*/
|
|
143
|
+
recall(sessionId: string, query?: string, limit?: number, options?: RecallOptions): Promise<MemoryEntry[]>;
|
|
144
|
+
private augmentWithGraph;
|
|
145
|
+
graph(sessionId: string, entity: string): Promise<GraphResult>;
|
|
154
146
|
history(sessionId: string, limit?: number): Promise<MemoryEntry[]>;
|
|
155
|
-
/**
|
|
156
|
-
* Forget (delete) memories
|
|
157
|
-
*/
|
|
158
147
|
forget(sessionId: string, options?: {
|
|
159
148
|
before?: Date;
|
|
160
|
-
after?: Date;
|
|
161
149
|
id?: string;
|
|
150
|
+
includeLongTerm?: boolean;
|
|
162
151
|
}): Promise<number>;
|
|
163
|
-
/**
|
|
164
|
-
* Get memory statistics for a session
|
|
165
|
-
*/
|
|
166
152
|
stats(sessionId: string): Promise<{
|
|
167
153
|
total: number;
|
|
168
154
|
byRole: Record<string, number>;
|
|
155
|
+
byTier: Record<MemoryTier, number>;
|
|
169
156
|
oldest: Date | null;
|
|
170
157
|
newest: Date | null;
|
|
158
|
+
withEmbeddings: number;
|
|
159
|
+
graphNodes?: number;
|
|
160
|
+
graphEdges?: number;
|
|
171
161
|
}>;
|
|
172
|
-
/**
|
|
173
|
-
* Get temporal statistics for a session
|
|
174
|
-
*
|
|
175
|
-
* Returns memory counts grouped by day, useful for activity visualization
|
|
176
|
-
*/
|
|
177
|
-
temporalStats(sessionId: string, days?: number): Promise<{
|
|
178
|
-
date: string;
|
|
179
|
-
count: number;
|
|
180
|
-
byRole: Record<string, number>;
|
|
181
|
-
}[]>;
|
|
182
|
-
/**
|
|
183
|
-
* Close the database connection
|
|
184
|
-
*/
|
|
185
162
|
close(): Promise<void>;
|
|
186
163
|
}
|
|
187
164
|
export default Engram;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|