superlocalmemory 3.4.11 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superlocalmemory",
3
- "version": "3.4.11",
3
+ "version": "3.4.12",
4
4
  "description": "Information-geometric agent memory with mathematical guarantees. 4-channel retrieval, Fisher-Rao similarity, zero-LLM mode, EU AI Act compliant. Works with Claude, Cursor, Windsurf, and 17+ AI tools.",
5
5
  "keywords": [
6
6
  "ai-memory",
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "superlocalmemory"
3
- version = "3.4.11"
3
+ version = "3.4.12"
4
4
  description = "Information-geometric agent memory with mathematical guarantees"
5
5
  readme = "README.md"
6
6
  license = {text = "AGPL-3.0-or-later"}
@@ -187,6 +187,37 @@ class RetrievalEngine:
187
187
  except Exception as exc:
188
188
  logger.warning("Scene expansion: %s", exc)
189
189
 
190
+ # V3.4.11: Entity graph signal enhancement (post-RRF boost)
191
+ # Instead of competing as independent channel, entity_graph SCORES
192
+ # the candidates from other channels by graph proximity to query entities.
193
+ # Research: Microsoft GraphRAG DRIFT, Pistis-RAG cascaded architecture.
194
+ if (self._entity is not None
195
+ and "entity_graph" not in set(self._config.disabled_channels)
196
+ and fused):
197
+ try:
198
+ candidate_ids = [fr.fact_id for fr in fused[:100]]
199
+ eg_scores = self._entity.score_candidates(
200
+ query, candidate_ids, profile_id,
201
+ )
202
+ if eg_scores:
203
+ boosted = []
204
+ for fr in fused:
205
+ eg_sc = eg_scores.get(fr.fact_id, 0.0)
206
+ if eg_sc > 0:
207
+ eg_weight = strat.weights.get("entity_graph", 1.0)
208
+ boost = 1.0 + eg_sc * eg_weight * 0.3
209
+ boosted.append(FusionResult(
210
+ fact_id=fr.fact_id,
211
+ fused_score=fr.fused_score * boost,
212
+ channel_ranks=fr.channel_ranks,
213
+ channel_scores={**fr.channel_scores, "entity_graph": eg_sc},
214
+ ))
215
+ else:
216
+ boosted.append(fr)
217
+ fused = sorted(boosted, key=lambda r: r.fused_score, reverse=True)
218
+ except Exception as exc:
219
+ logger.warning("Entity graph signal enhancement: %s", exc)
220
+
190
221
  # 4. Load facts for rerank pool
191
222
  pool = min(len(fused), max(effective_limit * 3, 30))
192
223
  top = fused[:pool]
@@ -448,13 +479,9 @@ class RetrievalEngine:
448
479
  except Exception as exc:
449
480
  logger.warning("BM25 channel: %s", exc)
450
481
 
451
- if self._entity is not None and "entity_graph" not in disabled:
452
- try:
453
- r = self._entity.search(query, profile_id, top_k=self._config.bm25_top_k)
454
- if r:
455
- out["entity_graph"] = r
456
- except Exception as exc:
457
- logger.warning("Entity channel: %s", exc)
482
+ # V3.4.12: entity_graph is now a signal enhancer (post-RRF boost),
483
+ # not an independent channel. Removed from channel execution to avoid
484
+ # running spreading activation twice. See score_candidates() in engine.recall().
458
485
 
459
486
  if self._temporal is not None and "temporal" not in disabled:
460
487
  try:
@@ -370,6 +370,124 @@ class EntityGraphChannel:
370
370
  results.sort(key=lambda x: x[1], reverse=True)
371
371
  return results[:top_k]
372
372
 
373
+ def score_candidates(
374
+ self,
375
+ query: str,
376
+ candidate_fact_ids: list[str],
377
+ profile_id: str,
378
+ ) -> dict[str, float]:
379
+ """Score candidate facts by their entity-graph proximity to query entities.
380
+
381
+ V3.4.11 "Signal Enhancer" architecture: instead of returning its own
382
+ independent set of fact_ids (which get outranked by multi-channel facts
383
+ in RRF), this method scores EXISTING candidates from semantic/BM25
384
+ by their graph connectivity to query entities.
385
+
386
+ Research basis: Microsoft GraphRAG DRIFT Search, HippoRAG, Pistis-RAG
387
+ cascaded architecture. Graph signals act as post-retrieval boosters,
388
+ not independent retrievers. Avoids the "weakest link" phenomenon where
389
+ non-overlapping result sets cause rank collapse in RRF fusion.
390
+
391
+ Args:
392
+ query: The user's query string.
393
+ candidate_fact_ids: Fact IDs from semantic/BM25/other channels.
394
+ profile_id: User profile.
395
+
396
+ Returns:
397
+ Dict mapping fact_id → entity_graph score [0, 1].
398
+ Facts with no entity connection return 0.
399
+ Facts directly linked to query entities score ~1.0.
400
+ Facts 1-hop away score ~0.7 (decay factor).
401
+ """
402
+ if not candidate_fact_ids:
403
+ return {}
404
+
405
+ raw_entities = extract_query_entities(query)
406
+ if not raw_entities:
407
+ return {}
408
+
409
+ canonical_ids = self._resolve_entities(raw_entities, profile_id)
410
+ if not canonical_ids:
411
+ return {}
412
+
413
+ self._ensure_adjacency(profile_id)
414
+
415
+ # Run full spreading activation (same as search())
416
+ activation: dict[str, float] = defaultdict(float)
417
+ visited_entities: set[str] = set(canonical_ids)
418
+ use_cache = bool(self._entity_to_facts)
419
+
420
+ for eid in canonical_ids:
421
+ if use_cache:
422
+ for fid in self._entity_to_facts.get(eid, ()):
423
+ activation[fid] = max(activation[fid], 1.0)
424
+ else:
425
+ for fact in self._db.get_facts_by_entity(eid, profile_id):
426
+ activation[fact.fact_id] = max(activation[fact.fact_id], 1.0)
427
+
428
+ frontier = set(activation.keys())
429
+ for hop in range(1, self._max_hops):
430
+ hop_decay = self._decay ** hop
431
+ if hop_decay < self._threshold:
432
+ break
433
+ next_frontier: set[str] = set()
434
+ for fid in frontier:
435
+ if use_cache:
436
+ for neighbor, edge_weight in self._adj.get(fid, ()):
437
+ if self._graph_metrics:
438
+ weighted = activation[fid] * self._decay * edge_weight
439
+ if neighbor in self._graph_metrics:
440
+ pr = self._graph_metrics[neighbor].get("pagerank_score", 0.0)
441
+ weighted *= min(1.0 + pr * 2.0, 2.0)
442
+ else:
443
+ weighted = activation[fid] * self._decay
444
+ if weighted >= self._threshold and weighted > activation.get(neighbor, 0.0):
445
+ activation[neighbor] = weighted
446
+ next_frontier.add(neighbor)
447
+
448
+ if use_cache:
449
+ for fid in frontier:
450
+ for eid in self._fact_to_entities.get(fid, ()):
451
+ if eid not in visited_entities:
452
+ visited_entities.add(eid)
453
+ for linked_fid in self._entity_to_facts.get(eid, ()):
454
+ if hop_decay > activation.get(linked_fid, 0.0):
455
+ activation[linked_fid] = hop_decay
456
+ next_frontier.add(linked_fid)
457
+
458
+ frontier = next_frontier
459
+ if not frontier:
460
+ break
461
+
462
+ # Community-aware boosting (same as search)
463
+ if self._graph_metrics and use_cache:
464
+ from collections import Counter as _Counter
465
+ seed_communities: _Counter = _Counter()
466
+ for eid in canonical_ids:
467
+ for fid in self._entity_to_facts.get(eid, ()):
468
+ m = self._graph_metrics.get(fid, {})
469
+ comm = m.get("community_id")
470
+ if comm is not None:
471
+ seed_communities[comm] += 1
472
+ if seed_communities:
473
+ total_seeds = sum(seed_communities.values())
474
+ for fid in list(activation.keys()):
475
+ m = self._graph_metrics.get(fid, {})
476
+ fact_comm = m.get("community_id")
477
+ if fact_comm is not None and fact_comm in seed_communities:
478
+ boost = min(1.0 + 0.15 * (seed_communities[fact_comm] / total_seeds), 1.3)
479
+ activation[fid] *= boost
480
+
481
+ # Extract scores ONLY for the candidate set, normalize to [0, 1]
482
+ candidate_set = set(candidate_fact_ids)
483
+ scored = {fid: activation.get(fid, 0.0) for fid in candidate_set}
484
+
485
+ max_score = max(scored.values()) if scored else 0
486
+ if max_score > 0:
487
+ scored = {fid: sc / max_score for fid, sc in scored.items()}
488
+
489
+ return scored
490
+
373
491
  def _suppress_contradictions(
374
492
  self, activation: dict[str, float], profile_id: str,
375
493
  ) -> None:
@@ -587,6 +587,9 @@ def _register_daemon_routes(application: FastAPI) -> None:
587
587
  "score": round(r.score, 4),
588
588
  "fact_type": getattr(r.fact.fact_type, 'value', str(r.fact.fact_type)),
589
589
  "fact_id": r.fact.fact_id,
590
+ "channel_scores": {
591
+ k: round(v, 4) for k, v in r.channel_scores.items()
592
+ } if r.channel_scores else {},
590
593
  }
