superlocalmemory 3.4.10 → 3.4.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +17 -11
  2. package/docs/skill-evolution.md +77 -10
  3. package/ide/hooks/tool-event-hook.sh +4 -4
  4. package/package.json +1 -1
  5. package/pyproject.toml +3 -2
  6. package/src/superlocalmemory/cli/commands.py +170 -0
  7. package/src/superlocalmemory/cli/main.py +21 -0
  8. package/src/superlocalmemory/cli/setup_wizard.py +54 -11
  9. package/src/superlocalmemory/core/config.py +35 -0
  10. package/src/superlocalmemory/core/consolidation_engine.py +128 -0
  11. package/src/superlocalmemory/core/embedding_worker.py +1 -1
  12. package/src/superlocalmemory/core/engine.py +12 -0
  13. package/src/superlocalmemory/core/fact_consolidator.py +425 -0
  14. package/src/superlocalmemory/core/graph_pruner.py +290 -0
  15. package/src/superlocalmemory/core/maintenance_scheduler.py +20 -0
  16. package/src/superlocalmemory/core/recall_pipeline.py +9 -0
  17. package/src/superlocalmemory/core/tier_manager.py +325 -0
  18. package/src/superlocalmemory/encoding/entity_resolver.py +6 -5
  19. package/src/superlocalmemory/evolution/__init__.py +29 -0
  20. package/src/superlocalmemory/evolution/blind_verifier.py +115 -0
  21. package/src/superlocalmemory/evolution/evolution_store.py +302 -0
  22. package/src/superlocalmemory/evolution/mutation_generator.py +181 -0
  23. package/src/superlocalmemory/evolution/skill_evolver.py +555 -0
  24. package/src/superlocalmemory/evolution/triggers.py +367 -0
  25. package/src/superlocalmemory/evolution/types.py +92 -0
  26. package/src/superlocalmemory/hooks/hook_handlers.py +13 -0
  27. package/src/superlocalmemory/learning/skill_performance_miner.py +44 -11
  28. package/src/superlocalmemory/mcp/server.py +4 -0
  29. package/src/superlocalmemory/mcp/tools_evolution.py +338 -0
  30. package/src/superlocalmemory/retrieval/engine.py +98 -11
  31. package/src/superlocalmemory/retrieval/entity_channel.py +118 -0
  32. package/src/superlocalmemory/retrieval/forgetting_filter.py +22 -7
  33. package/src/superlocalmemory/retrieval/strategy.py +2 -2
  34. package/src/superlocalmemory/server/routes/behavioral.py +19 -15
  35. package/src/superlocalmemory/server/routes/evolution.py +213 -0
  36. package/src/superlocalmemory/server/routes/tiers.py +195 -0
  37. package/src/superlocalmemory/server/unified_daemon.py +39 -5
  38. package/src/superlocalmemory/storage/schema_v3411.py +149 -0
  39. package/src/superlocalmemory/ui/index.html +5 -2
  40. package/src/superlocalmemory/ui/js/lifecycle.js +83 -0
  41. package/src/superlocalmemory/ui/js/ng-skills.js +394 -10
  42. package/src/superlocalmemory.egg-info/PKG-INFO +614 -0
  43. package/src/superlocalmemory.egg-info/SOURCES.txt +335 -0
  44. package/src/superlocalmemory.egg-info/dependency_links.txt +1 -0
  45. package/src/superlocalmemory.egg-info/entry_points.txt +2 -0
  46. package/src/superlocalmemory.egg-info/requires.txt +55 -0
  47. package/src/superlocalmemory.egg-info/top_level.txt +1 -0
