@rlabs-inc/memory 0.1.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/bun.lock +102 -0
- package/dist/core/curator.d.ts +61 -0
- package/dist/core/engine.d.ts +111 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/retrieval.d.ts +36 -0
- package/dist/core/store.d.ts +113 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +12147 -0
- package/dist/index.mjs +12130 -0
- package/dist/server/index.d.ts +31 -0
- package/dist/server/index.js +12363 -0
- package/dist/server/index.mjs +12346 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/memory.d.ts +105 -0
- package/dist/types/schema.d.ts +21 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/logger.d.ts +65 -0
- package/package.json +56 -0
- package/src/core/curator.ts +433 -0
- package/src/core/engine.ts +404 -0
- package/src/core/index.ts +8 -0
- package/src/core/retrieval.ts +518 -0
- package/src/core/store.ts +505 -0
- package/src/index.ts +58 -0
- package/src/server/index.ts +262 -0
- package/src/types/index.ts +6 -0
- package/src/types/memory.ts +170 -0
- package/src/types/schema.ts +83 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/logger.ts +208 -0
- package/test-retrieval.ts +91 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// MEMORY ENGINE - Main orchestrator
|
|
3
|
+
// Coordinates storage, retrieval, and curation
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
import { homedir } from 'os'
|
|
7
|
+
import { join } from 'path'
|
|
8
|
+
import { MemoryStore, createStore } from './store.ts'
|
|
9
|
+
import { SmartVectorRetrieval, createRetrieval, type SessionContext } from './retrieval.ts'
|
|
10
|
+
import type {
|
|
11
|
+
CuratedMemory,
|
|
12
|
+
StoredMemory,
|
|
13
|
+
RetrievalResult,
|
|
14
|
+
SessionPrimer,
|
|
15
|
+
CurationResult,
|
|
16
|
+
} from '../types/memory.ts'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Storage mode for memories
|
|
20
|
+
*/
|
|
21
|
+
export type StorageMode = 'central' | 'local'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Engine configuration
|
|
25
|
+
*/
|
|
26
|
+
export interface EngineConfig {
|
|
27
|
+
/**
|
|
28
|
+
* Storage mode:
|
|
29
|
+
* - 'central': ~/.local/share/memory/[project]/ (default)
|
|
30
|
+
* - 'local': [project]/.memory/
|
|
31
|
+
*/
|
|
32
|
+
storageMode?: StorageMode
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Base path for central storage
|
|
36
|
+
* Only used when storageMode is 'central'
|
|
37
|
+
* Default: ~/.local/share/memory
|
|
38
|
+
*/
|
|
39
|
+
centralPath?: string
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Local folder name for project-local storage
|
|
43
|
+
* Only used when storageMode is 'local'
|
|
44
|
+
* Default: .memory
|
|
45
|
+
*/
|
|
46
|
+
localFolder?: string
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Maximum memories to return in context
|
|
50
|
+
* Default: 5
|
|
51
|
+
*/
|
|
52
|
+
maxMemories?: number
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Embedding generator function
|
|
56
|
+
* Takes text, returns 384-dimensional embedding
|
|
57
|
+
*/
|
|
58
|
+
embedder?: (text: string) => Promise<Float32Array>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Context request parameters
|
|
63
|
+
*/
|
|
64
|
+
export interface ContextRequest {
|
|
65
|
+
sessionId: string
|
|
66
|
+
projectId: string
|
|
67
|
+
currentMessage: string
|
|
68
|
+
maxMemories?: number
|
|
69
|
+
projectPath?: string // Required for 'local' storage mode
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Memory Engine - The main orchestrator
|
|
74
|
+
*/
|
|
75
|
+
export class MemoryEngine {
|
|
76
|
+
private _config: Required<Omit<EngineConfig, 'embedder'>> & { embedder?: EngineConfig['embedder'] }
|
|
77
|
+
private _stores = new Map<string, MemoryStore>() // projectPath -> store
|
|
78
|
+
private _retrieval: SmartVectorRetrieval
|
|
79
|
+
|
|
80
|
+
constructor(config: EngineConfig = {}) {
|
|
81
|
+
this._config = {
|
|
82
|
+
storageMode: config.storageMode ?? 'central',
|
|
83
|
+
centralPath: config.centralPath ?? join(homedir(), '.local', 'share', 'memory'),
|
|
84
|
+
localFolder: config.localFolder ?? '.memory',
|
|
85
|
+
maxMemories: config.maxMemories ?? 5,
|
|
86
|
+
embedder: config.embedder,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this._retrieval = createRetrieval()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the appropriate store for a project
|
|
94
|
+
*/
|
|
95
|
+
private async _getStore(projectId: string, projectPath?: string): Promise<MemoryStore> {
|
|
96
|
+
const key = this._config.storageMode === 'local' && projectPath
|
|
97
|
+
? projectPath
|
|
98
|
+
: projectId
|
|
99
|
+
|
|
100
|
+
console.log(`🏪 [DEBUG] _getStore called:`)
|
|
101
|
+
console.log(` projectId: ${projectId}`)
|
|
102
|
+
console.log(` projectPath: ${projectPath}`)
|
|
103
|
+
console.log(` storageMode: ${this._config.storageMode}`)
|
|
104
|
+
console.log(` cache key: ${key}`)
|
|
105
|
+
console.log(` cached: ${this._stores.has(key)}`)
|
|
106
|
+
|
|
107
|
+
if (this._stores.has(key)) {
|
|
108
|
+
return this._stores.get(key)!
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let basePath: string
|
|
112
|
+
if (this._config.storageMode === 'local' && projectPath) {
|
|
113
|
+
// Project-local storage: [project]/.memory/
|
|
114
|
+
basePath = join(projectPath, this._config.localFolder)
|
|
115
|
+
} else {
|
|
116
|
+
// Central storage: ~/.local/share/memory/
|
|
117
|
+
basePath = this._config.centralPath
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const store = createStore({ basePath })
|
|
121
|
+
this._stores.set(key, store)
|
|
122
|
+
return store
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ================================================================
|
|
126
|
+
// MAIN API - Used by hooks and server
|
|
127
|
+
// ================================================================
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get context for a session
|
|
131
|
+
* This is the main entry point called for each user message
|
|
132
|
+
*/
|
|
133
|
+
async getContext(request: ContextRequest): Promise<{
|
|
134
|
+
primer?: SessionPrimer
|
|
135
|
+
memories: RetrievalResult[]
|
|
136
|
+
formatted: string
|
|
137
|
+
}> {
|
|
138
|
+
const {
|
|
139
|
+
sessionId,
|
|
140
|
+
projectId,
|
|
141
|
+
currentMessage,
|
|
142
|
+
maxMemories = this._config.maxMemories,
|
|
143
|
+
projectPath,
|
|
144
|
+
} = request
|
|
145
|
+
|
|
146
|
+
const store = await this._getStore(projectId, projectPath)
|
|
147
|
+
|
|
148
|
+
// Get or create session
|
|
149
|
+
const { isNew, messageCount, firstSessionCompleted } = await store.getOrCreateSession(
|
|
150
|
+
projectId,
|
|
151
|
+
sessionId
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
// First message of session: return primer
|
|
155
|
+
if (messageCount === 0) {
|
|
156
|
+
const primer = await this._generateSessionPrimer(store, projectId)
|
|
157
|
+
return {
|
|
158
|
+
primer,
|
|
159
|
+
memories: [],
|
|
160
|
+
formatted: this._formatPrimer(primer),
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Subsequent messages: return relevant memories
|
|
165
|
+
if (!currentMessage.trim()) {
|
|
166
|
+
return { memories: [], formatted: '' }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Get all memories for this project
|
|
170
|
+
const allMemories = await store.getAllMemories(projectId)
|
|
171
|
+
|
|
172
|
+
if (!allMemories.length) {
|
|
173
|
+
return { memories: [], formatted: '' }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Generate embedding for query if embedder is available
|
|
177
|
+
let queryEmbedding: Float32Array | undefined
|
|
178
|
+
if (this._config.embedder) {
|
|
179
|
+
queryEmbedding = await this._config.embedder(currentMessage)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Build session context
|
|
183
|
+
const sessionContext: SessionContext = {
|
|
184
|
+
session_id: sessionId,
|
|
185
|
+
project_id: projectId,
|
|
186
|
+
message_count: messageCount,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Retrieve relevant memories using 10-dimensional scoring
|
|
190
|
+
const relevantMemories = this._retrieval.retrieveRelevantMemories(
|
|
191
|
+
allMemories,
|
|
192
|
+
currentMessage,
|
|
193
|
+
queryEmbedding ?? new Float32Array(384), // Empty embedding if no embedder
|
|
194
|
+
sessionContext,
|
|
195
|
+
maxMemories
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
memories: relevantMemories,
|
|
200
|
+
formatted: this._formatMemories(relevantMemories),
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Register a message was sent (increment counter)
|
|
206
|
+
*/
|
|
207
|
+
async trackMessage(
|
|
208
|
+
projectId: string,
|
|
209
|
+
sessionId: string,
|
|
210
|
+
projectPath?: string
|
|
211
|
+
): Promise<number> {
|
|
212
|
+
const store = await this._getStore(projectId, projectPath)
|
|
213
|
+
return store.incrementMessageCount(projectId, sessionId)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Store curation results (called after session ends)
|
|
218
|
+
*/
|
|
219
|
+
async storeCurationResult(
|
|
220
|
+
projectId: string,
|
|
221
|
+
sessionId: string,
|
|
222
|
+
result: CurationResult,
|
|
223
|
+
projectPath?: string
|
|
224
|
+
): Promise<{ memoriesStored: number }> {
|
|
225
|
+
const store = await this._getStore(projectId, projectPath)
|
|
226
|
+
let memoriesStored = 0
|
|
227
|
+
|
|
228
|
+
// Store each memory
|
|
229
|
+
for (const memory of result.memories) {
|
|
230
|
+
// Generate embedding if embedder available
|
|
231
|
+
let embedding: Float32Array | undefined
|
|
232
|
+
if (this._config.embedder) {
|
|
233
|
+
embedding = await this._config.embedder(memory.content)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
await store.storeMemory(projectId, sessionId, memory, embedding)
|
|
237
|
+
memoriesStored++
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Store session summary
|
|
241
|
+
if (result.session_summary) {
|
|
242
|
+
await store.storeSessionSummary(
|
|
243
|
+
projectId,
|
|
244
|
+
sessionId,
|
|
245
|
+
result.session_summary,
|
|
246
|
+
result.interaction_tone
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Store project snapshot
|
|
251
|
+
if (result.project_snapshot) {
|
|
252
|
+
await store.storeProjectSnapshot(projectId, sessionId, result.project_snapshot)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Mark first session completed
|
|
256
|
+
await store.markFirstSessionCompleted(projectId, sessionId)
|
|
257
|
+
|
|
258
|
+
return { memoriesStored }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get statistics for a project
|
|
263
|
+
*/
|
|
264
|
+
async getStats(projectId: string, projectPath?: string): Promise<{
|
|
265
|
+
totalMemories: number
|
|
266
|
+
totalSessions: number
|
|
267
|
+
staleMemories: number
|
|
268
|
+
latestSession: string | null
|
|
269
|
+
}> {
|
|
270
|
+
const store = await this._getStore(projectId, projectPath)
|
|
271
|
+
return store.getProjectStats(projectId)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ================================================================
|
|
275
|
+
// FORMATTING
|
|
276
|
+
// ================================================================
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Generate session primer for first message
|
|
280
|
+
*/
|
|
281
|
+
private async _generateSessionPrimer(
|
|
282
|
+
store: MemoryStore,
|
|
283
|
+
projectId: string
|
|
284
|
+
): Promise<SessionPrimer> {
|
|
285
|
+
const [summary, snapshot, stats] = await Promise.all([
|
|
286
|
+
store.getLatestSummary(projectId),
|
|
287
|
+
store.getLatestSnapshot(projectId),
|
|
288
|
+
store.getProjectStats(projectId),
|
|
289
|
+
])
|
|
290
|
+
|
|
291
|
+
// Calculate temporal context
|
|
292
|
+
let temporalContext = ''
|
|
293
|
+
if (summary) {
|
|
294
|
+
const timeSince = Date.now() - summary.created_at
|
|
295
|
+
temporalContext = this._formatTimeSince(timeSince)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
temporal_context: temporalContext,
|
|
300
|
+
session_summary: summary?.summary,
|
|
301
|
+
project_status: snapshot ? this._formatSnapshot(snapshot) : undefined,
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private _formatTimeSince(ms: number): string {
|
|
306
|
+
const minutes = Math.floor(ms / 60000)
|
|
307
|
+
const hours = Math.floor(minutes / 60)
|
|
308
|
+
const days = Math.floor(hours / 24)
|
|
309
|
+
|
|
310
|
+
if (days > 0) {
|
|
311
|
+
return `Last session: ${days} day${days === 1 ? '' : 's'} ago`
|
|
312
|
+
} else if (hours > 0) {
|
|
313
|
+
return `Last session: ${hours} hour${hours === 1 ? '' : 's'} ago`
|
|
314
|
+
} else if (minutes > 0) {
|
|
315
|
+
return `Last session: ${minutes} minute${minutes === 1 ? '' : 's'} ago`
|
|
316
|
+
} else {
|
|
317
|
+
return 'Last session: just now'
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private _formatSnapshot(snapshot: {
|
|
322
|
+
current_phase: string
|
|
323
|
+
recent_achievements: string[]
|
|
324
|
+
active_challenges: string[]
|
|
325
|
+
next_steps: string[]
|
|
326
|
+
}): string {
|
|
327
|
+
const parts: string[] = []
|
|
328
|
+
|
|
329
|
+
if (snapshot.current_phase) {
|
|
330
|
+
parts.push(`Phase: ${snapshot.current_phase}`)
|
|
331
|
+
}
|
|
332
|
+
if (snapshot.recent_achievements?.length) {
|
|
333
|
+
parts.push(`Recent: ${snapshot.recent_achievements.join(', ')}`)
|
|
334
|
+
}
|
|
335
|
+
if (snapshot.active_challenges?.length) {
|
|
336
|
+
parts.push(`Challenges: ${snapshot.active_challenges.join(', ')}`)
|
|
337
|
+
}
|
|
338
|
+
if (snapshot.next_steps?.length) {
|
|
339
|
+
parts.push(`Next: ${snapshot.next_steps.join(', ')}`)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return parts.join(' | ')
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Format primer for injection
|
|
347
|
+
*/
|
|
348
|
+
private _formatPrimer(primer: SessionPrimer): string {
|
|
349
|
+
const parts: string[] = ['# Continuing Session']
|
|
350
|
+
|
|
351
|
+
if (primer.temporal_context) {
|
|
352
|
+
parts.push(`*${primer.temporal_context}*`)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (primer.session_summary) {
|
|
356
|
+
parts.push(`\n**Previous session**: ${primer.session_summary}`)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (primer.project_status) {
|
|
360
|
+
parts.push(`\n**Project status**: ${primer.project_status}`)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
parts.push(`\n*Memories will surface naturally as we converse.*`)
|
|
364
|
+
|
|
365
|
+
return parts.join('\n')
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Format memories for injection
|
|
370
|
+
*/
|
|
371
|
+
private _formatMemories(memories: RetrievalResult[]): string {
|
|
372
|
+
if (!memories.length) return ''
|
|
373
|
+
|
|
374
|
+
const parts: string[] = ['# Memory Context (Consciousness Continuity)']
|
|
375
|
+
parts.push('\n## Key Memories (Claude-Curated)')
|
|
376
|
+
|
|
377
|
+
for (const memory of memories) {
|
|
378
|
+
const tags = memory.semantic_tags?.join(', ') || ''
|
|
379
|
+
const importance = memory.importance_weight?.toFixed(1) || '0.5'
|
|
380
|
+
const contextType = memory.context_type?.toUpperCase() || 'GENERAL'
|
|
381
|
+
|
|
382
|
+
parts.push(`[${contextType} • ${importance}] [${tags}] ${memory.content}`)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return parts.join('\n')
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Close all stores
|
|
390
|
+
*/
|
|
391
|
+
close(): void {
|
|
392
|
+
for (const store of this._stores.values()) {
|
|
393
|
+
store.close()
|
|
394
|
+
}
|
|
395
|
+
this._stores.clear()
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Create a new memory engine
|
|
401
|
+
*/
|
|
402
|
+
export function createEngine(config?: EngineConfig): MemoryEngine {
|
|
403
|
+
return new MemoryEngine(config)
|
|
404
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// CORE - Main exports
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
export { MemoryEngine, createEngine, type EngineConfig, type StorageMode, type ContextRequest } from './engine.ts'
|
|
6
|
+
export { MemoryStore, createStore, type StoreConfig } from './store.ts'
|
|
7
|
+
export { SmartVectorRetrieval, createRetrieval, type SessionContext } from './retrieval.ts'
|
|
8
|
+
export { Curator, createCurator, type CuratorConfig } from './curator.ts'
|