591
594
  for r in response.results
592
595
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: superlocalmemory
3
- Version: 3.4.11
3
+ Version: 3.4.12
4
4
  Summary: Information-geometric agent memory with mathematical guarantees
5
5
  Author-email: Varun Pratap Bhardwaj <admin@superlocalmemory.com>
6
6
  License: AGPL-3.0-or-later
@@ -83,7 +83,7 @@ Dynamic: license-file
83
83
 
84
84
  <h1 align="center">SuperLocalMemory V3.4</h1>
85
85
  <p align="center"><strong>Every other AI forgets. Yours won't.</strong><br/><em>Infinite memory for Claude Code, Cursor, Windsurf & 17+ AI tools.</em></p>
86
- <p align="center"><code>v3.4.4 "Neural Glass"</code> — Install once. Every session remembers the last. Automatically.</p>
86
+ <p align="center"><code>v3.4.11</code> — Install once. Every session remembers the last. Automatically.</p>
87
87
  <p align="center"><strong>Backed by 3 peer-reviewed research papers</strong> · <a href="https://arxiv.org/abs/2603.02240">arXiv:2603.02240</a> · <a href="https://arxiv.org/abs/2603.14588">arXiv:2603.14588</a> · <a href="https://arxiv.org/abs/2604.04514">arXiv:2604.04514</a></p>
