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.
- package/CHANGELOG.md +120 -155
- package/README.md +115 -89
- package/api_server.py +2 -12
- package/docs/PATTERN-LEARNING.md +64 -199
- package/docs/example_graph_usage.py +4 -6
- package/install.sh +59 -0
- package/mcp_server.py +83 -7
- package/package.json +1 -8
- package/scripts/generate-thumbnails.py +3 -5
- package/skills/slm-build-graph/SKILL.md +1 -1
- package/skills/slm-list-recent/SKILL.md +1 -1
- package/skills/slm-recall/SKILL.md +1 -1
- package/skills/slm-remember/SKILL.md +1 -1
- package/skills/slm-show-patterns/SKILL.md +1 -1
- package/skills/slm-status/SKILL.md +1 -1
- package/skills/slm-switch-profile/SKILL.md +1 -1
- package/src/agent_registry.py +7 -18
- package/src/auth_middleware.py +3 -5
- package/src/auto_backup.py +3 -7
- package/src/behavioral/__init__.py +49 -0
- package/src/behavioral/behavioral_listener.py +203 -0
- package/src/behavioral/behavioral_patterns.py +275 -0
- package/src/behavioral/cross_project_transfer.py +206 -0
- package/src/behavioral/outcome_inference.py +194 -0
- package/src/behavioral/outcome_tracker.py +193 -0
- package/src/behavioral/tests/__init__.py +4 -0
- package/src/behavioral/tests/test_behavioral_integration.py +108 -0
- package/src/behavioral/tests/test_behavioral_patterns.py +150 -0
- package/src/behavioral/tests/test_cross_project_transfer.py +142 -0
- package/src/behavioral/tests/test_mcp_behavioral.py +139 -0
- package/src/behavioral/tests/test_mcp_report_outcome.py +117 -0
- package/src/behavioral/tests/test_outcome_inference.py +107 -0
- package/src/behavioral/tests/test_outcome_tracker.py +96 -0
- package/src/cache_manager.py +4 -6
- package/src/compliance/__init__.py +48 -0
- package/src/compliance/abac_engine.py +149 -0
- package/src/compliance/abac_middleware.py +116 -0
- package/src/compliance/audit_db.py +215 -0
- package/src/compliance/audit_logger.py +148 -0
- package/src/compliance/retention_manager.py +289 -0
- package/src/compliance/retention_scheduler.py +186 -0
- package/src/compliance/tests/__init__.py +4 -0
- package/src/compliance/tests/test_abac_enforcement.py +95 -0
- package/src/compliance/tests/test_abac_engine.py +124 -0
- package/src/compliance/tests/test_abac_mcp_integration.py +118 -0
- package/src/compliance/tests/test_audit_db.py +123 -0
- package/src/compliance/tests/test_audit_logger.py +98 -0
- package/src/compliance/tests/test_mcp_audit.py +128 -0
- package/src/compliance/tests/test_mcp_retention_policy.py +125 -0
- package/src/compliance/tests/test_retention_manager.py +131 -0
- package/src/compliance/tests/test_retention_scheduler.py +99 -0
- package/src/db_connection_manager.py +2 -12
- package/src/embedding_engine.py +61 -669
- package/src/embeddings/__init__.py +47 -0
- package/src/embeddings/cache.py +70 -0
- package/src/embeddings/cli.py +113 -0
- package/src/embeddings/constants.py +47 -0
- package/src/embeddings/database.py +91 -0
- package/src/embeddings/engine.py +247 -0
- package/src/embeddings/model_loader.py +145 -0
- package/src/event_bus.py +3 -13
- package/src/graph/__init__.py +36 -0
- package/src/graph/build_helpers.py +74 -0
- package/src/graph/cli.py +87 -0
- package/src/graph/cluster_builder.py +188 -0
- package/src/graph/cluster_summary.py +148 -0
- package/src/graph/constants.py +47 -0
- package/src/graph/edge_builder.py +162 -0
- package/src/graph/entity_extractor.py +95 -0
- package/src/graph/graph_core.py +226 -0
- package/src/graph/graph_search.py +231 -0
- package/src/graph/hierarchical.py +207 -0
- package/src/graph/schema.py +99 -0
- package/src/graph_engine.py +45 -1451
- package/src/hnsw_index.py +3 -7
- package/src/hybrid_search.py +36 -683
- package/src/learning/__init__.py +27 -12
- package/src/learning/adaptive_ranker.py +50 -12
- package/src/learning/cross_project_aggregator.py +2 -12
- package/src/learning/engagement_tracker.py +2 -12
- package/src/learning/feature_extractor.py +175 -43
- package/src/learning/feedback_collector.py +7 -12
- package/src/learning/learning_db.py +180 -12
- package/src/learning/project_context_manager.py +2 -12
- package/src/learning/source_quality_scorer.py +2 -12
- package/src/learning/synthetic_bootstrap.py +2 -12
- package/src/learning/tests/__init__.py +2 -0
- package/src/learning/tests/test_adaptive_ranker.py +2 -6
- package/src/learning/tests/test_adaptive_ranker_v28.py +60 -0
- package/src/learning/tests/test_aggregator.py +2 -6
- package/src/learning/tests/test_auto_retrain_v28.py +35 -0
- package/src/learning/tests/test_e2e_ranking_v28.py +82 -0
- package/src/learning/tests/test_feature_extractor_v28.py +93 -0
- package/src/learning/tests/test_feedback_collector.py +2 -6
- package/src/learning/tests/test_learning_db.py +2 -6
- package/src/learning/tests/test_learning_db_v28.py +110 -0
- package/src/learning/tests/test_learning_init_v28.py +48 -0
- package/src/learning/tests/test_outcome_signals.py +48 -0
- package/src/learning/tests/test_project_context.py +2 -6
- package/src/learning/tests/test_schema_migration.py +319 -0
- package/src/learning/tests/test_signal_inference.py +11 -13
- package/src/learning/tests/test_source_quality.py +2 -6
- package/src/learning/tests/test_synthetic_bootstrap.py +3 -7
- package/src/learning/tests/test_workflow_miner.py +2 -6
- package/src/learning/workflow_pattern_miner.py +2 -12
- package/src/lifecycle/__init__.py +54 -0
- package/src/lifecycle/bounded_growth.py +239 -0
- package/src/lifecycle/compaction_engine.py +226 -0
- package/src/lifecycle/lifecycle_engine.py +302 -0
- package/src/lifecycle/lifecycle_evaluator.py +225 -0
- package/src/lifecycle/lifecycle_scheduler.py +130 -0
- package/src/lifecycle/retention_policy.py +285 -0
- package/src/lifecycle/tests/__init__.py +4 -0
- package/src/lifecycle/tests/test_bounded_growth.py +193 -0
- package/src/lifecycle/tests/test_compaction.py +179 -0
- package/src/lifecycle/tests/test_lifecycle_engine.py +137 -0
- package/src/lifecycle/tests/test_lifecycle_evaluation.py +177 -0
- package/src/lifecycle/tests/test_lifecycle_scheduler.py +127 -0
- package/src/lifecycle/tests/test_lifecycle_search.py +109 -0
- package/src/lifecycle/tests/test_mcp_compact.py +149 -0
- package/src/lifecycle/tests/test_mcp_lifecycle_status.py +114 -0
- package/src/lifecycle/tests/test_retention_policy.py +162 -0
- package/src/mcp_tools_v28.py +280 -0
- package/src/memory-profiles.py +2 -12
- package/src/memory-reset.py +2 -12
- package/src/memory_compression.py +2 -12
- package/src/memory_store_v2.py +76 -20
- package/src/migrate_v1_to_v2.py +2 -12
- package/src/pattern_learner.py +29 -975
- package/src/patterns/__init__.py +24 -0
- package/src/patterns/analyzers.py +247 -0
- package/src/patterns/learner.py +267 -0
- package/src/patterns/scoring.py +167 -0
- package/src/patterns/store.py +223 -0
- package/src/patterns/terminology.py +138 -0
- package/src/provenance_tracker.py +4 -14
- package/src/query_optimizer.py +4 -6
- package/src/rate_limiter.py +2 -6
- package/src/search/__init__.py +20 -0
- package/src/search/cli.py +77 -0
- package/src/search/constants.py +26 -0
- package/src/search/engine.py +239 -0
- package/src/search/fusion.py +122 -0
- package/src/search/index_loader.py +112 -0
- package/src/search/methods.py +162 -0
- package/src/search_engine_v2.py +4 -6
- package/src/setup_validator.py +7 -13
- package/src/subscription_manager.py +2 -12
- package/src/tree/__init__.py +59 -0
- package/src/tree/builder.py +183 -0
- package/src/tree/nodes.py +196 -0
- package/src/tree/queries.py +252 -0
- package/src/tree/schema.py +76 -0
- package/src/tree_manager.py +10 -711
- package/src/trust/__init__.py +45 -0
- package/src/trust/constants.py +66 -0
- package/src/trust/queries.py +157 -0
- package/src/trust/schema.py +95 -0
- package/src/trust/scorer.py +299 -0
- package/src/trust/signals.py +95 -0
- package/src/trust_scorer.py +39 -697
- package/src/webhook_dispatcher.py +2 -12
- package/ui/app.js +1 -1
- package/ui/js/agents.js +1 -1
- package/ui_server.py +2 -14
- package/ATTRIBUTION.md +0 -140
- package/docs/ARCHITECTURE-V2.5.md +0 -190
- package/docs/GRAPH-ENGINE.md +0 -503
- package/docs/architecture-diagram.drawio +0 -405
- 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)}
|
package/src/memory-profiles.py
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
package/src/memory-reset.py
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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.
|
package/src/memory_store_v2.py
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
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
|
}
|
package/src/migrate_v1_to_v2.py
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|