superlocalmemory 3.4.5 → 3.4.8

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 (232) hide show
  1. package/ide/hooks/tool-event-hook.sh +46 -0
  2. package/package.json +1 -1
  3. package/pyproject.toml +1 -1
  4. package/src/superlocalmemory/attribution/mathematical_dna.py +1 -1
  5. package/src/superlocalmemory/attribution/signer.py +1 -1
  6. package/src/superlocalmemory/attribution/watermark.py +1 -1
  7. package/src/superlocalmemory/cli/__init__.py +1 -1
  8. package/src/superlocalmemory/cli/commands.py +9 -1
  9. package/src/superlocalmemory/cli/daemon.py +57 -27
  10. package/src/superlocalmemory/cli/ingest_cmd.py +261 -0
  11. package/src/superlocalmemory/cli/json_output.py +1 -1
  12. package/src/superlocalmemory/cli/main.py +21 -1
  13. package/src/superlocalmemory/cli/migrate_cmd.py +1 -1
  14. package/src/superlocalmemory/cli/pending_store.py +1 -1
  15. package/src/superlocalmemory/cli/post_install.py +1 -1
  16. package/src/superlocalmemory/cli/service_installer.py +1 -1
  17. package/src/superlocalmemory/cli/setup_wizard.py +1 -1
  18. package/src/superlocalmemory/code_graph/__init__.py +1 -1
  19. package/src/superlocalmemory/code_graph/blast_radius.py +1 -1
  20. package/src/superlocalmemory/code_graph/bridge/__init__.py +1 -1
  21. package/src/superlocalmemory/code_graph/bridge/entity_resolver.py +1 -1
  22. package/src/superlocalmemory/code_graph/bridge/event_listeners.py +1 -1
  23. package/src/superlocalmemory/code_graph/bridge/fact_enricher.py +1 -1
  24. package/src/superlocalmemory/code_graph/bridge/hebbian_linker.py +1 -1
  25. package/src/superlocalmemory/code_graph/bridge/temporal_checker.py +1 -1
  26. package/src/superlocalmemory/code_graph/changes.py +1 -1
  27. package/src/superlocalmemory/code_graph/communities.py +1 -1
  28. package/src/superlocalmemory/code_graph/config.py +1 -1
  29. package/src/superlocalmemory/code_graph/database.py +1 -1
  30. package/src/superlocalmemory/code_graph/extractors/__init__.py +1 -1
  31. package/src/superlocalmemory/code_graph/extractors/python.py +1 -1
  32. package/src/superlocalmemory/code_graph/extractors/typescript.py +1 -1
  33. package/src/superlocalmemory/code_graph/flows.py +1 -1
  34. package/src/superlocalmemory/code_graph/git_hooks.py +1 -1
  35. package/src/superlocalmemory/code_graph/graph_engine.py +1 -1
  36. package/src/superlocalmemory/code_graph/graph_store.py +1 -1
  37. package/src/superlocalmemory/code_graph/incremental.py +1 -1
  38. package/src/superlocalmemory/code_graph/models.py +1 -1
  39. package/src/superlocalmemory/code_graph/parser.py +1 -1
  40. package/src/superlocalmemory/code_graph/resolver.py +1 -1
  41. package/src/superlocalmemory/code_graph/search.py +1 -1
  42. package/src/superlocalmemory/code_graph/service.py +1 -1
  43. package/src/superlocalmemory/code_graph/watcher.py +1 -1
  44. package/src/superlocalmemory/compliance/abac.py +1 -1
  45. package/src/superlocalmemory/compliance/audit.py +1 -1
  46. package/src/superlocalmemory/compliance/eu_ai_act.py +1 -1
  47. package/src/superlocalmemory/compliance/gdpr.py +1 -1
  48. package/src/superlocalmemory/compliance/lifecycle.py +1 -1
  49. package/src/superlocalmemory/compliance/retention.py +1 -1
  50. package/src/superlocalmemory/compliance/scheduler.py +1 -1
  51. package/src/superlocalmemory/core/config.py +1 -1
  52. package/src/superlocalmemory/core/consolidation_engine.py +52 -1
  53. package/src/superlocalmemory/core/embedding_worker.py +1 -1
  54. package/src/superlocalmemory/core/embeddings.py +1 -1
  55. package/src/superlocalmemory/core/engine.py +17 -1
  56. package/src/superlocalmemory/core/engine_wiring.py +21 -1
  57. package/src/superlocalmemory/core/graph_analyzer.py +15 -1
  58. package/src/superlocalmemory/core/health_monitor.py +1 -1
  59. package/src/superlocalmemory/core/hooks.py +1 -1
  60. package/src/superlocalmemory/core/maintenance.py +1 -1
  61. package/src/superlocalmemory/core/maintenance_scheduler.py +1 -1
  62. package/src/superlocalmemory/core/modes.py +1 -1
  63. package/src/superlocalmemory/core/ollama_embedder.py +1 -1
  64. package/src/superlocalmemory/core/profiles.py +1 -1
  65. package/src/superlocalmemory/core/recall_pipeline.py +16 -3
  66. package/src/superlocalmemory/core/recall_worker.py +1 -1
  67. package/src/superlocalmemory/core/registry.py +1 -1
  68. package/src/superlocalmemory/core/reranker_worker.py +1 -1
  69. package/src/superlocalmemory/core/store_pipeline.py +1 -1
  70. package/src/superlocalmemory/core/summarizer.py +1 -1
  71. package/src/superlocalmemory/core/worker_pool.py +1 -1
  72. package/src/superlocalmemory/dynamics/activation_guided_quantization.py +1 -1
  73. package/src/superlocalmemory/dynamics/eap_scheduler.py +1 -1
  74. package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +1 -1
  75. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +1 -1
  76. package/src/superlocalmemory/encoding/auto_linker.py +1 -1
  77. package/src/superlocalmemory/encoding/cognitive_consolidator.py +1 -1
  78. package/src/superlocalmemory/encoding/consolidator.py +1 -1
  79. package/src/superlocalmemory/encoding/context_generator.py +1 -1
  80. package/src/superlocalmemory/encoding/emotional.py +1 -1
  81. package/src/superlocalmemory/encoding/entity_resolver.py +45 -6
  82. package/src/superlocalmemory/encoding/entropy_gate.py +1 -1
  83. package/src/superlocalmemory/encoding/fact_extractor.py +1 -1
  84. package/src/superlocalmemory/encoding/foresight.py +1 -1
  85. package/src/superlocalmemory/encoding/graph_builder.py +1 -1
  86. package/src/superlocalmemory/encoding/observation_builder.py +1 -1
  87. package/src/superlocalmemory/encoding/scene_builder.py +1 -1
  88. package/src/superlocalmemory/encoding/signal_inference.py +1 -1
  89. package/src/superlocalmemory/encoding/temporal_parser.py +1 -1
  90. package/src/superlocalmemory/encoding/temporal_validator.py +1 -1
  91. package/src/superlocalmemory/encoding/type_router.py +1 -1
  92. package/src/superlocalmemory/hooks/__init__.py +1 -1
  93. package/src/superlocalmemory/hooks/auto_capture.py +1 -1
  94. package/src/superlocalmemory/hooks/auto_invoker.py +1 -1
  95. package/src/superlocalmemory/hooks/auto_parameterize.py +1 -1
  96. package/src/superlocalmemory/hooks/auto_recall.py +1 -1
  97. package/src/superlocalmemory/hooks/claude_code_hooks.py +1 -1
  98. package/src/superlocalmemory/hooks/hook_handlers.py +1 -1
  99. package/src/superlocalmemory/hooks/ide_connector.py +1 -1
  100. package/src/superlocalmemory/hooks/rules_engine.py +1 -1
  101. package/src/superlocalmemory/infra/__init__.py +1 -1
  102. package/src/superlocalmemory/infra/auth_middleware.py +1 -1
  103. package/src/superlocalmemory/infra/backup.py +1 -1
  104. package/src/superlocalmemory/infra/cache_manager.py +1 -1
  105. package/src/superlocalmemory/infra/event_bus.py +1 -1
  106. package/src/superlocalmemory/infra/heartbeat_monitor.py +1 -1
  107. package/src/superlocalmemory/infra/pid_manager.py +1 -1
  108. package/src/superlocalmemory/infra/process_reaper.py +1 -1
  109. package/src/superlocalmemory/infra/rate_limiter.py +1 -1
  110. package/src/superlocalmemory/infra/webhook_dispatcher.py +1 -1
  111. package/src/superlocalmemory/ingestion/__init__.py +1 -1
  112. package/src/superlocalmemory/ingestion/adapter_manager.py +1 -1
  113. package/src/superlocalmemory/ingestion/base_adapter.py +1 -1
  114. package/src/superlocalmemory/ingestion/calendar_adapter.py +1 -1
  115. package/src/superlocalmemory/ingestion/credentials.py +1 -1
  116. package/src/superlocalmemory/ingestion/gmail_adapter.py +1 -1
  117. package/src/superlocalmemory/ingestion/parsers.py +1 -1
  118. package/src/superlocalmemory/ingestion/transcript_adapter.py +1 -1
  119. package/src/superlocalmemory/learning/adaptive.py +1 -1
  120. package/src/superlocalmemory/learning/assertion_miner.py +403 -0
  121. package/src/superlocalmemory/learning/behavioral.py +1 -1
  122. package/src/superlocalmemory/learning/behavioral_listener.py +1 -1
  123. package/src/superlocalmemory/learning/bootstrap.py +1 -1
  124. package/src/superlocalmemory/learning/consolidation_quantization_worker.py +1 -1
  125. package/src/superlocalmemory/learning/consolidation_worker.py +25 -9
  126. package/src/superlocalmemory/learning/cross_project.py +1 -1
  127. package/src/superlocalmemory/learning/database.py +1 -1
  128. package/src/superlocalmemory/learning/engagement.py +2 -3
  129. package/src/superlocalmemory/learning/entity_compiler.py +1 -1
  130. package/src/superlocalmemory/learning/features.py +1 -1
  131. package/src/superlocalmemory/learning/forgetting_scheduler.py +1 -1
  132. package/src/superlocalmemory/learning/outcomes.py +1 -1
  133. package/src/superlocalmemory/learning/project_context.py +1 -1
  134. package/src/superlocalmemory/learning/quantization_scheduler.py +1 -1
  135. package/src/superlocalmemory/learning/ranker.py +1 -1
  136. package/src/superlocalmemory/learning/signals.py +1 -1
  137. package/src/superlocalmemory/learning/workflows.py +1 -1
  138. package/src/superlocalmemory/llm/backbone.py +1 -1
  139. package/src/superlocalmemory/math/ebbinghaus.py +1 -1
  140. package/src/superlocalmemory/math/fisher.py +1 -1
  141. package/src/superlocalmemory/math/fisher_quantized.py +1 -1
  142. package/src/superlocalmemory/math/hopfield.py +1 -1
  143. package/src/superlocalmemory/math/langevin.py +1 -1
  144. package/src/superlocalmemory/math/polar_quant.py +1 -1
  145. package/src/superlocalmemory/math/qjl.py +1 -1
  146. package/src/superlocalmemory/math/sheaf.py +1 -1
  147. package/src/superlocalmemory/math/turbo_quant.py +1 -1
  148. package/src/superlocalmemory/mcp/resources.py +1 -1
  149. package/src/superlocalmemory/mcp/server.py +17 -2
  150. package/src/superlocalmemory/mcp/shared.py +1 -1
  151. package/src/superlocalmemory/mcp/tools.py +1 -1
  152. package/src/superlocalmemory/mcp/tools_active.py +1 -1
  153. package/src/superlocalmemory/mcp/tools_code_graph.py +1 -1
  154. package/src/superlocalmemory/mcp/tools_core.py +1 -1
  155. package/src/superlocalmemory/mcp/tools_learning.py +221 -0
  156. package/src/superlocalmemory/mcp/tools_mesh.py +55 -12
  157. package/src/superlocalmemory/mcp/tools_v28.py +23 -1
  158. package/src/superlocalmemory/mcp/tools_v3.py +1 -1
  159. package/src/superlocalmemory/mcp/tools_v33.py +1 -1
  160. package/src/superlocalmemory/mesh/__init__.py +1 -1
  161. package/src/superlocalmemory/mesh/broker.py +194 -38
  162. package/src/superlocalmemory/parameterization/__init__.py +1 -1
  163. package/src/superlocalmemory/parameterization/pattern_extractor.py +35 -1
  164. package/src/superlocalmemory/parameterization/pii_filter.py +1 -1
  165. package/src/superlocalmemory/parameterization/prompt_injector.py +3 -2
  166. package/src/superlocalmemory/parameterization/prompt_lifecycle.py +1 -1
  167. package/src/superlocalmemory/parameterization/soft_prompt_generator.py +7 -1
  168. package/src/superlocalmemory/retrieval/agentic.py +1 -1
  169. package/src/superlocalmemory/retrieval/ann_index.py +1 -1
  170. package/src/superlocalmemory/retrieval/bm25_channel.py +1 -1
  171. package/src/superlocalmemory/retrieval/bridge_discovery.py +1 -1
  172. package/src/superlocalmemory/retrieval/channel_registry.py +1 -1
  173. package/src/superlocalmemory/retrieval/engine.py +1 -1
  174. package/src/superlocalmemory/retrieval/entity_channel.py +1 -1
  175. package/src/superlocalmemory/retrieval/forgetting_filter.py +1 -1
  176. package/src/superlocalmemory/retrieval/fusion.py +1 -1
  177. package/src/superlocalmemory/retrieval/hopfield_channel.py +1 -1
  178. package/src/superlocalmemory/retrieval/profile_channel.py +1 -1
  179. package/src/superlocalmemory/retrieval/quantization_aware_search.py +1 -1
  180. package/src/superlocalmemory/retrieval/reranker.py +1 -1
  181. package/src/superlocalmemory/retrieval/semantic_channel.py +1 -1
  182. package/src/superlocalmemory/retrieval/spreading_activation.py +1 -1
  183. package/src/superlocalmemory/retrieval/strategy.py +1 -1
  184. package/src/superlocalmemory/retrieval/temporal_channel.py +1 -1
  185. package/src/superlocalmemory/retrieval/vector_store.py +1 -1
  186. package/src/superlocalmemory/server/api.py +1 -1
  187. package/src/superlocalmemory/server/routes/__init__.py +1 -1
  188. package/src/superlocalmemory/server/routes/adapters.py +1 -1
  189. package/src/superlocalmemory/server/routes/agents.py +2 -2
  190. package/src/superlocalmemory/server/routes/backup.py +2 -2
  191. package/src/superlocalmemory/server/routes/behavioral.py +129 -2
  192. package/src/superlocalmemory/server/routes/compliance.py +2 -2
  193. package/src/superlocalmemory/server/routes/data_io.py +2 -2
  194. package/src/superlocalmemory/server/routes/entity.py +1 -1
  195. package/src/superlocalmemory/server/routes/events.py +2 -2
  196. package/src/superlocalmemory/server/routes/helpers.py +2 -2
  197. package/src/superlocalmemory/server/routes/ingest.py +1 -1
  198. package/src/superlocalmemory/server/routes/learning.py +22 -5
  199. package/src/superlocalmemory/server/routes/lifecycle.py +2 -2
  200. package/src/superlocalmemory/server/routes/memories.py +2 -2
  201. package/src/superlocalmemory/server/routes/mesh.py +25 -7
  202. package/src/superlocalmemory/server/routes/profiles.py +2 -2
  203. package/src/superlocalmemory/server/routes/stats.py +26 -7
  204. package/src/superlocalmemory/server/routes/v3_api.py +1 -1
  205. package/src/superlocalmemory/server/routes/ws.py +2 -2
  206. package/src/superlocalmemory/server/ui.py +1 -1
  207. package/src/superlocalmemory/server/unified_daemon.py +42 -1
  208. package/src/superlocalmemory/storage/access_control.py +1 -1
  209. package/src/superlocalmemory/storage/access_log.py +1 -1
  210. package/src/superlocalmemory/storage/database.py +1 -1
  211. package/src/superlocalmemory/storage/embedding_migrator.py +1 -1
  212. package/src/superlocalmemory/storage/migration_v33.py +1 -1
  213. package/src/superlocalmemory/storage/migrations.py +1 -1
  214. package/src/superlocalmemory/storage/models.py +1 -1
  215. package/src/superlocalmemory/storage/quantized_store.py +1 -1
  216. package/src/superlocalmemory/storage/schema.py +1 -1
  217. package/src/superlocalmemory/storage/schema_code_graph.py +1 -1
  218. package/src/superlocalmemory/storage/schema_v32.py +1 -1
  219. package/src/superlocalmemory/storage/schema_v343.py +75 -1
  220. package/src/superlocalmemory/storage/schema_v347.py +136 -0
  221. package/src/superlocalmemory/storage/v2_migrator.py +1 -1
  222. package/src/superlocalmemory/trust/gate.py +1 -1
  223. package/src/superlocalmemory/trust/provenance.py +1 -1
  224. package/src/superlocalmemory/trust/scorer.py +1 -1
  225. package/src/superlocalmemory/trust/signals.py +1 -1
  226. package/src/superlocalmemory/ui/js/behavioral.js +174 -3
  227. package/src/superlocalmemory.egg-info/PKG-INFO +0 -601
  228. package/src/superlocalmemory.egg-info/SOURCES.txt +0 -313
  229. package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
  230. package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
  231. package/src/superlocalmemory.egg-info/requires.txt +0 -55
  232. package/src/superlocalmemory.egg-info/top_level.txt +0 -1