88
88
 
89
89
  <p align="center">
@@ -457,15 +457,20 @@ Auto-capture hooks: `slm hooks install` + `slm observe` + `slm session-context`.
457
457
  - Auto-learned soft prompts injected into agent context
458
458
  - Behavioral pattern detection and outcome tracking
459
459
 
460
- ### Skill Evolution (NEW in v3.4.10)
461
- - **Per-skill performance tracking** — automatically tracks which skills succeed and which fail, across sessions (zero-LLM, always on)
462
- - **Execution trace analysis** — mines tool usage patterns around skill invocations to determine effectiveness
463
- - **Skill entities in Entity Explorer** — each skill becomes a browsable entity with performance facts and evolution history
464
- - **Dedicated Skill Evolution dashboard tab** — overview cards, performance assertions, skill correlations
465
- - **Behavioral assertions for skill routing** — soft prompts recommend high-performing skills in future sessions
466
- - **LLM-powered skill evolution** — 3-trigger system (post-session + degradation + health check) with blind verification. **Off by default** — opt-in via `slm config set evolution.enabled true`. Supports Ollama (free, local), Anthropic API, and OpenAI API backends.
467
- - **ECC integration** — enhanced observation support with [Everything Claude Code](https://github.com/affaan-m/everything-claude-code) via `slm ingest --source ecc`
468
- - **IDE compatibility:** Skill tracking currently works with Claude Code. The `/api/v3/tool-event` endpoint accepts events from any IDE client — adapters for other IDEs in future releases.
460
+ ### Skill Evolution
461
+ - **Per-skill performance tracking** — tracks which skills succeed and fail across sessions (zero-LLM, always on)
462
+ - **Evolution engine** — 3-trigger system with blind verification. Off by default enable via `slm config set evolution.enabled true`
463
+ - **MCP tools** — `evolve_skill`, `skill_health`, `skill_lineage` for programmatic access
464
+ - **Lineage DAG** — visual evolution history in the dashboard
465
+ - **CLI config** — `slm config get/set` for all evolution settings
466
+ - **Post-session triggers** — automatic analysis on session end via Stop hook
467
+ - **[ECC](https://github.com/affaan-m/everything-claude-code) integration** — optional enhanced observations via `slm ingest --source ecc`
468
+
469
+ ### Tiered Storage & Scaling
470
+ - **4-tier lifecycle** — active, warm, cold, archived with automatic promotion/demotion
471
+ - **Deep recall** — archived facts searchable at reduced weight
472
+ - **Graph pruning** — automatic cleanup of orphan edges, self-loops, duplicates
473
+ - **Fact consolidation** — clusters related facts into consolidated summaries
469
474
 
470
475
  ### Trust & Security
471
476
  - Bayesian Beta-distribution trust scoring (per-agent, per-fact)
@@ -476,9 +481,9 @@ Auto-capture hooks: `slm hooks install` + `slm observe` + `slm session-context`.
476
481
  ### Infrastructure
477
482
  - 23-tab web dashboard with real-time visualization
478
483
  - 17+ IDE integrations (Claude, Cursor, Windsurf, VS Code, JetBrains, Zed, etc.)
479
- - 35 MCP tools + 7 MCP resources
484
+ - 38 MCP tools + 7 MCP resources
480
485
  - Profile isolation (independent memory spaces)
481
- - 1400+ tests, AGPL v3, cross-platform (Mac/Linux/Windows)
486
+ - 2,900+ tests, AGPL v3, cross-platform (Mac/Linux/Windows)
482
487
  - CPU-only — no GPU required
483
488
  - Automatic orphaned process cleanup
484
489