superlocalmemory 2.8.2 → 2.8.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 +7 -5
- package/api_server.py +5 -0
- package/bin/slm.bat +3 -3
- package/docs/SECURITY-QUICK-REFERENCE.md +214 -0
- package/install.ps1 +11 -11
- package/mcp_server.py +3 -3
- package/package.json +2 -2
- package/requirements-core.txt +16 -18
- package/requirements-learning.txt +8 -8
- package/requirements.txt +9 -7
- package/scripts/prepack.js +33 -0
- package/scripts/verify-v27.ps1 +301 -0
- package/src/agent_registry.py +32 -28
- package/src/auto_backup.py +12 -6
- package/src/cache_manager.py +2 -2
- package/src/compression/__init__.py +25 -0
- package/src/compression/cli.py +150 -0
- package/src/compression/cold_storage.py +217 -0
- package/src/compression/config.py +72 -0
- package/src/compression/orchestrator.py +133 -0
- package/src/compression/tier2_compressor.py +228 -0
- package/src/compression/tier3_compressor.py +153 -0
- package/src/compression/tier_classifier.py +148 -0
- package/src/db_connection_manager.py +5 -5
- package/src/event_bus.py +24 -22
- package/src/hnsw_index.py +3 -3
- package/src/learning/__init__.py +5 -4
- package/src/learning/adaptive_ranker.py +14 -265
- package/src/learning/bootstrap/__init__.py +69 -0
- package/src/learning/bootstrap/constants.py +93 -0
- package/src/learning/bootstrap/db_queries.py +316 -0
- package/src/learning/bootstrap/sampling.py +82 -0
- package/src/learning/bootstrap/text_utils.py +71 -0
- package/src/learning/cross_project_aggregator.py +58 -57
- package/src/learning/db/__init__.py +40 -0
- package/src/learning/db/constants.py +44 -0
- package/src/learning/db/schema.py +279 -0
- package/src/learning/learning_db.py +15 -234
- package/src/learning/ranking/__init__.py +33 -0
- package/src/learning/ranking/constants.py +84 -0
- package/src/learning/ranking/helpers.py +278 -0
- package/src/learning/source_quality_scorer.py +66 -65
- package/src/learning/synthetic_bootstrap.py +28 -310
- package/src/memory/__init__.py +36 -0
- package/src/memory/cli.py +205 -0
- package/src/memory/constants.py +39 -0
- package/src/memory/helpers.py +28 -0
- package/src/memory/schema.py +166 -0
- package/src/memory-profiles.py +94 -86
- package/src/memory-reset.py +187 -185
- package/src/memory_compression.py +2 -2
- package/src/memory_store_v2.py +34 -354
- package/src/migrate_v1_to_v2.py +11 -10
- package/src/patterns/analyzers.py +104 -100
- package/src/patterns/learner.py +17 -13
- package/src/patterns/scoring.py +25 -21
- package/src/patterns/store.py +40 -38
- package/src/patterns/terminology.py +53 -51
- package/src/provenance_tracker.py +2 -2
- package/src/qualixar_attribution.py +1 -1
- package/src/search/engine.py +16 -14
- package/src/search/index_loader.py +13 -11
- package/src/setup_validator.py +160 -158
- package/src/subscription_manager.py +20 -18
- package/src/tree/builder.py +66 -64
- package/src/tree/nodes.py +103 -97
- package/src/tree/queries.py +142 -137
- package/src/tree/schema.py +46 -42
- package/src/webhook_dispatcher.py +3 -3
- package/ui_server.py +7 -4
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# SuperLocalMemory V2.7 — Quick Verification Script (PowerShell)
|
|
3
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj
|
|
4
|
+
# Licensed under MIT License
|
|
5
|
+
# Repository: https://github.com/varun369/SuperLocalMemoryV2
|
|
6
|
+
#
|
|
7
|
+
# Run this after installation to verify everything works:
|
|
8
|
+
# .\scripts\verify-v27.ps1
|
|
9
|
+
# ============================================================================
|
|
10
|
+
|
|
11
|
+
param()
|
|
12
|
+
|
|
13
|
+
$ErrorActionPreference = "Stop"
|
|
14
|
+
|
|
15
|
+
$INSTALL_DIR = Join-Path $env:USERPROFILE ".claude-memory"
|
|
16
|
+
$PASS = 0
|
|
17
|
+
$WARN = 0
|
|
18
|
+
$FAIL = 0
|
|
19
|
+
|
|
20
|
+
Write-Host ""
|
|
21
|
+
Write-Host "SuperLocalMemory v2.7 Verification"
|
|
22
|
+
Write-Host "==================================="
|
|
23
|
+
Write-Host ""
|
|
24
|
+
|
|
25
|
+
# ── Check 1: Installation directory ──────────────────────────────────────────
|
|
26
|
+
if (Test-Path $INSTALL_DIR) {
|
|
27
|
+
Write-Host "[PASS] Installation directory exists: $INSTALL_DIR" -ForegroundColor Green
|
|
28
|
+
$PASS++
|
|
29
|
+
} else {
|
|
30
|
+
Write-Host "[FAIL] Installation directory missing. Run install.ps1 first." -ForegroundColor Red
|
|
31
|
+
$FAIL++
|
|
32
|
+
Write-Host ""
|
|
33
|
+
Write-Host "==================================="
|
|
34
|
+
Write-Host "Result: FAIL — SuperLocalMemory is not installed."
|
|
35
|
+
Write-Host "Run: .\install.ps1"
|
|
36
|
+
exit 1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ── Check 2: Core modules ────────────────────────────────────────────────────
|
|
40
|
+
Write-Host ""
|
|
41
|
+
Write-Host "Core Modules:"
|
|
42
|
+
$coreModules = @("memory_store_v2.py", "graph_engine.py", "pattern_learner.py", "mcp_server.py", "tree_manager.py")
|
|
43
|
+
foreach ($mod in $coreModules) {
|
|
44
|
+
$modPath = Join-Path $INSTALL_DIR $mod
|
|
45
|
+
if (Test-Path $modPath) {
|
|
46
|
+
Write-Host " [PASS] $mod" -ForegroundColor Green
|
|
47
|
+
$PASS++
|
|
48
|
+
} else {
|
|
49
|
+
Write-Host " [FAIL] Missing: $mod" -ForegroundColor Red
|
|
50
|
+
$FAIL++
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# ── Check 3: v2.5 modules ────────────────────────────────────────────────────
|
|
55
|
+
Write-Host ""
|
|
56
|
+
Write-Host "Event System (v2.5):"
|
|
57
|
+
$v25Modules = @("event_bus.py", "subscription_manager.py", "webhook_dispatcher.py", "agent_registry.py", "provenance_tracker.py", "trust_scorer.py", "db_connection_manager.py")
|
|
58
|
+
foreach ($mod in $v25Modules) {
|
|
59
|
+
$modPath = Join-Path $INSTALL_DIR $mod
|
|
60
|
+
if (Test-Path $modPath) {
|
|
61
|
+
Write-Host " [PASS] $mod" -ForegroundColor Green
|
|
62
|
+
$PASS++
|
|
63
|
+
} else {
|
|
64
|
+
Write-Host " [WARN] Missing: $mod (v2.5 feature)" -ForegroundColor Yellow
|
|
65
|
+
$WARN++
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ── Check 4: Learning modules (v2.7) ─────────────────────────────────────────
|
|
70
|
+
Write-Host ""
|
|
71
|
+
Write-Host "Learning System (v2.7):"
|
|
72
|
+
$learningDir = Join-Path $INSTALL_DIR "learning"
|
|
73
|
+
if (Test-Path $learningDir) {
|
|
74
|
+
Write-Host " [PASS] learning/ directory exists" -ForegroundColor Green
|
|
75
|
+
$PASS++
|
|
76
|
+
|
|
77
|
+
$learningModules = @("__init__.py", "learning_db.py", "adaptive_ranker.py", "feedback_collector.py",
|
|
78
|
+
"engagement_tracker.py", "cross_project_aggregator.py", "project_context_manager.py",
|
|
79
|
+
"workflow_pattern_miner.py", "source_quality_scorer.py", "synthetic_bootstrap.py",
|
|
80
|
+
"feature_extractor.py")
|
|
81
|
+
foreach ($mod in $learningModules) {
|
|
82
|
+
$modPath = Join-Path $learningDir $mod
|
|
83
|
+
if (Test-Path $modPath) {
|
|
84
|
+
Write-Host " [PASS] learning/$mod" -ForegroundColor Green
|
|
85
|
+
$PASS++
|
|
86
|
+
} else {
|
|
87
|
+
Write-Host " [WARN] Missing: learning/$mod" -ForegroundColor Yellow
|
|
88
|
+
$WARN++
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
Write-Host " [FAIL] learning/ directory missing (v2.7 not fully installed)" -ForegroundColor Red
|
|
93
|
+
$FAIL++
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# ── Check 5: Learning dependencies ───────────────────────────────────────────
|
|
97
|
+
Write-Host ""
|
|
98
|
+
Write-Host "Learning Dependencies:"
|
|
99
|
+
try {
|
|
100
|
+
python -c "import lightgbm; print(f' [PASS] LightGBM {lightgbm.__version__}')" 2>$null
|
|
101
|
+
if ($LASTEXITCODE -eq 0) {
|
|
102
|
+
$PASS++
|
|
103
|
+
} else {
|
|
104
|
+
throw
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
Write-Host " [INFO] LightGBM not installed (optional — rule-based ranking will be used)" -ForegroundColor Yellow
|
|
108
|
+
$WARN++
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
python -c "import scipy; print(f' [PASS] SciPy {scipy.__version__}')" 2>$null
|
|
113
|
+
if ($LASTEXITCODE -eq 0) {
|
|
114
|
+
$PASS++
|
|
115
|
+
} else {
|
|
116
|
+
throw
|
|
117
|
+
}
|
|
118
|
+
} catch {
|
|
119
|
+
Write-Host " [INFO] SciPy not installed (optional — install for full learning features)" -ForegroundColor Yellow
|
|
120
|
+
$WARN++
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# ── Check 6: Core dependencies ───────────────────────────────────────────────
|
|
124
|
+
Write-Host ""
|
|
125
|
+
Write-Host "Core Dependencies:"
|
|
126
|
+
try {
|
|
127
|
+
python -c "import sklearn; print(f' [PASS] scikit-learn {sklearn.__version__}')" 2>$null
|
|
128
|
+
if ($LASTEXITCODE -eq 0) {
|
|
129
|
+
$PASS++
|
|
130
|
+
} else {
|
|
131
|
+
throw
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
Write-Host " [WARN] scikit-learn not installed (needed for knowledge graph)" -ForegroundColor Yellow
|
|
135
|
+
$WARN++
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
python -c "import numpy; print(f' [PASS] numpy {numpy.__version__}')" 2>$null
|
|
140
|
+
if ($LASTEXITCODE -eq 0) {
|
|
141
|
+
$PASS++
|
|
142
|
+
} else {
|
|
143
|
+
throw
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
Write-Host " [WARN] numpy not installed" -ForegroundColor Yellow
|
|
147
|
+
$WARN++
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
python -c "import igraph; print(f' [PASS] python-igraph {igraph.__version__}')" 2>$null
|
|
152
|
+
if ($LASTEXITCODE -eq 0) {
|
|
153
|
+
$PASS++
|
|
154
|
+
} else {
|
|
155
|
+
throw
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
Write-Host " [WARN] python-igraph not installed (needed for graph clustering)" -ForegroundColor Yellow
|
|
159
|
+
$WARN++
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# ── Check 7: Database ────────────────────────────────────────────────────────
|
|
163
|
+
Write-Host ""
|
|
164
|
+
Write-Host "Databases:"
|
|
165
|
+
$memoryDb = Join-Path $INSTALL_DIR "memory.db"
|
|
166
|
+
if (Test-Path $memoryDb) {
|
|
167
|
+
try {
|
|
168
|
+
$MEMORY_COUNT = sqlite3 $memoryDb "SELECT COUNT(*) FROM memories;" 2>$null
|
|
169
|
+
$DB_SIZE = [math]::Round((Get-Item $memoryDb).Length / 1KB, 2)
|
|
170
|
+
Write-Host " [PASS] memory.db exists ($MEMORY_COUNT memories, $DB_SIZE KB)" -ForegroundColor Green
|
|
171
|
+
$PASS++
|
|
172
|
+
} catch {
|
|
173
|
+
Write-Host " [INFO] memory.db exists but cannot query (sqlite3 not available)" -ForegroundColor Yellow
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
Write-Host " [INFO] memory.db not yet created (will auto-create on first use)" -ForegroundColor Yellow
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
$learningDb = Join-Path $INSTALL_DIR "learning.db"
|
|
180
|
+
if (Test-Path $learningDb) {
|
|
181
|
+
try {
|
|
182
|
+
$FEEDBACK_COUNT = sqlite3 $learningDb "SELECT COUNT(*) FROM ranking_feedback;" 2>$null
|
|
183
|
+
Write-Host " [PASS] learning.db exists ($FEEDBACK_COUNT feedback signals)" -ForegroundColor Green
|
|
184
|
+
$PASS++
|
|
185
|
+
} catch {
|
|
186
|
+
Write-Host " [INFO] learning.db exists but cannot query (sqlite3 not available)" -ForegroundColor Yellow
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
Write-Host " [INFO] learning.db not yet created (will auto-create on first recall)" -ForegroundColor Yellow
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# ── Check 8: CLI ──────────────────────────────────────────────────────────────
|
|
193
|
+
Write-Host ""
|
|
194
|
+
Write-Host "CLI:"
|
|
195
|
+
$slmCommand = Get-Command slm -ErrorAction SilentlyContinue
|
|
196
|
+
if ($slmCommand) {
|
|
197
|
+
Write-Host " [PASS] slm command available in PATH" -ForegroundColor Green
|
|
198
|
+
$PASS++
|
|
199
|
+
} else {
|
|
200
|
+
$slmBin = Join-Path $INSTALL_DIR "bin\slm"
|
|
201
|
+
if (Test-Path $slmBin) {
|
|
202
|
+
Write-Host " [WARN] slm exists at $slmBin but not in PATH" -ForegroundColor Yellow
|
|
203
|
+
Write-Host " Add to PATH: `$env:PATH = `"`$env:USERPROFILE\.claude-memory\bin;`$env:PATH`""
|
|
204
|
+
$WARN++
|
|
205
|
+
} else {
|
|
206
|
+
Write-Host " [FAIL] slm command not found" -ForegroundColor Red
|
|
207
|
+
$FAIL++
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# ── Check 9: MCP server ──────────────────────────────────────────────────────
|
|
212
|
+
Write-Host ""
|
|
213
|
+
Write-Host "MCP Server:"
|
|
214
|
+
$mcpServer = Join-Path $INSTALL_DIR "mcp_server.py"
|
|
215
|
+
if (Test-Path $mcpServer) {
|
|
216
|
+
Write-Host " [PASS] mcp_server.py installed" -ForegroundColor Green
|
|
217
|
+
$PASS++
|
|
218
|
+
} else {
|
|
219
|
+
Write-Host " [FAIL] mcp_server.py missing" -ForegroundColor Red
|
|
220
|
+
$FAIL++
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
python -c "from mcp.server.fastmcp import FastMCP" 2>$null
|
|
225
|
+
if ($LASTEXITCODE -eq 0) {
|
|
226
|
+
Write-Host " [PASS] MCP SDK installed" -ForegroundColor Green
|
|
227
|
+
$PASS++
|
|
228
|
+
} else {
|
|
229
|
+
throw
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
Write-Host " [WARN] MCP SDK not installed (install: pip install mcp)" -ForegroundColor Yellow
|
|
233
|
+
$WARN++
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# ── Check 10: Import chain verification ───────────────────────────────────────
|
|
237
|
+
Write-Host ""
|
|
238
|
+
Write-Host "Import Chain:"
|
|
239
|
+
$importTest = @"
|
|
240
|
+
import sys
|
|
241
|
+
sys.path.insert(0, '$($INSTALL_DIR -replace '\\', '\\')')
|
|
242
|
+
try:
|
|
243
|
+
from learning import get_learning_db, get_status, FULL_LEARNING_AVAILABLE, ML_RANKING_AVAILABLE
|
|
244
|
+
status = get_status()
|
|
245
|
+
ml = 'yes' if status['ml_ranking_available'] else 'no'
|
|
246
|
+
full = 'yes' if status['learning_available'] else 'no'
|
|
247
|
+
print(f'OK ml_ranking={ml} full_learning={full}')
|
|
248
|
+
except ImportError as e:
|
|
249
|
+
print(f'IMPORT_ERROR {e}')
|
|
250
|
+
except Exception as e:
|
|
251
|
+
print(f'ERROR {e}')
|
|
252
|
+
"@
|
|
253
|
+
|
|
254
|
+
$IMPORT_RESULT = python -c $importTest 2>&1
|
|
255
|
+
|
|
256
|
+
if ($IMPORT_RESULT -like "OK*") {
|
|
257
|
+
Write-Host " [PASS] Learning system imports successfully" -ForegroundColor Green
|
|
258
|
+
Write-Host " $IMPORT_RESULT"
|
|
259
|
+
$PASS++
|
|
260
|
+
} elseif ($IMPORT_RESULT -like "IMPORT_ERROR*") {
|
|
261
|
+
Write-Host " [WARN] Learning import failed: $($IMPORT_RESULT -replace 'IMPORT_ERROR ', '')" -ForegroundColor Yellow
|
|
262
|
+
Write-Host " This may be normal if learning modules are not yet installed."
|
|
263
|
+
$WARN++
|
|
264
|
+
} else {
|
|
265
|
+
Write-Host " [WARN] Learning check: $IMPORT_RESULT" -ForegroundColor Yellow
|
|
266
|
+
$WARN++
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
# ── Summary ───────────────────────────────────────────────────────────────────
|
|
270
|
+
Write-Host ""
|
|
271
|
+
Write-Host "==================================="
|
|
272
|
+
Write-Host "Verification Summary"
|
|
273
|
+
Write-Host " Passed: $PASS"
|
|
274
|
+
Write-Host " Warnings: $WARN"
|
|
275
|
+
Write-Host " Failed: $FAIL"
|
|
276
|
+
Write-Host "==================================="
|
|
277
|
+
Write-Host ""
|
|
278
|
+
|
|
279
|
+
if ($FAIL -eq 0) {
|
|
280
|
+
Write-Host "Status: READY" -ForegroundColor Green
|
|
281
|
+
Write-Host ""
|
|
282
|
+
Write-Host "Quick start:"
|
|
283
|
+
Write-Host " slm remember `"My first memory`""
|
|
284
|
+
Write-Host " slm recall `"first`""
|
|
285
|
+
Write-Host " slm status"
|
|
286
|
+
Write-Host ""
|
|
287
|
+
if ($WARN -gt 0) {
|
|
288
|
+
Write-Host "Some optional features may not be available."
|
|
289
|
+
Write-Host "Install missing dependencies to enable them:"
|
|
290
|
+
Write-Host " pip install lightgbm scipy # Learning system"
|
|
291
|
+
Write-Host " pip install scikit-learn igraph # Knowledge graph"
|
|
292
|
+
Write-Host ""
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
Write-Host "Status: INCOMPLETE" -ForegroundColor Red
|
|
296
|
+
Write-Host ""
|
|
297
|
+
Write-Host "Fix the failed checks above, then re-run:"
|
|
298
|
+
Write-Host " .\scripts\verify-v27.ps1"
|
|
299
|
+
Write-Host ""
|
|
300
|
+
exit 1
|
|
301
|
+
}
|
package/src/agent_registry.py
CHANGED
|
@@ -105,24 +105,26 @@ class AgentRegistry:
|
|
|
105
105
|
except ImportError:
|
|
106
106
|
import sqlite3
|
|
107
107
|
conn = sqlite3.connect(str(self.db_path))
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
108
|
+
try:
|
|
109
|
+
conn.execute('''
|
|
110
|
+
CREATE TABLE IF NOT EXISTS agent_registry (
|
|
111
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
112
|
+
agent_id TEXT NOT NULL UNIQUE,
|
|
113
|
+
agent_name TEXT,
|
|
114
|
+
protocol TEXT NOT NULL,
|
|
115
|
+
first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
116
|
+
last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
117
|
+
memories_written INTEGER DEFAULT 0,
|
|
118
|
+
memories_recalled INTEGER DEFAULT 0,
|
|
119
|
+
trust_score REAL DEFAULT 0.667,
|
|
120
|
+
metadata TEXT DEFAULT '{}'
|
|
121
|
+
)
|
|
122
|
+
''')
|
|
123
|
+
conn.execute('CREATE INDEX IF NOT EXISTS idx_agent_protocol ON agent_registry(protocol)')
|
|
124
|
+
conn.execute('CREATE INDEX IF NOT EXISTS idx_agent_last_seen ON agent_registry(last_seen)')
|
|
125
|
+
conn.commit()
|
|
126
|
+
finally:
|
|
127
|
+
conn.close()
|
|
126
128
|
|
|
127
129
|
# =========================================================================
|
|
128
130
|
# Agent Registration
|
|
@@ -179,16 +181,18 @@ class AgentRegistry:
|
|
|
179
181
|
except ImportError:
|
|
180
182
|
import sqlite3
|
|
181
183
|
conn = sqlite3.connect(str(self.db_path))
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
184
|
+
try:
|
|
185
|
+
conn.execute('''
|
|
186
|
+
INSERT INTO agent_registry (agent_id, agent_name, protocol, first_seen, last_seen, metadata)
|
|
187
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
188
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
189
|
+
last_seen = excluded.last_seen,
|
|
190
|
+
metadata = excluded.metadata,
|
|
191
|
+
agent_name = COALESCE(excluded.agent_name, agent_registry.agent_name)
|
|
192
|
+
''', (agent_id, agent_name, protocol, now, now, meta_json))
|
|
193
|
+
conn.commit()
|
|
194
|
+
finally:
|
|
195
|
+
conn.close()
|
|
192
196
|
|
|
193
197
|
# Emit agent.connected event
|
|
194
198
|
try:
|
package/src/auto_backup.py
CHANGED
|
@@ -157,8 +157,10 @@ class AutoBackup:
|
|
|
157
157
|
# Use SQLite backup API for consistency (safe even during writes)
|
|
158
158
|
source_conn = sqlite3.connect(self.db_path)
|
|
159
159
|
backup_conn = sqlite3.connect(backup_path)
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
try:
|
|
161
|
+
source_conn.backup(backup_conn)
|
|
162
|
+
finally:
|
|
163
|
+
backup_conn.close()
|
|
162
164
|
source_conn.close()
|
|
163
165
|
|
|
164
166
|
# Get backup size
|
|
@@ -179,8 +181,10 @@ class AutoBackup:
|
|
|
179
181
|
learning_backup_path = self.backup_dir / learning_backup_name
|
|
180
182
|
l_source = sqlite3.connect(learning_db)
|
|
181
183
|
l_backup = sqlite3.connect(learning_backup_path)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
try:
|
|
185
|
+
l_source.backup(l_backup)
|
|
186
|
+
finally:
|
|
187
|
+
l_backup.close()
|
|
184
188
|
l_source.close()
|
|
185
189
|
l_size = learning_backup_path.stat().st_size / (1024 * 1024)
|
|
186
190
|
logger.info(f"Learning backup created: {learning_backup_name} ({l_size:.1f} MB)")
|
|
@@ -281,8 +285,10 @@ class AutoBackup:
|
|
|
281
285
|
# Restore using SQLite backup API
|
|
282
286
|
source_conn = sqlite3.connect(backup_path)
|
|
283
287
|
target_conn = sqlite3.connect(target_db)
|
|
284
|
-
|
|
285
|
-
|
|
288
|
+
try:
|
|
289
|
+
source_conn.backup(target_conn)
|
|
290
|
+
finally:
|
|
291
|
+
target_conn.close()
|
|
286
292
|
source_conn.close()
|
|
287
293
|
|
|
288
294
|
logger.info(f"Restored from backup: {filename} → {target_db.name}")
|
package/src/cache_manager.py
CHANGED
|
@@ -88,7 +88,7 @@ class CacheEntry:
|
|
|
88
88
|
age = time.time() - self.timestamp
|
|
89
89
|
return age > ttl_seconds
|
|
90
90
|
|
|
91
|
-
def mark_accessed(self):
|
|
91
|
+
def mark_accessed(self) -> None:
|
|
92
92
|
"""Mark entry as accessed (increment counter)."""
|
|
93
93
|
self.access_count += 1
|
|
94
94
|
|
|
@@ -425,7 +425,7 @@ if __name__ == "__main__":
|
|
|
425
425
|
]
|
|
426
426
|
|
|
427
427
|
# Mock search results
|
|
428
|
-
def mock_search(query: str):
|
|
428
|
+
def mock_search(query: str) -> None:
|
|
429
429
|
"""Simulate search result."""
|
|
430
430
|
return [
|
|
431
431
|
(f"doc_{i}", random.random())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
+
"""
|
|
5
|
+
Compression sub-package for SuperLocalMemory.
|
|
6
|
+
Provides tier-based progressive summarization and archival.
|
|
7
|
+
|
|
8
|
+
Note: Uses relative imports to avoid collision with Python stdlib.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .config import CompressionConfig
|
|
12
|
+
from .tier_classifier import TierClassifier
|
|
13
|
+
from .tier2_compressor import Tier2Compressor
|
|
14
|
+
from .tier3_compressor import Tier3Compressor
|
|
15
|
+
from .cold_storage import ColdStorageManager
|
|
16
|
+
from .orchestrator import CompressionOrchestrator
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
'CompressionConfig',
|
|
20
|
+
'TierClassifier',
|
|
21
|
+
'Tier2Compressor',
|
|
22
|
+
'Tier3Compressor',
|
|
23
|
+
'ColdStorageManager',
|
|
24
|
+
'CompressionOrchestrator',
|
|
25
|
+
]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
+
"""
|
|
5
|
+
CLI interface for compression operations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
from compression.config import CompressionConfig
|
|
12
|
+
from compression.tier_classifier import TierClassifier
|
|
13
|
+
from compression.tier2_compressor import Tier2Compressor
|
|
14
|
+
from compression.tier3_compressor import Tier3Compressor
|
|
15
|
+
from compression.cold_storage import ColdStorageManager
|
|
16
|
+
from compression.orchestrator import CompressionOrchestrator
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
"""Main CLI entry point."""
|
|
21
|
+
if len(sys.argv) < 2:
|
|
22
|
+
print("Progressive Summarization Compression for SuperLocalMemory\n")
|
|
23
|
+
print("Usage:")
|
|
24
|
+
print(" python compression.py classify # Classify memories into tiers")
|
|
25
|
+
print(" python compression.py compress # Run full compression cycle")
|
|
26
|
+
print(" python compression.py stats # Show compression statistics")
|
|
27
|
+
print(" python compression.py tier2 <id> # Compress specific memory to Tier 2")
|
|
28
|
+
print(" python compression.py tier3 <id> # Compress specific memory to Tier 3")
|
|
29
|
+
print(" python compression.py cold-storage # Move old memories to cold storage")
|
|
30
|
+
print(" python compression.py restore <id> # Restore memory from cold storage")
|
|
31
|
+
print(" python compression.py init-config # Initialize compression config")
|
|
32
|
+
sys.exit(0)
|
|
33
|
+
|
|
34
|
+
command = sys.argv[1]
|
|
35
|
+
orchestrator = CompressionOrchestrator()
|
|
36
|
+
|
|
37
|
+
if command == "classify":
|
|
38
|
+
classifier = TierClassifier()
|
|
39
|
+
updates = classifier.classify_memories()
|
|
40
|
+
print(f"Classified {len(updates)} memories")
|
|
41
|
+
|
|
42
|
+
stats = classifier.get_tier_stats()
|
|
43
|
+
print(f"\nTier breakdown:")
|
|
44
|
+
print(f" Tier 1 (Full content): {stats['tier1']} memories")
|
|
45
|
+
print(f" Tier 2 (Summary+excerpts): {stats['tier2']} memories")
|
|
46
|
+
print(f" Tier 3 (Bullets only): {stats['tier3']} memories")
|
|
47
|
+
|
|
48
|
+
elif command == "compress":
|
|
49
|
+
print("Running full compression cycle...")
|
|
50
|
+
stats = orchestrator.run_full_compression()
|
|
51
|
+
|
|
52
|
+
print(f"\nCompression Results:")
|
|
53
|
+
print(f" Tier updates: {stats['tier_updates']}")
|
|
54
|
+
print(f" Tier 2 compressed: {stats['tier2_compressed']}")
|
|
55
|
+
print(f" Tier 3 compressed: {stats['tier3_compressed']}")
|
|
56
|
+
print(f" Moved to cold storage: {stats['cold_stored']}")
|
|
57
|
+
|
|
58
|
+
if 'space_savings' in stats:
|
|
59
|
+
savings = stats['space_savings']
|
|
60
|
+
print(f"\nSpace Savings:")
|
|
61
|
+
print(f" Original size: {savings['estimated_original_bytes']:,} bytes")
|
|
62
|
+
print(f" Current size: {savings['current_size_bytes']:,} bytes")
|
|
63
|
+
print(f" Savings: {savings['savings_bytes']:,} bytes ({savings['savings_percent']}%)")
|
|
64
|
+
|
|
65
|
+
if stats.get('errors'):
|
|
66
|
+
print(f"\nErrors: {stats['errors']}")
|
|
67
|
+
|
|
68
|
+
elif command == "stats":
|
|
69
|
+
classifier = TierClassifier()
|
|
70
|
+
tier_stats = classifier.get_tier_stats()
|
|
71
|
+
|
|
72
|
+
cold_storage = ColdStorageManager()
|
|
73
|
+
cold_stats = cold_storage.get_cold_storage_stats()
|
|
74
|
+
|
|
75
|
+
savings = orchestrator._calculate_space_savings()
|
|
76
|
+
|
|
77
|
+
print("Compression Statistics\n")
|
|
78
|
+
print("Tier Breakdown:")
|
|
79
|
+
print(f" Tier 1 (Full content): {tier_stats['tier1']} memories")
|
|
80
|
+
print(f" Tier 2 (Summary+excerpts): {tier_stats['tier2']} memories")
|
|
81
|
+
print(f" Tier 3 (Bullets only): {tier_stats['tier3']} memories")
|
|
82
|
+
|
|
83
|
+
print(f"\nCold Storage:")
|
|
84
|
+
print(f" Archive files: {cold_stats['archive_count']}")
|
|
85
|
+
print(f" Total memories: {cold_stats['total_memories']}")
|
|
86
|
+
print(f" Total size: {cold_stats['total_size_bytes']:,} bytes")
|
|
87
|
+
|
|
88
|
+
print(f"\nSpace Savings:")
|
|
89
|
+
print(f" Estimated original: {savings['estimated_original_bytes']:,} bytes")
|
|
90
|
+
print(f" Current size: {savings['current_size_bytes']:,} bytes")
|
|
91
|
+
print(f" Savings: {savings['savings_bytes']:,} bytes ({savings['savings_percent']}%)")
|
|
92
|
+
|
|
93
|
+
elif command == "tier2" and len(sys.argv) >= 3:
|
|
94
|
+
try:
|
|
95
|
+
memory_id = int(sys.argv[2])
|
|
96
|
+
compressor = Tier2Compressor()
|
|
97
|
+
if compressor.compress_to_tier2(memory_id):
|
|
98
|
+
print(f"Memory #{memory_id} compressed to Tier 2")
|
|
99
|
+
else:
|
|
100
|
+
print(f"Failed to compress memory #{memory_id}")
|
|
101
|
+
except ValueError:
|
|
102
|
+
print("Error: Memory ID must be a number")
|
|
103
|
+
|
|
104
|
+
elif command == "tier3" and len(sys.argv) >= 3:
|
|
105
|
+
try:
|
|
106
|
+
memory_id = int(sys.argv[2])
|
|
107
|
+
compressor = Tier3Compressor()
|
|
108
|
+
if compressor.compress_to_tier3(memory_id):
|
|
109
|
+
print(f"Memory #{memory_id} compressed to Tier 3")
|
|
110
|
+
else:
|
|
111
|
+
print(f"Failed to compress memory #{memory_id}")
|
|
112
|
+
except ValueError:
|
|
113
|
+
print("Error: Memory ID must be a number")
|
|
114
|
+
|
|
115
|
+
elif command == "cold-storage":
|
|
116
|
+
cold_storage = ColdStorageManager()
|
|
117
|
+
candidates = cold_storage.get_cold_storage_candidates()
|
|
118
|
+
|
|
119
|
+
if not candidates:
|
|
120
|
+
print("No memories ready for cold storage")
|
|
121
|
+
else:
|
|
122
|
+
print(f"Moving {len(candidates)} memories to cold storage...")
|
|
123
|
+
count = cold_storage.move_to_cold_storage(candidates)
|
|
124
|
+
print(f"Archived {count} memories")
|
|
125
|
+
|
|
126
|
+
elif command == "restore" and len(sys.argv) >= 3:
|
|
127
|
+
try:
|
|
128
|
+
memory_id = int(sys.argv[2])
|
|
129
|
+
cold_storage = ColdStorageManager()
|
|
130
|
+
content = cold_storage.restore_from_cold_storage(memory_id)
|
|
131
|
+
|
|
132
|
+
if content:
|
|
133
|
+
print(f"Memory #{memory_id} restored from cold storage")
|
|
134
|
+
else:
|
|
135
|
+
print(f"Memory #{memory_id} not found in cold storage")
|
|
136
|
+
except ValueError:
|
|
137
|
+
print("Error: Memory ID must be a number")
|
|
138
|
+
|
|
139
|
+
elif command == "init-config":
|
|
140
|
+
config = CompressionConfig()
|
|
141
|
+
config.initialize_defaults()
|
|
142
|
+
print("Compression configuration initialized")
|
|
143
|
+
print(json.dumps(config.compression_settings, indent=2))
|
|
144
|
+
|
|
145
|
+
else:
|
|
146
|
+
print(f"Unknown command: {command}")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if __name__ == "__main__":
|
|
150
|
+
main()
|