superlocalmemory 2.7.6 → 2.8.0

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 (170) hide show
  1. package/CHANGELOG.md +120 -155
  2. package/README.md +115 -89
  3. package/api_server.py +2 -12
  4. package/docs/PATTERN-LEARNING.md +64 -199
  5. package/docs/example_graph_usage.py +4 -6
  6. package/install.sh +59 -0
  7. package/mcp_server.py +83 -7
  8. package/package.json +1 -8
  9. package/scripts/generate-thumbnails.py +3 -5
  10. package/skills/slm-build-graph/SKILL.md +1 -1
  11. package/skills/slm-list-recent/SKILL.md +1 -1
  12. package/skills/slm-recall/SKILL.md +1 -1
  13. package/skills/slm-remember/SKILL.md +1 -1
  14. package/skills/slm-show-patterns/SKILL.md +1 -1
  15. package/skills/slm-status/SKILL.md +1 -1
  16. package/skills/slm-switch-profile/SKILL.md +1 -1
  17. package/src/agent_registry.py +7 -18
  18. package/src/auth_middleware.py +3 -5
  19. package/src/auto_backup.py +3 -7
  20. package/src/behavioral/__init__.py +49 -0
  21. package/src/behavioral/behavioral_listener.py +203 -0
  22. package/src/behavioral/behavioral_patterns.py +275 -0
  23. package/src/behavioral/cross_project_transfer.py +206 -0
  24. package/src/behavioral/outcome_inference.py +194 -0
  25. package/src/behavioral/outcome_tracker.py +193 -0
  26. package/src/behavioral/tests/__init__.py +4 -0
  27. package/src/behavioral/tests/test_behavioral_integration.py +108 -0
  28. package/src/behavioral/tests/test_behavioral_patterns.py +150 -0
  29. package/src/behavioral/tests/test_cross_project_transfer.py +142 -0
  30. package/src/behavioral/tests/test_mcp_behavioral.py +139 -0
  31. package/src/behavioral/tests/test_mcp_report_outcome.py +117 -0
  32. package/src/behavioral/tests/test_outcome_inference.py +107 -0
  33. package/src/behavioral/tests/test_outcome_tracker.py +96 -0
  34. package/src/cache_manager.py +4 -6
  35. package/src/compliance/__init__.py +48 -0
  36. package/src/compliance/abac_engine.py +149 -0
  37. package/src/compliance/abac_middleware.py +116 -0
  38. package/src/compliance/audit_db.py +215 -0
  39. package/src/compliance/audit_logger.py +148 -0
  40. package/src/compliance/retention_manager.py +289 -0
  41. package/src/compliance/retention_scheduler.py +186 -0
  42. package/src/compliance/tests/__init__.py +4 -0
  43. package/src/compliance/tests/test_abac_enforcement.py +95 -0
  44. package/src/compliance/tests/test_abac_engine.py +124 -0
  45. package/src/compliance/tests/test_abac_mcp_integration.py +118 -0
  46. package/src/compliance/tests/test_audit_db.py +123 -0
  47. package/src/compliance/tests/test_audit_logger.py +98 -0
  48. package/src/compliance/tests/test_mcp_audit.py +128 -0
  49. package/src/compliance/tests/test_mcp_retention_policy.py +125 -0
  50. package/src/compliance/tests/test_retention_manager.py +131 -0
  51. package/src/compliance/tests/test_retention_scheduler.py +99 -0
  52. package/src/db_connection_manager.py +2 -12
  53. package/src/embedding_engine.py +61 -669
  54. package/src/embeddings/__init__.py +47 -0
  55. package/src/embeddings/cache.py +70 -0
  56. package/src/embeddings/cli.py +113 -0
  57. package/src/embeddings/constants.py +47 -0
  58. package/src/embeddings/database.py +91 -0
  59. package/src/embeddings/engine.py +247 -0
  60. package/src/embeddings/model_loader.py +145 -0
  61. package/src/event_bus.py +3 -13
  62. package/src/graph/__init__.py +36 -0
  63. package/src/graph/build_helpers.py +74 -0
  64. package/src/graph/cli.py +87 -0
  65. package/src/graph/cluster_builder.py +188 -0
  66. package/src/graph/cluster_summary.py +148 -0
  67. package/src/graph/constants.py +47 -0
  68. package/src/graph/edge_builder.py +162 -0
  69. package/src/graph/entity_extractor.py +95 -0
  70. package/src/graph/graph_core.py +226 -0
  71. package/src/graph/graph_search.py +231 -0
  72. package/src/graph/hierarchical.py +207 -0
  73. package/src/graph/schema.py +99 -0
  74. package/src/graph_engine.py +45 -1451
  75. package/src/hnsw_index.py +3 -7
  76. package/src/hybrid_search.py +36 -683
  77. package/src/learning/__init__.py +27 -12
  78. package/src/learning/adaptive_ranker.py +50 -12
  79. package/src/learning/cross_project_aggregator.py +2 -12
  80. package/src/learning/engagement_tracker.py +2 -12
  81. package/src/learning/feature_extractor.py +175 -43
  82. package/src/learning/feedback_collector.py +7 -12
  83. package/src/learning/learning_db.py +180 -12
  84. package/src/learning/project_context_manager.py +2 -12
  85. package/src/learning/source_quality_scorer.py +2 -12
  86. package/src/learning/synthetic_bootstrap.py +2 -12
  87. package/src/learning/tests/__init__.py +2 -0
  88. package/src/learning/tests/test_adaptive_ranker.py +2 -6
  89. package/src/learning/tests/test_adaptive_ranker_v28.py +60 -0
  90. package/src/learning/tests/test_aggregator.py +2 -6
  91. package/src/learning/tests/test_auto_retrain_v28.py +35 -0
  92. package/src/learning/tests/test_e2e_ranking_v28.py +82 -0
  93. package/src/learning/tests/test_feature_extractor_v28.py +93 -0
  94. package/src/learning/tests/test_feedback_collector.py +2 -6
  95. package/src/learning/tests/test_learning_db.py +2 -6
  96. package/src/learning/tests/test_learning_db_v28.py +110 -0
  97. package/src/learning/tests/test_learning_init_v28.py +48 -0
  98. package/src/learning/tests/test_outcome_signals.py +48 -0
  99. package/src/learning/tests/test_project_context.py +2 -6
  100. package/src/learning/tests/test_schema_migration.py +319 -0
  101. package/src/learning/tests/test_signal_inference.py +11 -13
  102. package/src/learning/tests/test_source_quality.py +2 -6
  103. package/src/learning/tests/test_synthetic_bootstrap.py +3 -7
  104. package/src/learning/tests/test_workflow_miner.py +2 -6
  105. package/src/learning/workflow_pattern_miner.py +2 -12
  106. package/src/lifecycle/__init__.py +54 -0
  107. package/src/lifecycle/bounded_growth.py +239 -0
  108. package/src/lifecycle/compaction_engine.py +226 -0
  109. package/src/lifecycle/lifecycle_engine.py +302 -0
  110. package/src/lifecycle/lifecycle_evaluator.py +225 -0
  111. package/src/lifecycle/lifecycle_scheduler.py +130 -0
  112. package/src/lifecycle/retention_policy.py +285 -0
  113. package/src/lifecycle/tests/__init__.py +4 -0
  114. package/src/lifecycle/tests/test_bounded_growth.py +193 -0
  115. package/src/lifecycle/tests/test_compaction.py +179 -0
  116. package/src/lifecycle/tests/test_lifecycle_engine.py +137 -0
  117. package/src/lifecycle/tests/test_lifecycle_evaluation.py +177 -0
  118. package/src/lifecycle/tests/test_lifecycle_scheduler.py +127 -0
  119. package/src/lifecycle/tests/test_lifecycle_search.py +109 -0
  120. package/src/lifecycle/tests/test_mcp_compact.py +149 -0
  121. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +114 -0
  122. package/src/lifecycle/tests/test_retention_policy.py +162 -0
  123. package/src/mcp_tools_v28.py +280 -0
  124. package/src/memory-profiles.py +2 -12
  125. package/src/memory-reset.py +2 -12
  126. package/src/memory_compression.py +2 -12
  127. package/src/memory_store_v2.py +76 -20
  128. package/src/migrate_v1_to_v2.py +2 -12
  129. package/src/pattern_learner.py +29 -975
  130. package/src/patterns/__init__.py +24 -0
  131. package/src/patterns/analyzers.py +247 -0
  132. package/src/patterns/learner.py +267 -0
  133. package/src/patterns/scoring.py +167 -0
  134. package/src/patterns/store.py +223 -0
  135. package/src/patterns/terminology.py +138 -0
  136. package/src/provenance_tracker.py +4 -14
  137. package/src/query_optimizer.py +4 -6
  138. package/src/rate_limiter.py +2 -6
  139. package/src/search/__init__.py +20 -0
  140. package/src/search/cli.py +77 -0
  141. package/src/search/constants.py +26 -0
  142. package/src/search/engine.py +239 -0
  143. package/src/search/fusion.py +122 -0
  144. package/src/search/index_loader.py +112 -0
  145. package/src/search/methods.py +162 -0
  146. package/src/search_engine_v2.py +4 -6
  147. package/src/setup_validator.py +7 -13
  148. package/src/subscription_manager.py +2 -12
  149. package/src/tree/__init__.py +59 -0
  150. package/src/tree/builder.py +183 -0
  151. package/src/tree/nodes.py +196 -0
  152. package/src/tree/queries.py +252 -0
  153. package/src/tree/schema.py +76 -0
  154. package/src/tree_manager.py +10 -711
  155. package/src/trust/__init__.py +45 -0
  156. package/src/trust/constants.py +66 -0
  157. package/src/trust/queries.py +157 -0
  158. package/src/trust/schema.py +95 -0
  159. package/src/trust/scorer.py +299 -0
  160. package/src/trust/signals.py +95 -0
  161. package/src/trust_scorer.py +39 -697
  162. package/src/webhook_dispatcher.py +2 -12
  163. package/ui/app.js +1 -1
  164. package/ui/js/agents.js +1 -1
  165. package/ui_server.py +2 -14
  166. package/ATTRIBUTION.md +0 -140
  167. package/docs/ARCHITECTURE-V2.5.md +0 -190
  168. package/docs/GRAPH-ENGINE.md +0 -503
  169. package/docs/architecture-diagram.drawio +0 -405
  170. package/docs/plans/2026-02-13-benchmark-suite.md +0 -1349
