claude-brain 0.14.0 → 0.14.2
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 +191 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +11 -11
- package/bunfig.toml +8 -8
- package/package.json +80 -80
- package/packs/backend/node.json +173 -173
- package/packs/core/javascript.json +176 -176
- package/packs/core/typescript.json +222 -222
- package/packs/frontend/react.json +254 -254
- package/packs/meta/testing.json +172 -172
- package/src/automation/auto-context.ts +240 -240
- package/src/automation/decision-detector.ts +452 -452
- package/src/automation/index.ts +11 -11
- package/src/automation/phase12-manager.ts +456 -456
- package/src/automation/proactive-recall.ts +373 -373
- package/src/automation/project-detector.ts +310 -310
- package/src/automation/repo-scanner.ts +205 -205
- package/src/cli/auto-setup.ts +82 -82
- package/src/cli/bin.ts +202 -202
- package/src/cli/commands/chroma.ts +573 -573
- package/src/cli/commands/git-hook.ts +189 -189
- package/src/cli/commands/hooks.ts +213 -213
- package/src/cli/commands/init.ts +122 -122
- package/src/cli/commands/install-mcp.ts +92 -92
- package/src/cli/commands/pack.ts +197 -197
- package/src/cli/commands/serve.ts +167 -167
- package/src/cli/commands/start.ts +42 -42
- package/src/cli/commands/uninstall-mcp.ts +41 -41
- package/src/cli/commands/update.ts +121 -121
- package/src/cli/diagnose.ts +4 -4
- package/src/cli/health-check.ts +4 -4
- package/src/cli/migrate-chroma.ts +106 -106
- package/src/cli/setup.ts +4 -4
- package/src/cli/ui/animations.ts +80 -80
- package/src/cli/ui/components.ts +82 -82
- package/src/cli/ui/index.ts +4 -4
- package/src/cli/ui/logo.ts +36 -36
- package/src/cli/ui/theme.ts +55 -55
- package/src/config/defaults.ts +50 -50
- package/src/config/home.ts +55 -55
- package/src/config/index.ts +7 -7
- package/src/config/loader.ts +166 -166
- package/src/config/migration.ts +76 -76
- package/src/config/schema.ts +360 -360
- package/src/config/validator.ts +184 -184
- package/src/config/watcher.ts +86 -86
- package/src/context/assembler.ts +398 -398
- package/src/context/cache-manager.ts +101 -101
- package/src/context/formatter.ts +84 -84
- package/src/context/hierarchy.ts +85 -85
- package/src/context/index.ts +83 -83
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/types.ts +252 -252
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +123 -123
- package/src/health/index.ts +229 -229
- package/src/hooks/brain-hook.ts +112 -112
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +207 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +194 -181
- package/src/hooks/passive-classifier.ts +366 -366
- package/src/hooks/queue.ts +129 -122
- package/src/hooks/session-tracker.ts +275 -275
- package/src/hooks/types.ts +47 -47
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/affinity.ts +162 -162
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +13 -13
- package/src/intelligence/cross-project/transfer.ts +201 -201
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +207 -207
- package/src/intelligence/prediction/context-anticipator.ts +198 -198
- package/src/intelligence/prediction/decision-predictor.ts +184 -184
- package/src/intelligence/prediction/index.ts +13 -13
- package/src/intelligence/prediction/recommender.ts +268 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +247 -247
- package/src/intelligence/reasoning/counterfactual.ts +248 -248
- package/src/intelligence/reasoning/index.ts +13 -13
- package/src/intelligence/reasoning/synthesizer.ts +169 -169
- package/src/intelligence/temporal/evolution.ts +197 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +259 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/knowledge/entity-extractor.ts +416 -416
- package/src/knowledge/graph/builder.ts +185 -185
- package/src/knowledge/graph/linker.ts +201 -201
- package/src/knowledge/graph/memory-graph.ts +359 -359
- package/src/knowledge/graph/schema.ts +99 -99
- package/src/knowledge/graph/search.ts +168 -168
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +174 -174
- package/src/memory/chroma/collection-manager.ts +94 -94
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +153 -153
- package/src/memory/chroma/index.ts +82 -82
- package/src/memory/chroma/migration.ts +270 -270
- package/src/memory/chroma/schemas.ts +69 -69
- package/src/memory/chroma/search.ts +315 -315
- package/src/memory/chroma/store.ts +741 -741
- package/src/memory/consolidation/archiver.ts +164 -164
- package/src/memory/consolidation/merger.ts +186 -186
- package/src/memory/consolidation/scorer.ts +138 -138
- package/src/memory/context-builder.ts +236 -236
- package/src/memory/database.ts +169 -169
- package/src/memory/embedding-utils.ts +156 -156
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +351 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/index.ts +582 -582
- package/src/memory/knowledge-extractor.ts +455 -455
- package/src/memory/learning.ts +378 -378
- package/src/memory/patterns.ts +396 -396
- package/src/memory/schema.ts +88 -88
- package/src/memory/search.ts +309 -309
- package/src/memory/store.ts +787 -787
- package/src/memory/types.ts +121 -121
- package/src/orchestrator/coordinator.ts +272 -272
- package/src/orchestrator/decision-logger.ts +228 -228
- package/src/orchestrator/event-emitter.ts +198 -198
- package/src/orchestrator/event-queue.ts +184 -184
- package/src/orchestrator/handlers/base-handler.ts +70 -70
- package/src/orchestrator/handlers/context-handler.ts +73 -73
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- package/src/orchestrator/handlers/index.ts +10 -10
- package/src/orchestrator/handlers/status-handler.ts +131 -131
- package/src/orchestrator/handlers/task-handler.ts +171 -171
- package/src/orchestrator/index.ts +275 -275
- package/src/orchestrator/task-parser.ts +284 -284
- package/src/orchestrator/types.ts +98 -98
- package/src/packs/index.ts +9 -9
- package/src/packs/loader.ts +134 -134
- package/src/packs/manager.ts +204 -204
- package/src/packs/ranker.ts +78 -78
- package/src/packs/types.ts +81 -81
- package/src/phase12/index.ts +5 -5
- package/src/retrieval/bm25/index.ts +300 -300
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +223 -223
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +223 -223
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +163 -163
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +198 -198
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +236 -236
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +188 -188
- package/src/retrieval/reranker/model.ts +95 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +428 -428
- package/src/routing/intent-classifier.ts +436 -436
- package/src/routing/response-filter.ts +254 -254
- package/src/routing/router.ts +1314 -1314
- package/src/routing/search-engine.ts +475 -475
- package/src/routing/types.ts +84 -84
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/handlers/call-tool.ts +156 -156
- package/src/server/handlers/index.ts +9 -9
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/analyze-decision-evolution.ts +151 -151
- package/src/server/handlers/tools/auto-remember.ts +200 -200
- package/src/server/handlers/tools/brain.ts +85 -85
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/detect-trends.ts +144 -144
- package/src/server/handlers/tools/find-cross-project-patterns.ts +168 -168
- package/src/server/handlers/tools/get-activity-log.ts +194 -194
- package/src/server/handlers/tools/get-code-standards.ts +124 -124
- package/src/server/handlers/tools/get-corrections.ts +154 -154
- package/src/server/handlers/tools/get-decision-timeline.ts +172 -172
- package/src/server/handlers/tools/get-episode.ts +103 -103
- package/src/server/handlers/tools/get-patterns.ts +158 -158
- package/src/server/handlers/tools/get-phase12-status.ts +63 -63
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/get-recommendations.ts +145 -145
- package/src/server/handlers/tools/index.ts +31 -31
- package/src/server/handlers/tools/init-project.ts +757 -757
- package/src/server/handlers/tools/list-episodes.ts +90 -90
- package/src/server/handlers/tools/list-projects.ts +125 -125
- package/src/server/handlers/tools/rate-memory.ts +101 -101
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +126 -126
- package/src/server/handlers/tools/record-correction.ts +125 -125
- package/src/server/handlers/tools/remember-decision.ts +153 -153
- package/src/server/handlers/tools/schemas.ts +253 -253
- package/src/server/handlers/tools/search-knowledge-graph.ts +102 -102
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/handlers/tools/what-if-analysis.ts +135 -135
- package/src/server/http-api.ts +693 -693
- package/src/server/index.ts +40 -40
- package/src/server/mcp-server.ts +283 -283
- package/src/server/providers/index.ts +7 -7
- package/src/server/providers/prompts.ts +327 -327
- package/src/server/providers/resources.ts +622 -622
- package/src/server/services.ts +468 -468
- package/src/server/types.ts +39 -39
- package/src/server/utils/error-handler.ts +155 -155
- package/src/server/utils/index.ts +13 -13
- package/src/server/utils/memory-indicator.ts +83 -83
- package/src/server/utils/request-context.ts +122 -122
- package/src/server/utils/response-formatter.ts +124 -124
- package/src/server/utils/validators.ts +210 -210
- package/src/setup/index.ts +48 -48
- package/src/setup/wizard.ts +461 -461
- package/src/tools/index.ts +24 -24
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.test.ts +30 -30
- package/src/tools/schemas.ts +617 -617
- package/src/tools/types.ts +412 -412
- package/src/utils/circuit-breaker.ts +130 -130
- package/src/utils/cleanup.ts +34 -34
- package/src/utils/error-handler.ts +132 -132
- package/src/utils/error-messages.ts +60 -60
- package/src/utils/fallback.ts +45 -45
- package/src/utils/index.ts +54 -54
- package/src/utils/logger-utils.ts +80 -80
- package/src/utils/logger.ts +88 -88
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/retry.ts +94 -94
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/frontmatter.ts +264 -264
- package/src/vault/index.ts +318 -318
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +422 -422
- package/src/vault/reader.ts +264 -264
- package/src/vault/templates.ts +186 -186
- package/src/vault/types.ts +73 -73
- package/src/vault/watcher.ts +277 -277
- package/src/vault/writer.ts +413 -413
- package/tsconfig.json +30 -30
package/src/memory/index.ts
CHANGED
|
@@ -1,582 +1,582 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory System - Main Module
|
|
3
|
-
* Phase 3: Memory and Embedding System
|
|
4
|
-
*
|
|
5
|
-
* Unified memory system manager that combines all components
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { Logger } from 'pino'
|
|
9
|
-
import { MemoryDatabase } from './database'
|
|
10
|
-
import { EmbeddingService } from './embeddings'
|
|
11
|
-
import { MemoryStore } from './store'
|
|
12
|
-
import { SemanticSearch } from './search'
|
|
13
|
-
import { MemoryContextBuilder } from './context-builder'
|
|
14
|
-
import type { MemorySystemStats } from './types'
|
|
15
|
-
import { ChromaManager, DEFAULT_CHROMA_CONFIG, getChromaConfigFromEnv, ChromaMigration, type MigrationOptions } from './chroma'
|
|
16
|
-
|
|
17
|
-
// Re-export all types and classes for external use
|
|
18
|
-
export * from './types'
|
|
19
|
-
export { MemoryDatabase } from './database'
|
|
20
|
-
export { EmbeddingService } from './embeddings'
|
|
21
|
-
export { MemoryStore } from './store'
|
|
22
|
-
export { SemanticSearch } from './search'
|
|
23
|
-
export { MemoryContextBuilder, type ContextOptions } from './context-builder'
|
|
24
|
-
export {
|
|
25
|
-
embeddingToBuffer,
|
|
26
|
-
bufferToEmbedding,
|
|
27
|
-
normalizeEmbedding,
|
|
28
|
-
euclideanDistance,
|
|
29
|
-
cosineSimilarity,
|
|
30
|
-
dotProduct,
|
|
31
|
-
magnitude,
|
|
32
|
-
averageEmbeddings,
|
|
33
|
-
topKSimilar
|
|
34
|
-
} from './embedding-utils'
|
|
35
|
-
|
|
36
|
-
// Phase 12: Advanced Memory Features
|
|
37
|
-
export { PatternRecognizer, type Pattern } from './patterns'
|
|
38
|
-
export { LearningSystem, type Correction, type Preference, type LearningInsights } from './learning'
|
|
39
|
-
export { KnowledgeExtractor, type ExtractedKnowledge, type ExtractionResult } from './knowledge-extractor'
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Unified memory system manager
|
|
43
|
-
* Combines database, embeddings, store, search, and context building
|
|
44
|
-
*/
|
|
45
|
-
export class MemoryManager {
|
|
46
|
-
readonly database: MemoryDatabase
|
|
47
|
-
readonly embeddings: EmbeddingService
|
|
48
|
-
readonly contextBuilder: MemoryContextBuilder
|
|
49
|
-
readonly chroma: ChromaManager
|
|
50
|
-
|
|
51
|
-
// Store and search are initialized after database is ready
|
|
52
|
-
private _store: MemoryStore | null = null
|
|
53
|
-
private _search: SemanticSearch | null = null
|
|
54
|
-
|
|
55
|
-
private logger: Logger
|
|
56
|
-
private initialized: boolean = false
|
|
57
|
-
private useChromaDB: boolean = true
|
|
58
|
-
private onDecisionStoredCallbacks: ((input: any) => void)[] = []
|
|
59
|
-
private onDecisionDeletedCallbacks: ((id: string) => void)[] = []
|
|
60
|
-
|
|
61
|
-
constructor(
|
|
62
|
-
dbPath: string,
|
|
63
|
-
logger: Logger,
|
|
64
|
-
useChromaDB: boolean = true,
|
|
65
|
-
chromaConfig?: any,
|
|
66
|
-
customEmbeddings?: EmbeddingService
|
|
67
|
-
) {
|
|
68
|
-
this.logger = logger.child({ component: 'memory-manager' })
|
|
69
|
-
this.useChromaDB = useChromaDB
|
|
70
|
-
|
|
71
|
-
this.database = new MemoryDatabase(dbPath, logger)
|
|
72
|
-
this.embeddings = customEmbeddings || new EmbeddingService(logger)
|
|
73
|
-
this.contextBuilder = new MemoryContextBuilder(logger)
|
|
74
|
-
|
|
75
|
-
const envConfig = getChromaConfigFromEnv()
|
|
76
|
-
const config = { ...DEFAULT_CHROMA_CONFIG, ...envConfig, ...chromaConfig }
|
|
77
|
-
this.chroma = new ChromaManager(logger, config)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Initialize memory system
|
|
82
|
-
* Must be called before using store or search
|
|
83
|
-
*/
|
|
84
|
-
async initialize(): Promise<void> {
|
|
85
|
-
if (this.initialized) {
|
|
86
|
-
this.logger.warn('Memory system already initialized')
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
this.logger.info('Initializing memory system...')
|
|
92
|
-
|
|
93
|
-
await this.database.initialize()
|
|
94
|
-
|
|
95
|
-
await this.embeddings.initialize()
|
|
96
|
-
|
|
97
|
-
const db = this.database.getDb()
|
|
98
|
-
this._store = new MemoryStore(db, this.embeddings, this.logger)
|
|
99
|
-
this._search = new SemanticSearch(db, this.embeddings, this.logger)
|
|
100
|
-
|
|
101
|
-
if (this.useChromaDB) {
|
|
102
|
-
try {
|
|
103
|
-
await this.chroma.initialize()
|
|
104
|
-
this.logger.info('ChromaDB backend initialized successfully')
|
|
105
|
-
} catch (error) {
|
|
106
|
-
this.logger.warn({ error }, 'Failed to initialize ChromaDB, falling back to SQLite backend')
|
|
107
|
-
this.useChromaDB = false
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
this.initialized = true
|
|
112
|
-
this.logger.info('Memory system initialized successfully')
|
|
113
|
-
} catch (error) {
|
|
114
|
-
this.logger.error({ error }, 'Failed to initialize memory system')
|
|
115
|
-
throw error
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Get the memory store (throws if not initialized)
|
|
121
|
-
*/
|
|
122
|
-
get store(): MemoryStore {
|
|
123
|
-
if (!this._store) {
|
|
124
|
-
throw new Error('Memory system not initialized. Call initialize() first.')
|
|
125
|
-
}
|
|
126
|
-
return this._store
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Get the semantic search engine (throws if not initialized)
|
|
131
|
-
*/
|
|
132
|
-
get search(): SemanticSearch {
|
|
133
|
-
if (!this._search) {
|
|
134
|
-
throw new Error('Memory system not initialized. Call initialize() first.')
|
|
135
|
-
}
|
|
136
|
-
return this._search
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Check if memory system is initialized
|
|
141
|
-
*/
|
|
142
|
-
isInitialized(): boolean {
|
|
143
|
-
return this.initialized
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
close(): void {
|
|
147
|
-
if (this.useChromaDB) {
|
|
148
|
-
this.chroma.close()
|
|
149
|
-
}
|
|
150
|
-
this.database.close()
|
|
151
|
-
this._store = null
|
|
152
|
-
this._search = null
|
|
153
|
-
this.initialized = false
|
|
154
|
-
this.logger.info('Memory system closed')
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Get system statistics
|
|
159
|
-
*/
|
|
160
|
-
getStats(): MemorySystemStats {
|
|
161
|
-
if (!this.initialized) {
|
|
162
|
-
throw new Error('Memory system not initialized')
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
database: this.database.getStats(),
|
|
167
|
-
embeddings: this.embeddings.getCacheStats()
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Health check
|
|
173
|
-
*/
|
|
174
|
-
async healthCheck(): Promise<boolean> {
|
|
175
|
-
if (!this.initialized) {
|
|
176
|
-
return false
|
|
177
|
-
}
|
|
178
|
-
return this.database.healthCheck()
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Check if ChromaDB backend is enabled and connected
|
|
183
|
-
*/
|
|
184
|
-
isChromaDBEnabled(): boolean {
|
|
185
|
-
return this.useChromaDB
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Add a listener that fires when a decision is stored (from any backend)
|
|
190
|
-
*/
|
|
191
|
-
addDecisionStoredListener(callback: (input: any) => void): void {
|
|
192
|
-
this.onDecisionStoredCallbacks.push(callback)
|
|
193
|
-
// Also wire to ChromaDB listener if available
|
|
194
|
-
if (this.useChromaDB) {
|
|
195
|
-
this.chroma.store.addDecisionStoredListener(callback)
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Add a listener that fires when a decision is deleted
|
|
201
|
-
*/
|
|
202
|
-
addDecisionDeletedListener(callback: (id: string) => void): void {
|
|
203
|
-
this.onDecisionDeletedCallbacks.push(callback)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async rememberDecision(
|
|
207
|
-
project: string,
|
|
208
|
-
context: string,
|
|
209
|
-
decision: string,
|
|
210
|
-
reasoning: string,
|
|
211
|
-
options?: { alternatives?: string; tags?: string[] }
|
|
212
|
-
): Promise<string> {
|
|
213
|
-
if (this.useChromaDB) {
|
|
214
|
-
return this.chroma.store.storeDecision({
|
|
215
|
-
project,
|
|
216
|
-
context,
|
|
217
|
-
decision,
|
|
218
|
-
reasoning,
|
|
219
|
-
alternatives: options?.alternatives,
|
|
220
|
-
tags: options?.tags
|
|
221
|
-
})
|
|
222
|
-
}
|
|
223
|
-
const id = await this.store.storeDecision({
|
|
224
|
-
project,
|
|
225
|
-
context,
|
|
226
|
-
decision,
|
|
227
|
-
reasoning,
|
|
228
|
-
alternatives: options?.alternatives,
|
|
229
|
-
tags: options?.tags
|
|
230
|
-
})
|
|
231
|
-
// Notify listeners (e.g., knowledge graph builder) for SQLite path
|
|
232
|
-
for (const cb of this.onDecisionStoredCallbacks) {
|
|
233
|
-
try {
|
|
234
|
-
cb({ project, context, decision, reasoning, alternatives: options?.alternatives, tags: options?.tags, id })
|
|
235
|
-
} catch {}
|
|
236
|
-
}
|
|
237
|
-
return id
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Get raw search results - routes to ChromaDB when enabled
|
|
242
|
-
* Use this for internal operations that need raw results
|
|
243
|
-
*/
|
|
244
|
-
async searchRaw(
|
|
245
|
-
query: string,
|
|
246
|
-
options?: { project?: string; limit?: number; minSimilarity?: number }
|
|
247
|
-
): Promise<any[]> {
|
|
248
|
-
if (this.useChromaDB) {
|
|
249
|
-
const chromaResults = await this.chroma.search.searchDecisions(query, {
|
|
250
|
-
project: options?.project,
|
|
251
|
-
limit: options?.limit || 5,
|
|
252
|
-
minSimilarity: options?.minSimilarity || 0.5
|
|
253
|
-
})
|
|
254
|
-
// Transform ChromaDB results to match MemorySearchResult structure
|
|
255
|
-
// Includes flat `content` field for direct access (Phase 19)
|
|
256
|
-
return chromaResults.map(r => {
|
|
257
|
-
const memoryContent = typeof r.content === 'string' ? r.content : JSON.stringify(r.content)
|
|
258
|
-
const decisionObj = r.metadata.decision ? {
|
|
259
|
-
id: r.id,
|
|
260
|
-
project: r.metadata.project || options?.project || 'unknown',
|
|
261
|
-
context: r.metadata.context || '',
|
|
262
|
-
decision: r.metadata.decision || memoryContent,
|
|
263
|
-
reasoning: r.metadata.reasoning || '',
|
|
264
|
-
alternatives: r.metadata.alternatives_considered || '',
|
|
265
|
-
tags: r.metadata.tags || [],
|
|
266
|
-
outcome: r.metadata.outcome,
|
|
267
|
-
createdAt: r.metadata.created_at ? new Date(r.metadata.created_at) : new Date()
|
|
268
|
-
} : undefined
|
|
269
|
-
|
|
270
|
-
return {
|
|
271
|
-
// Flat fields for direct access (Phase 19)
|
|
272
|
-
id: r.id,
|
|
273
|
-
content: decisionObj ? decisionObj.decision : memoryContent,
|
|
274
|
-
// Nested fields for backward compatibility
|
|
275
|
-
memory: {
|
|
276
|
-
id: r.id,
|
|
277
|
-
project: r.metadata.project || options?.project || 'unknown',
|
|
278
|
-
content: memoryContent,
|
|
279
|
-
createdAt: r.metadata.created_at ? new Date(r.metadata.created_at) : new Date(),
|
|
280
|
-
metadata: r.metadata
|
|
281
|
-
},
|
|
282
|
-
similarity: r.similarity,
|
|
283
|
-
decision: decisionObj,
|
|
284
|
-
metadata: r.metadata
|
|
285
|
-
}
|
|
286
|
-
})
|
|
287
|
-
} else {
|
|
288
|
-
return await this.search.search(query, {
|
|
289
|
-
project: options?.project,
|
|
290
|
-
limit: options?.limit || 5,
|
|
291
|
-
minSimilarity: options?.minSimilarity || 0.5
|
|
292
|
-
})
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
async recallSimilar(
|
|
297
|
-
query: string,
|
|
298
|
-
options?: { project?: string; limit?: number; minSimilarity?: number }
|
|
299
|
-
): Promise<string> {
|
|
300
|
-
const results = await this.searchRaw(query, options)
|
|
301
|
-
return this.contextBuilder.buildDecisionContext(results)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
async getRecommendations(
|
|
305
|
-
currentContext: string,
|
|
306
|
-
project: string,
|
|
307
|
-
limit: number = 3
|
|
308
|
-
): Promise<string> {
|
|
309
|
-
const results = await this.search.getRecommendations(currentContext, project, limit)
|
|
310
|
-
return this.contextBuilder.buildRecommendationContext(results)
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
async migrateToChromaDB(options: MigrationOptions = {}): Promise<any> {
|
|
314
|
-
if (!this.useChromaDB) {
|
|
315
|
-
throw new Error('ChromaDB is not enabled')
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const migration = new ChromaMigration(
|
|
319
|
-
this.logger,
|
|
320
|
-
this.database.getDb(),
|
|
321
|
-
this.chroma.store,
|
|
322
|
-
this.chroma.collections
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
return migration.migrate(options)
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Store a pattern in memory — routes to ChromaDB or SQLite
|
|
330
|
-
*/
|
|
331
|
-
async storePattern(input: {
|
|
332
|
-
project: string
|
|
333
|
-
pattern_type: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
334
|
-
description: string
|
|
335
|
-
example?: string
|
|
336
|
-
confidence: number
|
|
337
|
-
context?: string
|
|
338
|
-
source?: string
|
|
339
|
-
}): Promise<string> {
|
|
340
|
-
if (this.useChromaDB) {
|
|
341
|
-
return this.chroma.store.storePattern(input)
|
|
342
|
-
}
|
|
343
|
-
return this.store.storePattern(input)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Store a correction/lesson learned — routes to ChromaDB or SQLite
|
|
348
|
-
*/
|
|
349
|
-
async storeCorrection(input: {
|
|
350
|
-
project: string
|
|
351
|
-
original: string
|
|
352
|
-
correction: string
|
|
353
|
-
reasoning: string
|
|
354
|
-
context?: string
|
|
355
|
-
confidence: number
|
|
356
|
-
}): Promise<string> {
|
|
357
|
-
if (this.useChromaDB) {
|
|
358
|
-
return this.chroma.store.storeCorrection(input)
|
|
359
|
-
}
|
|
360
|
-
return this.store.storeCorrection(input)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Get patterns for a project — routes to ChromaDB or SQLite
|
|
365
|
-
*/
|
|
366
|
-
async getPatterns(
|
|
367
|
-
project?: string,
|
|
368
|
-
options?: {
|
|
369
|
-
pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
370
|
-
limit?: number
|
|
371
|
-
}
|
|
372
|
-
): Promise<any[]> {
|
|
373
|
-
if (this.useChromaDB) {
|
|
374
|
-
if (project) {
|
|
375
|
-
return this.chroma.store.getPatternsByProject(project, options)
|
|
376
|
-
}
|
|
377
|
-
return this.chroma.store.searchPatterns('', { limit: options?.limit || 10 })
|
|
378
|
-
}
|
|
379
|
-
if (project) {
|
|
380
|
-
return this.store.getPatternsByProject(project, options)
|
|
381
|
-
}
|
|
382
|
-
return this.store.searchPatterns('', { limit: options?.limit || 10 })
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Get corrections for a project — routes to ChromaDB or SQLite
|
|
387
|
-
*/
|
|
388
|
-
async getCorrections(
|
|
389
|
-
project?: string,
|
|
390
|
-
options?: { limit?: number }
|
|
391
|
-
): Promise<any[]> {
|
|
392
|
-
if (this.useChromaDB) {
|
|
393
|
-
if (project) {
|
|
394
|
-
return this.chroma.store.getCorrectionsByProject(project, options?.limit || 10)
|
|
395
|
-
}
|
|
396
|
-
return this.chroma.store.searchCorrections('', { limit: options?.limit || 10 })
|
|
397
|
-
}
|
|
398
|
-
if (project) {
|
|
399
|
-
return this.store.getCorrectionsByProject(project, options?.limit || 10)
|
|
400
|
-
}
|
|
401
|
-
return this.store.searchCorrections('', { limit: options?.limit || 10 })
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Fetch all decisions with content — routes to ChromaDB or SQLite
|
|
406
|
-
* Used by analytical tools that need bulk access to decision data
|
|
407
|
-
*/
|
|
408
|
-
async fetchAllDecisions(project?: string): Promise<any[]> {
|
|
409
|
-
if (this.useChromaDB) {
|
|
410
|
-
try {
|
|
411
|
-
const collection = await this.chroma.collections.getDecisions()
|
|
412
|
-
const results = await collection.get({
|
|
413
|
-
where: project ? { project } : undefined
|
|
414
|
-
})
|
|
415
|
-
if (results && results.ids) {
|
|
416
|
-
return results.ids.map((id: string, i: number) => ({
|
|
417
|
-
id,
|
|
418
|
-
content: results.documents?.[i] || '',
|
|
419
|
-
date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
|
|
420
|
-
project: results.metadatas?.[i]?.project || project || 'unknown',
|
|
421
|
-
context: results.metadatas?.[i]?.context || '',
|
|
422
|
-
decision: results.metadatas?.[i]?.decision || results.documents?.[i] || '',
|
|
423
|
-
reasoning: results.metadatas?.[i]?.reasoning || '',
|
|
424
|
-
alternatives: results.metadatas?.[i]?.alternatives_considered || '',
|
|
425
|
-
tags: results.metadatas?.[i]?.tags || []
|
|
426
|
-
}))
|
|
427
|
-
}
|
|
428
|
-
return []
|
|
429
|
-
} catch (error) {
|
|
430
|
-
this.logger.warn({ error }, 'ChromaDB fetchAllDecisions failed, falling back to SQLite')
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return this.store.getAllDecisionsWithContent(project)
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Fetch all patterns with content — routes to ChromaDB or SQLite
|
|
438
|
-
*/
|
|
439
|
-
async fetchAllPatterns(project?: string): Promise<any[]> {
|
|
440
|
-
if (this.useChromaDB) {
|
|
441
|
-
try {
|
|
442
|
-
const collection = await this.chroma.collections.getPatterns()
|
|
443
|
-
const results = await collection.get({
|
|
444
|
-
where: project ? { project } : undefined
|
|
445
|
-
})
|
|
446
|
-
if (results && results.ids) {
|
|
447
|
-
return results.ids.map((id: string, i: number) => ({
|
|
448
|
-
id,
|
|
449
|
-
content: results.documents?.[i] || '',
|
|
450
|
-
date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
|
|
451
|
-
project: results.metadatas?.[i]?.project || project || 'unknown',
|
|
452
|
-
pattern_type: results.metadatas?.[i]?.pattern_type || '',
|
|
453
|
-
description: results.metadatas?.[i]?.description || results.documents?.[i] || '',
|
|
454
|
-
example: results.metadatas?.[i]?.example || '',
|
|
455
|
-
confidence: results.metadatas?.[i]?.confidence || 0,
|
|
456
|
-
context: results.metadatas?.[i]?.context || ''
|
|
457
|
-
}))
|
|
458
|
-
}
|
|
459
|
-
return []
|
|
460
|
-
} catch (error) {
|
|
461
|
-
this.logger.warn({ error }, 'ChromaDB fetchAllPatterns failed, falling back to SQLite')
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return this.store.getAllPatternsWithContent(project)
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Fetch all corrections with content — routes to ChromaDB or SQLite
|
|
469
|
-
*/
|
|
470
|
-
async fetchAllCorrections(project?: string): Promise<any[]> {
|
|
471
|
-
if (this.useChromaDB) {
|
|
472
|
-
try {
|
|
473
|
-
const collection = await this.chroma.collections.getCorrections()
|
|
474
|
-
const results = await collection.get({
|
|
475
|
-
where: project ? { project } : undefined
|
|
476
|
-
})
|
|
477
|
-
if (results && results.ids) {
|
|
478
|
-
return results.ids.map((id: string, i: number) => ({
|
|
479
|
-
id,
|
|
480
|
-
content: results.documents?.[i] || '',
|
|
481
|
-
date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
|
|
482
|
-
project: results.metadatas?.[i]?.project || project || 'unknown',
|
|
483
|
-
original: results.metadatas?.[i]?.original || '',
|
|
484
|
-
correction: results.metadatas?.[i]?.correction || results.documents?.[i] || '',
|
|
485
|
-
reasoning: results.metadatas?.[i]?.reasoning || '',
|
|
486
|
-
context: results.metadatas?.[i]?.context || '',
|
|
487
|
-
confidence: results.metadatas?.[i]?.confidence || 0
|
|
488
|
-
}))
|
|
489
|
-
}
|
|
490
|
-
return []
|
|
491
|
-
} catch (error) {
|
|
492
|
-
this.logger.warn({ error }, 'ChromaDB fetchAllCorrections failed, falling back to SQLite')
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
return this.store.getAllCorrectionsWithContent(project)
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Search patterns by query — routes to ChromaDB or SQLite
|
|
500
|
-
*/
|
|
501
|
-
async searchPatterns(
|
|
502
|
-
query: string,
|
|
503
|
-
options?: {
|
|
504
|
-
project?: string
|
|
505
|
-
pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
506
|
-
limit?: number
|
|
507
|
-
minSimilarity?: number
|
|
508
|
-
}
|
|
509
|
-
): Promise<any[]> {
|
|
510
|
-
if (this.useChromaDB) {
|
|
511
|
-
return this.chroma.store.searchPatterns(query, options)
|
|
512
|
-
}
|
|
513
|
-
return this.store.searchPatterns(query, options)
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Search corrections by query — routes to ChromaDB or SQLite
|
|
518
|
-
*/
|
|
519
|
-
async searchCorrections(
|
|
520
|
-
query: string,
|
|
521
|
-
options?: {
|
|
522
|
-
project?: string
|
|
523
|
-
limit?: number
|
|
524
|
-
minSimilarity?: number
|
|
525
|
-
}
|
|
526
|
-
): Promise<any[]> {
|
|
527
|
-
if (this.useChromaDB) {
|
|
528
|
-
return this.chroma.store.searchCorrections(query, options)
|
|
529
|
-
}
|
|
530
|
-
return this.store.searchCorrections(query, options)
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* Delete a decision by ID — routes to ChromaDB or SQLite
|
|
535
|
-
*/
|
|
536
|
-
async deleteDecision(id: string): Promise<void> {
|
|
537
|
-
if (this.useChromaDB) {
|
|
538
|
-
await this.chroma.store.deleteDecision(id)
|
|
539
|
-
} else {
|
|
540
|
-
this.store.deleteMemory(id)
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Notify listeners (e.g., knowledge graph builder) about deletion
|
|
544
|
-
for (const cb of this.onDecisionDeletedCallbacks) {
|
|
545
|
-
try {
|
|
546
|
-
cb(id)
|
|
547
|
-
} catch {}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* Update a decision by deleting the old one and storing a new version.
|
|
553
|
-
* Phase 20: Ensures both ChromaDB and knowledge graph are atomically updated.
|
|
554
|
-
*/
|
|
555
|
-
async updateDecision(
|
|
556
|
-
oldId: string,
|
|
557
|
-
project: string,
|
|
558
|
-
context: string,
|
|
559
|
-
decision: string,
|
|
560
|
-
reasoning: string,
|
|
561
|
-
options?: { alternatives?: string; tags?: string[] }
|
|
562
|
-
): Promise<string> {
|
|
563
|
-
// Delete old version — fires onDecisionDeletedCallbacks (graph + episode cleanup)
|
|
564
|
-
try {
|
|
565
|
-
await this.deleteDecision(oldId)
|
|
566
|
-
this.logger.debug({ oldId }, 'Old decision deleted for update')
|
|
567
|
-
} catch (error) {
|
|
568
|
-
this.logger.warn({ error, oldId }, 'Failed to delete old decision during update, storing new version anyway')
|
|
569
|
-
}
|
|
570
|
-
// Store new version — fires onDecisionStoredCallbacks (graph rebuild)
|
|
571
|
-
const newId = await this.rememberDecision(project, context, decision, reasoning, options)
|
|
572
|
-
this.logger.debug({ oldId, newId }, 'Decision updated: old deleted, new stored')
|
|
573
|
-
return newId
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Create a memory manager instance
|
|
579
|
-
*/
|
|
580
|
-
export function createMemoryManager(dbPath: string, logger: Logger): MemoryManager {
|
|
581
|
-
return new MemoryManager(dbPath, logger)
|
|
582
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Memory System - Main Module
|
|
3
|
+
* Phase 3: Memory and Embedding System
|
|
4
|
+
*
|
|
5
|
+
* Unified memory system manager that combines all components
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Logger } from 'pino'
|
|
9
|
+
import { MemoryDatabase } from './database'
|
|
10
|
+
import { EmbeddingService } from './embeddings'
|
|
11
|
+
import { MemoryStore } from './store'
|
|
12
|
+
import { SemanticSearch } from './search'
|
|
13
|
+
import { MemoryContextBuilder } from './context-builder'
|
|
14
|
+
import type { MemorySystemStats } from './types'
|
|
15
|
+
import { ChromaManager, DEFAULT_CHROMA_CONFIG, getChromaConfigFromEnv, ChromaMigration, type MigrationOptions } from './chroma'
|
|
16
|
+
|
|
17
|
+
// Re-export all types and classes for external use
|
|
18
|
+
export * from './types'
|
|
19
|
+
export { MemoryDatabase } from './database'
|
|
20
|
+
export { EmbeddingService } from './embeddings'
|
|
21
|
+
export { MemoryStore } from './store'
|
|
22
|
+
export { SemanticSearch } from './search'
|
|
23
|
+
export { MemoryContextBuilder, type ContextOptions } from './context-builder'
|
|
24
|
+
export {
|
|
25
|
+
embeddingToBuffer,
|
|
26
|
+
bufferToEmbedding,
|
|
27
|
+
normalizeEmbedding,
|
|
28
|
+
euclideanDistance,
|
|
29
|
+
cosineSimilarity,
|
|
30
|
+
dotProduct,
|
|
31
|
+
magnitude,
|
|
32
|
+
averageEmbeddings,
|
|
33
|
+
topKSimilar
|
|
34
|
+
} from './embedding-utils'
|
|
35
|
+
|
|
36
|
+
// Phase 12: Advanced Memory Features
|
|
37
|
+
export { PatternRecognizer, type Pattern } from './patterns'
|
|
38
|
+
export { LearningSystem, type Correction, type Preference, type LearningInsights } from './learning'
|
|
39
|
+
export { KnowledgeExtractor, type ExtractedKnowledge, type ExtractionResult } from './knowledge-extractor'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Unified memory system manager
|
|
43
|
+
* Combines database, embeddings, store, search, and context building
|
|
44
|
+
*/
|
|
45
|
+
export class MemoryManager {
|
|
46
|
+
readonly database: MemoryDatabase
|
|
47
|
+
readonly embeddings: EmbeddingService
|
|
48
|
+
readonly contextBuilder: MemoryContextBuilder
|
|
49
|
+
readonly chroma: ChromaManager
|
|
50
|
+
|
|
51
|
+
// Store and search are initialized after database is ready
|
|
52
|
+
private _store: MemoryStore | null = null
|
|
53
|
+
private _search: SemanticSearch | null = null
|
|
54
|
+
|
|
55
|
+
private logger: Logger
|
|
56
|
+
private initialized: boolean = false
|
|
57
|
+
private useChromaDB: boolean = true
|
|
58
|
+
private onDecisionStoredCallbacks: ((input: any) => void)[] = []
|
|
59
|
+
private onDecisionDeletedCallbacks: ((id: string) => void)[] = []
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
dbPath: string,
|
|
63
|
+
logger: Logger,
|
|
64
|
+
useChromaDB: boolean = true,
|
|
65
|
+
chromaConfig?: any,
|
|
66
|
+
customEmbeddings?: EmbeddingService
|
|
67
|
+
) {
|
|
68
|
+
this.logger = logger.child({ component: 'memory-manager' })
|
|
69
|
+
this.useChromaDB = useChromaDB
|
|
70
|
+
|
|
71
|
+
this.database = new MemoryDatabase(dbPath, logger)
|
|
72
|
+
this.embeddings = customEmbeddings || new EmbeddingService(logger)
|
|
73
|
+
this.contextBuilder = new MemoryContextBuilder(logger)
|
|
74
|
+
|
|
75
|
+
const envConfig = getChromaConfigFromEnv()
|
|
76
|
+
const config = { ...DEFAULT_CHROMA_CONFIG, ...envConfig, ...chromaConfig }
|
|
77
|
+
this.chroma = new ChromaManager(logger, config)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Initialize memory system
|
|
82
|
+
* Must be called before using store or search
|
|
83
|
+
*/
|
|
84
|
+
async initialize(): Promise<void> {
|
|
85
|
+
if (this.initialized) {
|
|
86
|
+
this.logger.warn('Memory system already initialized')
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
this.logger.info('Initializing memory system...')
|
|
92
|
+
|
|
93
|
+
await this.database.initialize()
|
|
94
|
+
|
|
95
|
+
await this.embeddings.initialize()
|
|
96
|
+
|
|
97
|
+
const db = this.database.getDb()
|
|
98
|
+
this._store = new MemoryStore(db, this.embeddings, this.logger)
|
|
99
|
+
this._search = new SemanticSearch(db, this.embeddings, this.logger)
|
|
100
|
+
|
|
101
|
+
if (this.useChromaDB) {
|
|
102
|
+
try {
|
|
103
|
+
await this.chroma.initialize()
|
|
104
|
+
this.logger.info('ChromaDB backend initialized successfully')
|
|
105
|
+
} catch (error) {
|
|
106
|
+
this.logger.warn({ error }, 'Failed to initialize ChromaDB, falling back to SQLite backend')
|
|
107
|
+
this.useChromaDB = false
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.initialized = true
|
|
112
|
+
this.logger.info('Memory system initialized successfully')
|
|
113
|
+
} catch (error) {
|
|
114
|
+
this.logger.error({ error }, 'Failed to initialize memory system')
|
|
115
|
+
throw error
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get the memory store (throws if not initialized)
|
|
121
|
+
*/
|
|
122
|
+
get store(): MemoryStore {
|
|
123
|
+
if (!this._store) {
|
|
124
|
+
throw new Error('Memory system not initialized. Call initialize() first.')
|
|
125
|
+
}
|
|
126
|
+
return this._store
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get the semantic search engine (throws if not initialized)
|
|
131
|
+
*/
|
|
132
|
+
get search(): SemanticSearch {
|
|
133
|
+
if (!this._search) {
|
|
134
|
+
throw new Error('Memory system not initialized. Call initialize() first.')
|
|
135
|
+
}
|
|
136
|
+
return this._search
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check if memory system is initialized
|
|
141
|
+
*/
|
|
142
|
+
isInitialized(): boolean {
|
|
143
|
+
return this.initialized
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
close(): void {
|
|
147
|
+
if (this.useChromaDB) {
|
|
148
|
+
this.chroma.close()
|
|
149
|
+
}
|
|
150
|
+
this.database.close()
|
|
151
|
+
this._store = null
|
|
152
|
+
this._search = null
|
|
153
|
+
this.initialized = false
|
|
154
|
+
this.logger.info('Memory system closed')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get system statistics
|
|
159
|
+
*/
|
|
160
|
+
getStats(): MemorySystemStats {
|
|
161
|
+
if (!this.initialized) {
|
|
162
|
+
throw new Error('Memory system not initialized')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
database: this.database.getStats(),
|
|
167
|
+
embeddings: this.embeddings.getCacheStats()
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Health check
|
|
173
|
+
*/
|
|
174
|
+
async healthCheck(): Promise<boolean> {
|
|
175
|
+
if (!this.initialized) {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
return this.database.healthCheck()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Check if ChromaDB backend is enabled and connected
|
|
183
|
+
*/
|
|
184
|
+
isChromaDBEnabled(): boolean {
|
|
185
|
+
return this.useChromaDB
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Add a listener that fires when a decision is stored (from any backend)
|
|
190
|
+
*/
|
|
191
|
+
addDecisionStoredListener(callback: (input: any) => void): void {
|
|
192
|
+
this.onDecisionStoredCallbacks.push(callback)
|
|
193
|
+
// Also wire to ChromaDB listener if available
|
|
194
|
+
if (this.useChromaDB) {
|
|
195
|
+
this.chroma.store.addDecisionStoredListener(callback)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Add a listener that fires when a decision is deleted
|
|
201
|
+
*/
|
|
202
|
+
addDecisionDeletedListener(callback: (id: string) => void): void {
|
|
203
|
+
this.onDecisionDeletedCallbacks.push(callback)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async rememberDecision(
|
|
207
|
+
project: string,
|
|
208
|
+
context: string,
|
|
209
|
+
decision: string,
|
|
210
|
+
reasoning: string,
|
|
211
|
+
options?: { alternatives?: string; tags?: string[] }
|
|
212
|
+
): Promise<string> {
|
|
213
|
+
if (this.useChromaDB) {
|
|
214
|
+
return this.chroma.store.storeDecision({
|
|
215
|
+
project,
|
|
216
|
+
context,
|
|
217
|
+
decision,
|
|
218
|
+
reasoning,
|
|
219
|
+
alternatives: options?.alternatives,
|
|
220
|
+
tags: options?.tags
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
const id = await this.store.storeDecision({
|
|
224
|
+
project,
|
|
225
|
+
context,
|
|
226
|
+
decision,
|
|
227
|
+
reasoning,
|
|
228
|
+
alternatives: options?.alternatives,
|
|
229
|
+
tags: options?.tags
|
|
230
|
+
})
|
|
231
|
+
// Notify listeners (e.g., knowledge graph builder) for SQLite path
|
|
232
|
+
for (const cb of this.onDecisionStoredCallbacks) {
|
|
233
|
+
try {
|
|
234
|
+
cb({ project, context, decision, reasoning, alternatives: options?.alternatives, tags: options?.tags, id })
|
|
235
|
+
} catch {}
|
|
236
|
+
}
|
|
237
|
+
return id
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get raw search results - routes to ChromaDB when enabled
|
|
242
|
+
* Use this for internal operations that need raw results
|
|
243
|
+
*/
|
|
244
|
+
async searchRaw(
|
|
245
|
+
query: string,
|
|
246
|
+
options?: { project?: string; limit?: number; minSimilarity?: number }
|
|
247
|
+
): Promise<any[]> {
|
|
248
|
+
if (this.useChromaDB) {
|
|
249
|
+
const chromaResults = await this.chroma.search.searchDecisions(query, {
|
|
250
|
+
project: options?.project,
|
|
251
|
+
limit: options?.limit || 5,
|
|
252
|
+
minSimilarity: options?.minSimilarity || 0.5
|
|
253
|
+
})
|
|
254
|
+
// Transform ChromaDB results to match MemorySearchResult structure
|
|
255
|
+
// Includes flat `content` field for direct access (Phase 19)
|
|
256
|
+
return chromaResults.map(r => {
|
|
257
|
+
const memoryContent = typeof r.content === 'string' ? r.content : JSON.stringify(r.content)
|
|
258
|
+
const decisionObj = r.metadata.decision ? {
|
|
259
|
+
id: r.id,
|
|
260
|
+
project: r.metadata.project || options?.project || 'unknown',
|
|
261
|
+
context: r.metadata.context || '',
|
|
262
|
+
decision: r.metadata.decision || memoryContent,
|
|
263
|
+
reasoning: r.metadata.reasoning || '',
|
|
264
|
+
alternatives: r.metadata.alternatives_considered || '',
|
|
265
|
+
tags: r.metadata.tags || [],
|
|
266
|
+
outcome: r.metadata.outcome,
|
|
267
|
+
createdAt: r.metadata.created_at ? new Date(r.metadata.created_at) : new Date()
|
|
268
|
+
} : undefined
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
// Flat fields for direct access (Phase 19)
|
|
272
|
+
id: r.id,
|
|
273
|
+
content: decisionObj ? decisionObj.decision : memoryContent,
|
|
274
|
+
// Nested fields for backward compatibility
|
|
275
|
+
memory: {
|
|
276
|
+
id: r.id,
|
|
277
|
+
project: r.metadata.project || options?.project || 'unknown',
|
|
278
|
+
content: memoryContent,
|
|
279
|
+
createdAt: r.metadata.created_at ? new Date(r.metadata.created_at) : new Date(),
|
|
280
|
+
metadata: r.metadata
|
|
281
|
+
},
|
|
282
|
+
similarity: r.similarity,
|
|
283
|
+
decision: decisionObj,
|
|
284
|
+
metadata: r.metadata
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
} else {
|
|
288
|
+
return await this.search.search(query, {
|
|
289
|
+
project: options?.project,
|
|
290
|
+
limit: options?.limit || 5,
|
|
291
|
+
minSimilarity: options?.minSimilarity || 0.5
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async recallSimilar(
|
|
297
|
+
query: string,
|
|
298
|
+
options?: { project?: string; limit?: number; minSimilarity?: number }
|
|
299
|
+
): Promise<string> {
|
|
300
|
+
const results = await this.searchRaw(query, options)
|
|
301
|
+
return this.contextBuilder.buildDecisionContext(results)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async getRecommendations(
|
|
305
|
+
currentContext: string,
|
|
306
|
+
project: string,
|
|
307
|
+
limit: number = 3
|
|
308
|
+
): Promise<string> {
|
|
309
|
+
const results = await this.search.getRecommendations(currentContext, project, limit)
|
|
310
|
+
return this.contextBuilder.buildRecommendationContext(results)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async migrateToChromaDB(options: MigrationOptions = {}): Promise<any> {
|
|
314
|
+
if (!this.useChromaDB) {
|
|
315
|
+
throw new Error('ChromaDB is not enabled')
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const migration = new ChromaMigration(
|
|
319
|
+
this.logger,
|
|
320
|
+
this.database.getDb(),
|
|
321
|
+
this.chroma.store,
|
|
322
|
+
this.chroma.collections
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return migration.migrate(options)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Store a pattern in memory — routes to ChromaDB or SQLite
|
|
330
|
+
*/
|
|
331
|
+
async storePattern(input: {
|
|
332
|
+
project: string
|
|
333
|
+
pattern_type: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
334
|
+
description: string
|
|
335
|
+
example?: string
|
|
336
|
+
confidence: number
|
|
337
|
+
context?: string
|
|
338
|
+
source?: string
|
|
339
|
+
}): Promise<string> {
|
|
340
|
+
if (this.useChromaDB) {
|
|
341
|
+
return this.chroma.store.storePattern(input)
|
|
342
|
+
}
|
|
343
|
+
return this.store.storePattern(input)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Store a correction/lesson learned — routes to ChromaDB or SQLite
|
|
348
|
+
*/
|
|
349
|
+
async storeCorrection(input: {
|
|
350
|
+
project: string
|
|
351
|
+
original: string
|
|
352
|
+
correction: string
|
|
353
|
+
reasoning: string
|
|
354
|
+
context?: string
|
|
355
|
+
confidence: number
|
|
356
|
+
}): Promise<string> {
|
|
357
|
+
if (this.useChromaDB) {
|
|
358
|
+
return this.chroma.store.storeCorrection(input)
|
|
359
|
+
}
|
|
360
|
+
return this.store.storeCorrection(input)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get patterns for a project — routes to ChromaDB or SQLite
|
|
365
|
+
*/
|
|
366
|
+
async getPatterns(
|
|
367
|
+
project?: string,
|
|
368
|
+
options?: {
|
|
369
|
+
pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
370
|
+
limit?: number
|
|
371
|
+
}
|
|
372
|
+
): Promise<any[]> {
|
|
373
|
+
if (this.useChromaDB) {
|
|
374
|
+
if (project) {
|
|
375
|
+
return this.chroma.store.getPatternsByProject(project, options)
|
|
376
|
+
}
|
|
377
|
+
return this.chroma.store.searchPatterns('', { limit: options?.limit || 10 })
|
|
378
|
+
}
|
|
379
|
+
if (project) {
|
|
380
|
+
return this.store.getPatternsByProject(project, options)
|
|
381
|
+
}
|
|
382
|
+
return this.store.searchPatterns('', { limit: options?.limit || 10 })
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Get corrections for a project — routes to ChromaDB or SQLite
|
|
387
|
+
*/
|
|
388
|
+
async getCorrections(
|
|
389
|
+
project?: string,
|
|
390
|
+
options?: { limit?: number }
|
|
391
|
+
): Promise<any[]> {
|
|
392
|
+
if (this.useChromaDB) {
|
|
393
|
+
if (project) {
|
|
394
|
+
return this.chroma.store.getCorrectionsByProject(project, options?.limit || 10)
|
|
395
|
+
}
|
|
396
|
+
return this.chroma.store.searchCorrections('', { limit: options?.limit || 10 })
|
|
397
|
+
}
|
|
398
|
+
if (project) {
|
|
399
|
+
return this.store.getCorrectionsByProject(project, options?.limit || 10)
|
|
400
|
+
}
|
|
401
|
+
return this.store.searchCorrections('', { limit: options?.limit || 10 })
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Fetch all decisions with content — routes to ChromaDB or SQLite
|
|
406
|
+
* Used by analytical tools that need bulk access to decision data
|
|
407
|
+
*/
|
|
408
|
+
async fetchAllDecisions(project?: string): Promise<any[]> {
|
|
409
|
+
if (this.useChromaDB) {
|
|
410
|
+
try {
|
|
411
|
+
const collection = await this.chroma.collections.getDecisions()
|
|
412
|
+
const results = await collection.get({
|
|
413
|
+
where: project ? { project } : undefined
|
|
414
|
+
})
|
|
415
|
+
if (results && results.ids) {
|
|
416
|
+
return results.ids.map((id: string, i: number) => ({
|
|
417
|
+
id,
|
|
418
|
+
content: results.documents?.[i] || '',
|
|
419
|
+
date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
|
|
420
|
+
project: results.metadatas?.[i]?.project || project || 'unknown',
|
|
421
|
+
context: results.metadatas?.[i]?.context || '',
|
|
422
|
+
decision: results.metadatas?.[i]?.decision || results.documents?.[i] || '',
|
|
423
|
+
reasoning: results.metadatas?.[i]?.reasoning || '',
|
|
424
|
+
alternatives: results.metadatas?.[i]?.alternatives_considered || '',
|
|
425
|
+
tags: results.metadatas?.[i]?.tags || []
|
|
426
|
+
}))
|
|
427
|
+
}
|
|
428
|
+
return []
|
|
429
|
+
} catch (error) {
|
|
430
|
+
this.logger.warn({ error }, 'ChromaDB fetchAllDecisions failed, falling back to SQLite')
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return this.store.getAllDecisionsWithContent(project)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Fetch all patterns with content — routes to ChromaDB or SQLite
|
|
438
|
+
*/
|
|
439
|
+
async fetchAllPatterns(project?: string): Promise<any[]> {
|
|
440
|
+
if (this.useChromaDB) {
|
|
441
|
+
try {
|
|
442
|
+
const collection = await this.chroma.collections.getPatterns()
|
|
443
|
+
const results = await collection.get({
|
|
444
|
+
where: project ? { project } : undefined
|
|
445
|
+
})
|
|
446
|
+
if (results && results.ids) {
|
|
447
|
+
return results.ids.map((id: string, i: number) => ({
|
|
448
|
+
id,
|
|
449
|
+
content: results.documents?.[i] || '',
|
|
450
|
+
date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
|
|
451
|
+
project: results.metadatas?.[i]?.project || project || 'unknown',
|
|
452
|
+
pattern_type: results.metadatas?.[i]?.pattern_type || '',
|
|
453
|
+
description: results.metadatas?.[i]?.description || results.documents?.[i] || '',
|
|
454
|
+
example: results.metadatas?.[i]?.example || '',
|
|
455
|
+
confidence: results.metadatas?.[i]?.confidence || 0,
|
|
456
|
+
context: results.metadatas?.[i]?.context || ''
|
|
457
|
+
}))
|
|
458
|
+
}
|
|
459
|
+
return []
|
|
460
|
+
} catch (error) {
|
|
461
|
+
this.logger.warn({ error }, 'ChromaDB fetchAllPatterns failed, falling back to SQLite')
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return this.store.getAllPatternsWithContent(project)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Fetch all corrections with content — routes to ChromaDB or SQLite
|
|
469
|
+
*/
|
|
470
|
+
async fetchAllCorrections(project?: string): Promise<any[]> {
|
|
471
|
+
if (this.useChromaDB) {
|
|
472
|
+
try {
|
|
473
|
+
const collection = await this.chroma.collections.getCorrections()
|
|
474
|
+
const results = await collection.get({
|
|
475
|
+
where: project ? { project } : undefined
|
|
476
|
+
})
|
|
477
|
+
if (results && results.ids) {
|
|
478
|
+
return results.ids.map((id: string, i: number) => ({
|
|
479
|
+
id,
|
|
480
|
+
content: results.documents?.[i] || '',
|
|
481
|
+
date: results.metadatas?.[i]?.created_at || new Date().toISOString(),
|
|
482
|
+
project: results.metadatas?.[i]?.project || project || 'unknown',
|
|
483
|
+
original: results.metadatas?.[i]?.original || '',
|
|
484
|
+
correction: results.metadatas?.[i]?.correction || results.documents?.[i] || '',
|
|
485
|
+
reasoning: results.metadatas?.[i]?.reasoning || '',
|
|
486
|
+
context: results.metadatas?.[i]?.context || '',
|
|
487
|
+
confidence: results.metadatas?.[i]?.confidence || 0
|
|
488
|
+
}))
|
|
489
|
+
}
|
|
490
|
+
return []
|
|
491
|
+
} catch (error) {
|
|
492
|
+
this.logger.warn({ error }, 'ChromaDB fetchAllCorrections failed, falling back to SQLite')
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return this.store.getAllCorrectionsWithContent(project)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Search patterns by query — routes to ChromaDB or SQLite
|
|
500
|
+
*/
|
|
501
|
+
async searchPatterns(
|
|
502
|
+
query: string,
|
|
503
|
+
options?: {
|
|
504
|
+
project?: string
|
|
505
|
+
pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
506
|
+
limit?: number
|
|
507
|
+
minSimilarity?: number
|
|
508
|
+
}
|
|
509
|
+
): Promise<any[]> {
|
|
510
|
+
if (this.useChromaDB) {
|
|
511
|
+
return this.chroma.store.searchPatterns(query, options)
|
|
512
|
+
}
|
|
513
|
+
return this.store.searchPatterns(query, options)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Search corrections by query — routes to ChromaDB or SQLite
|
|
518
|
+
*/
|
|
519
|
+
async searchCorrections(
|
|
520
|
+
query: string,
|
|
521
|
+
options?: {
|
|
522
|
+
project?: string
|
|
523
|
+
limit?: number
|
|
524
|
+
minSimilarity?: number
|
|
525
|
+
}
|
|
526
|
+
): Promise<any[]> {
|
|
527
|
+
if (this.useChromaDB) {
|
|
528
|
+
return this.chroma.store.searchCorrections(query, options)
|
|
529
|
+
}
|
|
530
|
+
return this.store.searchCorrections(query, options)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Delete a decision by ID — routes to ChromaDB or SQLite
|
|
535
|
+
*/
|
|
536
|
+
async deleteDecision(id: string): Promise<void> {
|
|
537
|
+
if (this.useChromaDB) {
|
|
538
|
+
await this.chroma.store.deleteDecision(id)
|
|
539
|
+
} else {
|
|
540
|
+
this.store.deleteMemory(id)
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Notify listeners (e.g., knowledge graph builder) about deletion
|
|
544
|
+
for (const cb of this.onDecisionDeletedCallbacks) {
|
|
545
|
+
try {
|
|
546
|
+
cb(id)
|
|
547
|
+
} catch {}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Update a decision by deleting the old one and storing a new version.
|
|
553
|
+
* Phase 20: Ensures both ChromaDB and knowledge graph are atomically updated.
|
|
554
|
+
*/
|
|
555
|
+
async updateDecision(
|
|
556
|
+
oldId: string,
|
|
557
|
+
project: string,
|
|
558
|
+
context: string,
|
|
559
|
+
decision: string,
|
|
560
|
+
reasoning: string,
|
|
561
|
+
options?: { alternatives?: string; tags?: string[] }
|
|
562
|
+
): Promise<string> {
|
|
563
|
+
// Delete old version — fires onDecisionDeletedCallbacks (graph + episode cleanup)
|
|
564
|
+
try {
|
|
565
|
+
await this.deleteDecision(oldId)
|
|
566
|
+
this.logger.debug({ oldId }, 'Old decision deleted for update')
|
|
567
|
+
} catch (error) {
|
|
568
|
+
this.logger.warn({ error, oldId }, 'Failed to delete old decision during update, storing new version anyway')
|
|
569
|
+
}
|
|
570
|
+
// Store new version — fires onDecisionStoredCallbacks (graph rebuild)
|
|
571
|
+
const newId = await this.rememberDecision(project, context, decision, reasoning, options)
|
|
572
|
+
this.logger.debug({ oldId, newId }, 'Decision updated: old deleted, new stored')
|
|
573
|
+
return newId
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Create a memory manager instance
|
|
579
|
+
*/
|
|
580
|
+
export function createMemoryManager(dbPath: string, logger: Logger): MemoryManager {
|
|
581
|
+
return new MemoryManager(dbPath, logger)
|
|
582
|
+
}
|