@@ -0,0 +1,149 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """SuperLocalMemory V3.4.11 "Scale-Ready" — Schema Extensions.
6
+
7
+ New tables for tiered storage + future backend integrations:
8
+ - pinned_facts: User-pinned facts that stay in hot tier forever
9
+ - backend_status: Tracks initialization state of LanceDB/KùzuDB backends
10
+ - fact_consolidations: History of merged/consolidated facts
11
+
12
+ Design rules (inherited):
13
+ - CREATE IF NOT EXISTS for idempotency
14
+ - profile_id where applicable
15
+ - Never ALTER existing column types
16
+
17
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import logging
23
+ import sqlite3
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # DDL — Pinned Facts (user override on lifecycle demotion)
29
+ # ---------------------------------------------------------------------------
30
+
31
+ _PINNED_FACTS_DDL = """
32
+ CREATE TABLE IF NOT EXISTS pinned_facts (
33
+ fact_id TEXT PRIMARY KEY,
34
+ profile_id TEXT DEFAULT 'default',
35
+ pinned_at TEXT NOT NULL,
36
+ reason TEXT DEFAULT ''
37
+ );
38
+
39
+ CREATE INDEX IF NOT EXISTS idx_pinned_facts_profile
40
+ ON pinned_facts(profile_id);
41
+ """
42
+
43
+ # ---------------------------------------------------------------------------
44
+ # DDL — Backend Status (LanceDB, KùzuDB, sqlite-vec tracking)
45
+ # ---------------------------------------------------------------------------
46
+
47
+ _BACKEND_STATUS_DDL = """
48
+ CREATE TABLE IF NOT EXISTS backend_status (
49
+ backend_name TEXT PRIMARY KEY,
50
+ status TEXT DEFAULT 'not_initialized',
51
+ record_count INTEGER DEFAULT 0,
52
+ last_sync_at TEXT,
53
+ error_message TEXT DEFAULT '',
54
+ config TEXT DEFAULT '{}'
55
+ );
56
+ """
57
+
58
+ # ---------------------------------------------------------------------------
59
+ # DDL — Fact Consolidations (merge history)
60
+ # ---------------------------------------------------------------------------
61
+
62
+ _FACT_CONSOLIDATIONS_DDL = """
63
+ CREATE TABLE IF NOT EXISTS fact_consolidations (
64
+ consolidation_id TEXT PRIMARY KEY,
65
+ profile_id TEXT DEFAULT 'default',
66
+ consolidated_fact_id TEXT NOT NULL,
67
+ source_fact_ids TEXT NOT NULL,
68
+ strategy TEXT DEFAULT 'entity_cluster',
69
+ created_at TEXT NOT NULL
70
+ );
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_fact_consolidations_profile
73
+ ON fact_consolidations(profile_id);
74
+ CREATE INDEX IF NOT EXISTS idx_fact_consolidations_target
75
+ ON fact_consolidations(consolidated_fact_id);
76
+ """
77
+
78
+ # ---------------------------------------------------------------------------
79
+ # DDL — Index on lifecycle for fast tier queries
80
+ # ---------------------------------------------------------------------------
81
+
82
+ _LIFECYCLE_INDEX_DDL = """
83
+ CREATE INDEX IF NOT EXISTS idx_atomic_facts_lifecycle
84
+ ON atomic_facts(lifecycle, profile_id);
85
+ """
86
+
87
+
88
+ # ---------------------------------------------------------------------------
89
+ # Migration runner
90
+ # ---------------------------------------------------------------------------
91
+
92
+ def apply_v3411_schema(db_path: str | sqlite3.Connection) -> dict:
93
+ """Apply all v3.4.11 schema changes. Idempotent."""
94
+ result = {"applied": [], "errors": []}
95
+
96
+ if isinstance(db_path, sqlite3.Connection):
97
+ conn = db_path
98
+ own_connection = False
99
+ else:
100
+ conn = sqlite3.connect(str(db_path))
101
+ own_connection = True
102
+
103
+ try:
104
+ for name, ddl in [
105
+ ("pinned_facts", _PINNED_FACTS_DDL),
106
+ ("backend_status", _BACKEND_STATUS_DDL),
107
+ ("fact_consolidations", _FACT_CONSOLIDATIONS_DDL),
108
+ ("lifecycle_index", _LIFECYCLE_INDEX_DDL),
109
+ ]:
110
+ try:
111
+ conn.executescript(ddl)
112
+ result["applied"].append(name)
113
+ except sqlite3.OperationalError as e:
114
+ result["errors"].append(f"{name}: {e}")
115
+
116
+ # Seed default backend entries
117
+ now = __import__("datetime").datetime.now().isoformat()
118
+ for backend in ("sqlite_vec", "sqlite_graph", "lancedb", "kuzu"):
119
+ try:
120
+ conn.execute(
121
+ "INSERT OR IGNORE INTO backend_status "
122
+ "(backend_name, status, config) VALUES (?, ?, ?)",
123
+ (backend, "not_initialized" if backend in ("lancedb", "kuzu") else "active", "{}"),
124
+ )
125
+ except sqlite3.OperationalError:
126
+ pass
127
+
128
+ # Mark version
129
+ try:
130
+ conn.execute(
131
+ "INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (?, ?)",
132
+ ("3.4.11", now),
133
+ )
134
+ except sqlite3.OperationalError:
135
+ pass
136
+
137
+ conn.commit()
138
+
139
+ if result["applied"]:
140
+ logger.info("Schema v3.4.11 applied: %s", ", ".join(result["applied"]))
141
+
142
+ except Exception as e:
143
+ result["errors"].append(f"fatal: {e}")
144
+ logger.error("Schema v3.4.11 migration failed: %s", e)
145
+ finally:
146
+ if own_connection:
147
+ conn.close()
148
+
149
+ return result
@@ -1519,10 +1519,12 @@
1519
1519
  <h5 class="mb-0"><i class="bi bi-hourglass-split text-warning"></i> Memory Lifecycle</h5>
1520
1520
  <div>
1521
1521
  <span class="badge bg-secondary me-2" id="lifecycle-profile-badge">default</span>
1522
- <button class="btn btn-sm btn-outline-info" onclick="compactDryRun()"><i class="bi bi-funnel"></i> Preview Compaction</button>
1522
+ <button class="btn btn-sm btn-outline-success" onclick="evaluateTiersNow()"><i class="bi bi-arrow-repeat"></i> Evaluate Tiers</button>
1523
+ <button class="btn btn-sm btn-outline-info ms-1" onclick="compactDryRun()"><i class="bi bi-funnel"></i> Preview Compaction</button>
1523
1524
  <button class="btn btn-sm btn-outline-warning ms-1" onclick="compactExecute()"><i class="bi bi-lightning"></i> Compact Now</button>
1524
1525
  </div>
1525
1526
  </div>
1527
+
1526
1528
  <!-- State Distribution -->
1527
1529
  <div class="row g-3 mb-4" id="lifecycle-states-row">
1528
1530
  <div class="col-md-2"><div class="card p-3 text-center"><div class="text-success fw-bold fs-3" id="lc-active-count">-</div><small class="text-muted">Active</small></div></div>
@@ -1741,6 +1743,7 @@
1741
1743
  <div style="margin-top:8px">Loading skill performance data...</div>
1742
1744
  </div>
1743
1745
  </div>
1746
+ <div id="skill-lineage-container" class="mt-3"></div>
1744
1747
  <div id="skill-detail-panel" style="display:none;margin-top:24px"></div>
1745
1748
  </div>
1746
1749
 
@@ -2150,7 +2153,7 @@
2150
2153
  <script src="static/js/ng-health.js?v=345"></script>
2151
2154
  <script src="static/js/ng-ingestion.js?v=345"></script>
2152
2155
  <script src="static/js/ng-entities.js?v=3410"></script>
2153
- <script src="static/js/ng-skills.js?v=3410"></script>
2156
+ <script src="static/js/ng-skills.js?v=3411"></script>
2154
2157
  <script src="static/js/ng-mesh.js?v=345"></script>
2155
2158
  <script src="static/js/ng-shell.js?v=3410"></script>
2156
2159
 
@@ -7,10 +7,27 @@ var _lifecycleData = null;
7
7
 
8
8
  async function loadLifecycle() {
9
9
  try {
10
+ // V3.4.11: Load tier stats from the authoritative source (atomic_facts.lifecycle)
11
+ // and merge into the existing lifecycle view for a unified display.
12
+ var tierResponse = await fetch('/api/tiers/stats');
13
+ var tierData = await tierResponse.json();
14
+
10
15
  var response = await fetch('/api/lifecycle/status');
11
16
  var data = await response.json();
12
17
  _lifecycleData = data;
13
18
 
19
+ // Override state counts with tier stats (atomic_facts is the truth)
20
+ if (tierData && tierData.total > 0) {
21
+ data.states = data.states || {};
22
+ data.states.active = tierData.active;
23
+ data.states.warm = tierData.warm;
24
+ data.states.cold = tierData.cold;
25
+ data.states.archived = tierData.archived;
26
+ data.total_memories = tierData.total;
27
+ data._pinned = tierData.pinned;
28
+ data.available = true;
29
+ }
30
+
14
31
  if (!data.available) {
15
32
  showEmpty('lifecycle-states-row', 'hourglass-split', 'Lifecycle engine not available. Upgrade to v2.8.');
16
33
  return;
@@ -21,6 +38,19 @@ async function loadLifecycle() {
21
38
  renderLifecycleAgeStats(data);
22
39
  renderLifecycleTransitions(data);
23
40
 
41
+ // V3.4.11: Show pinned count below the state cards
42
+ var pinnedInfo = document.getElementById('lifecycle-pinned-info');
43
+ if (!pinnedInfo) {
44
+ pinnedInfo = document.createElement('div');
45
+ pinnedInfo.id = 'lifecycle-pinned-info';
46
+ pinnedInfo.className = 'small text-muted mb-3';
47
+ var statesRow = document.getElementById('lifecycle-states-row');
48
+ if (statesRow) statesRow.parentNode.insertBefore(pinnedInfo, statesRow.nextSibling);
49
+ }
50
+ var pinCount = data._pinned || 0;
51
+ pinnedInfo.innerHTML = '<i class="bi bi-pin-angle"></i> ' + pinCount + ' pinned facts (protected from demotion). ' +
52
+ '<span class="text-muted" style="font-size:11px;">Active = full weight | Warm = 0.7x | Cold = 0.3x | Archived = deep recall only</span>';
53
+
24
54
  var badge = document.getElementById('lifecycle-profile-badge');
25
55
  if (badge) badge.textContent = data.active_profile || 'default';
26
56
  } catch (error) {
@@ -296,3 +326,56 @@ async function compactExecute() {
296
326
  console.error('Compaction error:', e);
297
327
  }
298
328
  }
329
+
330
+
331
+ // ---- V3.4.11: Tier Management (pinning + evaluation) ----
332
+
333
+ async function evaluateTiersNow() {
334
+ showToast('Evaluating tiers...');
335
+ try {
336
+ var response = await fetch('/api/tiers/evaluate', { method: 'POST' });
337
+ var data = await response.json();
338
+ if (data.success) {
339
+ var s = data.stats;
340
+ var demoted = (s.demoted_to_warm || 0) + (s.demoted_to_cold || 0) + (s.demoted_to_archive || 0);
341
+ showToast('Tier evaluation: ' + demoted + ' facts demoted, ' + s.pinned_protected + ' pinned protected');
342
+ loadTierStats();
343
+ loadLifecycle();
344
+ }
345
+ } catch (error) {
346
+ showToast('Tier evaluation failed');
347
+ }
348
+ }
349
+
350
+ async function pinFact(factId, reason) {
351
+ try {
352
+ var response = await fetch('/api/tiers/pin', {
353
+ method: 'POST',
354
+ headers: { 'Content-Type': 'application/json' },
355
+ body: JSON.stringify({ fact_id: factId, reason: reason || '' })
356
+ });
357
+ var data = await response.json();
358
+ if (data.success) {
359
+ showToast('Fact pinned');
360
+ loadTierStats();
361
+ }
362
+ } catch (error) {
363
+ showToast('Failed to pin fact');
364
+ }
365
+ }
366
+
367
+ async function unpinFact(factId) {
368
+ try {
369
+ var response = await fetch('/api/tiers/unpin', {
370
+ method: 'POST',
371
+ headers: { 'Content-Type': 'application/json' },
372
+ body: JSON.stringify({ fact_id: factId })
373
+ });
374
+ if (response.ok) {
375
+ showToast('Fact unpinned');
376
+ loadTierStats();
377
+ }
378
+ } catch (error) {
379
+ showToast('Failed to unpin fact');
380
+ }
381
+ }