@@ -0,0 +1,280 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
+ """v2.8 MCP tool handlers — lifecycle, behavioral, compliance tools.
4
+
5
+ These functions implement the 6 new MCP tools added in v2.8.
6
+ They are registered with the MCP server in mcp_server.py.
7
+ Each function is a thin wrapper around the appropriate engine.
8
+
9
+ Tool list:
10
+ 1. report_outcome — Record action outcomes for behavioral learning
11
+ 2. get_lifecycle_status — View memory lifecycle states
12
+ 3. set_retention_policy — Configure compliance retention policies
13
+ 4. compact_memories — Trigger lifecycle transitions
14
+ 5. get_behavioral_patterns — View learned behavioral patterns
15
+ 6. audit_trail — Query compliance audit trail
16
+ """
17
+ import json
18
+ import sys
19
+ from pathlib import Path
20
+ from typing import Optional, Dict, Any
21
+
22
+ # Default database paths — overridable for testing
23
+ DEFAULT_MEMORY_DB = str(Path.home() / ".claude-memory" / "memory.db")
24
+ DEFAULT_LEARNING_DB = str(Path.home() / ".claude-memory" / "learning.db")
25
+ DEFAULT_AUDIT_DB = str(Path.home() / ".claude-memory" / "audit.db")
26
+
27
+ # Ensure src/ is on the path so subpackage imports work
28
+ _SRC_DIR = str(Path(__file__).resolve().parent)
29
+ if _SRC_DIR not in sys.path:
30
+ sys.path.insert(0, _SRC_DIR)
31
+
32
+
33
+ async def report_outcome(
34
+ memory_ids: list,
35
+ outcome: str,
36
+ action_type: str = "other",
37
+ context: Optional[str] = None,
38
+ agent_id: str = "user",
39
+ project: Optional[str] = None,
40
+ ) -> Dict[str, Any]:
41
+ """Record an action outcome for behavioral learning.
42
+
43
+ Args:
44
+ memory_ids: List of memory IDs involved in the action.
45
+ outcome: One of 'success', 'failure', or 'partial'.
46
+ action_type: Category (code_written, decision_made, debug_resolved, etc.).
47
+ context: Optional JSON string with additional context metadata.
48
+ agent_id: Identifier for the reporting agent.
49
+ project: Project name for scoping.
50
+
51
+ Returns:
52
+ Dict with success status and outcome_id on success.
53
+ """
54
+ try:
55
+ from behavioral.outcome_tracker import OutcomeTracker
56
+
57
+ tracker = OutcomeTracker(DEFAULT_LEARNING_DB)
58
+ ctx = json.loads(context) if context else {}
59
+ outcome_id = tracker.record_outcome(
60
+ memory_ids=memory_ids,
61
+ outcome=outcome,
62
+ action_type=action_type,
63
+ context=ctx,
64
+ agent_id=agent_id,
65
+ project=project,
66
+ )
67
+ if outcome_id is None:
68
+ return {
69
+ "success": False,
70
+ "error": f"Invalid outcome: {outcome}. Use success/failure/partial.",
71
+ }
72
+ return {
73
+ "success": True,
74
+ "outcome_id": outcome_id,
75
+ "outcome": outcome,
76
+ "memory_ids": memory_ids,
77
+ }
78
+ except Exception as e:
79
+ return {"success": False, "error": str(e)}
80
+
81
+
82
+ async def get_lifecycle_status(
83
+ memory_id: Optional[int] = None,
84
+ ) -> Dict[str, Any]:
85
+ """Get lifecycle status — distribution across states or single memory state.
86
+
87
+ Args:
88
+ memory_id: Optional specific memory ID. If None, returns full distribution.
89
+
90
+ Returns:
91
+ Dict with state distribution or single memory lifecycle state.
92
+ """
93
+ try:
94
+ from lifecycle.lifecycle_engine import LifecycleEngine
95
+
96
+ engine = LifecycleEngine(DEFAULT_MEMORY_DB)
97
+ if memory_id is not None:
98
+ state = engine.get_memory_state(memory_id)
99
+ if state is None:
100
+ return {"success": False, "error": f"Memory {memory_id} not found"}
101
+ return {
102
+ "success": True,
103
+ "memory_id": memory_id,
104
+ "lifecycle_state": state,
105
+ }
106
+ else:
107
+ dist = engine.get_state_distribution()
108
+ total = sum(dist.values())
109
+ return {
110
+ "success": True,
111
+ "distribution": dist,
112
+ "total_memories": total,
113
+ }
114
+ except Exception as e:
115
+ return {"success": False, "error": str(e)}
116
+
117
+
118
+ async def set_retention_policy(
119
+ name: str,
120
+ framework: str,
121
+ retention_days: int,
122
+ action: str = "retain",
123
+ applies_to_tags: Optional[list] = None,
124
+ applies_to_project: Optional[str] = None,
125
+ ) -> Dict[str, Any]:
126
+ """Create a retention policy for compliance.
127
+
128
+ Args:
129
+ name: Policy name (e.g., 'GDPR Erasure', 'HIPAA Retention').
130
+ framework: Regulatory framework (gdpr, hipaa, eu_ai_act, internal).
131
+ retention_days: Days to retain (0 = immediate action).
132
+ action: Policy action (retain, tombstone, archive).
133
+ applies_to_tags: Tags that trigger this policy.
134
+ applies_to_project: Project name that triggers this policy.
135
+
136
+ Returns:
137
+ Dict with policy_id on success.
138
+ """
139
+ try:
140
+ from lifecycle.retention_policy import RetentionPolicyManager
141
+
142
+ mgr = RetentionPolicyManager(DEFAULT_MEMORY_DB)
143
+ applies_to: Dict[str, Any] = {}
144
+ if applies_to_tags:
145
+ applies_to["tags"] = applies_to_tags
146
+ if applies_to_project:
147
+ applies_to["project_name"] = applies_to_project
148
+ policy_id = mgr.create_policy(
149
+ name=name,
150
+ retention_days=retention_days,
151
+ framework=framework,
152
+ action=action,
153
+ applies_to=applies_to,
154
+ )
155
+ return {
156
+ "success": True,
157
+ "policy_id": policy_id,
158
+ "name": name,
159
+ "framework": framework,
160
+ }
161
+ except Exception as e:
162
+ return {"success": False, "error": str(e)}
163
+
164
+
165
+ async def compact_memories(
166
+ dry_run: bool = True,
167
+ profile: Optional[str] = None,
168
+ ) -> Dict[str, Any]:
169
+ """Evaluate and compact memories — transition stale ones through lifecycle.
170
+
171
+ Args:
172
+ dry_run: If True (default), show what would happen without changes.
173
+ profile: Optional profile filter.
174
+
175
+ Returns:
176
+ Dict with recommendations (dry_run=True) or transition counts (dry_run=False).
177
+ """
178
+ try:
179
+ from lifecycle.lifecycle_evaluator import LifecycleEvaluator
180
+ from lifecycle.lifecycle_engine import LifecycleEngine
181
+
182
+ evaluator = LifecycleEvaluator(DEFAULT_MEMORY_DB)
183
+ recommendations = evaluator.evaluate_memories(profile=profile)
184
+
185
+ if dry_run:
186
+ return {
187
+ "success": True,
188
+ "dry_run": True,
189
+ "recommendations": len(recommendations),
190
+ "details": [
191
+ {
192
+ "memory_id": r["memory_id"],
193
+ "from": r["from_state"],
194
+ "to": r["to_state"],
195
+ "reason": r["reason"],
196
+ }
197
+ for r in recommendations[:20]
198
+ ],
199
+ }
200
+
201
+ engine = LifecycleEngine(DEFAULT_MEMORY_DB)
202
+ transitioned = 0
203
+ for rec in recommendations:
204
+ result = engine.transition_memory(
205
+ rec["memory_id"], rec["to_state"], reason=rec["reason"]
206
+ )
207
+ if result.get("success"):
208
+ transitioned += 1
209
+
210
+ return {
211
+ "success": True,
212
+ "dry_run": False,
213
+ "evaluated": len(recommendations),
214
+ "transitioned": transitioned,
215
+ }
216
+ except Exception as e:
217
+ return {"success": False, "error": str(e)}
218
+
219
+
220
+ async def get_behavioral_patterns(
221
+ min_confidence: float = 0.0,
222
+ project: Optional[str] = None,
223
+ ) -> Dict[str, Any]:
224
+ """Get learned behavioral patterns from outcome analysis.
225
+
226
+ Args:
227
+ min_confidence: Minimum confidence threshold (0.0-1.0).
228
+ project: Optional project filter.
229
+
230
+ Returns:
231
+ Dict with patterns list and count.
232
+ """
233
+ try:
234
+ from behavioral.behavioral_patterns import BehavioralPatternExtractor
235
+
236
+ extractor = BehavioralPatternExtractor(DEFAULT_LEARNING_DB)
237
+ patterns = extractor.get_patterns(
238
+ min_confidence=min_confidence, project=project
239
+ )
240
+ return {"success": True, "patterns": patterns, "count": len(patterns)}
241
+ except Exception as e:
242
+ return {"success": False, "error": str(e)}
243
+
244
+
245
+ async def audit_trail(
246
+ event_type: Optional[str] = None,
247
+ actor: Optional[str] = None,
248
+ limit: int = 50,
249
+ verify_chain: bool = False,
250
+ ) -> Dict[str, Any]:
251
+ """Query the compliance audit trail.
252
+
253
+ Args:
254
+ event_type: Filter by event type (memory.created, memory.recalled, etc.).
255
+ actor: Filter by actor (user, agent_id, etc.).
256
+ limit: Max events to return.
257
+ verify_chain: If True, verify hash chain integrity.
258
+
259
+ Returns:
260
+ Dict with events list, count, and optional chain verification result.
261
+ """
262
+ try:
263
+ from compliance.audit_db import AuditDB
264
+
265
+ db = AuditDB(DEFAULT_AUDIT_DB)
266
+ result: Dict[str, Any] = {"success": True}
267
+
268
+ if verify_chain:
269
+ chain_result = db.verify_chain()
270
+ result["chain_valid"] = chain_result["valid"]
271
+ result["chain_entries"] = chain_result["entries_checked"]
272
+
273
+ events = db.query_events(
274
+ event_type=event_type, actor=actor, limit=limit
275
+ )
276
+ result["events"] = events
277
+ result["count"] = len(events)
278
+ return result
279
+ except Exception as e:
280
+ return {"success": False, "error": str(e)}
@@ -1,16 +1,6 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- SuperLocalMemory V2 - Intelligent Local Memory System
4
- Copyright (c) 2026 Varun Pratap Bhardwaj
5
- Licensed under MIT License
6
-
7
- Repository: https://github.com/varun369/SuperLocalMemoryV2
8
- Author: Varun Pratap Bhardwaj (Solution Architect)
9
-
10
- NOTICE: This software is protected by MIT License.
11
- Attribution must be preserved in all copies or derivatives.
12
- """
13
-
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
14
4
  """
