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,121 +1,121 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Observation Compressor — Phase 30
|
|
3
|
-
* Optional LLM-based compression for long observations.
|
|
4
|
-
* Uses Anthropic API directly via fetch (no Agent SDK dependency).
|
|
5
|
-
* Graceful fallback: if compression fails, store original text.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { Logger } from 'pino'
|
|
9
|
-
|
|
10
|
-
export interface CompressionConfig {
|
|
11
|
-
enabled: boolean
|
|
12
|
-
provider: 'claude' | 'ollama'
|
|
13
|
-
model: string
|
|
14
|
-
apiKey?: string
|
|
15
|
-
minContentLength: number
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface CompressedObservation {
|
|
19
|
-
summary: string
|
|
20
|
-
original?: string
|
|
21
|
-
compressed: boolean
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class ObservationCompressor {
|
|
25
|
-
private config: CompressionConfig
|
|
26
|
-
private logger: Logger
|
|
27
|
-
|
|
28
|
-
constructor(config: CompressionConfig, logger: Logger) {
|
|
29
|
-
this.config = config
|
|
30
|
-
this.logger = logger.child({ component: 'compressor' })
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async compress(content: string, category: string): Promise<CompressedObservation> {
|
|
34
|
-
if (!this.config.enabled || content.length < this.config.minContentLength) {
|
|
35
|
-
return { summary: content, compressed: false }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const prompt = `Compress this ${category} observation into a concise summary (2-3 sentences max).
|
|
39
|
-
Preserve: the core decision/insight, reasoning, and any specific technologies mentioned.
|
|
40
|
-
Drop: filler words, repetition, context that's obvious from the category.
|
|
41
|
-
|
|
42
|
-
Observation: ${content}`
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
const startTime = Date.now()
|
|
46
|
-
const response = await this.callLLM(prompt)
|
|
47
|
-
const result: CompressedObservation = { summary: response, original: content, compressed: true }
|
|
48
|
-
|
|
49
|
-
// SLM Phase 1A: Log compression input/output pair as gold training data
|
|
50
|
-
try {
|
|
51
|
-
const { logTrainingData } = require('@/training/data-store')
|
|
52
|
-
logTrainingData({
|
|
53
|
-
task: 'compress' as const,
|
|
54
|
-
input: content,
|
|
55
|
-
output: JSON.stringify({ summary: response }),
|
|
56
|
-
metadata: JSON.stringify({ category, elapsed_ms: Date.now() - startTime, provider: this.config.provider }),
|
|
57
|
-
})
|
|
58
|
-
} catch {
|
|
59
|
-
// Non-critical
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return result
|
|
63
|
-
} catch (error) {
|
|
64
|
-
this.logger.warn({ error }, 'LLM compression failed, storing original')
|
|
65
|
-
return { summary: content, compressed: false }
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private async callLLM(prompt: string): Promise<string> {
|
|
70
|
-
const apiKey = this.config.apiKey || process.env.ANTHROPIC_API_KEY
|
|
71
|
-
if (!apiKey) {
|
|
72
|
-
throw new Error('No API key configured for compression')
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (this.config.provider === 'ollama') {
|
|
76
|
-
return this.callOllama(prompt)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
80
|
-
method: 'POST',
|
|
81
|
-
headers: {
|
|
82
|
-
'Content-Type': 'application/json',
|
|
83
|
-
'x-api-key': apiKey,
|
|
84
|
-
'anthropic-version': '2023-06-01'
|
|
85
|
-
},
|
|
86
|
-
body: JSON.stringify({
|
|
87
|
-
model: this.config.model || 'claude-haiku-4-5-20251001',
|
|
88
|
-
max_tokens: 200,
|
|
89
|
-
messages: [{ role: 'user', content: prompt }]
|
|
90
|
-
}),
|
|
91
|
-
signal: AbortSignal.timeout(10000)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
if (!res.ok) {
|
|
95
|
-
throw new Error(`Anthropic API error: ${res.status} ${res.statusText}`)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const data = await res.json() as { content: Array<{ text: string }> }
|
|
99
|
-
return data.content[0].text
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private async callOllama(prompt: string): Promise<string> {
|
|
103
|
-
const res = await fetch('http://localhost:11434/api/generate', {
|
|
104
|
-
method: 'POST',
|
|
105
|
-
headers: { 'Content-Type': 'application/json' },
|
|
106
|
-
body: JSON.stringify({
|
|
107
|
-
model: this.config.model || 'llama3',
|
|
108
|
-
prompt,
|
|
109
|
-
stream: false
|
|
110
|
-
}),
|
|
111
|
-
signal: AbortSignal.timeout(10000)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
if (!res.ok) {
|
|
115
|
-
throw new Error(`Ollama API error: ${res.status} ${res.statusText}`)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const data = await res.json() as { response: string }
|
|
119
|
-
return data.response
|
|
120
|
-
}
|
|
121
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Observation Compressor — Phase 30
|
|
3
|
+
* Optional LLM-based compression for long observations.
|
|
4
|
+
* Uses Anthropic API directly via fetch (no Agent SDK dependency).
|
|
5
|
+
* Graceful fallback: if compression fails, store original text.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Logger } from 'pino'
|
|
9
|
+
|
|
10
|
+
export interface CompressionConfig {
|
|
11
|
+
enabled: boolean
|
|
12
|
+
provider: 'claude' | 'ollama'
|
|
13
|
+
model: string
|
|
14
|
+
apiKey?: string
|
|
15
|
+
minContentLength: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CompressedObservation {
|
|
19
|
+
summary: string
|
|
20
|
+
original?: string
|
|
21
|
+
compressed: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class ObservationCompressor {
|
|
25
|
+
private config: CompressionConfig
|
|
26
|
+
private logger: Logger
|
|
27
|
+
|
|
28
|
+
constructor(config: CompressionConfig, logger: Logger) {
|
|
29
|
+
this.config = config
|
|
30
|
+
this.logger = logger.child({ component: 'compressor' })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async compress(content: string, category: string): Promise<CompressedObservation> {
|
|
34
|
+
if (!this.config.enabled || content.length < this.config.minContentLength) {
|
|
35
|
+
return { summary: content, compressed: false }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const prompt = `Compress this ${category} observation into a concise summary (2-3 sentences max).
|
|
39
|
+
Preserve: the core decision/insight, reasoning, and any specific technologies mentioned.
|
|
40
|
+
Drop: filler words, repetition, context that's obvious from the category.
|
|
41
|
+
|
|
42
|
+
Observation: ${content}`
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const startTime = Date.now()
|
|
46
|
+
const response = await this.callLLM(prompt)
|
|
47
|
+
const result: CompressedObservation = { summary: response, original: content, compressed: true }
|
|
48
|
+
|
|
49
|
+
// SLM Phase 1A: Log compression input/output pair as gold training data
|
|
50
|
+
try {
|
|
51
|
+
const { logTrainingData } = require('@/training/data-store')
|
|
52
|
+
logTrainingData({
|
|
53
|
+
task: 'compress' as const,
|
|
54
|
+
input: content,
|
|
55
|
+
output: JSON.stringify({ summary: response }),
|
|
56
|
+
metadata: JSON.stringify({ category, elapsed_ms: Date.now() - startTime, provider: this.config.provider }),
|
|
57
|
+
})
|
|
58
|
+
} catch {
|
|
59
|
+
// Non-critical
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return result
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.logger.warn({ error }, 'LLM compression failed, storing original')
|
|
65
|
+
return { summary: content, compressed: false }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async callLLM(prompt: string): Promise<string> {
|
|
70
|
+
const apiKey = this.config.apiKey || process.env.ANTHROPIC_API_KEY
|
|
71
|
+
if (!apiKey) {
|
|
72
|
+
throw new Error('No API key configured for compression')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this.config.provider === 'ollama') {
|
|
76
|
+
return this.callOllama(prompt)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
'x-api-key': apiKey,
|
|
84
|
+
'anthropic-version': '2023-06-01'
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
model: this.config.model || 'claude-haiku-4-5-20251001',
|
|
88
|
+
max_tokens: 200,
|
|
89
|
+
messages: [{ role: 'user', content: prompt }]
|
|
90
|
+
}),
|
|
91
|
+
signal: AbortSignal.timeout(10000)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
if (!res.ok) {
|
|
95
|
+
throw new Error(`Anthropic API error: ${res.status} ${res.statusText}`)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const data = await res.json() as { content: Array<{ text: string }> }
|
|
99
|
+
return data.content[0].text
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private async callOllama(prompt: string): Promise<string> {
|
|
103
|
+
const res = await fetch('http://localhost:11434/api/generate', {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Content-Type': 'application/json' },
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
model: this.config.model || 'llama3',
|
|
108
|
+
prompt,
|
|
109
|
+
stream: false
|
|
110
|
+
}),
|
|
111
|
+
signal: AbortSignal.timeout(10000)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
throw new Error(`Ollama API error: ${res.status} ${res.statusText}`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const data = await res.json() as { response: string }
|
|
119
|
+
return data.response
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -1,165 +1,162 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory Archiver
|
|
3
|
-
* Moves low-importance memories to archive collection
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
9
|
-
import type {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
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
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
delete
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Memory Archiver
|
|
3
|
+
* Moves low-importance memories to archive collection
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Metadata } from 'chromadb'
|
|
7
|
+
import type { Logger } from 'pino'
|
|
8
|
+
import type { CollectionManager } from '../chroma/collection-manager'
|
|
9
|
+
import type { EmbeddingProvider } from '../chroma/embeddings'
|
|
10
|
+
import type { ImportanceScorer } from './scorer'
|
|
11
|
+
|
|
12
|
+
export interface ArchiveResult {
|
|
13
|
+
archivedCount: number
|
|
14
|
+
archivedIds: string[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class MemoryArchiver {
|
|
18
|
+
private logger: Logger
|
|
19
|
+
private collections: CollectionManager
|
|
20
|
+
private embeddings?: EmbeddingProvider
|
|
21
|
+
private scorer: ImportanceScorer
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
logger: Logger,
|
|
25
|
+
collections: CollectionManager,
|
|
26
|
+
scorer: ImportanceScorer,
|
|
27
|
+
embeddings?: EmbeddingProvider
|
|
28
|
+
) {
|
|
29
|
+
this.logger = logger.child({ component: 'memory-archiver' })
|
|
30
|
+
this.collections = collections
|
|
31
|
+
this.scorer = scorer
|
|
32
|
+
this.embeddings = embeddings
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async archiveLowImportance(threshold: number = 0.2): Promise<ArchiveResult> {
|
|
36
|
+
const archivedIds: string[] = []
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const decisionsCollection = await this.collections.getDecisions()
|
|
40
|
+
const archiveCollection = await this.collections.getArchivedMemories()
|
|
41
|
+
|
|
42
|
+
const results = await decisionsCollection.get({
|
|
43
|
+
include: ['documents', 'metadatas', 'embeddings']
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < results.ids.length; i++) {
|
|
47
|
+
const id = results.ids[i] ?? ''
|
|
48
|
+
if (!id) continue
|
|
49
|
+
const metadata = results.metadatas?.[i] as Metadata
|
|
50
|
+
|
|
51
|
+
const score = this.scorer.score({
|
|
52
|
+
id,
|
|
53
|
+
created_at: (metadata?.created_at as string) || new Date().toISOString(),
|
|
54
|
+
recall_count: (metadata?.recall_count as number) || 0,
|
|
55
|
+
average_rating: metadata?.average_rating as number | undefined,
|
|
56
|
+
max_similarity: metadata?.max_similarity as number | undefined
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
if (score.total < threshold) {
|
|
60
|
+
// Move to archive
|
|
61
|
+
try {
|
|
62
|
+
await archiveCollection.add({
|
|
63
|
+
ids: [id],
|
|
64
|
+
documents: [results.documents?.[i] as string],
|
|
65
|
+
metadatas: [{
|
|
66
|
+
...metadata,
|
|
67
|
+
archived_at: new Date().toISOString(),
|
|
68
|
+
importance_score: score.total
|
|
69
|
+
}],
|
|
70
|
+
...(results.embeddings?.[i] ? { embeddings: [results.embeddings[i] as number[]] } : {})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
await decisionsCollection.delete({ ids: [id] })
|
|
74
|
+
archivedIds.push(id)
|
|
75
|
+
} catch (moveError) {
|
|
76
|
+
this.logger.warn({ error: moveError, id }, 'Failed to archive individual memory')
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.logger.info({
|
|
82
|
+
archivedCount: archivedIds.length,
|
|
83
|
+
totalChecked: results.ids.length,
|
|
84
|
+
threshold
|
|
85
|
+
}, 'Archive sweep complete')
|
|
86
|
+
|
|
87
|
+
} catch (error) {
|
|
88
|
+
this.logger.error({ error }, 'Failed to archive low-importance memories')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { archivedCount: archivedIds.length, archivedIds }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async restore(memoryId: string): Promise<boolean> {
|
|
95
|
+
try {
|
|
96
|
+
const archiveCollection = await this.collections.getArchivedMemories()
|
|
97
|
+
const decisionsCollection = await this.collections.getDecisions()
|
|
98
|
+
|
|
99
|
+
const result = await archiveCollection.get({
|
|
100
|
+
ids: [memoryId],
|
|
101
|
+
include: ['documents', 'metadatas', 'embeddings']
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
if (result.ids.length === 0) {
|
|
105
|
+
this.logger.warn({ memoryId }, 'Memory not found in archive')
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const rawMetadata = { ...(result.metadatas?.[0] as Metadata) }
|
|
110
|
+
delete rawMetadata.archived_at
|
|
111
|
+
delete rawMetadata.importance_score
|
|
112
|
+
rawMetadata.updated_at = new Date().toISOString()
|
|
113
|
+
rawMetadata.restored_from_archive = true
|
|
114
|
+
const metadata = rawMetadata as Metadata
|
|
115
|
+
|
|
116
|
+
await decisionsCollection.add({
|
|
117
|
+
ids: [memoryId],
|
|
118
|
+
documents: [result.documents?.[0] as string],
|
|
119
|
+
metadatas: [metadata],
|
|
120
|
+
...(result.embeddings?.[0] ? { embeddings: [result.embeddings[0] as number[]] } : {})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
await archiveCollection.delete({ ids: [memoryId] })
|
|
124
|
+
|
|
125
|
+
this.logger.info({ memoryId }, 'Memory restored from archive')
|
|
126
|
+
return true
|
|
127
|
+
} catch (error) {
|
|
128
|
+
this.logger.error({ error, memoryId }, 'Failed to restore memory')
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async searchArchive(query: string, limit: number = 10): Promise<Record<string, unknown>[]> {
|
|
134
|
+
try {
|
|
135
|
+
const archiveCollection = await this.collections.getArchivedMemories()
|
|
136
|
+
|
|
137
|
+
const results = this.embeddings
|
|
138
|
+
? await archiveCollection.query({
|
|
139
|
+
queryEmbeddings: [await this.embeddings.generate(query)],
|
|
140
|
+
nResults: limit,
|
|
141
|
+
include: ['documents', 'metadatas', 'distances']
|
|
142
|
+
})
|
|
143
|
+
: await archiveCollection.query({
|
|
144
|
+
queryTexts: [query],
|
|
145
|
+
nResults: limit,
|
|
146
|
+
include: ['documents', 'metadatas', 'distances']
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
if (!results.ids?.[0]) return []
|
|
150
|
+
|
|
151
|
+
return results.ids[0].map((id: string, i: number) => ({
|
|
152
|
+
id,
|
|
153
|
+
content: results.documents?.[0]?.[i],
|
|
154
|
+
metadata: results.metadatas?.[0]?.[i],
|
|
155
|
+
similarity: 1 - (results.distances?.[0]?.[i] || 0)
|
|
156
|
+
}))
|
|
157
|
+
} catch (error) {
|
|
158
|
+
this.logger.error({ error, query }, 'Failed to search archive')
|
|
159
|
+
return []
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|