@@ -0,0 +1,403 @@
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
+ """Behavioral Assertion Miner — extracts learned patterns from tool events and facts.
6
+
7
+ Zero-LLM approach: uses frequency analysis, co-occurrence detection, and
8
+ temporal clustering to discover behavioral patterns. No external model calls.
9
+
10
+ Runs as Step 8 in the consolidation pipeline.
11
+
12
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import hashlib
18
+ import json
19
+ import logging
20
+ import sqlite3
21
+ import uuid
22
+ from collections import Counter
23
+ from datetime import datetime, timezone
24
+ from pathlib import Path
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ MIN_EVIDENCE = 3 # Minimum events to create an assertion
29
+ MAX_ASSERTIONS_PER_RUN = 20 # Cap assertions per mining cycle
30
+ REINFORCEMENT_NUDGE = 0.15 # Bayesian confidence increase
31
+ PROMOTION_MIN_PROJECTS = 2 # Minimum projects for cross-project promotion
32
+ PROMOTION_MIN_CONFIDENCE = 0.8 # Minimum avg confidence for promotion
33
+
34
+
35
+ class AssertionMiner:
36
+ """Mine tool_events + atomic_facts for behavioral assertions.
37
+
38
+ Discovers patterns like:
39
+ - "User always reads a file before editing it" (tool sequence)
40
+ - "User corrects verbose output frequently" (correction pattern)
41
+ - "User prefers morning for debugging tasks" (temporal clustering)
42
+ - "User pairs tool X with tool Y 90% of the time" (co-occurrence)
43
+ """
44
+
45
+ def __init__(self, db_path: str | Path):
46
+ self._db_path = str(db_path)
47
+
48
+ def mine(self, profile_id: str = "default") -> dict:
49
+ """Run all mining strategies. Returns summary of assertions created/reinforced."""
50
+ conn = sqlite3.connect(self._db_path, timeout=10)
51
+ conn.row_factory = sqlite3.Row
52
+ results = {"created": 0, "reinforced": 0, "strategies": {}}
53
+
54
+ try:
55
+ # Strategy 1: Tool sequence patterns
56
+ s1 = self._mine_tool_sequences(conn, profile_id)
57
+ results["strategies"]["tool_sequences"] = s1
58
+ results["created"] += s1.get("created", 0)
59
+ results["reinforced"] += s1.get("reinforced", 0)
60
+
61
+ # Strategy 2: Correction patterns
62
+ s2 = self._mine_correction_patterns(conn, profile_id)
63
+ results["strategies"]["corrections"] = s2
64
+ results["created"] += s2.get("created", 0)
65
+ results["reinforced"] += s2.get("reinforced", 0)
66
+
67
+ # Strategy 3: Tool preference patterns
68
+ s3 = self._mine_tool_preferences(conn, profile_id)
69
+ results["strategies"]["tool_preferences"] = s3
70
+ results["created"] += s3.get("created", 0)
71
+ results["reinforced"] += s3.get("reinforced", 0)
72
+
73
+ # Strategy 4: Temporal patterns
74
+ s4 = self._mine_temporal_patterns(conn, profile_id)
75
+ results["strategies"]["temporal"] = s4
76
+ results["created"] += s4.get("created", 0)
77
+ results["reinforced"] += s4.get("reinforced", 0)
78
+
79
+ # Strategy 5: Cross-project assertion promotion
80
+ s5 = self._promote_cross_project(conn, profile_id)
81
+ results["strategies"]["cross_project"] = s5
82
+ results["created"] += s5.get("promoted", 0)
83
+
84
+ conn.commit()
85
+ except Exception as exc:
86
+ logger.warning("Assertion mining failed: %s", exc)
87
+ results["error"] = str(exc)
88
+ finally:
89
+ conn.close()
90
+
91
+ logger.info(
92
+ "Assertion mining: %d created, %d reinforced",
93
+ results["created"], results["reinforced"],
94
+ )
95
+ return results
96
+
97
+ # ------------------------------------------------------------------
98
+ # Strategy 1: Tool sequence patterns
99
+ # ------------------------------------------------------------------
100
+
101
+ def _mine_tool_sequences(self, conn: sqlite3.Connection, profile_id: str) -> dict:
102
+ """Detect repeated tool sequences (A→B patterns)."""
103
+ result = {"created": 0, "reinforced": 0}
104
+
105
+ rows = conn.execute(
106
+ "SELECT tool_name, created_at FROM tool_events "
107
+ "WHERE profile_id = ? ORDER BY created_at ASC LIMIT 5000",
108
+ (profile_id,),
109
+ ).fetchall()
110
+
111
+ if len(rows) < MIN_EVIDENCE * 2:
112
+ return result
113
+
114
+ # Count bigram sequences
115
+ bigrams: Counter = Counter()
116
+ for i in range(len(rows) - 1):
117
+ a = rows[i]["tool_name"]
118
+ b = rows[i + 1]["tool_name"]
119
+ if a != b: # Skip self-loops
120
+ bigrams[(a, b)] += 1
121
+
122
+ total_transitions = sum(bigrams.values())
123
+ for (tool_a, tool_b), count in bigrams.most_common(10):
124
+ if count < MIN_EVIDENCE:
125
+ break
126
+ pct = count / total_transitions
127
+ if pct < 0.05: # Skip rare sequences
128
+ continue
129
+
130
+ trigger = f"when using {tool_a}"
131
+ action = f"typically follow with {tool_b} ({count} times, {pct:.0%} of transitions)"
132
+ r = self._upsert_assertion(
133
+ conn, profile_id,
134
+ trigger=trigger, action=action,
135
+ category="tool_preference",
136
+ evidence_count=count,
137
+ confidence=min(0.9, pct * 2),
138
+ )
139
+ result[r] = result.get(r, 0) + 1
140
+
141
+ return result
142
+
143
+ # ------------------------------------------------------------------
144
+ # Strategy 2: Correction patterns
145
+ # ------------------------------------------------------------------
146
+
147
+ def _mine_correction_patterns(self, conn: sqlite3.Connection, profile_id: str) -> dict:
148
+ """Detect tools with high correction rates."""
149
+ result = {"created": 0, "reinforced": 0}
150
+
151
+ rows = conn.execute(
152
+ "SELECT tool_name, event_type, COUNT(*) as cnt "
153
+ "FROM tool_events WHERE profile_id = ? "
154
+ "GROUP BY tool_name, event_type",
155
+ (profile_id,),
156
+ ).fetchall()
157
+
158
+ tool_stats: dict = {}
159
+ for row in rows:
160
+ tool = row["tool_name"]
161
+ if tool not in tool_stats:
162
+ tool_stats[tool] = {"total": 0, "corrections": 0, "errors": 0}
163
+ tool_stats[tool]["total"] += row["cnt"]
164
+ if row["event_type"] == "correction":
165
+ tool_stats[tool]["corrections"] = row["cnt"]
166
+ elif row["event_type"] == "error":
167
+ tool_stats[tool]["errors"] = row["cnt"]
168
+
169
+ for tool, stats in tool_stats.items():
170
+ if stats["total"] < MIN_EVIDENCE:
171
+ continue
172
+ correction_rate = stats["corrections"] / stats["total"]
173
+ if correction_rate > 0.15: # 15%+ correction rate
174
+ trigger = f"when using {tool}"
175
+ action = (
176
+ f"high correction rate ({stats['corrections']}/{stats['total']} = "
177
+ f"{correction_rate:.0%}) — review output before accepting"
178
+ )
179
+ r = self._upsert_assertion(
180
+ conn, profile_id,
181
+ trigger=trigger, action=action,
182
+ category="workflow",
183
+ evidence_count=stats["corrections"],
184
+ confidence=min(0.85, correction_rate),
185
+ )
186
+ result[r] = result.get(r, 0) + 1
187
+
188
+ return result
189
+
190
+ # ------------------------------------------------------------------
191
+ # Strategy 3: Tool preference patterns
192
+ # ------------------------------------------------------------------
193
+
194
+ def _mine_tool_preferences(self, conn: sqlite3.Connection, profile_id: str) -> dict:
195
+ """Detect dominant tool usage patterns."""
196
+ result = {"created": 0, "reinforced": 0}
197
+
198
+ rows = conn.execute(
199
+ "SELECT tool_name, COUNT(*) as cnt "
200
+ "FROM tool_events WHERE profile_id = ? "
201
+ "GROUP BY tool_name ORDER BY cnt DESC LIMIT 10",
202
+ (profile_id,),
203
+ ).fetchall()
204
+
205
+ if not rows:
206
+ return result
207
+
208
+ total = sum(r["cnt"] for r in rows)
209
+ for row in rows[:5]:
210
+ tool = row["tool_name"]
211
+ count = row["cnt"]
212
+ pct = count / total
213
+ if count >= MIN_EVIDENCE and pct > 0.1:
214
+ trigger = "general workflow"
215
+ action = f"frequently uses {tool} ({count} times, {pct:.0%} of all tool usage)"
216
+ r = self._upsert_assertion(
217
+ conn, profile_id,
218
+ trigger=trigger, action=action,
219
+ category="tool_preference",
220
+ evidence_count=count,
221
+ confidence=min(0.8, pct),
222
+ )
223
+ result[r] = result.get(r, 0) + 1
224
+
225
+ return result
226
+
227
+ # ------------------------------------------------------------------
228
+ # Strategy 4: Temporal patterns
229
+ # ------------------------------------------------------------------
230
+
231
+ def _mine_temporal_patterns(self, conn: sqlite3.Connection, profile_id: str) -> dict:
232
+ """Detect time-of-day usage patterns."""
233
+ result = {"created": 0, "reinforced": 0}
234
+
235
+ rows = conn.execute(
236
+ "SELECT created_at FROM tool_events "
237
+ "WHERE profile_id = ? AND created_at IS NOT NULL LIMIT 5000",
238
+ (profile_id,),
239
+ ).fetchall()
240
+
241
+ if len(rows) < MIN_EVIDENCE:
242
+ return result
243
+
244
+ hour_counts: Counter = Counter()
245
+ for row in rows:
246
+ try:
247
+ dt = datetime.fromisoformat(row["created_at"])
248
+ hour_counts[dt.hour] += 1
249
+ except (ValueError, TypeError):
250
+ continue
251
+
252
+ if not hour_counts:
253
+ return result
254
+
255
+ total = sum(hour_counts.values())
256
+ # Find peak hours (>15% of activity)
257
+ peak_hours = [h for h, c in hour_counts.most_common() if c / total > 0.15]
258
+ if peak_hours:
259
+ hour_labels = ", ".join(f"{h}:00" for h in sorted(peak_hours))
260
+ trigger = "session scheduling"
261
+ action = f"peak activity hours: {hour_labels} (based on {total} events)"
262
+ r = self._upsert_assertion(
263
+ conn, profile_id,
264
+ trigger=trigger, action=action,
265
+ category="workflow",
266
+ evidence_count=total,
267
+ confidence=0.6,
268
+ )
269
+ result[r] = result.get(r, 0) + 1
270
+
271
+ return result
272
+
273
+ # ------------------------------------------------------------------
274
+ # Strategy 5: Cross-project assertion promotion
275
+ # ------------------------------------------------------------------
276
+
277
+ def _promote_cross_project(
278
+ self, conn: sqlite3.Connection, profile_id: str,
279
+ ) -> dict:
280
+ """Promote assertions that appear in 2+ projects to global scope.
281
+
282
+ When the same trigger+action pattern is observed across multiple
283
+ project_paths with avg confidence >= 0.8, create a global assertion
284
+ (project_path='') so it applies everywhere.
285
+ """
286
+ result = {"promoted": 0, "candidates": 0}
287
+
288
+ # Find assertions grouped by trigger+action across projects
289
+ rows = conn.execute(
290
+ "SELECT trigger_condition, action, category, "
291
+ "COUNT(DISTINCT project_path) AS project_count, "
292
+ "AVG(confidence) AS avg_confidence, "
293
+ "SUM(evidence_count) AS total_evidence "
294
+ "FROM behavioral_assertions "
295
+ "WHERE profile_id = ? AND project_path != '' "
296
+ "GROUP BY trigger_condition, action "
297
+ "HAVING COUNT(DISTINCT project_path) >= ?",
298
+ (profile_id, PROMOTION_MIN_PROJECTS),
299
+ ).fetchall()
300
+
301
+ result["candidates"] = len(rows)
302
+
303
+ for row in rows:
304
+ avg_conf = row["avg_confidence"]
305
+ if avg_conf < PROMOTION_MIN_CONFIDENCE:
306
+ continue
307
+
308
+ trigger = row["trigger_condition"]
309
+ action = row["action"]
310
+ category = row["category"]
311
+ total_ev = row["total_evidence"]
312
+ project_count = row["project_count"]
313
+
314
+ # Check if global assertion already exists
315
+ global_id = hashlib.sha256(
316
+ f"{profile_id}:{trigger}:{action}".encode()
317
+ ).hexdigest()[:16]
318
+
319
+ existing = conn.execute(
320
+ "SELECT id FROM behavioral_assertions "
321
+ "WHERE id = ? AND project_path = ''",
322
+ (global_id,),
323
+ ).fetchone()
324
+
325
+ if existing:
326
+ # Reinforce existing global assertion
327
+ now = datetime.now(timezone.utc).isoformat()
328
+ conn.execute(
329
+ "UPDATE behavioral_assertions SET "
330
+ "confidence = MIN(0.95, confidence + ?), "
331
+ "evidence_count = ?, "
332
+ "reinforcement_count = reinforcement_count + 1, "
333
+ "last_reinforced_at = ?, updated_at = ? "
334
+ "WHERE id = ?",
335
+ (REINFORCEMENT_NUDGE, total_ev, now, now, global_id),
336
+ )
337
+ else:
338
+ # Create new global assertion from cross-project evidence
339
+ now = datetime.now(timezone.utc).isoformat()
340
+ promoted_conf = min(0.9, avg_conf)
341
+ conn.execute(
342
+ "INSERT INTO behavioral_assertions "
343
+ "(id, profile_id, project_path, trigger_condition, action, "
344
+ " category, confidence, evidence_count, source, "
345
+ " created_at, updated_at) "
346
+ "VALUES (?, ?, '', ?, ?, ?, ?, ?, 'cross_project', ?, ?)",
347
+ (global_id, profile_id, trigger, action,
348
+ category, round(promoted_conf, 4), total_ev, now, now),
349
+ )
350
+ result["promoted"] += 1
351
+ logger.info(
352
+ "Promoted assertion to global: '%s' → '%s' "
353
+ "(from %d projects, avg_conf=%.2f)",
354
+ trigger, action, project_count, avg_conf,
355
+ )
356
+
357
+ return result
358
+
359
+ # ------------------------------------------------------------------
360
+ # Upsert logic
361
+ # ------------------------------------------------------------------
362
+
363
+ def _upsert_assertion(
364
+ self, conn: sqlite3.Connection, profile_id: str, *,
365
+ trigger: str, action: str, category: str,
366
+ evidence_count: int, confidence: float,
367
+ project_path: str = "",
368
+ ) -> str:
369
+ """Create or reinforce a behavioral assertion. Returns 'created' or 'reinforced'."""
370
+ now = datetime.now(timezone.utc).isoformat()
371
+
372
+ # Deterministic ID from trigger+action for idempotent upsert
373
+ assertion_id = hashlib.sha256(
374
+ f"{profile_id}:{trigger}:{action}".encode()
375
+ ).hexdigest()[:16]
376
+
377
+ existing = conn.execute(
378
+ "SELECT id, confidence, reinforcement_count FROM behavioral_assertions WHERE id = ?",
379
+ (assertion_id,),
380
+ ).fetchone()
381
+
382
+ if existing:
383
+ # Reinforce: Bayesian nudge toward 1.0
384
+ old_conf = existing["confidence"]
385
+ new_conf = old_conf + (1.0 - old_conf) * REINFORCEMENT_NUDGE
386
+ conn.execute(
387
+ "UPDATE behavioral_assertions SET confidence = ?, "
388
+ "evidence_count = ?, reinforcement_count = reinforcement_count + 1, "
389
+ "last_reinforced_at = ?, updated_at = ? WHERE id = ?",
390
+ (round(new_conf, 4), evidence_count, now, now, assertion_id),
391
+ )
392
+ return "reinforced"
393
+ else:
394
+ # Create new assertion at initial confidence
395
+ conn.execute(
396
+ "INSERT INTO behavioral_assertions "
397
+ "(id, profile_id, project_path, trigger_condition, action, "
398
+ " category, confidence, evidence_count, source, created_at, updated_at) "
399
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'auto', ?, ?)",
400
+ (assertion_id, profile_id, project_path, trigger, action,
401
+ category, round(min(confidence, 0.7), 4), evidence_count, now, now),
402
+ )
403
+ return "created"
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """SuperLocalMemory V3 — Behavioral Pattern Store.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Behavioral listener — subscribes to event bus, captures patterns.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Synthetic bootstrap for cold-start ML ranking.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """CCQ Worker — background scheduling for Cognitive Consolidation Quantization.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Sleep-Time Consolidation Worker — background memory maintenance.
@@ -332,14 +332,30 @@ class ConsolidationWorker:
332
332
  except (ValueError, TypeError):
333
333
  pass
334
334
 
335
+ # v3.4.7: Resolve entity IDs to readable canonical names
336
+ entity_names: dict = {}
337
+ try:
338
+ eid_list = list(entity_counts.keys())
339
+ if eid_list:
340
+ placeholders = ",".join("?" * len(eid_list))
341
+ name_rows = conn.execute(
342
+ f"SELECT entity_id, canonical_name FROM canonical_entities "
343
+ f"WHERE entity_id IN ({placeholders})",
344
+ eid_list,
345
+ ).fetchall()
346
+ entity_names = {dict(r)["entity_id"]: dict(r)["canonical_name"] for r in name_rows}
347
+ except Exception:
348
+ pass
349
+
335
350
  for entity, count in entity_counts.most_common(15):
336
351
  if count >= 3 and not dry_run:
352
+ readable = entity_names.get(entity, entity)
337
353
  confidence = min(1.0, count / max(len(facts) * 0.05, 10))
338
354
  store.record_pattern(
339
355
  profile_id=profile_id,
340
- pattern_type="interest",
341
- data={"topic": entity, "pattern_key": f"entity:{entity}",
342
- "value": entity, "evidence": count,
356
+ pattern_type="entity_preferences",
357
+ data={"topic": readable, "pattern_key": f"entity:{readable}",
358
+ "value": readable, "evidence": count,
343
359
  "source": "entity_frequency"},
344
360
  success_rate=confidence,
345
361
  confidence=confidence,
@@ -359,7 +375,7 @@ class ConsolidationWorker:
359
375
  if heavy_sessions and not dry_run:
360
376
  store.record_pattern(
361
377
  profile_id=profile_id,
362
- pattern_type="workflow",
378
+ pattern_type="session_activity",
363
379
  data={"pattern_key": "heavy_session_usage",
364
380
  "value": f"{len(heavy_sessions)} intensive sessions",
365
381
  "evidence": len(heavy_sessions),
@@ -382,7 +398,7 @@ class ConsolidationWorker:
382
398
  pct = round(dominant_type[1] / total_ft * 100)
383
399
  store.record_pattern(
384
400
  profile_id=profile_id,
385
- pattern_type="style",
401
+ pattern_type="fact_type_distribution",
386
402
  data={"pattern_key": "memory_style",
387
403
  "value": f"{dominant_type[0]} dominant ({pct}%)",
388
404
  "evidence": dominant_type[1],
@@ -415,7 +431,7 @@ class ConsolidationWorker:
415
431
  if cnt >= 5 and not dry_run:
416
432
  store.record_pattern(
417
433
  profile_id=profile_id,
418
- pattern_type="style",
434
+ pattern_type="channel_performance",
419
435
  data={"pattern_key": f"channel:{ch}",
420
436
  "value": f"{ch} ({cnt} hits, {avg_sig} avg)",
421
437
  "evidence": cnt,
@@ -437,7 +453,7 @@ class ConsolidationWorker:
437
453
  if coret_rows and not dry_run:
438
454
  store.record_pattern(
439
455
  profile_id=profile_id,
440
- pattern_type="workflow",
456
+ pattern_type="co_retrieval_clusters",
441
457
  data={"pattern_key": "co_retrieval_clusters",
442
458
  "value": f"{len(coret_rows)} strong fact pairs",
443
459
  "evidence": len(coret_rows),
@@ -466,7 +482,7 @@ class ConsolidationWorker:
466
482
  total_comm = sum(dict(r)["cnt"] for r in comm_rows)
467
483
  store.record_pattern(
468
484
  profile_id=profile_id,
469
- pattern_type="style",
485
+ pattern_type="knowledge_structure",
470
486
  data={"pattern_key": "knowledge_structure",
471
487
  "value": f"{len(comm_rows)} topic communities, {total_comm} classified facts",
472
488
  "evidence": total_comm,
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Cross-project knowledge aggregator.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """SuperLocalMemory V3 — Learning Database.
@@ -1,7 +1,6 @@
1
- #!/usr/bin/env python3
2
- # SPDX-License-Identifier: Elastic-2.0
3
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
4
- # Part of Qualixar | Author: Varun Pratap Bhardwaj (qualixar.com | varunpratap.com)
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
5
4
  """
6
5
  EngagementTracker -- Local-only engagement metrics for V3 learning.
7
6
 
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Entity Compilation Engine — auto-generates compiled truth per entity.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """V3-native feature extractor for adaptive ranking.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Forgetting scheduler — periodic retention decay and lifecycle management.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """SuperLocalMemory V3 — Outcome Tracking & Inference.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Project context detection via filesystem analysis.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3
4
4
 
5
5
  """Quantization Scheduler -- combined SAGQ + EAP precision management.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """3-phase adaptive ranker — from heuristic to ML.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Zero-Cost Learning Signals — mathematical learning without LLM tokens.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Workflow pattern miner -- sliding-window sequence and temporal mining.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """LLM backbone — unified interface for LLM providers.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Ebbinghaus forgetting curve — memory strength and retention.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Fisher-Rao geodesic metric with Bayesian variance update.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
4
 
5
5
  """Fisher-Rao Quantization-Aware Distance (FRQAD).
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
- # Licensed under the Elastic License 2.0 - see LICENSE file
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
3
  # Part of SuperLocalMemory V3
4
4
 
5
5
  """Modern Continuous Hopfield Network (Ramsauer et al., 2020).