15
5
  SuperLocalMemory V2 - Profile Management System (Column-Based)
16
6
 
@@ -1,16 +1,6 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- SuperLocalMemory V2 - Intelligent Local Memory System
4
- Copyright (c) 2026 Varun Pratap Bhardwaj
5
- Licensed under MIT License
6
-
7
- Repository: https://github.com/varun369/SuperLocalMemoryV2
8
- Author: Varun Pratap Bhardwaj (Solution Architect)
9
-
10
- NOTICE: This software is protected by MIT License.
11
- Attribution must be preserved in all copies or derivatives.
12
- """
13
-
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
14
4
  """
15
5
  SuperLocalMemory V2 - Reset & Reinitialize Utility
16
6
 
@@ -1,16 +1,6 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- SuperLocalMemory V2 - Intelligent Local Memory System
4
- Copyright (c) 2026 Varun Pratap Bhardwaj
5
- Licensed under MIT License
6
-
7
- Repository: https://github.com/varun369/SuperLocalMemoryV2
8
- Author: Varun Pratap Bhardwaj (Solution Architect)
9
-
10
- NOTICE: This software is protected by MIT License.
11
- Attribution must be preserved in all copies or derivatives.
12
- """
13
-
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
14
4
  """
15
5
  Progressive Summarization Compression for SuperLocalMemory
