@claude-flow/memory 3.0.0-alpha.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.
- package/.agentic-flow/intelligence.json +16 -0
- package/README.md +249 -0
- package/__tests__/coverage/base.css +224 -0
- package/__tests__/coverage/block-navigation.js +87 -0
- package/__tests__/coverage/coverage-final.json +19 -0
- package/__tests__/coverage/favicon.png +0 -0
- package/__tests__/coverage/index.html +206 -0
- package/__tests__/coverage/lcov-report/base.css +224 -0
- package/__tests__/coverage/lcov-report/block-navigation.js +87 -0
- package/__tests__/coverage/lcov-report/favicon.png +0 -0
- package/__tests__/coverage/lcov-report/index.html +206 -0
- package/__tests__/coverage/lcov-report/prettify.css +1 -0
- package/__tests__/coverage/lcov-report/prettify.js +2 -0
- package/__tests__/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/__tests__/coverage/lcov-report/sorter.js +210 -0
- package/__tests__/coverage/lcov-report/src/agentdb-adapter.ts.html +2737 -0
- package/__tests__/coverage/lcov-report/src/agentdb-backend.ts.html +3130 -0
- package/__tests__/coverage/lcov-report/src/application/commands/delete-memory.command.ts.html +601 -0
- package/__tests__/coverage/lcov-report/src/application/commands/index.html +131 -0
- package/__tests__/coverage/lcov-report/src/application/commands/store-memory.command.ts.html +394 -0
- package/__tests__/coverage/lcov-report/src/application/queries/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/application/queries/search-memory.query.ts.html +796 -0
- package/__tests__/coverage/lcov-report/src/application/services/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/application/services/memory-application-service.ts.html +793 -0
- package/__tests__/coverage/lcov-report/src/cache-manager.ts.html +1633 -0
- package/__tests__/coverage/lcov-report/src/database-provider.ts.html +1618 -0
- package/__tests__/coverage/lcov-report/src/domain/entities/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/domain/entities/memory-entry.ts.html +952 -0
- package/__tests__/coverage/lcov-report/src/domain/services/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/domain/services/memory-domain-service.ts.html +1294 -0
- package/__tests__/coverage/lcov-report/src/hnsw-index.ts.html +3124 -0
- package/__tests__/coverage/lcov-report/src/hybrid-backend.ts.html +2167 -0
- package/__tests__/coverage/lcov-report/src/index.html +266 -0
- package/__tests__/coverage/lcov-report/src/infrastructure/repositories/hybrid-memory-repository.ts.html +1633 -0
- package/__tests__/coverage/lcov-report/src/infrastructure/repositories/index.html +116 -0
- package/__tests__/coverage/lcov-report/src/migration.ts.html +2092 -0
- package/__tests__/coverage/lcov-report/src/query-builder.ts.html +1711 -0
- package/__tests__/coverage/lcov-report/src/sqlite-backend.ts.html +2281 -0
- package/__tests__/coverage/lcov-report/src/sqljs-backend.ts.html +2374 -0
- package/__tests__/coverage/lcov-report/src/types.ts.html +2266 -0
- package/__tests__/coverage/lcov.info +10238 -0
- package/__tests__/coverage/prettify.css +1 -0
- package/__tests__/coverage/prettify.js +2 -0
- package/__tests__/coverage/sort-arrow-sprite.png +0 -0
- package/__tests__/coverage/sorter.js +210 -0
- package/__tests__/coverage/src/agentdb-adapter.ts.html +2737 -0
- package/__tests__/coverage/src/agentdb-backend.ts.html +3130 -0
- package/__tests__/coverage/src/application/commands/delete-memory.command.ts.html +601 -0
- package/__tests__/coverage/src/application/commands/index.html +131 -0
- package/__tests__/coverage/src/application/commands/store-memory.command.ts.html +394 -0
- package/__tests__/coverage/src/application/queries/index.html +116 -0
- package/__tests__/coverage/src/application/queries/search-memory.query.ts.html +796 -0
- package/__tests__/coverage/src/application/services/index.html +116 -0
- package/__tests__/coverage/src/application/services/memory-application-service.ts.html +793 -0
- package/__tests__/coverage/src/cache-manager.ts.html +1633 -0
- package/__tests__/coverage/src/database-provider.ts.html +1618 -0
- package/__tests__/coverage/src/domain/entities/index.html +116 -0
- package/__tests__/coverage/src/domain/entities/memory-entry.ts.html +952 -0
- package/__tests__/coverage/src/domain/services/index.html +116 -0
- package/__tests__/coverage/src/domain/services/memory-domain-service.ts.html +1294 -0
- package/__tests__/coverage/src/hnsw-index.ts.html +3124 -0
- package/__tests__/coverage/src/hybrid-backend.ts.html +2167 -0
- package/__tests__/coverage/src/index.html +266 -0
- package/__tests__/coverage/src/infrastructure/repositories/hybrid-memory-repository.ts.html +1633 -0
- package/__tests__/coverage/src/infrastructure/repositories/index.html +116 -0
- package/__tests__/coverage/src/migration.ts.html +2092 -0
- package/__tests__/coverage/src/query-builder.ts.html +1711 -0
- package/__tests__/coverage/src/sqlite-backend.ts.html +2281 -0
- package/__tests__/coverage/src/sqljs-backend.ts.html +2374 -0
- package/__tests__/coverage/src/types.ts.html +2266 -0
- package/benchmarks/cache-hit-rate.bench.ts +535 -0
- package/benchmarks/hnsw-indexing.bench.ts +552 -0
- package/benchmarks/memory-write.bench.ts +469 -0
- package/benchmarks/vector-search.bench.ts +449 -0
- package/dist/agentdb-adapter.d.ts +146 -0
- package/dist/agentdb-adapter.d.ts.map +1 -0
- package/dist/agentdb-adapter.js +679 -0
- package/dist/agentdb-adapter.js.map +1 -0
- package/dist/agentdb-backend.d.ts +214 -0
- package/dist/agentdb-backend.d.ts.map +1 -0
- package/dist/agentdb-backend.js +827 -0
- package/dist/agentdb-backend.js.map +1 -0
- package/dist/agentdb-backend.test.d.ts +7 -0
- package/dist/agentdb-backend.test.d.ts.map +1 -0
- package/dist/agentdb-backend.test.js +258 -0
- package/dist/agentdb-backend.test.js.map +1 -0
- package/dist/application/commands/delete-memory.command.d.ts +65 -0
- package/dist/application/commands/delete-memory.command.d.ts.map +1 -0
- package/dist/application/commands/delete-memory.command.js +129 -0
- package/dist/application/commands/delete-memory.command.js.map +1 -0
- package/dist/application/commands/store-memory.command.d.ts +48 -0
- package/dist/application/commands/store-memory.command.d.ts.map +1 -0
- package/dist/application/commands/store-memory.command.js +72 -0
- package/dist/application/commands/store-memory.command.js.map +1 -0
- package/dist/application/index.d.ts +12 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +15 -0
- package/dist/application/index.js.map +1 -0
- package/dist/application/queries/search-memory.query.d.ts +72 -0
- package/dist/application/queries/search-memory.query.d.ts.map +1 -0
- package/dist/application/queries/search-memory.query.js +143 -0
- package/dist/application/queries/search-memory.query.js.map +1 -0
- package/dist/application/services/memory-application-service.d.ts +121 -0
- package/dist/application/services/memory-application-service.d.ts.map +1 -0
- package/dist/application/services/memory-application-service.js +190 -0
- package/dist/application/services/memory-application-service.js.map +1 -0
- package/dist/cache-manager.d.ts +134 -0
- package/dist/cache-manager.d.ts.map +1 -0
- package/dist/cache-manager.js +407 -0
- package/dist/cache-manager.js.map +1 -0
- package/dist/database-provider.d.ts +86 -0
- package/dist/database-provider.d.ts.map +1 -0
- package/dist/database-provider.js +385 -0
- package/dist/database-provider.js.map +1 -0
- package/dist/database-provider.test.d.ts +7 -0
- package/dist/database-provider.test.d.ts.map +1 -0
- package/dist/database-provider.test.js +285 -0
- package/dist/database-provider.test.js.map +1 -0
- package/dist/domain/entities/memory-entry.d.ts +143 -0
- package/dist/domain/entities/memory-entry.d.ts.map +1 -0
- package/dist/domain/entities/memory-entry.js +226 -0
- package/dist/domain/entities/memory-entry.js.map +1 -0
- package/dist/domain/index.d.ts +11 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +12 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/repositories/memory-repository.interface.d.ts +102 -0
- package/dist/domain/repositories/memory-repository.interface.d.ts.map +1 -0
- package/dist/domain/repositories/memory-repository.interface.js +11 -0
- package/dist/domain/repositories/memory-repository.interface.js.map +1 -0
- package/dist/domain/services/memory-domain-service.d.ts +105 -0
- package/dist/domain/services/memory-domain-service.d.ts.map +1 -0
- package/dist/domain/services/memory-domain-service.js +297 -0
- package/dist/domain/services/memory-domain-service.js.map +1 -0
- package/dist/hnsw-index.d.ts +111 -0
- package/dist/hnsw-index.d.ts.map +1 -0
- package/dist/hnsw-index.js +781 -0
- package/dist/hnsw-index.js.map +1 -0
- package/dist/hybrid-backend.d.ts +217 -0
- package/dist/hybrid-backend.d.ts.map +1 -0
- package/dist/hybrid-backend.js +491 -0
- package/dist/hybrid-backend.js.map +1 -0
- package/dist/hybrid-backend.test.d.ts +8 -0
- package/dist/hybrid-backend.test.d.ts.map +1 -0
- package/dist/hybrid-backend.test.js +320 -0
- package/dist/hybrid-backend.test.js.map +1 -0
- package/dist/index.d.ts +188 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +345 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/index.d.ts +17 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +16 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/infrastructure/repositories/hybrid-memory-repository.d.ts +66 -0
- package/dist/infrastructure/repositories/hybrid-memory-repository.d.ts.map +1 -0
- package/dist/infrastructure/repositories/hybrid-memory-repository.js +409 -0
- package/dist/infrastructure/repositories/hybrid-memory-repository.js.map +1 -0
- package/dist/migration.d.ts +68 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +513 -0
- package/dist/migration.js.map +1 -0
- package/dist/query-builder.d.ts +211 -0
- package/dist/query-builder.d.ts.map +1 -0
- package/dist/query-builder.js +438 -0
- package/dist/query-builder.js.map +1 -0
- package/dist/sqlite-backend.d.ts +121 -0
- package/dist/sqlite-backend.d.ts.map +1 -0
- package/dist/sqlite-backend.js +564 -0
- package/dist/sqlite-backend.js.map +1 -0
- package/dist/sqljs-backend.d.ts +128 -0
- package/dist/sqljs-backend.d.ts.map +1 -0
- package/dist/sqljs-backend.js +598 -0
- package/dist/sqljs-backend.js.map +1 -0
- package/dist/types.d.ts +481 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +58 -0
- package/dist/types.js.map +1 -0
- package/docs/AGENTDB-INTEGRATION.md +388 -0
- package/docs/CROSS_PLATFORM.md +505 -0
- package/docs/WINDOWS_SUPPORT.md +422 -0
- package/examples/agentdb-example.ts +345 -0
- package/examples/cross-platform-usage.ts +326 -0
- package/framework/benchmark.ts +112 -0
- package/package.json +31 -0
- package/src/agentdb-adapter.ts +884 -0
- package/src/agentdb-backend.test.ts +339 -0
- package/src/agentdb-backend.ts +1016 -0
- package/src/application/commands/delete-memory.command.ts +172 -0
- package/src/application/commands/store-memory.command.ts +103 -0
- package/src/application/index.ts +36 -0
- package/src/application/queries/search-memory.query.ts +237 -0
- package/src/application/services/memory-application-service.ts +236 -0
- package/src/cache-manager.ts +516 -0
- package/src/database-provider.test.ts +364 -0
- package/src/database-provider.ts +511 -0
- package/src/domain/entities/memory-entry.ts +289 -0
- package/src/domain/index.ts +35 -0
- package/src/domain/repositories/memory-repository.interface.ts +120 -0
- package/src/domain/services/memory-domain-service.ts +403 -0
- package/src/hnsw-index.ts +1013 -0
- package/src/hybrid-backend.test.ts +399 -0
- package/src/hybrid-backend.ts +694 -0
- package/src/index.ts +515 -0
- package/src/infrastructure/index.ts +23 -0
- package/src/infrastructure/repositories/hybrid-memory-repository.ts +516 -0
- package/src/migration.ts +669 -0
- package/src/query-builder.ts +542 -0
- package/src/sqlite-backend.ts +732 -0
- package/src/sqljs-backend.ts +763 -0
- package/src/types.ts +727 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/verify-cross-platform.ts +170 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Memory Repository - Infrastructure Layer
|
|
3
|
+
*
|
|
4
|
+
* Implements IMemoryRepository using SQLite + AgentDB hybrid backend.
|
|
5
|
+
* Per ADR-009, this is the default memory backend.
|
|
6
|
+
*
|
|
7
|
+
* @module v3/memory/infrastructure/repositories
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { MemoryEntry, MemoryType, MemoryStatus } from '../../domain/entities/memory-entry.js';
|
|
11
|
+
import {
|
|
12
|
+
IMemoryRepository,
|
|
13
|
+
MemoryQueryOptions,
|
|
14
|
+
VectorSearchOptions,
|
|
15
|
+
VectorSearchResult,
|
|
16
|
+
BulkOperationResult,
|
|
17
|
+
MemoryStatistics,
|
|
18
|
+
} from '../../domain/repositories/memory-repository.interface.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Repository configuration
|
|
22
|
+
*/
|
|
23
|
+
export interface HybridRepositoryConfig {
|
|
24
|
+
sqlitePath: string;
|
|
25
|
+
agentDbPath?: string;
|
|
26
|
+
enableVectorSearch?: boolean;
|
|
27
|
+
cacheSize?: number;
|
|
28
|
+
verbose?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* In-memory cache for hot entries
|
|
33
|
+
*/
|
|
34
|
+
interface CacheEntry {
|
|
35
|
+
entry: MemoryEntry;
|
|
36
|
+
timestamp: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Hybrid Memory Repository
|
|
41
|
+
*
|
|
42
|
+
* Uses SQLite for metadata and AgentDB for vectors.
|
|
43
|
+
* Implements hot caching for frequently accessed entries.
|
|
44
|
+
*/
|
|
45
|
+
export class HybridMemoryRepository implements IMemoryRepository {
|
|
46
|
+
private entries: Map<string, MemoryEntry> = new Map();
|
|
47
|
+
private namespaceIndex: Map<string, Set<string>> = new Map();
|
|
48
|
+
private vectorIndex: Map<string, Float32Array> = new Map();
|
|
49
|
+
private cache: Map<string, CacheEntry> = new Map();
|
|
50
|
+
private initialized = false;
|
|
51
|
+
|
|
52
|
+
constructor(private readonly config: HybridRepositoryConfig) {}
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Lifecycle
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
async initialize(): Promise<void> {
|
|
59
|
+
if (this.initialized) return;
|
|
60
|
+
|
|
61
|
+
// In production, would initialize SQLite and AgentDB connections
|
|
62
|
+
// For now, using in-memory implementation
|
|
63
|
+
this.entries = new Map();
|
|
64
|
+
this.namespaceIndex = new Map();
|
|
65
|
+
this.vectorIndex = new Map();
|
|
66
|
+
this.cache = new Map();
|
|
67
|
+
|
|
68
|
+
this.initialized = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async shutdown(): Promise<void> {
|
|
72
|
+
// Clear all data
|
|
73
|
+
this.cache.clear();
|
|
74
|
+
this.initialized = false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async clear(): Promise<void> {
|
|
78
|
+
this.entries.clear();
|
|
79
|
+
this.namespaceIndex.clear();
|
|
80
|
+
this.vectorIndex.clear();
|
|
81
|
+
this.cache.clear();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Basic CRUD
|
|
86
|
+
// ============================================================================
|
|
87
|
+
|
|
88
|
+
async save(entry: MemoryEntry): Promise<void> {
|
|
89
|
+
this.ensureInitialized();
|
|
90
|
+
|
|
91
|
+
// Store entry
|
|
92
|
+
this.entries.set(entry.id, entry);
|
|
93
|
+
|
|
94
|
+
// Update namespace index
|
|
95
|
+
if (!this.namespaceIndex.has(entry.namespace)) {
|
|
96
|
+
this.namespaceIndex.set(entry.namespace, new Set());
|
|
97
|
+
}
|
|
98
|
+
this.namespaceIndex.get(entry.namespace)!.add(entry.id);
|
|
99
|
+
|
|
100
|
+
// Store vector if present
|
|
101
|
+
if (entry.vector) {
|
|
102
|
+
this.vectorIndex.set(entry.id, entry.vector);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Update cache
|
|
106
|
+
this.updateCache(entry);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async findById(id: string): Promise<MemoryEntry | null> {
|
|
110
|
+
this.ensureInitialized();
|
|
111
|
+
|
|
112
|
+
// Check cache first
|
|
113
|
+
const cached = this.cache.get(id);
|
|
114
|
+
if (cached) {
|
|
115
|
+
return cached.entry;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return this.entries.get(id) ?? null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async findByKey(namespace: string, key: string): Promise<MemoryEntry | null> {
|
|
122
|
+
this.ensureInitialized();
|
|
123
|
+
|
|
124
|
+
for (const entry of this.entries.values()) {
|
|
125
|
+
if (entry.namespace === namespace && entry.key === key) {
|
|
126
|
+
return entry;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async findByCompositeKey(compositeKey: string): Promise<MemoryEntry | null> {
|
|
133
|
+
const [namespace, key] = compositeKey.split(':');
|
|
134
|
+
return this.findByKey(namespace, key);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async delete(id: string): Promise<boolean> {
|
|
138
|
+
this.ensureInitialized();
|
|
139
|
+
|
|
140
|
+
const entry = this.entries.get(id);
|
|
141
|
+
if (!entry) return false;
|
|
142
|
+
|
|
143
|
+
// Remove from all indexes
|
|
144
|
+
this.entries.delete(id);
|
|
145
|
+
this.namespaceIndex.get(entry.namespace)?.delete(id);
|
|
146
|
+
this.vectorIndex.delete(id);
|
|
147
|
+
this.cache.delete(id);
|
|
148
|
+
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async exists(id: string): Promise<boolean> {
|
|
153
|
+
return this.entries.has(id);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// Bulk Operations
|
|
158
|
+
// ============================================================================
|
|
159
|
+
|
|
160
|
+
async saveMany(entries: MemoryEntry[]): Promise<BulkOperationResult> {
|
|
161
|
+
const errors: Array<{ id: string; error: string }> = [];
|
|
162
|
+
let success = 0;
|
|
163
|
+
|
|
164
|
+
for (const entry of entries) {
|
|
165
|
+
try {
|
|
166
|
+
await this.save(entry);
|
|
167
|
+
success++;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
errors.push({
|
|
170
|
+
id: entry.id,
|
|
171
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
success,
|
|
178
|
+
failed: errors.length,
|
|
179
|
+
errors,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async findByIds(ids: string[]): Promise<MemoryEntry[]> {
|
|
184
|
+
return ids
|
|
185
|
+
.map((id) => this.entries.get(id))
|
|
186
|
+
.filter((e): e is MemoryEntry => e !== undefined);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async deleteMany(ids: string[]): Promise<BulkOperationResult> {
|
|
190
|
+
const errors: Array<{ id: string; error: string }> = [];
|
|
191
|
+
let success = 0;
|
|
192
|
+
|
|
193
|
+
for (const id of ids) {
|
|
194
|
+
try {
|
|
195
|
+
if (await this.delete(id)) {
|
|
196
|
+
success++;
|
|
197
|
+
} else {
|
|
198
|
+
errors.push({ id, error: 'Entry not found' });
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
errors.push({
|
|
202
|
+
id,
|
|
203
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
success,
|
|
210
|
+
failed: errors.length,
|
|
211
|
+
errors,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ============================================================================
|
|
216
|
+
// Query Operations
|
|
217
|
+
// ============================================================================
|
|
218
|
+
|
|
219
|
+
async findAll(options?: MemoryQueryOptions): Promise<MemoryEntry[]> {
|
|
220
|
+
this.ensureInitialized();
|
|
221
|
+
|
|
222
|
+
let results = Array.from(this.entries.values());
|
|
223
|
+
|
|
224
|
+
// Apply filters
|
|
225
|
+
if (options?.namespace) {
|
|
226
|
+
results = results.filter((e) => e.namespace === options.namespace);
|
|
227
|
+
}
|
|
228
|
+
if (options?.type) {
|
|
229
|
+
results = results.filter((e) => e.type === options.type);
|
|
230
|
+
}
|
|
231
|
+
if (options?.status) {
|
|
232
|
+
results = results.filter((e) => e.status === options.status);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Apply sorting
|
|
236
|
+
const orderBy = options?.orderBy ?? 'createdAt';
|
|
237
|
+
const orderDir = options?.orderDirection ?? 'desc';
|
|
238
|
+
results.sort((a, b) => {
|
|
239
|
+
let aVal: number, bVal: number;
|
|
240
|
+
switch (orderBy) {
|
|
241
|
+
case 'accessCount':
|
|
242
|
+
aVal = a.accessCount;
|
|
243
|
+
bVal = b.accessCount;
|
|
244
|
+
break;
|
|
245
|
+
case 'lastAccessedAt':
|
|
246
|
+
aVal = a.lastAccessedAt.getTime();
|
|
247
|
+
bVal = b.lastAccessedAt.getTime();
|
|
248
|
+
break;
|
|
249
|
+
case 'updatedAt':
|
|
250
|
+
aVal = a.updatedAt.getTime();
|
|
251
|
+
bVal = b.updatedAt.getTime();
|
|
252
|
+
break;
|
|
253
|
+
default:
|
|
254
|
+
aVal = a.createdAt.getTime();
|
|
255
|
+
bVal = b.createdAt.getTime();
|
|
256
|
+
}
|
|
257
|
+
return orderDir === 'asc' ? aVal - bVal : bVal - aVal;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Apply pagination
|
|
261
|
+
if (options?.offset) {
|
|
262
|
+
results = results.slice(options.offset);
|
|
263
|
+
}
|
|
264
|
+
if (options?.limit) {
|
|
265
|
+
results = results.slice(0, options.limit);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return results;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async findByNamespace(
|
|
272
|
+
namespace: string,
|
|
273
|
+
options?: Omit<MemoryQueryOptions, 'namespace'>
|
|
274
|
+
): Promise<MemoryEntry[]> {
|
|
275
|
+
return this.findAll({ ...options, namespace });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async findByType(
|
|
279
|
+
type: MemoryType,
|
|
280
|
+
options?: Omit<MemoryQueryOptions, 'type'>
|
|
281
|
+
): Promise<MemoryEntry[]> {
|
|
282
|
+
return this.findAll({ ...options, type });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async findByStatus(
|
|
286
|
+
status: MemoryStatus,
|
|
287
|
+
options?: Omit<MemoryQueryOptions, 'status'>
|
|
288
|
+
): Promise<MemoryEntry[]> {
|
|
289
|
+
return this.findAll({ ...options, status });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ============================================================================
|
|
293
|
+
// Vector Search
|
|
294
|
+
// ============================================================================
|
|
295
|
+
|
|
296
|
+
async searchByVector(options: VectorSearchOptions): Promise<VectorSearchResult[]> {
|
|
297
|
+
this.ensureInitialized();
|
|
298
|
+
|
|
299
|
+
const results: VectorSearchResult[] = [];
|
|
300
|
+
|
|
301
|
+
for (const [id, vector] of this.vectorIndex) {
|
|
302
|
+
const entry = this.entries.get(id);
|
|
303
|
+
if (!entry) continue;
|
|
304
|
+
if (options.namespace && entry.namespace !== options.namespace) continue;
|
|
305
|
+
if (options.type && entry.type !== options.type) continue;
|
|
306
|
+
|
|
307
|
+
const similarity = this.cosineSimilarity(options.vector, vector);
|
|
308
|
+
const threshold = options.threshold ?? 0.5;
|
|
309
|
+
|
|
310
|
+
if (similarity >= threshold) {
|
|
311
|
+
results.push({
|
|
312
|
+
entry,
|
|
313
|
+
similarity,
|
|
314
|
+
distance: 1 - similarity,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Sort by similarity descending
|
|
320
|
+
results.sort((a, b) => b.similarity - a.similarity);
|
|
321
|
+
|
|
322
|
+
// Limit results
|
|
323
|
+
return results.slice(0, options.limit ?? 10);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async findSimilar(entryId: string, limit = 10): Promise<VectorSearchResult[]> {
|
|
327
|
+
const entry = this.entries.get(entryId);
|
|
328
|
+
if (!entry || !entry.vector) return [];
|
|
329
|
+
|
|
330
|
+
return this.searchByVector({
|
|
331
|
+
vector: entry.vector,
|
|
332
|
+
namespace: entry.namespace,
|
|
333
|
+
limit: limit + 1, // Include self
|
|
334
|
+
}).then((results) => results.filter((r) => r.entry.id !== entryId).slice(0, limit));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ============================================================================
|
|
338
|
+
// Maintenance Operations
|
|
339
|
+
// ============================================================================
|
|
340
|
+
|
|
341
|
+
async findExpired(): Promise<MemoryEntry[]> {
|
|
342
|
+
return Array.from(this.entries.values()).filter((e) => e.isExpired());
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async deleteExpired(): Promise<number> {
|
|
346
|
+
const expired = await this.findExpired();
|
|
347
|
+
for (const entry of expired) {
|
|
348
|
+
await this.delete(entry.id);
|
|
349
|
+
}
|
|
350
|
+
return expired.length;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async findCold(milliseconds: number): Promise<MemoryEntry[]> {
|
|
354
|
+
return Array.from(this.entries.values()).filter((e) => e.isCold(milliseconds));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async archiveCold(milliseconds: number): Promise<number> {
|
|
358
|
+
const cold = await this.findCold(milliseconds);
|
|
359
|
+
for (const entry of cold) {
|
|
360
|
+
entry.archive();
|
|
361
|
+
await this.save(entry);
|
|
362
|
+
}
|
|
363
|
+
return cold.length;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ============================================================================
|
|
367
|
+
// Statistics
|
|
368
|
+
// ============================================================================
|
|
369
|
+
|
|
370
|
+
async getStatistics(): Promise<MemoryStatistics> {
|
|
371
|
+
const entries = Array.from(this.entries.values());
|
|
372
|
+
|
|
373
|
+
const entriesByNamespace: Record<string, number> = {};
|
|
374
|
+
const entriesByType: Record<MemoryType, number> = {
|
|
375
|
+
semantic: 0,
|
|
376
|
+
episodic: 0,
|
|
377
|
+
procedural: 0,
|
|
378
|
+
working: 0,
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
let totalAccessCount = 0;
|
|
382
|
+
let totalSize = 0;
|
|
383
|
+
let activeCount = 0;
|
|
384
|
+
let archivedCount = 0;
|
|
385
|
+
let deletedCount = 0;
|
|
386
|
+
|
|
387
|
+
for (const entry of entries) {
|
|
388
|
+
// Count by namespace
|
|
389
|
+
entriesByNamespace[entry.namespace] = (entriesByNamespace[entry.namespace] ?? 0) + 1;
|
|
390
|
+
|
|
391
|
+
// Count by type
|
|
392
|
+
entriesByType[entry.type]++;
|
|
393
|
+
|
|
394
|
+
// Accumulate stats
|
|
395
|
+
totalAccessCount += entry.accessCount;
|
|
396
|
+
totalSize += JSON.stringify(entry.value).length;
|
|
397
|
+
|
|
398
|
+
// Count by status
|
|
399
|
+
switch (entry.status) {
|
|
400
|
+
case 'active':
|
|
401
|
+
activeCount++;
|
|
402
|
+
break;
|
|
403
|
+
case 'archived':
|
|
404
|
+
archivedCount++;
|
|
405
|
+
break;
|
|
406
|
+
case 'deleted':
|
|
407
|
+
deletedCount++;
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Find hottest and coldest
|
|
413
|
+
const sorted = [...entries].sort((a, b) => b.accessCount - a.accessCount);
|
|
414
|
+
const hottestEntries = sorted.slice(0, 5).map((e) => e.id);
|
|
415
|
+
const coldestEntries = sorted.slice(-5).reverse().map((e) => e.id);
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
totalEntries: entries.length,
|
|
419
|
+
activeEntries: activeCount,
|
|
420
|
+
archivedEntries: archivedCount,
|
|
421
|
+
deletedEntries: deletedCount,
|
|
422
|
+
totalSize,
|
|
423
|
+
entriesByNamespace,
|
|
424
|
+
entriesByType,
|
|
425
|
+
averageAccessCount: entries.length > 0 ? totalAccessCount / entries.length : 0,
|
|
426
|
+
hottestEntries,
|
|
427
|
+
coldestEntries,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async count(options?: MemoryQueryOptions): Promise<number> {
|
|
432
|
+
const entries = await this.findAll(options);
|
|
433
|
+
return entries.length;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ============================================================================
|
|
437
|
+
// Namespace Operations
|
|
438
|
+
// ============================================================================
|
|
439
|
+
|
|
440
|
+
async listNamespaces(): Promise<string[]> {
|
|
441
|
+
return Array.from(this.namespaceIndex.keys());
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async deleteNamespace(namespace: string): Promise<number> {
|
|
445
|
+
const ids = this.namespaceIndex.get(namespace);
|
|
446
|
+
if (!ids) return 0;
|
|
447
|
+
|
|
448
|
+
const count = ids.size;
|
|
449
|
+
for (const id of ids) {
|
|
450
|
+
await this.delete(id);
|
|
451
|
+
}
|
|
452
|
+
this.namespaceIndex.delete(namespace);
|
|
453
|
+
return count;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async getNamespaceSize(namespace: string): Promise<number> {
|
|
457
|
+
const ids = this.namespaceIndex.get(namespace);
|
|
458
|
+
if (!ids) return 0;
|
|
459
|
+
|
|
460
|
+
let size = 0;
|
|
461
|
+
for (const id of ids) {
|
|
462
|
+
const entry = this.entries.get(id);
|
|
463
|
+
if (entry) {
|
|
464
|
+
size += JSON.stringify(entry.value).length;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return size;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ============================================================================
|
|
471
|
+
// Private Methods
|
|
472
|
+
// ============================================================================
|
|
473
|
+
|
|
474
|
+
private ensureInitialized(): void {
|
|
475
|
+
if (!this.initialized) {
|
|
476
|
+
throw new Error('Repository not initialized. Call initialize() first.');
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
private updateCache(entry: MemoryEntry): void {
|
|
481
|
+
const maxCacheSize = this.config.cacheSize ?? 100;
|
|
482
|
+
|
|
483
|
+
// Evict oldest entries if cache is full
|
|
484
|
+
if (this.cache.size >= maxCacheSize) {
|
|
485
|
+
const oldest = Array.from(this.cache.entries())
|
|
486
|
+
.sort(([, a], [, b]) => a.timestamp - b.timestamp)
|
|
487
|
+
.slice(0, Math.floor(maxCacheSize * 0.2));
|
|
488
|
+
|
|
489
|
+
for (const [id] of oldest) {
|
|
490
|
+
this.cache.delete(id);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
this.cache.set(entry.id, {
|
|
495
|
+
entry,
|
|
496
|
+
timestamp: Date.now(),
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
private cosineSimilarity(a: Float32Array, b: Float32Array): number {
|
|
501
|
+
if (a.length !== b.length) return 0;
|
|
502
|
+
|
|
503
|
+
let dotProduct = 0;
|
|
504
|
+
let normA = 0;
|
|
505
|
+
let normB = 0;
|
|
506
|
+
|
|
507
|
+
for (let i = 0; i < a.length; i++) {
|
|
508
|
+
dotProduct += a[i] * b[i];
|
|
509
|
+
normA += a[i] * a[i];
|
|
510
|
+
normB += b[i] * b[i];
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
514
|
+
return denominator === 0 ? 0 : dotProduct / denominator;
|
|
515
|
+
}
|
|
516
|
+
}
|