codebaxing 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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +402 -0
  3. package/README.vi.md +402 -0
  4. package/dist/core/exceptions.d.ts +25 -0
  5. package/dist/core/exceptions.d.ts.map +1 -0
  6. package/dist/core/exceptions.js +46 -0
  7. package/dist/core/exceptions.js.map +1 -0
  8. package/dist/core/interfaces.d.ts +13 -0
  9. package/dist/core/interfaces.d.ts.map +1 -0
  10. package/dist/core/interfaces.js +5 -0
  11. package/dist/core/interfaces.js.map +1 -0
  12. package/dist/core/models.d.ts +132 -0
  13. package/dist/core/models.d.ts.map +1 -0
  14. package/dist/core/models.js +303 -0
  15. package/dist/core/models.js.map +1 -0
  16. package/dist/index.d.ts +17 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +20 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/indexing/embedding-service.d.ts +66 -0
  21. package/dist/indexing/embedding-service.d.ts.map +1 -0
  22. package/dist/indexing/embedding-service.js +271 -0
  23. package/dist/indexing/embedding-service.js.map +1 -0
  24. package/dist/indexing/memory-retriever.d.ts +58 -0
  25. package/dist/indexing/memory-retriever.d.ts.map +1 -0
  26. package/dist/indexing/memory-retriever.js +327 -0
  27. package/dist/indexing/memory-retriever.js.map +1 -0
  28. package/dist/indexing/parallel-indexer.d.ts +36 -0
  29. package/dist/indexing/parallel-indexer.d.ts.map +1 -0
  30. package/dist/indexing/parallel-indexer.js +67 -0
  31. package/dist/indexing/parallel-indexer.js.map +1 -0
  32. package/dist/indexing/source-retriever.d.ts +66 -0
  33. package/dist/indexing/source-retriever.d.ts.map +1 -0
  34. package/dist/indexing/source-retriever.js +420 -0
  35. package/dist/indexing/source-retriever.js.map +1 -0
  36. package/dist/mcp/server.d.ts +16 -0
  37. package/dist/mcp/server.d.ts.map +1 -0
  38. package/dist/mcp/server.js +370 -0
  39. package/dist/mcp/server.js.map +1 -0
  40. package/dist/mcp/state.d.ts +26 -0
  41. package/dist/mcp/state.d.ts.map +1 -0
  42. package/dist/mcp/state.js +154 -0
  43. package/dist/mcp/state.js.map +1 -0
  44. package/dist/parsers/language-configs.d.ts +26 -0
  45. package/dist/parsers/language-configs.d.ts.map +1 -0
  46. package/dist/parsers/language-configs.js +422 -0
  47. package/dist/parsers/language-configs.js.map +1 -0
  48. package/dist/parsers/treesitter-parser.d.ts +37 -0
  49. package/dist/parsers/treesitter-parser.d.ts.map +1 -0
  50. package/dist/parsers/treesitter-parser.js +602 -0
  51. package/dist/parsers/treesitter-parser.js.map +1 -0
  52. package/package.json +91 -0
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Embedding service using @huggingface/transformers (Transformers.js).
3
+ *
4
+ * Runs ONNX models locally in Node.js — no external server needed.
5
+ * Default model: Xenova/all-MiniLM-L6-v2 (384 dims, fast, well-supported)
6
+ *
7
+ * Device Support:
8
+ * Set CODEBAXING_DEVICE environment variable:
9
+ * - 'cpu' (default): CPU inference, works everywhere
10
+ * - 'webgpu': WebGPU backend (experimental, uses Metal on macOS)
11
+ * - 'cuda': NVIDIA GPU (Linux/Windows only, requires CUDA drivers)
12
+ * - 'auto': Auto-detect best available device
13
+ *
14
+ * Note: macOS does not support CUDA. Use 'webgpu' for GPU acceleration on Mac.
15
+ */
16
+ import { EmbeddingError } from '../core/exceptions.js';
17
+ const VALID_DEVICES = ['cpu', 'cuda', 'webgpu', 'auto'];
18
+ /**
19
+ * Get the configured device from environment variable.
20
+ * Defaults to 'cpu' for maximum compatibility.
21
+ */
22
+ export function getConfiguredDevice() {
23
+ const envDevice = process.env.CODEBAXING_DEVICE?.toLowerCase();
24
+ if (envDevice && VALID_DEVICES.includes(envDevice)) {
25
+ return envDevice;
26
+ }
27
+ return 'cpu';
28
+ }
29
+ // Lazy import transformers (heavy dependency)
30
+ // Using any types for transformers.js as its types are complex and version-dependent
31
+ /* eslint-disable @typescript-eslint/no-explicit-any */
32
+ let pipeline;
33
+ let env;
34
+ let transformersLoaded = false;
35
+ /* eslint-enable @typescript-eslint/no-explicit-any */
36
+ async function importTransformers() {
37
+ if (transformersLoaded)
38
+ return;
39
+ const transformers = await import('@huggingface/transformers');
40
+ pipeline = transformers.pipeline;
41
+ env = transformers.env;
42
+ // Disable remote model downloads warning
43
+ env.allowLocalModels = true;
44
+ env.allowRemoteModels = true;
45
+ transformersLoaded = true;
46
+ }
47
+ export const EMBEDDING_MODELS = {
48
+ 'all-MiniLM-L6-v2': {
49
+ modelId: 'Xenova/all-MiniLM-L6-v2',
50
+ dimensions: 384,
51
+ maxSeqLength: 256,
52
+ queryPrefix: '',
53
+ documentPrefix: '',
54
+ },
55
+ 'coderankembed': {
56
+ modelId: 'nomic-ai/CodeRankEmbed',
57
+ dimensions: 768,
58
+ maxSeqLength: 8192,
59
+ queryPrefix: 'Represent this query for searching relevant code: ',
60
+ documentPrefix: '',
61
+ },
62
+ };
63
+ export const DEFAULT_MODEL = 'all-MiniLM-L6-v2';
64
+ // ─── EmbeddingService ────────────────────────────────────────────────────────
65
+ export class EmbeddingService {
66
+ modelName;
67
+ config;
68
+ device;
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ extractor = null;
71
+ loading = null;
72
+ showProgress;
73
+ // LRU cache
74
+ cache = new Map();
75
+ cacheMaxSize = 5000;
76
+ // Stats
77
+ stats = {
78
+ totalEmbeddings: 0,
79
+ totalBatches: 0,
80
+ totalTime: 0,
81
+ cacheHits: 0,
82
+ cacheMisses: 0,
83
+ };
84
+ constructor(modelName = DEFAULT_MODEL, options = {}) {
85
+ this.modelName = modelName;
86
+ this.showProgress = options.showProgress ?? false;
87
+ this.device = options.device ?? getConfiguredDevice();
88
+ if (modelName in EMBEDDING_MODELS) {
89
+ this.config = { ...EMBEDDING_MODELS[modelName] };
90
+ }
91
+ else {
92
+ // Assume it's a HuggingFace model ID
93
+ this.config = {
94
+ modelId: modelName,
95
+ dimensions: 384,
96
+ maxSeqLength: 512,
97
+ queryPrefix: '',
98
+ documentPrefix: '',
99
+ };
100
+ }
101
+ }
102
+ /** Get the device being used for inference */
103
+ getDevice() {
104
+ return this.device;
105
+ }
106
+ get dimensions() {
107
+ return this.config.dimensions;
108
+ }
109
+ async loadModel() {
110
+ if (this.extractor)
111
+ return;
112
+ if (this.loading)
113
+ return this.loading;
114
+ this.loading = (async () => {
115
+ await importTransformers();
116
+ const deviceLabel = this.device === 'cpu' ? 'CPU' : this.device.toUpperCase();
117
+ if (this.showProgress) {
118
+ console.log(`Loading embedding model: ${this.config.modelId} (device: ${deviceLabel})`);
119
+ }
120
+ // Build pipeline options
121
+ const pipelineOptions = {
122
+ quantized: true,
123
+ };
124
+ // Configure device (Transformers.js uses 'device' option)
125
+ // Note: 'auto' lets Transformers.js pick the best available
126
+ if (this.device !== 'cpu') {
127
+ pipelineOptions.device = this.device;
128
+ }
129
+ try {
130
+ this.extractor = await pipeline('feature-extraction', this.config.modelId, pipelineOptions);
131
+ }
132
+ catch (e) {
133
+ const errorMsg = e.message;
134
+ // If GPU failed, try falling back to CPU
135
+ if (this.device !== 'cpu' && (errorMsg.includes('GPU') || errorMsg.includes('CUDA') || errorMsg.includes('WebGPU'))) {
136
+ console.warn(`Failed to use ${deviceLabel}: ${errorMsg}. Falling back to CPU.`);
137
+ this.device = 'cpu';
138
+ this.extractor = await pipeline('feature-extraction', this.config.modelId, {
139
+ quantized: true,
140
+ });
141
+ }
142
+ else if (this.modelName !== DEFAULT_MODEL) {
143
+ // Fall back to default model if custom model fails
144
+ console.warn(`Failed to load model ${this.config.modelId}: ${errorMsg}. ` +
145
+ `Falling back to ${DEFAULT_MODEL}`);
146
+ this.config = { ...EMBEDDING_MODELS[DEFAULT_MODEL] };
147
+ this.extractor = await pipeline('feature-extraction', this.config.modelId, {
148
+ quantized: true,
149
+ });
150
+ }
151
+ else {
152
+ throw new EmbeddingError(`Failed to load embedding model: ${errorMsg}`);
153
+ }
154
+ }
155
+ if (this.showProgress) {
156
+ const actualDevice = this.device === 'cpu' ? 'CPU' : this.device.toUpperCase();
157
+ console.log(`Model loaded: ${this.config.modelId} (${this.config.dimensions} dims, ${actualDevice})`);
158
+ }
159
+ this.loading = null;
160
+ })();
161
+ return this.loading;
162
+ }
163
+ async embed(text, isQuery = false) {
164
+ // Check cache
165
+ const cacheKey = `${isQuery ? 'q:' : 'd:'}${text}`;
166
+ const cached = this.cache.get(cacheKey);
167
+ if (cached) {
168
+ this.stats.cacheHits++;
169
+ return cached;
170
+ }
171
+ await this.loadModel();
172
+ const prefix = isQuery ? this.config.queryPrefix : this.config.documentPrefix;
173
+ const inputText = prefix ? prefix + text : text;
174
+ const output = await this.extractor(inputText, {
175
+ pooling: 'mean',
176
+ normalize: true,
177
+ });
178
+ const embedding = Array.from(output.data);
179
+ // Update cache
180
+ this.stats.cacheMisses++;
181
+ this.stats.totalEmbeddings++;
182
+ if (this.cache.size >= this.cacheMaxSize) {
183
+ // Remove oldest entry
184
+ const firstKey = this.cache.keys().next().value;
185
+ this.cache.delete(firstKey);
186
+ }
187
+ this.cache.set(cacheKey, embedding);
188
+ return embedding;
189
+ }
190
+ async embedBatch(texts, isQuery = false) {
191
+ if (texts.length === 0)
192
+ return [];
193
+ await this.loadModel();
194
+ const startTime = performance.now();
195
+ const prefix = isQuery ? this.config.queryPrefix : this.config.documentPrefix;
196
+ const inputTexts = prefix ? texts.map(t => prefix + t) : texts;
197
+ // Process in sub-batches to manage memory
198
+ const batchSize = 32;
199
+ const allEmbeddings = [];
200
+ for (let i = 0; i < inputTexts.length; i += batchSize) {
201
+ const batch = inputTexts.slice(i, i + batchSize);
202
+ // Process each text individually (Transformers.js handles batching internally)
203
+ for (const text of batch) {
204
+ try {
205
+ const output = await this.extractor(text, {
206
+ pooling: 'mean',
207
+ normalize: true,
208
+ });
209
+ allEmbeddings.push(Array.from(output.data));
210
+ }
211
+ catch (e) {
212
+ console.warn(`Embedding error for text: ${e.message}. Skipping.`);
213
+ // Push zero vector as fallback
214
+ allEmbeddings.push(new Array(this.config.dimensions).fill(0));
215
+ }
216
+ }
217
+ }
218
+ const elapsed = (performance.now() - startTime) / 1000;
219
+ this.stats.totalEmbeddings += texts.length;
220
+ this.stats.totalBatches++;
221
+ this.stats.totalTime += elapsed;
222
+ return allEmbeddings;
223
+ }
224
+ getStats() {
225
+ return {
226
+ ...this.stats,
227
+ device: this.device,
228
+ embeddingsPerSecond: this.stats.totalTime > 0
229
+ ? this.stats.totalEmbeddings / this.stats.totalTime
230
+ : 0,
231
+ };
232
+ }
233
+ clearCache() {
234
+ this.cache.clear();
235
+ this.stats.cacheHits = 0;
236
+ this.stats.cacheMisses = 0;
237
+ }
238
+ async unload() {
239
+ if (this.extractor) {
240
+ this.extractor = null;
241
+ this.cache.clear();
242
+ }
243
+ }
244
+ }
245
+ // ─── Singleton Management ────────────────────────────────────────────────────
246
+ const embeddingServices = new Map();
247
+ export function getEmbeddingService(modelName = DEFAULT_MODEL, options = {}) {
248
+ // Include device in cache key to support different devices per model
249
+ const device = options.device ?? getConfiguredDevice();
250
+ const cacheKey = `${modelName}:${device}`;
251
+ if (!embeddingServices.has(cacheKey)) {
252
+ embeddingServices.set(cacheKey, new EmbeddingService(modelName, { ...options, device }));
253
+ }
254
+ return embeddingServices.get(cacheKey);
255
+ }
256
+ export async function resetEmbeddingService(modelName) {
257
+ if (modelName) {
258
+ const service = embeddingServices.get(modelName);
259
+ if (service) {
260
+ await service.unload();
261
+ embeddingServices.delete(modelName);
262
+ }
263
+ }
264
+ else {
265
+ for (const service of embeddingServices.values()) {
266
+ await service.unload();
267
+ }
268
+ embeddingServices.clear();
269
+ }
270
+ }
271
+ //# sourceMappingURL=embedding-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding-service.js","sourceRoot":"","sources":["../../src/indexing/embedding-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Memory retriever for semantic memory search.
3
+ *
4
+ * Provides semantic search over memories (conversations, status,
5
+ * decisions, etc.) using the same embedding infrastructure as code search.
6
+ */
7
+ import { EmbeddingService } from './embedding-service.js';
8
+ import { Memory } from '../core/models.js';
9
+ export declare class MemoryRetriever {
10
+ static readonly COLLECTION_NAME = "memories";
11
+ static readonly METADATA_FILE = "memory_metadata.json";
12
+ private projectPath;
13
+ private projectId;
14
+ private embeddingService;
15
+ private chromaClient;
16
+ private collection;
17
+ private persistPath;
18
+ private verbose;
19
+ private initialized;
20
+ stats: Record<string, unknown>;
21
+ constructor(options: {
22
+ projectPath: string;
23
+ embeddingModel?: string;
24
+ verbose?: boolean;
25
+ persistPath?: string;
26
+ embeddingService?: EmbeddingService;
27
+ });
28
+ private ensureInitialized;
29
+ private log;
30
+ private loadStats;
31
+ private saveStats;
32
+ remember(options: {
33
+ content: string;
34
+ memoryType: string;
35
+ tags?: string[];
36
+ ttl?: string;
37
+ source?: string;
38
+ metadata?: Record<string, unknown>;
39
+ }): Promise<Memory>;
40
+ recall(options: {
41
+ query: string;
42
+ memoryType?: string;
43
+ tags?: string[];
44
+ nResults?: number;
45
+ timeRange?: string;
46
+ minRelevance?: number;
47
+ }): Promise<Record<string, unknown>[]>;
48
+ forget(options: {
49
+ memoryId?: string;
50
+ memoryType?: string;
51
+ tags?: string[];
52
+ olderThan?: string;
53
+ }): Promise<{
54
+ deleted: number;
55
+ }>;
56
+ getStats(): Promise<Record<string, unknown>>;
57
+ }
58
+ //# sourceMappingURL=memory-retriever.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-retriever.d.ts","sourceRoot":"","sources":["../../src/indexing/memory-retriever.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,gBAAgB,EAAuB,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,MAAM,EAA8C,MAAM,mBAAmB,CAAC;AAWvF,qBAAa,eAAe;IAC1B,MAAM,CAAC,QAAQ,CAAC,eAAe,cAAc;IAC7C,MAAM,CAAC,QAAQ,CAAC,aAAa,0BAA0B;IAEvD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,WAAW,CAAS;IAE5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI5B;gBAEU,OAAO,EAAE;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;KACrC;YAuBa,iBAAiB;IAY/B,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,SAAS;IASX,QAAQ,CAAC,OAAO,EAAE;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GAAG,OAAO,CAAC,MAAM,CAAC;IA6Cb,MAAM,CAAC,OAAO,EAAE;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAyGhC,MAAM,CAAC,OAAO,EAAE;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAmF1B,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CA6BnD"}
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Memory retriever for semantic memory search.
3
+ *
4
+ * Provides semantic search over memories (conversations, status,
5
+ * decisions, etc.) using the same embedding infrastructure as code search.
6
+ */
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import crypto from 'node:crypto';
10
+ import { ChromaClient, IncludeEnum } from 'chromadb';
11
+ import { getEmbeddingService } from './embedding-service.js';
12
+ import { Memory, MemoryType, memoryTypeFromString } from '../core/models.js';
13
+ // TTL duration mappings (in milliseconds)
14
+ const TTL_DURATIONS = {
15
+ session: 24 * 60 * 60 * 1000, // 24 hours
16
+ day: 24 * 60 * 60 * 1000, // 1 day
17
+ week: 7 * 24 * 60 * 60 * 1000, // 1 week
18
+ month: 30 * 24 * 60 * 60 * 1000, // 30 days
19
+ permanent: null, // Never expires
20
+ };
21
+ export class MemoryRetriever {
22
+ static COLLECTION_NAME = 'memories';
23
+ static METADATA_FILE = 'memory_metadata.json';
24
+ projectPath;
25
+ projectId;
26
+ embeddingService;
27
+ chromaClient;
28
+ collection;
29
+ persistPath;
30
+ verbose;
31
+ initialized = false;
32
+ stats = {
33
+ totalMemories: 0,
34
+ byType: {},
35
+ lastCleanup: null,
36
+ };
37
+ constructor(options) {
38
+ this.projectPath = path.resolve(options.projectPath);
39
+ this.projectId = this.projectPath;
40
+ this.verbose = options.verbose ?? false;
41
+ this.persistPath = options.persistPath;
42
+ this.embeddingService = options.embeddingService ?? getEmbeddingService(options.embeddingModel ?? 'all-MiniLM-L6-v2', { showProgress: false });
43
+ if (this.persistPath) {
44
+ fs.mkdirSync(this.persistPath, { recursive: true });
45
+ }
46
+ // ChromaDB Node.js client connects to a server.
47
+ // Set CHROMADB_URL env var to connect to a running ChromaDB server.
48
+ const chromaUrl = process.env.CHROMADB_URL;
49
+ this.chromaClient = chromaUrl
50
+ ? new ChromaClient({ path: chromaUrl })
51
+ : new ChromaClient();
52
+ }
53
+ async ensureInitialized() {
54
+ if (this.initialized)
55
+ return;
56
+ this.collection = await this.chromaClient.getOrCreateCollection({
57
+ name: MemoryRetriever.COLLECTION_NAME,
58
+ metadata: { description: 'Memory storage for conversations, status, decisions' },
59
+ });
60
+ this.loadStats();
61
+ this.initialized = true;
62
+ }
63
+ log(message) {
64
+ if (this.verbose)
65
+ console.log(`[Memory] ${message}`);
66
+ }
67
+ loadStats() {
68
+ if (this.persistPath) {
69
+ const metadataPath = path.join(path.dirname(this.persistPath), MemoryRetriever.METADATA_FILE);
70
+ try {
71
+ if (fs.existsSync(metadataPath)) {
72
+ const data = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
73
+ this.stats = data.stats ?? this.stats;
74
+ }
75
+ }
76
+ catch { /* ignore */ }
77
+ }
78
+ }
79
+ saveStats() {
80
+ if (this.persistPath) {
81
+ const metadataPath = path.join(path.dirname(this.persistPath), MemoryRetriever.METADATA_FILE);
82
+ try {
83
+ fs.writeFileSync(metadataPath, JSON.stringify({ stats: this.stats }, null, 2));
84
+ }
85
+ catch { /* ignore */ }
86
+ }
87
+ }
88
+ async remember(options) {
89
+ await this.ensureInitialized();
90
+ const memory = new Memory({
91
+ id: crypto.randomUUID(),
92
+ content: options.content,
93
+ memoryType: memoryTypeFromString(options.memoryType),
94
+ project: this.projectId,
95
+ tags: options.tags ?? [],
96
+ ttl: options.ttl ?? 'permanent',
97
+ source: options.source ?? 'user',
98
+ metadata: options.metadata ?? {},
99
+ });
100
+ const embedding = await this.embeddingService.embed(options.content);
101
+ // Flatten metadata for ChromaDB
102
+ const chromaMetadata = {
103
+ memory_type: memory.memoryType,
104
+ project: memory.project,
105
+ tags: memory.tags.join(','),
106
+ created_at: memory.createdAt,
107
+ accessed_at: memory.accessedAt,
108
+ ttl: memory.ttl,
109
+ source: memory.source,
110
+ };
111
+ await this.collection.add({
112
+ ids: [memory.id],
113
+ embeddings: [embedding],
114
+ documents: [memory.content],
115
+ metadatas: [chromaMetadata],
116
+ });
117
+ // Update stats
118
+ const count = await this.collection.count();
119
+ this.stats.totalMemories = count;
120
+ const byType = this.stats.byType;
121
+ byType[memory.memoryType] = (byType[memory.memoryType] ?? 0) + 1;
122
+ this.saveStats();
123
+ this.log(`Stored memory: ${memory.id.substring(0, 8)}... (${options.memoryType})`);
124
+ return memory;
125
+ }
126
+ async recall(options) {
127
+ await this.ensureInitialized();
128
+ const count = await this.collection.count();
129
+ if (count === 0)
130
+ return [];
131
+ const nResults = options.nResults ?? 5;
132
+ const queryEmbedding = await this.embeddingService.embed(options.query, true);
133
+ // Parse time range
134
+ let timeCutoff = null;
135
+ if (options.timeRange && options.timeRange !== 'all') {
136
+ const now = new Date();
137
+ const ranges = {
138
+ today: 24 * 60 * 60 * 1000,
139
+ week: 7 * 24 * 60 * 60 * 1000,
140
+ month: 30 * 24 * 60 * 60 * 1000,
141
+ };
142
+ if (options.timeRange in ranges) {
143
+ timeCutoff = new Date(now.getTime() - ranges[options.timeRange]);
144
+ }
145
+ }
146
+ // Build filter
147
+ const whereClauses = [{ project: this.projectId }];
148
+ if (options.memoryType) {
149
+ whereClauses.push({ memory_type: options.memoryType });
150
+ }
151
+ const chromaWhere = whereClauses.length > 1
152
+ ? { $and: whereClauses }
153
+ : whereClauses[0];
154
+ try {
155
+ const results = await this.collection.query({
156
+ queryEmbeddings: [queryEmbedding],
157
+ nResults: Math.min(nResults * 2, 50),
158
+ // ChromaDB types don't fully support $and operator typing
159
+ where: chromaWhere,
160
+ include: [IncludeEnum.Documents, IncludeEnum.Metadatas, IncludeEnum.Distances],
161
+ });
162
+ if (!results.ids?.[0]?.length)
163
+ return [];
164
+ const memories = [];
165
+ const ids = results.ids[0];
166
+ const docs = results.documents?.[0] ?? [];
167
+ const metas = results.metadatas?.[0] ?? [];
168
+ const distances = results.distances?.[0] ?? [];
169
+ for (let i = 0; i < ids.length; i++) {
170
+ const distance = distances[i];
171
+ const relevance = 1.0 / (1.0 + distance);
172
+ const minRelevance = options.minRelevance ?? 0;
173
+ if (relevance < minRelevance)
174
+ continue;
175
+ const metadata = metas[i];
176
+ // Time range filter
177
+ if (timeCutoff && metadata.created_at) {
178
+ try {
179
+ const created = new Date(metadata.created_at);
180
+ if (created < timeCutoff)
181
+ continue;
182
+ }
183
+ catch { /* skip */ }
184
+ }
185
+ // Tags filter
186
+ if (options.tags?.length) {
187
+ const storedTags = new Set(metadata.tags?.split(',') ?? []);
188
+ if (!options.tags.some(t => storedTags.has(t)))
189
+ continue;
190
+ }
191
+ memories.push({
192
+ id: ids[i],
193
+ content: docs[i],
194
+ memoryType: metadata.memory_type,
195
+ tags: metadata.tags ? metadata.tags.split(',').filter(Boolean) : [],
196
+ createdAt: metadata.created_at,
197
+ relevance: Math.round(relevance * 1000) / 1000,
198
+ source: metadata.source ?? 'unknown',
199
+ });
200
+ if (memories.length >= nResults)
201
+ break;
202
+ }
203
+ // Update accessed_at for returned memories
204
+ const now = new Date().toISOString();
205
+ for (const mem of memories) {
206
+ try {
207
+ await this.collection.update({
208
+ ids: [mem.id],
209
+ metadatas: [{ accessed_at: now }],
210
+ });
211
+ }
212
+ catch { /* non-critical */ }
213
+ }
214
+ this.log(`Recalled ${memories.length} memories for query: ${options.query.substring(0, 50)}...`);
215
+ return memories;
216
+ }
217
+ catch (e) {
218
+ this.log(`Query error: ${e.message}`);
219
+ return [];
220
+ }
221
+ }
222
+ async forget(options) {
223
+ await this.ensureInitialized();
224
+ let deleted = 0;
225
+ if (options.memoryId) {
226
+ try {
227
+ const existing = await this.collection.get({ ids: [options.memoryId] });
228
+ if (existing.ids.length > 0) {
229
+ await this.collection.delete({ ids: [options.memoryId] });
230
+ deleted = 1;
231
+ }
232
+ }
233
+ catch { /* ignore */ }
234
+ }
235
+ else if (options.olderThan || options.tags?.length) {
236
+ // Fetch all and filter
237
+ const allMemories = await this.collection.get({
238
+ where: { project: this.projectId },
239
+ include: [IncludeEnum.Metadatas],
240
+ });
241
+ const idsToDelete = [];
242
+ const durationMap = {
243
+ '1d': 24 * 60 * 60 * 1000,
244
+ '7d': 7 * 24 * 60 * 60 * 1000,
245
+ '30d': 30 * 24 * 60 * 60 * 1000,
246
+ '1y': 365 * 24 * 60 * 60 * 1000,
247
+ };
248
+ for (let i = 0; i < allMemories.ids.length; i++) {
249
+ const metadata = allMemories.metadatas?.[i];
250
+ let shouldDelete = false;
251
+ if (options.olderThan && options.olderThan in durationMap) {
252
+ const cutoff = Date.now() - durationMap[options.olderThan];
253
+ const created = new Date(metadata.created_at).getTime();
254
+ if (created < cutoff)
255
+ shouldDelete = true;
256
+ }
257
+ if (options.tags?.length) {
258
+ const storedTags = new Set(metadata.tags?.split(',') ?? []);
259
+ if (options.tags.some(t => storedTags.has(t)))
260
+ shouldDelete = true;
261
+ }
262
+ if (options.memoryType && metadata.memory_type !== options.memoryType) {
263
+ shouldDelete = false;
264
+ }
265
+ if (shouldDelete)
266
+ idsToDelete.push(allMemories.ids[i]);
267
+ }
268
+ if (idsToDelete.length > 0) {
269
+ await this.collection.delete({ ids: idsToDelete });
270
+ deleted = idsToDelete.length;
271
+ }
272
+ }
273
+ else if (options.memoryType) {
274
+ try {
275
+ const toDelete = await this.collection.get({
276
+ where: {
277
+ $and: [
278
+ { memory_type: options.memoryType },
279
+ { project: this.projectId },
280
+ ],
281
+ },
282
+ });
283
+ if (toDelete.ids.length > 0) {
284
+ await this.collection.delete({ ids: toDelete.ids });
285
+ deleted = toDelete.ids.length;
286
+ }
287
+ }
288
+ catch (e) {
289
+ this.log(`Delete error: ${e.message}`);
290
+ }
291
+ }
292
+ // Update stats
293
+ const count = await this.collection.count();
294
+ this.stats.totalMemories = count;
295
+ this.saveStats();
296
+ this.log(`Forgot ${deleted} memories`);
297
+ return { deleted };
298
+ }
299
+ async getStats() {
300
+ await this.ensureInitialized();
301
+ const total = await this.collection.count();
302
+ const byType = {};
303
+ for (const memType of Object.values(MemoryType)) {
304
+ try {
305
+ const result = await this.collection.get({
306
+ where: {
307
+ $and: [
308
+ { memory_type: memType },
309
+ { project: this.projectId },
310
+ ],
311
+ },
312
+ });
313
+ if (result.ids.length > 0) {
314
+ byType[memType] = result.ids.length;
315
+ }
316
+ }
317
+ catch { /* ignore */ }
318
+ }
319
+ return {
320
+ totalMemories: total,
321
+ byType,
322
+ project: this.projectId,
323
+ lastCleanup: this.stats.lastCleanup,
324
+ };
325
+ }
326
+ }
327
+ //# sourceMappingURL=memory-retriever.js.map