16
6
  Tier-based compression system to maintain 100+ memories efficiently.
@@ -1,16 +1,6 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- SuperLocalMemory V2 - Intelligent Local Memory System
4
- Copyright (c) 2026 Varun Pratap Bhardwaj
5
- Licensed under MIT License
6
-
7
- Repository: https://github.com/varun369/SuperLocalMemoryV2
8
- Author: Varun Pratap Bhardwaj (Solution Architect)
9
-
10
- NOTICE: This software is protected by MIT License.
11
- Attribution must be preserved in all copies or derivatives.
12
- """
13
-
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
14
4
  """
15
5
  MemoryStore V2 - Extended Memory System with Tree and Graph Support
16
6
  Maintains backward compatibility with V1 API while adding:
@@ -320,6 +310,19 @@ class MemoryStoreV2:
320
310
  # Column might already exist from concurrent migration
321
311
  pass
322
312
 
313
+ # v2.8.0 schema migration — lifecycle + access control columns
314
+ _v28_migrations = [
315
+ ("lifecycle_state", "TEXT DEFAULT 'active'"),
316
+ ("lifecycle_updated_at", "TIMESTAMP"),
317
+ ("lifecycle_history", "TEXT DEFAULT '[]'"),
318
+ ("access_level", "TEXT DEFAULT 'public'"),
319
+ ]
320
+ for col_name, col_type in _v28_migrations:
321
+ try:
322
+ cursor.execute(f"ALTER TABLE memories ADD COLUMN {col_name} {col_type}")
323
+ except sqlite3.OperationalError:
324
+ pass # Column already exists
325
+
323
326
  # Sessions table (V1 compatible)
