claude-brain 0.30.2 → 0.30.3
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 +241 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -29
- package/package.json +7 -3
- 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/scripts/postinstall.mjs +531 -531
- package/src/automation/decision-detector.ts +452 -452
- 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 +210 -205
- package/src/cli/auto-setup.ts +75 -75
- package/src/cli/auto-start.ts +266 -266
- package/src/cli/bin.ts +264 -264
- package/src/cli/commands/autostart.ts +90 -90
- package/src/cli/commands/chroma.ts +578 -577
- package/src/cli/commands/export-training.ts +70 -70
- package/src/cli/commands/export.ts +130 -130
- package/src/cli/commands/git-hook.ts +183 -183
- package/src/cli/commands/hooks.ts +217 -217
- package/src/cli/commands/init.ts +123 -123
- package/src/cli/commands/install-mcp.ts +122 -111
- package/src/cli/commands/models.ts +979 -979
- package/src/cli/commands/pack.ts +200 -200
- package/src/cli/commands/refresh.ts +344 -339
- package/src/cli/commands/reindex.ts +120 -120
- package/src/cli/commands/serve.ts +466 -463
- package/src/cli/commands/start.ts +44 -44
- package/src/cli/commands/status.ts +220 -203
- package/src/cli/commands/uninstall-mcp.ts +45 -41
- package/src/cli/commands/update.ts +130 -124
- package/src/cli/migrate-chroma.ts +106 -106
- 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/code-intelligence/indexer.ts +352 -352
- package/src/code-intelligence/linker.ts +178 -178
- package/src/code-intelligence/parser.ts +484 -484
- package/src/code-intelligence/query.ts +291 -291
- package/src/code-intelligence/schema.ts +83 -83
- package/src/code-intelligence/types.ts +95 -95
- package/src/config/defaults.ts +52 -52
- package/src/config/home.ts +56 -56
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +192 -192
- package/src/config/schema.ts +446 -415
- package/src/config/validator.ts +182 -182
- package/src/context/assembler.ts +407 -400
- package/src/context/index.ts +79 -79
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +122 -121
- package/src/health/index.ts +233 -232
- package/src/hooks/brain-hook.ts +134 -131
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/claude-code-mastery.md +112 -112
- package/src/hooks/context-hook.ts +260 -245
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +211 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +306 -288
- package/src/hooks/interceptor-hook.ts +204 -201
- package/src/hooks/passive-classifier.ts +397 -397
- package/src/hooks/queue.ts +160 -129
- package/src/hooks/session-tracker.ts +312 -312
- package/src/hooks/types.ts +52 -52
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +7 -7
- package/src/intelligence/hf-downloader.ts +222 -222
- package/src/intelligence/hf-manifest.json +78 -78
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/inference-router.ts +762 -762
- package/src/intelligence/model-manager.ts +263 -245
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +213 -207
- package/src/intelligence/prediction/index.ts +7 -7
- package/src/intelligence/prediction/recommender.ts +276 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +243 -247
- package/src/intelligence/reasoning/index.ts +7 -7
- package/src/intelligence/temporal/evolution.ts +193 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +272 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/intelligence/tokenizer.ts +118 -118
- package/src/knowledge/entity-extractor.ts +447 -443
- 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 +166 -166
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +211 -192
- package/src/memory/chroma/collection-manager.ts +92 -92
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +177 -175
- 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 +319 -315
- package/src/memory/chroma/store.ts +755 -747
- package/src/memory/compression.ts +121 -121
- package/src/memory/consolidation/archiver.ts +162 -165
- package/src/memory/consolidation/merger.ts +182 -186
- package/src/memory/consolidation/scorer.ts +136 -136
- package/src/memory/database.ts +9 -0
- package/src/memory/dual-write.ts +145 -0
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +347 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/fts5-search.ts +692 -633
- package/src/memory/index.ts +943 -1060
- package/src/memory/migrations/add-fts5.ts +118 -108
- package/src/memory/patterns.ts +438 -438
- package/src/memory/pruning.ts +60 -60
- package/src/memory/schema.ts +88 -88
- package/src/memory/store.ts +911 -787
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- 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 -297
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +221 -221
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +221 -221
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +165 -165
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +203 -203
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +252 -252
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +189 -188
- package/src/retrieval/reranker/model.ts +99 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +454 -454
- package/src/routing/handlers/exploration-handler.ts +369 -0
- package/src/routing/handlers/index.ts +19 -0
- package/src/routing/handlers/memory-handler.ts +273 -0
- package/src/routing/handlers/mutation-handler.ts +241 -0
- package/src/routing/handlers/recall-handler.ts +642 -0
- package/src/routing/handlers/shared.ts +515 -0
- package/src/routing/handlers/types.ts +48 -0
- package/src/routing/intent-classifier.ts +552 -552
- package/src/routing/response-filter.ts +399 -391
- package/src/routing/router.ts +245 -2193
- package/src/routing/search-engine.ts +521 -514
- package/src/routing/types.ts +104 -94
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/auto-updater.ts +283 -276
- package/src/server/handlers/call-tool.ts +159 -159
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/auto-remember.ts +165 -165
- package/src/server/handlers/tools/brain.ts +86 -86
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/get-code-standards.ts +123 -123
- package/src/server/handlers/tools/get-corrections.ts +152 -152
- package/src/server/handlers/tools/get-patterns.ts +156 -156
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/index.ts +30 -30
- package/src/server/handlers/tools/init-project.ts +756 -756
- package/src/server/handlers/tools/list-projects.ts +126 -126
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +132 -132
- package/src/server/handlers/tools/record-correction.ts +131 -131
- package/src/server/handlers/tools/remember-decision.ts +168 -168
- package/src/server/handlers/tools/schemas.ts +179 -179
- package/src/server/handlers/tools/search-code.ts +122 -122
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/http-api.ts +215 -1229
- package/src/server/mcp-proxy.ts +85 -84
- package/src/server/mcp-server.ts +285 -284
- package/src/server/middleware/auth.ts +39 -0
- package/src/server/middleware/error-handler.ts +37 -0
- package/src/server/middleware/rate-limit.ts +53 -0
- package/src/server/middleware/validate.ts +42 -0
- package/src/server/pid-manager.ts +137 -136
- package/src/server/providers/resources.ts +581 -581
- package/src/server/routes/code.ts +228 -0
- package/src/server/routes/context.ts +26 -0
- package/src/server/routes/health.ts +19 -0
- package/src/server/routes/helpers.ts +100 -0
- package/src/server/routes/hooks.ts +197 -0
- package/src/server/routes/mcp.ts +47 -0
- package/src/server/routes/memory.ts +397 -0
- package/src/server/routes/models.ts +96 -0
- package/src/server/routes/projects.ts +89 -0
- package/src/server/routes/types.ts +21 -0
- package/src/server/schemas/api-schemas.ts +202 -0
- package/src/server/services.ts +720 -720
- package/src/server/utils/memory-indicator.ts +84 -84
- package/src/server/utils/response-formatter.ts +129 -129
- package/src/server/web-viewer.ts +1145 -1115
- package/src/setup/index.ts +38 -38
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.ts +666 -666
- package/src/tools/types.ts +412 -412
- package/src/training/data-store.ts +320 -298
- package/src/training/retrain-pipeline.ts +399 -394
- package/src/utils/error-handler.ts +136 -136
- package/src/utils/index.ts +58 -58
- package/src/utils/kill-port.ts +55 -53
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/safe-path.ts +43 -0
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/index.ts +4 -3
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +4 -1
- package/src/vault/reader.ts +44 -1
- package/src/vault/watcher.ts +24 -1
- package/src/vault/writer.ts +487 -413
- package/skills/persistent-memory/SKILL.md +0 -148
- package/skills/persistent-memory/references/tool-reference.md +0 -90
|
@@ -1,747 +1,755 @@
|
|
|
1
|
-
import { randomUUID } from 'crypto'
|
|
2
|
-
import type { Logger } from 'pino'
|
|
3
|
-
import type { CollectionManager } from './collection-manager'
|
|
4
|
-
import type { MemoryMetadata } from './schemas'
|
|
5
|
-
import type { EmbeddingProvider } from './embeddings'
|
|
6
|
-
import type { SearchResult } from './search'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
this.logger.
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
//
|
|
574
|
-
where = {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
project
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
1
|
+
import { randomUUID } from 'crypto'
|
|
2
|
+
import type { Logger } from 'pino'
|
|
3
|
+
import type { CollectionManager } from './collection-manager'
|
|
4
|
+
import type { MemoryMetadata } from './schemas'
|
|
5
|
+
import type { EmbeddingProvider } from './embeddings'
|
|
6
|
+
import type { SearchResult } from './search'
|
|
7
|
+
|
|
8
|
+
/** ChromaDB query result shape */
|
|
9
|
+
interface ChromaQueryResult {
|
|
10
|
+
ids: string[][]
|
|
11
|
+
documents?: (string | null)[][]
|
|
12
|
+
metadatas?: (Record<string, unknown> | null)[][]
|
|
13
|
+
distances?: number[][]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sanitize metadata for ChromaDB v3.x compatibility.
|
|
18
|
+
* Strips undefined/null values (ChromaDB only accepts string, number, boolean).
|
|
19
|
+
*/
|
|
20
|
+
function sanitizeMetadata(metadata: Record<string, unknown>): Record<string, string | number | boolean> {
|
|
21
|
+
const clean: Record<string, string | number | boolean> = {}
|
|
22
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
23
|
+
if (value === undefined || value === null) continue
|
|
24
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
25
|
+
clean[key] = value
|
|
26
|
+
} else if (Array.isArray(value)) {
|
|
27
|
+
clean[key] = JSON.stringify(value)
|
|
28
|
+
} else {
|
|
29
|
+
clean[key] = String(value)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return clean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface StoreDecisionInput {
|
|
36
|
+
id?: string
|
|
37
|
+
project: string
|
|
38
|
+
context: string
|
|
39
|
+
decision: string
|
|
40
|
+
reasoning: string
|
|
41
|
+
alternatives?: string
|
|
42
|
+
outcome?: string
|
|
43
|
+
tags?: string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface StoreMemoryInput {
|
|
47
|
+
project: string
|
|
48
|
+
content: string
|
|
49
|
+
type?: 'fact' | 'preference' | 'constraint' | 'goal' | 'general'
|
|
50
|
+
source?: string
|
|
51
|
+
confidence?: number
|
|
52
|
+
metadata?: Record<string, unknown>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface StoredDecision {
|
|
56
|
+
id: string
|
|
57
|
+
decision: string
|
|
58
|
+
metadata: Record<string, unknown>
|
|
59
|
+
distance?: number
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface StoredMemory {
|
|
63
|
+
id: string
|
|
64
|
+
content: string
|
|
65
|
+
metadata: Record<string, unknown>
|
|
66
|
+
distance?: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type DecisionStoredCallback = (input: StoreDecisionInput & { id: string }) => void
|
|
70
|
+
|
|
71
|
+
export class ChromaMemoryStore {
|
|
72
|
+
private logger: Logger
|
|
73
|
+
private collections: CollectionManager
|
|
74
|
+
private embeddings?: EmbeddingProvider
|
|
75
|
+
private onDecisionStored: DecisionStoredCallback[] = []
|
|
76
|
+
|
|
77
|
+
constructor(logger: Logger, collections: CollectionManager, embeddings?: EmbeddingProvider) {
|
|
78
|
+
this.logger = logger.child({ component: 'chroma-memory-store' })
|
|
79
|
+
this.collections = collections
|
|
80
|
+
this.embeddings = embeddings
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
addDecisionStoredListener(callback: DecisionStoredCallback): () => void {
|
|
84
|
+
this.onDecisionStored.push(callback)
|
|
85
|
+
return () => {
|
|
86
|
+
const idx = this.onDecisionStored.indexOf(callback)
|
|
87
|
+
if (idx >= 0) this.onDecisionStored.splice(idx, 1)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check for duplicate decisions by searching for similar content
|
|
93
|
+
* Returns existing results if similarity >= threshold
|
|
94
|
+
*/
|
|
95
|
+
async searchForDuplicates(
|
|
96
|
+
decision: string,
|
|
97
|
+
project: string,
|
|
98
|
+
similarityThreshold: number = 0.9
|
|
99
|
+
): Promise<{ id: string; similarity: number }[]> {
|
|
100
|
+
try {
|
|
101
|
+
const collection = await this.collections.getDecisions()
|
|
102
|
+
|
|
103
|
+
let results: ChromaQueryResult
|
|
104
|
+
|
|
105
|
+
if (this.embeddings) {
|
|
106
|
+
const embedding = await this.embeddings.generate(decision)
|
|
107
|
+
results = await collection.query({
|
|
108
|
+
queryEmbeddings: [embedding],
|
|
109
|
+
nResults: 3,
|
|
110
|
+
where: { project: { $eq: project } },
|
|
111
|
+
include: ['documents', 'metadatas', 'distances']
|
|
112
|
+
}) as unknown as ChromaQueryResult
|
|
113
|
+
} else {
|
|
114
|
+
results = await collection.query({
|
|
115
|
+
queryTexts: [decision],
|
|
116
|
+
nResults: 3,
|
|
117
|
+
where: { project: { $eq: project } },
|
|
118
|
+
include: ['documents', 'metadatas', 'distances']
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!results.ids || results.ids[0]?.length === 0) {
|
|
123
|
+
return []
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const duplicates: { id: string; similarity: number }[] = []
|
|
127
|
+
const ids = results.ids[0] || []
|
|
128
|
+
const distances = results.distances?.[0] || []
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < ids.length; i++) {
|
|
131
|
+
const similarity = 1 - (distances[i] || 0)
|
|
132
|
+
if (similarity >= similarityThreshold) {
|
|
133
|
+
duplicates.push({ id: ids[i], similarity })
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return duplicates
|
|
138
|
+
} catch (error) {
|
|
139
|
+
this.logger.warn({ error }, 'Failed to check for duplicates, proceeding with store')
|
|
140
|
+
return []
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async storeDecision(input: StoreDecisionInput): Promise<string> {
|
|
145
|
+
// Check for duplicate decisions (similarity > 0.9)
|
|
146
|
+
const existing = await this.searchForDuplicates(input.decision, input.project, 0.9)
|
|
147
|
+
const firstDuplicate = existing[0]
|
|
148
|
+
if (firstDuplicate) {
|
|
149
|
+
this.logger.info({ existingId: firstDuplicate.id, similarity: firstDuplicate.similarity }, 'Skipping duplicate decision')
|
|
150
|
+
return firstDuplicate.id
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const id = input.id || randomUUID()
|
|
154
|
+
const now = new Date().toISOString()
|
|
155
|
+
|
|
156
|
+
const metadata: Record<string, unknown> = {
|
|
157
|
+
project: input.project,
|
|
158
|
+
context: input.context,
|
|
159
|
+
reasoning: input.reasoning,
|
|
160
|
+
alternatives: input.alternatives || '',
|
|
161
|
+
outcome: input.outcome || '',
|
|
162
|
+
tags: (input.tags || []).join(','),
|
|
163
|
+
created_at: now,
|
|
164
|
+
updated_at: now,
|
|
165
|
+
source: 'manual',
|
|
166
|
+
decision: input.decision // Include the decision text in metadata
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const collection = await this.collections.getDecisions()
|
|
171
|
+
|
|
172
|
+
const embeddings = this.embeddings
|
|
173
|
+
? [await this.embeddings.generate(input.decision)]
|
|
174
|
+
: undefined
|
|
175
|
+
|
|
176
|
+
await collection.add({
|
|
177
|
+
ids: [id],
|
|
178
|
+
documents: [input.decision],
|
|
179
|
+
metadatas: [sanitizeMetadata(metadata as Record<string, unknown>)],
|
|
180
|
+
...(embeddings ? { embeddings } : {})
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// ALSO store in memories collection for unified semantic search
|
|
184
|
+
try {
|
|
185
|
+
const memoriesCollection = await this.collections.getMemories()
|
|
186
|
+
const memoryContent = `Decision: ${input.decision}\nContext: ${input.context}\nReasoning: ${input.reasoning}`
|
|
187
|
+
const memoryMetadata = {
|
|
188
|
+
project: input.project,
|
|
189
|
+
type: 'decision',
|
|
190
|
+
source: 'remember_decision',
|
|
191
|
+
confidence: 1.0,
|
|
192
|
+
created_at: now,
|
|
193
|
+
updated_at: now,
|
|
194
|
+
decision_id: id,
|
|
195
|
+
// Phase 19: Include decision fields so memories collection results
|
|
196
|
+
// can surface decision content without cross-collection lookup
|
|
197
|
+
decision: input.decision,
|
|
198
|
+
reasoning: input.reasoning,
|
|
199
|
+
context: input.context
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
await memoriesCollection.add({
|
|
203
|
+
ids: [id], // Use same ID for cross-reference
|
|
204
|
+
documents: [memoryContent],
|
|
205
|
+
metadatas: [sanitizeMetadata(memoryMetadata)],
|
|
206
|
+
...(embeddings ? { embeddings } : {})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
this.logger.debug({ id }, 'Decision also stored in memories collection')
|
|
210
|
+
} catch (memError) {
|
|
211
|
+
this.logger.warn({ error: memError, id }, 'Failed to store decision in memories collection')
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.logger.info(
|
|
215
|
+
{ id, project: input.project },
|
|
216
|
+
'Decision stored in ChromaDB'
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
// Notify listeners (e.g., knowledge graph builder)
|
|
220
|
+
for (const callback of this.onDecisionStored) {
|
|
221
|
+
try {
|
|
222
|
+
callback({ ...input, id })
|
|
223
|
+
} catch (callbackError) {
|
|
224
|
+
this.logger.warn({ error: callbackError }, 'Decision stored callback failed')
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return id
|
|
229
|
+
|
|
230
|
+
} catch (error) {
|
|
231
|
+
this.logger.error({ error, input }, 'Failed to store decision')
|
|
232
|
+
throw error
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async storePattern(input: {
|
|
237
|
+
id?: string
|
|
238
|
+
project: string
|
|
239
|
+
pattern_type: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
240
|
+
description: string
|
|
241
|
+
example?: string
|
|
242
|
+
confidence: number
|
|
243
|
+
context?: string
|
|
244
|
+
source?: string
|
|
245
|
+
}): Promise<string> {
|
|
246
|
+
const id = input.id || randomUUID()
|
|
247
|
+
const now = new Date().toISOString()
|
|
248
|
+
|
|
249
|
+
const metadata: Record<string, unknown> = {
|
|
250
|
+
project: input.project,
|
|
251
|
+
pattern_type: input.pattern_type,
|
|
252
|
+
description: input.description,
|
|
253
|
+
example: input.example || '',
|
|
254
|
+
confidence: input.confidence,
|
|
255
|
+
context: input.context || '',
|
|
256
|
+
created_at: now,
|
|
257
|
+
updated_at: now,
|
|
258
|
+
source: input.source || 'manual'
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
const collection = await this.collections.getPatterns()
|
|
263
|
+
|
|
264
|
+
const embeddings = this.embeddings
|
|
265
|
+
? [await this.embeddings.generate(input.description)]
|
|
266
|
+
: undefined
|
|
267
|
+
|
|
268
|
+
await collection.add({
|
|
269
|
+
ids: [id],
|
|
270
|
+
documents: [input.description],
|
|
271
|
+
metadatas: [sanitizeMetadata(metadata)],
|
|
272
|
+
...(embeddings ? { embeddings } : {})
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
// ALSO store in memories collection for unified semantic search
|
|
276
|
+
try {
|
|
277
|
+
const memoriesCollection = await this.collections.getMemories()
|
|
278
|
+
const memoryContent = `Pattern (${input.pattern_type}): ${input.description}${input.context ? `\nContext: ${input.context}` : ''}${input.example ? `\nExample: ${input.example}` : ''}`
|
|
279
|
+
const memoryMetadata = {
|
|
280
|
+
project: input.project,
|
|
281
|
+
type: 'pattern',
|
|
282
|
+
source: 'recognize_pattern',
|
|
283
|
+
confidence: input.confidence,
|
|
284
|
+
created_at: now,
|
|
285
|
+
updated_at: now,
|
|
286
|
+
pattern_id: id,
|
|
287
|
+
pattern_type: input.pattern_type
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
await memoriesCollection.add({
|
|
291
|
+
ids: [id],
|
|
292
|
+
documents: [memoryContent],
|
|
293
|
+
metadatas: [sanitizeMetadata(memoryMetadata)],
|
|
294
|
+
...(embeddings ? { embeddings } : {})
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
this.logger.debug({ id }, 'Pattern also stored in memories collection')
|
|
298
|
+
} catch (memError) {
|
|
299
|
+
this.logger.warn({ error: memError, id }, 'Failed to store pattern in memories collection')
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this.logger.info({ id, pattern_type: input.pattern_type }, 'Pattern stored in ChromaDB')
|
|
303
|
+
|
|
304
|
+
return id
|
|
305
|
+
|
|
306
|
+
} catch (error) {
|
|
307
|
+
this.logger.error({ error, input }, 'Failed to store pattern')
|
|
308
|
+
throw error
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async storeCorrection(input: {
|
|
313
|
+
id?: string
|
|
314
|
+
project: string
|
|
315
|
+
original: string
|
|
316
|
+
correction: string
|
|
317
|
+
reasoning: string
|
|
318
|
+
context?: string
|
|
319
|
+
confidence: number
|
|
320
|
+
}): Promise<string> {
|
|
321
|
+
const id = input.id || randomUUID()
|
|
322
|
+
const now = new Date().toISOString()
|
|
323
|
+
|
|
324
|
+
const metadata: Record<string, unknown> = {
|
|
325
|
+
project: input.project,
|
|
326
|
+
original: input.original,
|
|
327
|
+
correction: input.correction,
|
|
328
|
+
reasoning: input.reasoning,
|
|
329
|
+
context: input.context || '',
|
|
330
|
+
confidence: input.confidence,
|
|
331
|
+
created_at: now,
|
|
332
|
+
updated_at: now,
|
|
333
|
+
source: 'manual'
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
const collection = await this.collections.getCorrections()
|
|
338
|
+
|
|
339
|
+
const embeddings = this.embeddings
|
|
340
|
+
? [await this.embeddings.generate(input.correction)]
|
|
341
|
+
: undefined
|
|
342
|
+
|
|
343
|
+
await collection.add({
|
|
344
|
+
ids: [id],
|
|
345
|
+
documents: [input.correction],
|
|
346
|
+
metadatas: [sanitizeMetadata(metadata)],
|
|
347
|
+
...(embeddings ? { embeddings } : {})
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
// ALSO store in memories collection for unified semantic search
|
|
351
|
+
try {
|
|
352
|
+
const memoriesCollection = await this.collections.getMemories()
|
|
353
|
+
const memoryContent = `Correction: ${input.correction}\nOriginal: ${input.original}\nReasoning: ${input.reasoning}${input.context ? `\nContext: ${input.context}` : ''}`
|
|
354
|
+
const memoryMetadata = {
|
|
355
|
+
project: input.project,
|
|
356
|
+
type: 'correction',
|
|
357
|
+
source: 'record_correction',
|
|
358
|
+
confidence: input.confidence,
|
|
359
|
+
created_at: now,
|
|
360
|
+
updated_at: now,
|
|
361
|
+
correction_id: id
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
await memoriesCollection.add({
|
|
365
|
+
ids: [id],
|
|
366
|
+
documents: [memoryContent],
|
|
367
|
+
metadatas: [sanitizeMetadata(memoryMetadata)],
|
|
368
|
+
...(embeddings ? { embeddings } : {})
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
this.logger.debug({ id }, 'Correction also stored in memories collection')
|
|
372
|
+
} catch (memError) {
|
|
373
|
+
this.logger.warn({ error: memError, id }, 'Failed to store correction in memories collection')
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
this.logger.info({ id, project: input.project }, 'Correction stored in ChromaDB')
|
|
377
|
+
|
|
378
|
+
return id
|
|
379
|
+
|
|
380
|
+
} catch (error) {
|
|
381
|
+
this.logger.error({ error, input }, 'Failed to store correction')
|
|
382
|
+
throw error
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async storeMemory(input: StoreMemoryInput): Promise<string> {
|
|
387
|
+
const id = randomUUID()
|
|
388
|
+
const now = new Date().toISOString()
|
|
389
|
+
|
|
390
|
+
const metadata: MemoryMetadata = {
|
|
391
|
+
project: input.project,
|
|
392
|
+
type: input.type || 'general',
|
|
393
|
+
source: input.source || 'unknown',
|
|
394
|
+
confidence: input.confidence || 1.0,
|
|
395
|
+
created_at: now,
|
|
396
|
+
updated_at: now
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
const collection = await this.collections.getMemories()
|
|
401
|
+
|
|
402
|
+
const embeddings = this.embeddings
|
|
403
|
+
? [await this.embeddings.generate(input.content)]
|
|
404
|
+
: undefined
|
|
405
|
+
|
|
406
|
+
await collection.add({
|
|
407
|
+
ids: [id],
|
|
408
|
+
documents: [input.content],
|
|
409
|
+
metadatas: [sanitizeMetadata(metadata as Record<string, unknown>)],
|
|
410
|
+
...(embeddings ? { embeddings } : {})
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
this.logger.info(
|
|
414
|
+
{ id, project: input.project, type: input.type },
|
|
415
|
+
'Memory stored in ChromaDB'
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
return id
|
|
419
|
+
|
|
420
|
+
} catch (error) {
|
|
421
|
+
this.logger.error({ error, input }, 'Failed to store memory')
|
|
422
|
+
throw error
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async upsertDecision(id: string, input: StoreDecisionInput): Promise<void> {
|
|
427
|
+
const now = new Date().toISOString()
|
|
428
|
+
|
|
429
|
+
const metadata: Record<string, unknown> = {
|
|
430
|
+
project: input.project,
|
|
431
|
+
context: input.context,
|
|
432
|
+
reasoning: input.reasoning,
|
|
433
|
+
alternatives: input.alternatives || '',
|
|
434
|
+
outcome: input.outcome || '',
|
|
435
|
+
tags: (input.tags || []).join(','),
|
|
436
|
+
created_at: now,
|
|
437
|
+
updated_at: now,
|
|
438
|
+
source: 'manual'
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
try {
|
|
442
|
+
const collection = await this.collections.getDecisions()
|
|
443
|
+
|
|
444
|
+
const embeddings = this.embeddings
|
|
445
|
+
? [await this.embeddings.generate(input.decision)]
|
|
446
|
+
: undefined
|
|
447
|
+
|
|
448
|
+
await collection.upsert({
|
|
449
|
+
ids: [id],
|
|
450
|
+
documents: [input.decision],
|
|
451
|
+
metadatas: [sanitizeMetadata(metadata as Record<string, unknown>)],
|
|
452
|
+
...(embeddings ? { embeddings } : {})
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
this.logger.info({ id }, 'Decision upserted')
|
|
456
|
+
|
|
457
|
+
} catch (error) {
|
|
458
|
+
this.logger.error({ error, id, errorMessage: error instanceof Error ? error.message : String(error) }, 'Failed to upsert decision')
|
|
459
|
+
throw error
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async getDecision(id: string): Promise<StoredDecision | null> {
|
|
464
|
+
try {
|
|
465
|
+
const collection = await this.collections.getDecisions()
|
|
466
|
+
|
|
467
|
+
const result = await collection.get({
|
|
468
|
+
ids: [id],
|
|
469
|
+
include: ['documents', 'metadatas']
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
if (result.ids.length === 0) {
|
|
473
|
+
return null
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
id: result.ids[0]!,
|
|
478
|
+
decision: result.documents![0] as string,
|
|
479
|
+
metadata: result.metadatas![0] as Record<string, unknown>
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
} catch (error) {
|
|
483
|
+
this.logger.error({ error, id }, 'Failed to get decision')
|
|
484
|
+
throw error
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
async getDecisionsByProject(project: string): Promise<StoredDecision[]> {
|
|
489
|
+
try {
|
|
490
|
+
const collection = await this.collections.getDecisions()
|
|
491
|
+
|
|
492
|
+
const result = await collection.get({
|
|
493
|
+
where: { project },
|
|
494
|
+
include: ['documents', 'metadatas']
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
return result.ids.map((id, i) => ({
|
|
498
|
+
id,
|
|
499
|
+
decision: result.documents![i] as string,
|
|
500
|
+
metadata: result.metadatas![i] as Record<string, unknown>
|
|
501
|
+
}))
|
|
502
|
+
|
|
503
|
+
} catch (error) {
|
|
504
|
+
this.logger.error({ error, project }, 'Failed to get decisions by project')
|
|
505
|
+
throw error
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
async deleteDecision(id: string): Promise<void> {
|
|
510
|
+
try {
|
|
511
|
+
// Delete from decisions collection
|
|
512
|
+
const collection = await this.collections.getDecisions()
|
|
513
|
+
await collection.delete({ ids: [id] })
|
|
514
|
+
|
|
515
|
+
// Verify deletion succeeded
|
|
516
|
+
try {
|
|
517
|
+
const verify = await collection.get({ ids: [id] })
|
|
518
|
+
if (verify.ids.length > 0) {
|
|
519
|
+
this.logger.warn({ id }, 'Decision still exists after delete — retrying')
|
|
520
|
+
await collection.delete({ ids: [id] })
|
|
521
|
+
}
|
|
522
|
+
} catch {
|
|
523
|
+
// Verification query failed, assume delete succeeded
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// ALSO delete from memories collection (dual storage uses same ID)
|
|
527
|
+
try {
|
|
528
|
+
const memoriesCollection = await this.collections.getMemories()
|
|
529
|
+
await memoriesCollection.delete({ ids: [id] })
|
|
530
|
+
this.logger.debug({ id }, 'Decision also deleted from memories collection')
|
|
531
|
+
} catch {
|
|
532
|
+
// Memories collection entry may not exist, that's ok
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
this.logger.info({ id }, 'Decision deleted from all collections')
|
|
536
|
+
|
|
537
|
+
} catch (error) {
|
|
538
|
+
this.logger.error({ error, id }, 'Failed to delete decision')
|
|
539
|
+
throw error
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
async getAllDecisions(): Promise<StoredDecision[]> {
|
|
544
|
+
try {
|
|
545
|
+
const collection = await this.collections.getDecisions()
|
|
546
|
+
|
|
547
|
+
const result = await collection.get({
|
|
548
|
+
include: ['documents', 'metadatas']
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
return result.ids.map((id, i) => ({
|
|
552
|
+
id,
|
|
553
|
+
decision: result.documents![i] as string,
|
|
554
|
+
metadata: result.metadatas![i] as Record<string, unknown>
|
|
555
|
+
}))
|
|
556
|
+
|
|
557
|
+
} catch (error) {
|
|
558
|
+
this.logger.error({ error }, 'Failed to get all decisions')
|
|
559
|
+
throw error
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
async getPatternsByProject(project: string, options?: {
|
|
564
|
+
pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
565
|
+
limit?: number
|
|
566
|
+
}): Promise<{ id: string; description: string; metadata: Record<string, unknown> }[]> {
|
|
567
|
+
try {
|
|
568
|
+
const collection = await this.collections.getPatterns()
|
|
569
|
+
|
|
570
|
+
// Build where clause - handle single vs multiple conditions
|
|
571
|
+
let where: Record<string, unknown>
|
|
572
|
+
if (options?.pattern_type) {
|
|
573
|
+
// Multiple conditions: use $and operator
|
|
574
|
+
where = {
|
|
575
|
+
$and: [
|
|
576
|
+
{ project: { $eq: project } },
|
|
577
|
+
{ pattern_type: { $eq: options.pattern_type } }
|
|
578
|
+
]
|
|
579
|
+
}
|
|
580
|
+
} else {
|
|
581
|
+
// Single condition
|
|
582
|
+
where = { project: { $eq: project } }
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const result = await collection.get({
|
|
586
|
+
where,
|
|
587
|
+
include: ['documents', 'metadatas'],
|
|
588
|
+
...(options?.limit ? { limit: options.limit } : {})
|
|
589
|
+
})
|
|
590
|
+
|
|
591
|
+
return result.ids.map((id, i) => ({
|
|
592
|
+
id,
|
|
593
|
+
description: result.documents![i] as string,
|
|
594
|
+
metadata: result.metadatas![i] as Record<string, unknown>
|
|
595
|
+
}))
|
|
596
|
+
|
|
597
|
+
} catch (error) {
|
|
598
|
+
this.logger.error({ error, project }, 'Failed to get patterns by project')
|
|
599
|
+
throw error
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async getCorrectionsByProject(project: string, limit: number = 10): Promise<{ id: string; correction: string; metadata: Record<string, unknown> }[]> {
|
|
604
|
+
try {
|
|
605
|
+
const collection = await this.collections.getCorrections()
|
|
606
|
+
|
|
607
|
+
const result = await collection.get({
|
|
608
|
+
where: { project },
|
|
609
|
+
include: ['documents', 'metadatas'],
|
|
610
|
+
limit
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
return result.ids.map((id, i) => ({
|
|
614
|
+
id,
|
|
615
|
+
correction: result.documents![i] as string,
|
|
616
|
+
metadata: result.metadatas![i] as Record<string, unknown>
|
|
617
|
+
}))
|
|
618
|
+
|
|
619
|
+
} catch (error) {
|
|
620
|
+
this.logger.error({ error, project }, 'Failed to get corrections by project')
|
|
621
|
+
throw error
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
async searchPatterns(
|
|
626
|
+
query: string,
|
|
627
|
+
options: {
|
|
628
|
+
project?: string
|
|
629
|
+
pattern_type?: 'solution' | 'anti-pattern' | 'best-practice' | 'common-issue'
|
|
630
|
+
limit?: number
|
|
631
|
+
minSimilarity?: number
|
|
632
|
+
} = {}
|
|
633
|
+
): Promise<SearchResult[]> {
|
|
634
|
+
const {
|
|
635
|
+
project,
|
|
636
|
+
pattern_type,
|
|
637
|
+
limit = 10,
|
|
638
|
+
minSimilarity = 0.5
|
|
639
|
+
} = options
|
|
640
|
+
|
|
641
|
+
try {
|
|
642
|
+
const collection = await this.collections.getPatterns()
|
|
643
|
+
|
|
644
|
+
const where: Record<string, unknown> = {}
|
|
645
|
+
if (project) {
|
|
646
|
+
where.project = project
|
|
647
|
+
}
|
|
648
|
+
if (pattern_type) {
|
|
649
|
+
where.pattern_type = pattern_type
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
let results: ChromaQueryResult
|
|
653
|
+
|
|
654
|
+
if (this.embeddings) {
|
|
655
|
+
const embedding = await this.embeddings.generate(query)
|
|
656
|
+
results = await collection.query({
|
|
657
|
+
queryEmbeddings: [embedding],
|
|
658
|
+
nResults: limit,
|
|
659
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
660
|
+
include: ['documents', 'metadatas', 'distances']
|
|
661
|
+
})
|
|
662
|
+
} else {
|
|
663
|
+
results = await collection.query({
|
|
664
|
+
queryTexts: [query],
|
|
665
|
+
nResults: limit,
|
|
666
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
667
|
+
include: ['documents', 'metadatas', 'distances']
|
|
668
|
+
})
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return this.processResults(results, minSimilarity)
|
|
672
|
+
|
|
673
|
+
} catch (error) {
|
|
674
|
+
this.logger.error({ error, query }, 'Pattern search failed')
|
|
675
|
+
throw error
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
async searchCorrections(
|
|
680
|
+
query: string,
|
|
681
|
+
options: {
|
|
682
|
+
project?: string
|
|
683
|
+
limit?: number
|
|
684
|
+
minSimilarity?: number
|
|
685
|
+
} = {}
|
|
686
|
+
): Promise<SearchResult[]> {
|
|
687
|
+
const {
|
|
688
|
+
project,
|
|
689
|
+
limit = 10,
|
|
690
|
+
minSimilarity = 0.5
|
|
691
|
+
} = options
|
|
692
|
+
|
|
693
|
+
try {
|
|
694
|
+
const collection = await this.collections.getCorrections()
|
|
695
|
+
|
|
696
|
+
const where: Record<string, unknown> = project ? { project } : {}
|
|
697
|
+
|
|
698
|
+
let results: ChromaQueryResult
|
|
699
|
+
|
|
700
|
+
if (this.embeddings) {
|
|
701
|
+
const embedding = await this.embeddings.generate(query)
|
|
702
|
+
results = await collection.query({
|
|
703
|
+
queryEmbeddings: [embedding],
|
|
704
|
+
nResults: limit,
|
|
705
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
706
|
+
include: ['documents', 'metadatas', 'distances']
|
|
707
|
+
})
|
|
708
|
+
} else {
|
|
709
|
+
results = await collection.query({
|
|
710
|
+
queryTexts: [query],
|
|
711
|
+
nResults: limit,
|
|
712
|
+
where: Object.keys(where).length > 0 ? where : undefined,
|
|
713
|
+
include: ['documents', 'metadatas', 'distances']
|
|
714
|
+
})
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return this.processResults(results, minSimilarity)
|
|
718
|
+
|
|
719
|
+
} catch (error) {
|
|
720
|
+
this.logger.error({ error, query }, 'Correction search failed')
|
|
721
|
+
throw error
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
private processResults(
|
|
726
|
+
results: ChromaQueryResult,
|
|
727
|
+
minSimilarity: number
|
|
728
|
+
): SearchResult[] {
|
|
729
|
+
if (!results.ids || results.ids.length === 0) {
|
|
730
|
+
return []
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const processed: SearchResult[] = []
|
|
734
|
+
|
|
735
|
+
const ids = results.ids[0] || []
|
|
736
|
+
const documents = results.documents?.[0] || []
|
|
737
|
+
const metadatas = results.metadatas?.[0] || []
|
|
738
|
+
const distances = results.distances?.[0] || []
|
|
739
|
+
|
|
740
|
+
for (let i = 0; i < ids.length; i++) {
|
|
741
|
+
const similarity = 1 - (distances[i] || 0)
|
|
742
|
+
|
|
743
|
+
if (similarity >= minSimilarity) {
|
|
744
|
+
processed.push({
|
|
745
|
+
id: ids[i],
|
|
746
|
+
content: documents[i],
|
|
747
|
+
metadata: metadatas[i],
|
|
748
|
+
similarity
|
|
749
|
+
})
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return processed.sort((a, b) => b.similarity - a.similarity)
|
|
754
|
+
}
|
|
755
|
+
}
|