324
327
  cursor.execute('''
325
328
  CREATE TABLE IF NOT EXISTS sessions (
@@ -382,6 +385,13 @@ class MemoryStoreV2:
382
385
  # Index will be created automatically on next schema upgrade
383
386
  pass
384
387
 
388
+ # v2.8.0 indexes for lifecycle + access control
389
+ try:
390
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_lifecycle_state ON memories(lifecycle_state)")
391
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_access_level ON memories(access_level)")
392
+ except sqlite3.OperationalError:
393
+ pass
394
+
385
395
  # Creator Attribution Metadata Table (REQUIRED by MIT License)
386
396
  # This table embeds creator information directly in the database
387
397
  cursor.execute('''
@@ -604,7 +614,9 @@ class MemoryStoreV2:
604
614
  memory_type: Optional[str] = None,
605
615
  category: Optional[str] = None,
606
616
  cluster_id: Optional[int] = None,
607
- min_importance: Optional[int] = None
617
+ min_importance: Optional[int] = None,
618
+ lifecycle_states: Optional[tuple] = None,
619
+ agent_context: Optional[Dict[str, Any]] = None,
608
620
  ) -> List[Dict[str, Any]]:
609
621
  """
610
622
  Search memories with enhanced V2 filtering.
@@ -617,10 +629,14 @@ class MemoryStoreV2:
617
629
  category: Filter by category
618
630
  cluster_id: Filter by graph cluster
619
631
  min_importance: Minimum importance score
632
+ lifecycle_states: Tuple of lifecycle states to include (default: active, warm)
620
633
 
621
634
  Returns:
622
635
  List of memory dictionaries with scores
623
636
  """
637
+ if lifecycle_states is None:
638
+ lifecycle_states = ("active", "warm")
639
+
624
640
  results = []
625
641
  active_profile = self._get_active_profile()
626
642
 
@@ -640,12 +656,12 @@ class MemoryStoreV2:
640
656
  SELECT id, content, summary, project_path, project_name, tags,
641
657
  category, parent_id, tree_path, depth,
642
658
  memory_type, importance, created_at, cluster_id,
643
- last_accessed, access_count
659
+ last_accessed, access_count, lifecycle_state
644
660
  FROM memories WHERE id = ? AND profile = ?
645
661
  ''', (memory_id, active_profile))
646
662
  row = cursor.fetchone()
647
663
  if row and self._apply_filters(row, project_path, memory_type,
648
- category, cluster_id, min_importance):
664
+ category, cluster_id, min_importance, lifecycle_states):
649
665
  results.append(self._row_to_dict(row, score, 'hnsw'))
650
666
  _hnsw_used = len(results) > 0
651
667
  except (ImportError, Exception):
@@ -670,13 +686,13 @@ class MemoryStoreV2:
670
686
  SELECT id, content, summary, project_path, project_name, tags,
671
687
  category, parent_id, tree_path, depth,
672
688
  memory_type, importance, created_at, cluster_id,
673
- last_accessed, access_count
689
+ last_accessed, access_count, lifecycle_state
674
690
  FROM memories WHERE id = ? AND profile = ?
675
691
  ''', (memory_id, active_profile))
676
692
  row = cursor.fetchone()
677
693
 
678
694
  if row and self._apply_filters(row, project_path, memory_type,
679
- category, cluster_id, min_importance):
695
+ category, cluster_id, min_importance, lifecycle_states):
680
696
  results.append(self._row_to_dict(row, score, 'semantic'))
681
697
 
682
698
  except Exception as e:
@@ -694,7 +710,7 @@ class MemoryStoreV2:
694
710
  SELECT m.id, m.content, m.summary, m.project_path, m.project_name,
695
711
  m.tags, m.category, m.parent_id, m.tree_path, m.depth,
696
712
  m.memory_type, m.importance, m.created_at, m.cluster_id,
697
- m.last_accessed, m.access_count
713
+ m.last_accessed, m.access_count, m.lifecycle_state
698
714
  FROM memories m
699
715
  JOIN memories_fts fts ON m.id = fts.rowid
700
716
  WHERE memories_fts MATCH ? AND m.profile = ?
@@ -707,12 +723,23 @@ class MemoryStoreV2:
707
723
  for row in cursor.fetchall():
708
724
  if row[0] not in existing_ids:
709
725
  if self._apply_filters(row, project_path, memory_type,
710
- category, cluster_id, min_importance):
726
+ category, cluster_id, min_importance, lifecycle_states):
711
727
  results.append(self._row_to_dict(row, 0.5, 'keyword'))
712
728
 
713
729
  # Update access tracking for returned results
714
730
  self._update_access_tracking([r['id'] for r in results])
715
731
 
732
+ # Reactivate warm memories that were recalled (lifecycle v2.8)
733
+ warm_ids = [r['id'] for r in results if r.get('lifecycle_state') == 'warm']
734
+ if warm_ids:
735
+ try:
736
+ from lifecycle.lifecycle_engine import LifecycleEngine
737
+ engine = LifecycleEngine(self.db_path)
738
+ for mem_id in warm_ids:
739
+ engine.reactivate_memory(mem_id, trigger="recall")
740
+ except (ImportError, Exception):
741
+ pass # Lifecycle engine not available
742
+
716
743
  # Sort by score and limit
717
744
  results.sort(key=lambda x: x['score'], reverse=True)
718
745
  return results[:limit]
@@ -724,7 +751,8 @@ class MemoryStoreV2:
724
751
  memory_type: Optional[str],
725
752
  category: Optional[str],
726
753
  cluster_id: Optional[int],
727
- min_importance: Optional[int]
754
+ min_importance: Optional[int],
755
+ lifecycle_states: Optional[tuple] = None,
728
756
  ) -> bool:
729
757
  """Apply filter criteria to a database row."""
730
758
  # Row indices: project_path=3, category=6, memory_type=10, importance=11, cluster_id=13
@@ -738,8 +766,35 @@ class MemoryStoreV2:
738
766
  return False
739
767
  if min_importance is not None and (row[11] or 0) < min_importance:
740
768
  return False
769
+ # Lifecycle state filter (v2.8) — index 16 if present
770
+ if lifecycle_states and len(row) > 16:
771
+ state = row[16] or "active"
772
+ if state not in lifecycle_states:
773
+ return False
741
774
  return True
742
775
 
776
+ def _check_abac(
777
+ self,
778
+ subject: Dict[str, Any],
779
+ resource: Dict[str, Any],
780
+ action: str,
781
+ policy_path: Optional[str] = None,
782
+ ) -> Dict[str, Any]:
783
+ """Check ABAC policy for an access request.
784
+
785
+ Returns {"allowed": True/False, "reason": str}.
786
+ When ABAC engine is unavailable (import error, missing file),
787
+ defaults to allow for backward compatibility with v2.7.
788
+ """
789
+ try:
790
+ from compliance.abac_engine import ABACEngine
791
+ if policy_path is None:
792
+ policy_path = str(Path(self.db_path).parent / "abac_policies.json")
793
+ engine = ABACEngine(config_path=policy_path)
794
+ return engine.evaluate(subject=subject, resource=resource, action=action)
795
+ except (ImportError, Exception):
796
+ return {"allowed": True, "reason": "ABAC unavailable — default allow"}
797
+
743
798
  def _row_to_dict(self, row: tuple, score: float, match_type: str) -> Dict[str, Any]:
744
799
  """Convert database row to memory dictionary."""
745
800
  # Backward compatibility: Handle both JSON array and comma-separated string tags
@@ -771,6 +826,7 @@ class MemoryStoreV2:
771
826
  'cluster_id': row[13],
772
827
  'last_accessed': row[14],
773
828
  'access_count': row[15],
829
+ 'lifecycle_state': row[16] if len(row) > 16 else 'active',
774
830
  'score': score,
775
831
  'match_type': match_type
776
832
  }
@@ -1,16 +1,6 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- SuperLocalMemory V2 - Intelligent Local Memory System
4
- Copyright (c) 2026 Varun Pratap Bhardwaj
5
- Licensed under MIT License
6
-
7
- Repository: https://github.com/varun369/SuperLocalMemoryV2
8
- Author: Varun Pratap Bhardwaj (Solution Architect)
9
-
10
- NOTICE: This software is protected by MIT License.
11
- Attribution must be preserved in all copies or derivatives.
12
- """
13
-
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
14
4
  """
15
5
  SuperLocalMemory V1 to V2 Migration Script
16
6