superlocalmemory 2.8.5 → 3.0.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 +11 -0
- package/LICENSE +9 -1
- package/NOTICE +63 -0
- package/README.md +165 -480
- package/bin/slm +17 -449
- package/bin/slm-npm +2 -2
- package/bin/slm.bat +4 -2
- package/conftest.py +5 -0
- package/docs/api-reference.md +284 -0
- package/docs/architecture.md +149 -0
- package/docs/auto-memory.md +150 -0
- package/docs/cli-reference.md +276 -0
- package/docs/compliance.md +191 -0
- package/docs/configuration.md +182 -0
- package/docs/getting-started.md +102 -0
- package/docs/ide-setup.md +261 -0
- package/docs/mcp-tools.md +220 -0
- package/docs/migration-from-v2.md +170 -0
- package/docs/profiles.md +173 -0
- package/docs/troubleshooting.md +310 -0
- package/{configs → ide/configs}/antigravity-mcp.json +3 -3
- package/ide/configs/chatgpt-desktop-mcp.json +16 -0
- package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
- package/{configs → ide/configs}/codex-mcp.toml +4 -4
- package/{configs → ide/configs}/continue-mcp.yaml +4 -3
- package/{configs → ide/configs}/continue-skills.yaml +6 -6
- package/ide/configs/cursor-mcp.json +15 -0
- package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
- package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
- package/{configs → ide/configs}/opencode-mcp.json +2 -2
- package/{configs → ide/configs}/perplexity-mcp.json +2 -2
- package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
- package/{configs → ide/configs}/windsurf-mcp.json +3 -3
- package/{configs → ide/configs}/zed-mcp.json +2 -2
- package/{hooks → ide/hooks}/context-hook.js +9 -20
- package/ide/hooks/memory-list-skill.js +70 -0
- package/ide/hooks/memory-profile-skill.js +101 -0
- package/ide/hooks/memory-recall-skill.js +62 -0
- package/ide/hooks/memory-remember-skill.js +68 -0
- package/ide/hooks/memory-reset-skill.js +160 -0
- package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
- package/ide/integrations/langchain/README.md +106 -0
- package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
- package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
- package/ide/integrations/langchain/pyproject.toml +38 -0
- package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
- package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
- package/ide/integrations/langchain/tests/test_security.py +117 -0
- package/ide/integrations/llamaindex/README.md +81 -0
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
- package/ide/integrations/llamaindex/pyproject.toml +43 -0
- package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
- package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
- package/ide/integrations/llamaindex/tests/test_security.py +241 -0
- package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
- package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
- package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
- package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
- package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
- package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
- package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
- package/package.json +13 -22
- package/pyproject.toml +85 -0
- package/scripts/build-dmg.sh +417 -0
- package/scripts/install-skills.ps1 +334 -0
- package/{install.ps1 → scripts/install.ps1} +36 -4
- package/{install.sh → scripts/install.sh} +14 -13
- package/scripts/postinstall.js +2 -2
- package/scripts/start-dashboard.ps1 +52 -0
- package/scripts/start-dashboard.sh +41 -0
- package/scripts/sync-wiki.ps1 +127 -0
- package/scripts/sync-wiki.sh +82 -0
- package/scripts/test-dmg.sh +161 -0
- package/scripts/test-npm-package.ps1 +252 -0
- package/scripts/test-npm-package.sh +207 -0
- package/scripts/verify-install.ps1 +294 -0
- package/scripts/verify-install.sh +266 -0
- package/src/superlocalmemory/__init__.py +0 -0
- package/src/superlocalmemory/attribution/__init__.py +9 -0
- package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
- package/src/superlocalmemory/attribution/signer.py +153 -0
- package/src/superlocalmemory/attribution/watermark.py +189 -0
- package/src/superlocalmemory/cli/__init__.py +5 -0
- package/src/superlocalmemory/cli/commands.py +245 -0
- package/src/superlocalmemory/cli/main.py +89 -0
- package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
- package/src/superlocalmemory/cli/post_install.py +99 -0
- package/src/superlocalmemory/cli/setup_wizard.py +129 -0
- package/src/superlocalmemory/compliance/__init__.py +0 -0
- package/src/superlocalmemory/compliance/abac.py +204 -0
- package/src/superlocalmemory/compliance/audit.py +314 -0
- package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
- package/src/superlocalmemory/compliance/gdpr.py +294 -0
- package/src/superlocalmemory/compliance/lifecycle.py +158 -0
- package/src/superlocalmemory/compliance/retention.py +232 -0
- package/src/superlocalmemory/compliance/scheduler.py +148 -0
- package/src/superlocalmemory/core/__init__.py +0 -0
- package/src/superlocalmemory/core/config.py +391 -0
- package/src/superlocalmemory/core/embeddings.py +293 -0
- package/src/superlocalmemory/core/engine.py +701 -0
- package/src/superlocalmemory/core/hooks.py +65 -0
- package/src/superlocalmemory/core/maintenance.py +172 -0
- package/src/superlocalmemory/core/modes.py +140 -0
- package/src/superlocalmemory/core/profiles.py +234 -0
- package/src/superlocalmemory/core/registry.py +117 -0
- package/src/superlocalmemory/dynamics/__init__.py +0 -0
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
- package/src/superlocalmemory/encoding/__init__.py +0 -0
- package/src/superlocalmemory/encoding/consolidator.py +485 -0
- package/src/superlocalmemory/encoding/emotional.py +125 -0
- package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
- package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
- package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
- package/src/superlocalmemory/encoding/foresight.py +91 -0
- package/src/superlocalmemory/encoding/graph_builder.py +302 -0
- package/src/superlocalmemory/encoding/observation_builder.py +160 -0
- package/src/superlocalmemory/encoding/scene_builder.py +183 -0
- package/src/superlocalmemory/encoding/signal_inference.py +90 -0
- package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
- package/src/superlocalmemory/encoding/type_router.py +235 -0
- package/src/superlocalmemory/hooks/__init__.py +3 -0
- package/src/superlocalmemory/hooks/auto_capture.py +111 -0
- package/src/superlocalmemory/hooks/auto_recall.py +93 -0
- package/src/superlocalmemory/hooks/ide_connector.py +204 -0
- package/src/superlocalmemory/hooks/rules_engine.py +99 -0
- package/src/superlocalmemory/infra/__init__.py +3 -0
- package/src/superlocalmemory/infra/auth_middleware.py +82 -0
- package/src/superlocalmemory/infra/backup.py +317 -0
- package/src/superlocalmemory/infra/cache_manager.py +267 -0
- package/src/superlocalmemory/infra/event_bus.py +381 -0
- package/src/superlocalmemory/infra/rate_limiter.py +135 -0
- package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
- package/src/superlocalmemory/learning/__init__.py +0 -0
- package/src/superlocalmemory/learning/adaptive.py +172 -0
- package/src/superlocalmemory/learning/behavioral.py +490 -0
- package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
- package/src/superlocalmemory/learning/bootstrap.py +298 -0
- package/src/superlocalmemory/learning/cross_project.py +399 -0
- package/src/superlocalmemory/learning/database.py +376 -0
- package/src/superlocalmemory/learning/engagement.py +323 -0
- package/src/superlocalmemory/learning/features.py +138 -0
- package/src/superlocalmemory/learning/feedback.py +316 -0
- package/src/superlocalmemory/learning/outcomes.py +255 -0
- package/src/superlocalmemory/learning/project_context.py +366 -0
- package/src/superlocalmemory/learning/ranker.py +155 -0
- package/src/superlocalmemory/learning/source_quality.py +303 -0
- package/src/superlocalmemory/learning/workflows.py +309 -0
- package/src/superlocalmemory/llm/__init__.py +0 -0
- package/src/superlocalmemory/llm/backbone.py +316 -0
- package/src/superlocalmemory/math/__init__.py +0 -0
- package/src/superlocalmemory/math/fisher.py +356 -0
- package/src/superlocalmemory/math/langevin.py +398 -0
- package/src/superlocalmemory/math/sheaf.py +257 -0
- package/src/superlocalmemory/mcp/__init__.py +0 -0
- package/src/superlocalmemory/mcp/resources.py +245 -0
- package/src/superlocalmemory/mcp/server.py +61 -0
- package/src/superlocalmemory/mcp/tools.py +18 -0
- package/src/superlocalmemory/mcp/tools_core.py +305 -0
- package/src/superlocalmemory/mcp/tools_v28.py +223 -0
- package/src/superlocalmemory/mcp/tools_v3.py +286 -0
- package/src/superlocalmemory/retrieval/__init__.py +0 -0
- package/src/superlocalmemory/retrieval/agentic.py +295 -0
- package/src/superlocalmemory/retrieval/ann_index.py +223 -0
- package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
- package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
- package/src/superlocalmemory/retrieval/engine.py +390 -0
- package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
- package/src/superlocalmemory/retrieval/fusion.py +78 -0
- package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
- package/src/superlocalmemory/retrieval/reranker.py +154 -0
- package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
- package/src/superlocalmemory/retrieval/strategy.py +96 -0
- package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
- package/src/superlocalmemory/server/__init__.py +1 -0
- package/src/superlocalmemory/server/api.py +248 -0
- package/src/superlocalmemory/server/routes/__init__.py +4 -0
- package/src/superlocalmemory/server/routes/agents.py +107 -0
- package/src/superlocalmemory/server/routes/backup.py +91 -0
- package/src/superlocalmemory/server/routes/behavioral.py +127 -0
- package/src/superlocalmemory/server/routes/compliance.py +160 -0
- package/src/superlocalmemory/server/routes/data_io.py +188 -0
- package/src/superlocalmemory/server/routes/events.py +183 -0
- package/src/superlocalmemory/server/routes/helpers.py +85 -0
- package/src/superlocalmemory/server/routes/learning.py +273 -0
- package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
- package/src/superlocalmemory/server/routes/memories.py +399 -0
- package/src/superlocalmemory/server/routes/profiles.py +219 -0
- package/src/superlocalmemory/server/routes/stats.py +346 -0
- package/src/superlocalmemory/server/routes/v3_api.py +365 -0
- package/src/superlocalmemory/server/routes/ws.py +82 -0
- package/src/superlocalmemory/server/security_middleware.py +57 -0
- package/src/superlocalmemory/server/ui.py +245 -0
- package/src/superlocalmemory/storage/__init__.py +0 -0
- package/src/superlocalmemory/storage/access_control.py +182 -0
- package/src/superlocalmemory/storage/database.py +594 -0
- package/src/superlocalmemory/storage/migrations.py +303 -0
- package/src/superlocalmemory/storage/models.py +406 -0
- package/src/superlocalmemory/storage/schema.py +726 -0
- package/src/superlocalmemory/storage/v2_migrator.py +317 -0
- package/src/superlocalmemory/trust/__init__.py +0 -0
- package/src/superlocalmemory/trust/gate.py +130 -0
- package/src/superlocalmemory/trust/provenance.py +124 -0
- package/src/superlocalmemory/trust/scorer.py +347 -0
- package/src/superlocalmemory/trust/signals.py +153 -0
- package/ui/index.html +278 -5
- package/ui/js/auto-settings.js +70 -0
- package/ui/js/dashboard.js +90 -0
- package/ui/js/fact-detail.js +92 -0
- package/ui/js/feedback.js +2 -2
- package/ui/js/ide-status.js +102 -0
- package/ui/js/math-health.js +98 -0
- package/ui/js/recall-lab.js +127 -0
- package/ui/js/settings.js +2 -2
- package/ui/js/trust-dashboard.js +73 -0
- package/api_server.py +0 -724
- package/bin/aider-smart +0 -72
- package/bin/superlocalmemoryv2-learning +0 -4
- package/bin/superlocalmemoryv2-list +0 -3
- package/bin/superlocalmemoryv2-patterns +0 -4
- package/bin/superlocalmemoryv2-profile +0 -3
- package/bin/superlocalmemoryv2-recall +0 -3
- package/bin/superlocalmemoryv2-remember +0 -3
- package/bin/superlocalmemoryv2-reset +0 -3
- package/bin/superlocalmemoryv2-status +0 -3
- package/configs/chatgpt-desktop-mcp.json +0 -16
- package/configs/cursor-mcp.json +0 -15
- package/docs/SECURITY-QUICK-REFERENCE.md +0 -214
- package/hooks/memory-list-skill.js +0 -139
- package/hooks/memory-profile-skill.js +0 -273
- package/hooks/memory-recall-skill.js +0 -114
- package/hooks/memory-remember-skill.js +0 -127
- package/hooks/memory-reset-skill.js +0 -274
- package/mcp_server.py +0 -1800
- package/requirements-core.txt +0 -22
- package/requirements-learning.txt +0 -12
- package/requirements.txt +0 -12
- package/src/agent_registry.py +0 -411
- package/src/auth_middleware.py +0 -61
- package/src/auto_backup.py +0 -459
- package/src/behavioral/__init__.py +0 -49
- package/src/behavioral/behavioral_listener.py +0 -203
- package/src/behavioral/behavioral_patterns.py +0 -275
- package/src/behavioral/cross_project_transfer.py +0 -206
- package/src/behavioral/outcome_inference.py +0 -194
- package/src/behavioral/outcome_tracker.py +0 -193
- package/src/behavioral/tests/__init__.py +0 -4
- package/src/behavioral/tests/test_behavioral_integration.py +0 -108
- package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
- package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
- package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
- package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
- package/src/behavioral/tests/test_outcome_inference.py +0 -107
- package/src/behavioral/tests/test_outcome_tracker.py +0 -96
- package/src/cache_manager.py +0 -518
- package/src/compliance/__init__.py +0 -48
- package/src/compliance/abac_engine.py +0 -149
- package/src/compliance/abac_middleware.py +0 -116
- package/src/compliance/audit_db.py +0 -215
- package/src/compliance/audit_logger.py +0 -148
- package/src/compliance/retention_manager.py +0 -289
- package/src/compliance/retention_scheduler.py +0 -186
- package/src/compliance/tests/__init__.py +0 -4
- package/src/compliance/tests/test_abac_enforcement.py +0 -95
- package/src/compliance/tests/test_abac_engine.py +0 -124
- package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
- package/src/compliance/tests/test_audit_db.py +0 -123
- package/src/compliance/tests/test_audit_logger.py +0 -98
- package/src/compliance/tests/test_mcp_audit.py +0 -128
- package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
- package/src/compliance/tests/test_retention_manager.py +0 -131
- package/src/compliance/tests/test_retention_scheduler.py +0 -99
- package/src/compression/__init__.py +0 -25
- package/src/compression/cli.py +0 -150
- package/src/compression/cold_storage.py +0 -217
- package/src/compression/config.py +0 -72
- package/src/compression/orchestrator.py +0 -133
- package/src/compression/tier2_compressor.py +0 -228
- package/src/compression/tier3_compressor.py +0 -153
- package/src/compression/tier_classifier.py +0 -148
- package/src/db_connection_manager.py +0 -536
- package/src/embedding_engine.py +0 -63
- package/src/embeddings/__init__.py +0 -47
- package/src/embeddings/cache.py +0 -70
- package/src/embeddings/cli.py +0 -113
- package/src/embeddings/constants.py +0 -47
- package/src/embeddings/database.py +0 -91
- package/src/embeddings/engine.py +0 -247
- package/src/embeddings/model_loader.py +0 -145
- package/src/event_bus.py +0 -562
- package/src/graph/__init__.py +0 -36
- package/src/graph/build_helpers.py +0 -74
- package/src/graph/cli.py +0 -87
- package/src/graph/cluster_builder.py +0 -188
- package/src/graph/cluster_summary.py +0 -148
- package/src/graph/constants.py +0 -47
- package/src/graph/edge_builder.py +0 -162
- package/src/graph/entity_extractor.py +0 -95
- package/src/graph/graph_core.py +0 -226
- package/src/graph/graph_search.py +0 -231
- package/src/graph/hierarchical.py +0 -207
- package/src/graph/schema.py +0 -99
- package/src/graph_engine.py +0 -52
- package/src/hnsw_index.py +0 -628
- package/src/hybrid_search.py +0 -46
- package/src/learning/__init__.py +0 -217
- package/src/learning/adaptive_ranker.py +0 -682
- package/src/learning/bootstrap/__init__.py +0 -69
- package/src/learning/bootstrap/constants.py +0 -93
- package/src/learning/bootstrap/db_queries.py +0 -316
- package/src/learning/bootstrap/sampling.py +0 -82
- package/src/learning/bootstrap/text_utils.py +0 -71
- package/src/learning/cross_project_aggregator.py +0 -857
- package/src/learning/db/__init__.py +0 -40
- package/src/learning/db/constants.py +0 -44
- package/src/learning/db/schema.py +0 -279
- package/src/learning/engagement_tracker.py +0 -628
- package/src/learning/feature_extractor.py +0 -708
- package/src/learning/feedback_collector.py +0 -806
- package/src/learning/learning_db.py +0 -915
- package/src/learning/project_context_manager.py +0 -572
- package/src/learning/ranking/__init__.py +0 -33
- package/src/learning/ranking/constants.py +0 -84
- package/src/learning/ranking/helpers.py +0 -278
- package/src/learning/source_quality_scorer.py +0 -676
- package/src/learning/synthetic_bootstrap.py +0 -755
- package/src/learning/tests/test_adaptive_ranker.py +0 -325
- package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
- package/src/learning/tests/test_aggregator.py +0 -306
- package/src/learning/tests/test_auto_retrain_v28.py +0 -35
- package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
- package/src/learning/tests/test_feature_extractor_v28.py +0 -93
- package/src/learning/tests/test_feedback_collector.py +0 -294
- package/src/learning/tests/test_learning_db.py +0 -602
- package/src/learning/tests/test_learning_db_v28.py +0 -110
- package/src/learning/tests/test_learning_init_v28.py +0 -48
- package/src/learning/tests/test_outcome_signals.py +0 -48
- package/src/learning/tests/test_project_context.py +0 -292
- package/src/learning/tests/test_schema_migration.py +0 -319
- package/src/learning/tests/test_signal_inference.py +0 -397
- package/src/learning/tests/test_source_quality.py +0 -351
- package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
- package/src/learning/tests/test_workflow_miner.py +0 -318
- package/src/learning/workflow_pattern_miner.py +0 -655
- package/src/lifecycle/__init__.py +0 -54
- package/src/lifecycle/bounded_growth.py +0 -239
- package/src/lifecycle/compaction_engine.py +0 -226
- package/src/lifecycle/lifecycle_engine.py +0 -355
- package/src/lifecycle/lifecycle_evaluator.py +0 -257
- package/src/lifecycle/lifecycle_scheduler.py +0 -130
- package/src/lifecycle/retention_policy.py +0 -285
- package/src/lifecycle/tests/test_bounded_growth.py +0 -193
- package/src/lifecycle/tests/test_compaction.py +0 -179
- package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
- package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
- package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
- package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
- package/src/lifecycle/tests/test_mcp_compact.py +0 -149
- package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
- package/src/lifecycle/tests/test_retention_policy.py +0 -162
- package/src/mcp_tools_v28.py +0 -281
- package/src/memory/__init__.py +0 -36
- package/src/memory/cli.py +0 -205
- package/src/memory/constants.py +0 -39
- package/src/memory/helpers.py +0 -28
- package/src/memory/schema.py +0 -166
- package/src/memory-profiles.py +0 -595
- package/src/memory-reset.py +0 -491
- package/src/memory_compression.py +0 -989
- package/src/memory_store_v2.py +0 -1155
- package/src/migrate_v1_to_v2.py +0 -629
- package/src/pattern_learner.py +0 -34
- package/src/patterns/__init__.py +0 -24
- package/src/patterns/analyzers.py +0 -251
- package/src/patterns/learner.py +0 -271
- package/src/patterns/scoring.py +0 -171
- package/src/patterns/store.py +0 -225
- package/src/patterns/terminology.py +0 -140
- package/src/provenance_tracker.py +0 -312
- package/src/qualixar_attribution.py +0 -139
- package/src/qualixar_watermark.py +0 -78
- package/src/query_optimizer.py +0 -511
- package/src/rate_limiter.py +0 -83
- package/src/search/__init__.py +0 -20
- package/src/search/cli.py +0 -77
- package/src/search/constants.py +0 -26
- package/src/search/engine.py +0 -241
- package/src/search/fusion.py +0 -122
- package/src/search/index_loader.py +0 -114
- package/src/search/methods.py +0 -162
- package/src/search_engine_v2.py +0 -401
- package/src/setup_validator.py +0 -482
- package/src/subscription_manager.py +0 -391
- package/src/tree/__init__.py +0 -59
- package/src/tree/builder.py +0 -185
- package/src/tree/nodes.py +0 -202
- package/src/tree/queries.py +0 -257
- package/src/tree/schema.py +0 -80
- package/src/tree_manager.py +0 -19
- package/src/trust/__init__.py +0 -45
- package/src/trust/constants.py +0 -66
- package/src/trust/queries.py +0 -157
- package/src/trust/schema.py +0 -95
- package/src/trust/scorer.py +0 -299
- package/src/trust/signals.py +0 -95
- package/src/trust_scorer.py +0 -44
- package/ui/app.js +0 -1588
- package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
- package/ui/js/graph-cytoscape.js +0 -1168
- package/ui/js/graph-d3-backup.js +0 -32
- package/ui/js/graph.js +0 -32
- package/ui_server.py +0 -266
- /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
- /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
- /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
- /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
- /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
- /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
- /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
- /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
- /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
- /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
- /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
- /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
- /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
- /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
- /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
- /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
- /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
- /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
- /package/{completions → ide/completions}/slm.bash +0 -0
- /package/{completions → ide/completions}/slm.zsh +0 -0
- /package/{configs → ide/configs}/cody-commands.json +0 -0
- /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under the MIT License - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""SuperLocalMemory V3 — EU AI Act Compliance Verification.
|
|
6
|
+
|
|
7
|
+
Verifies that each operating mode meets EU AI Act requirements.
|
|
8
|
+
Mode A and B: FULL compliance (zero cloud, zero generative AI / local only).
|
|
9
|
+
Mode C: NOT compliant (cloud LLM processing).
|
|
10
|
+
|
|
11
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from datetime import UTC, datetime
|
|
19
|
+
|
|
20
|
+
from superlocalmemory.core.modes import get_capabilities
|
|
21
|
+
from superlocalmemory.storage.models import Mode
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class ComplianceReport:
|
|
28
|
+
"""EU AI Act compliance assessment for a specific mode."""
|
|
29
|
+
|
|
30
|
+
mode: Mode
|
|
31
|
+
compliant: bool
|
|
32
|
+
risk_category: str # "minimal" / "limited" / "high" / "unacceptable"
|
|
33
|
+
data_stays_local: bool
|
|
34
|
+
uses_generative_ai: bool
|
|
35
|
+
transparency_met: bool
|
|
36
|
+
human_oversight: bool
|
|
37
|
+
findings: list[str]
|
|
38
|
+
timestamp: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class EUAIActChecker:
|
|
42
|
+
"""Verify EU AI Act compliance for each operating mode.
|
|
43
|
+
|
|
44
|
+
EU AI Act (effective Aug 2025) classifies AI systems by risk:
|
|
45
|
+
- Minimal risk: No obligations (most AI systems)
|
|
46
|
+
- Limited risk: Transparency obligations
|
|
47
|
+
- High risk: Strict requirements (biometric, critical infra)
|
|
48
|
+
- Unacceptable: Banned
|
|
49
|
+
|
|
50
|
+
Memory systems are generally "minimal risk" UNLESS they process
|
|
51
|
+
personal data via cloud AI services (then "limited risk" with
|
|
52
|
+
transparency obligations).
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def check_compliance(self, mode: Mode) -> ComplianceReport:
|
|
56
|
+
"""Generate compliance report for a mode."""
|
|
57
|
+
caps = get_capabilities(mode)
|
|
58
|
+
findings: list[str] = []
|
|
59
|
+
|
|
60
|
+
# Data locality
|
|
61
|
+
data_local = caps.data_stays_local
|
|
62
|
+
if not data_local:
|
|
63
|
+
findings.append(
|
|
64
|
+
"Data leaves device for cloud LLM processing. "
|
|
65
|
+
"Requires Data Processing Agreement (DPA) with provider."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Generative AI usage
|
|
69
|
+
uses_gen_ai = caps.llm_fact_extraction or caps.llm_answer_generation
|
|
70
|
+
local_gen_ai = uses_gen_ai and data_local
|
|
71
|
+
|
|
72
|
+
if uses_gen_ai and not data_local:
|
|
73
|
+
findings.append(
|
|
74
|
+
"Uses cloud generative AI. EU AI Act Art. 52 requires "
|
|
75
|
+
"transparency: users must be informed AI generates content."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Transparency
|
|
79
|
+
transparency = True # We always disclose AI usage
|
|
80
|
+
findings.append("Transparency requirement MET: system identifies as AI-assisted.")
|
|
81
|
+
|
|
82
|
+
# Human oversight
|
|
83
|
+
human_oversight = True # User controls all memory operations
|
|
84
|
+
findings.append("Human oversight MET: user controls store/recall/delete.")
|
|
85
|
+
|
|
86
|
+
# Risk classification
|
|
87
|
+
if not uses_gen_ai:
|
|
88
|
+
risk = "minimal"
|
|
89
|
+
findings.append("Minimal risk: no generative AI, local processing only.")
|
|
90
|
+
elif local_gen_ai:
|
|
91
|
+
risk = "minimal"
|
|
92
|
+
findings.append("Minimal risk: generative AI is local-only (Ollama).")
|
|
93
|
+
else:
|
|
94
|
+
risk = "limited"
|
|
95
|
+
findings.append(
|
|
96
|
+
"Limited risk: cloud generative AI requires transparency "
|
|
97
|
+
"disclosure and DPA with cloud provider."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
compliant = caps.eu_ai_act_compliant
|
|
101
|
+
if not compliant:
|
|
102
|
+
findings.append(
|
|
103
|
+
"Mode C is NOT EU AI Act compliant by design. "
|
|
104
|
+
"Use Mode A or B for EU-compliant deployments."
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return ComplianceReport(
|
|
108
|
+
mode=mode,
|
|
109
|
+
compliant=compliant,
|
|
110
|
+
risk_category=risk,
|
|
111
|
+
data_stays_local=data_local,
|
|
112
|
+
uses_generative_ai=uses_gen_ai,
|
|
113
|
+
transparency_met=transparency,
|
|
114
|
+
human_oversight=human_oversight,
|
|
115
|
+
findings=findings,
|
|
116
|
+
timestamp=datetime.now(UTC).isoformat(),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def verify_all_modes(self) -> dict[str, ComplianceReport]:
|
|
120
|
+
"""Generate compliance reports for all three modes."""
|
|
121
|
+
return {
|
|
122
|
+
mode.value: self.check_compliance(mode)
|
|
123
|
+
for mode in Mode
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
def get_compliant_modes(self) -> list[Mode]:
|
|
127
|
+
"""Return list of EU AI Act compliant modes."""
|
|
128
|
+
return [
|
|
129
|
+
mode for mode in Mode
|
|
130
|
+
if get_capabilities(mode).eu_ai_act_compliant
|
|
131
|
+
]
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under the MIT License - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""SuperLocalMemory V3 — GDPR Compliance.
|
|
6
|
+
|
|
7
|
+
Implements GDPR rights: right to access, right to erasure (forget),
|
|
8
|
+
right to data portability (export), and audit trail.
|
|
9
|
+
Profile-scoped. All operations logged to compliance_audit.
|
|
10
|
+
|
|
11
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
from datetime import UTC, datetime
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GDPRCompliance:
|
|
24
|
+
"""GDPR compliance operations for memory data.
|
|
25
|
+
|
|
26
|
+
Supports:
|
|
27
|
+
- Right to Access (Art. 15): Export all data for a profile
|
|
28
|
+
- Right to Erasure (Art. 17): Delete all data for a profile/entity
|
|
29
|
+
- Right to Portability (Art. 20): Export in machine-readable format
|
|
30
|
+
- Audit Trail: Log all data operations
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, db) -> None:
|
|
34
|
+
self._db = db
|
|
35
|
+
|
|
36
|
+
# -- Right to Access (Art. 15) -----------------------------------------
|
|
37
|
+
|
|
38
|
+
def export_profile_data(self, profile_id: str) -> dict:
|
|
39
|
+
"""Export ALL data for a profile in machine-readable format.
|
|
40
|
+
|
|
41
|
+
Returns a dict containing all memories, facts, entities,
|
|
42
|
+
edges, trust scores, feedback, and behavioral patterns.
|
|
43
|
+
"""
|
|
44
|
+
self._audit("export", "profile", profile_id, "Full data export")
|
|
45
|
+
|
|
46
|
+
data: dict = {"profile_id": profile_id, "exported_at": _now()}
|
|
47
|
+
|
|
48
|
+
# Memories
|
|
49
|
+
rows = self._db.execute(
|
|
50
|
+
"SELECT * FROM memories WHERE profile_id = ?", (profile_id,)
|
|
51
|
+
)
|
|
52
|
+
data["memories"] = [dict(r) for r in rows]
|
|
53
|
+
|
|
54
|
+
# Facts
|
|
55
|
+
rows = self._db.execute(
|
|
56
|
+
"SELECT * FROM atomic_facts WHERE profile_id = ?", (profile_id,)
|
|
57
|
+
)
|
|
58
|
+
data["facts"] = [dict(r) for r in rows]
|
|
59
|
+
|
|
60
|
+
# Entities
|
|
61
|
+
rows = self._db.execute(
|
|
62
|
+
"SELECT * FROM canonical_entities WHERE profile_id = ?", (profile_id,)
|
|
63
|
+
)
|
|
64
|
+
data["entities"] = [dict(r) for r in rows]
|
|
65
|
+
|
|
66
|
+
# Graph edges
|
|
67
|
+
rows = self._db.execute(
|
|
68
|
+
"SELECT * FROM graph_edges WHERE profile_id = ?", (profile_id,)
|
|
69
|
+
)
|
|
70
|
+
data["edges"] = [dict(r) for r in rows]
|
|
71
|
+
|
|
72
|
+
# Trust scores
|
|
73
|
+
rows = self._db.execute(
|
|
74
|
+
"SELECT * FROM trust_scores WHERE profile_id = ?", (profile_id,)
|
|
75
|
+
)
|
|
76
|
+
data["trust_scores"] = [dict(r) for r in rows]
|
|
77
|
+
|
|
78
|
+
# Feedback
|
|
79
|
+
rows = self._db.execute(
|
|
80
|
+
"SELECT * FROM feedback_records WHERE profile_id = ?", (profile_id,)
|
|
81
|
+
)
|
|
82
|
+
data["feedback"] = [dict(r) for r in rows]
|
|
83
|
+
|
|
84
|
+
# Entity profiles
|
|
85
|
+
rows = self._db.execute(
|
|
86
|
+
"SELECT * FROM entity_profiles WHERE profile_id = ?", (profile_id,)
|
|
87
|
+
)
|
|
88
|
+
data["entity_profiles"] = [dict(r) for r in rows]
|
|
89
|
+
|
|
90
|
+
# Memory scenes
|
|
91
|
+
rows = self._db.execute(
|
|
92
|
+
"SELECT * FROM memory_scenes WHERE profile_id = ?", (profile_id,)
|
|
93
|
+
)
|
|
94
|
+
data["scenes"] = [dict(r) for r in rows]
|
|
95
|
+
|
|
96
|
+
# Temporal events
|
|
97
|
+
rows = self._db.execute(
|
|
98
|
+
"SELECT * FROM temporal_events WHERE profile_id = ?", (profile_id,)
|
|
99
|
+
)
|
|
100
|
+
data["temporal_events"] = [dict(r) for r in rows]
|
|
101
|
+
|
|
102
|
+
# Consolidation log
|
|
103
|
+
rows = self._db.execute(
|
|
104
|
+
"SELECT * FROM consolidation_log WHERE profile_id = ?", (profile_id,)
|
|
105
|
+
)
|
|
106
|
+
data["consolidation_log"] = [dict(r) for r in rows]
|
|
107
|
+
|
|
108
|
+
# Behavioral patterns
|
|
109
|
+
rows = self._db.execute(
|
|
110
|
+
"SELECT * FROM behavioral_patterns WHERE profile_id = ?", (profile_id,)
|
|
111
|
+
)
|
|
112
|
+
data["behavioral_patterns"] = [dict(r) for r in rows]
|
|
113
|
+
|
|
114
|
+
# Action outcomes
|
|
115
|
+
rows = self._db.execute(
|
|
116
|
+
"SELECT * FROM action_outcomes WHERE profile_id = ?", (profile_id,)
|
|
117
|
+
)
|
|
118
|
+
data["action_outcomes"] = [dict(r) for r in rows]
|
|
119
|
+
|
|
120
|
+
# Compliance audit trail
|
|
121
|
+
rows = self._db.execute(
|
|
122
|
+
"SELECT * FROM compliance_audit WHERE profile_id = ?", (profile_id,)
|
|
123
|
+
)
|
|
124
|
+
data["compliance_audit"] = [dict(r) for r in rows]
|
|
125
|
+
|
|
126
|
+
# Provenance (data lineage — EU AI Act Art. 10)
|
|
127
|
+
rows = self._db.execute(
|
|
128
|
+
"SELECT * FROM provenance WHERE profile_id = ?", (profile_id,)
|
|
129
|
+
)
|
|
130
|
+
data["provenance"] = [dict(r) for r in rows]
|
|
131
|
+
|
|
132
|
+
# Entity aliases (indirect PII via entity relationships)
|
|
133
|
+
rows = self._db.execute(
|
|
134
|
+
"SELECT ea.* FROM entity_aliases ea "
|
|
135
|
+
"JOIN canonical_entities ce ON ea.entity_id = ce.entity_id "
|
|
136
|
+
"WHERE ce.profile_id = ?", (profile_id,)
|
|
137
|
+
)
|
|
138
|
+
data["entity_aliases"] = [dict(r) for r in rows]
|
|
139
|
+
|
|
140
|
+
# Profile record itself
|
|
141
|
+
rows = self._db.execute(
|
|
142
|
+
"SELECT * FROM profiles WHERE profile_id = ?", (profile_id,)
|
|
143
|
+
)
|
|
144
|
+
data["profile_record"] = [dict(r) for r in rows]
|
|
145
|
+
|
|
146
|
+
data["total_items"] = sum(
|
|
147
|
+
len(v) for v in data.values() if isinstance(v, list)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
logger.info("Exported %d items for profile '%s'", data["total_items"], profile_id)
|
|
151
|
+
return data
|
|
152
|
+
|
|
153
|
+
# -- Right to Erasure (Art. 17) ----------------------------------------
|
|
154
|
+
|
|
155
|
+
def forget_profile(self, profile_id: str) -> dict:
|
|
156
|
+
"""Delete ALL data for a profile (right to be forgotten).
|
|
157
|
+
|
|
158
|
+
CASCADE deletes handle most cleanup via foreign keys.
|
|
159
|
+
Returns counts of deleted items.
|
|
160
|
+
"""
|
|
161
|
+
if profile_id == "default":
|
|
162
|
+
raise ValueError("Cannot delete the default profile via GDPR erasure. "
|
|
163
|
+
"Use profile deletion instead.")
|
|
164
|
+
|
|
165
|
+
self._audit("delete", "profile", profile_id, "GDPR erasure request")
|
|
166
|
+
|
|
167
|
+
counts: dict[str, int] = {}
|
|
168
|
+
tables = [
|
|
169
|
+
"compliance_audit", "action_outcomes", "behavioral_patterns",
|
|
170
|
+
"feedback_records", "trust_scores", "provenance",
|
|
171
|
+
"consolidation_log", "graph_edges", "temporal_events",
|
|
172
|
+
"memory_scenes", "entity_profiles", "bm25_tokens",
|
|
173
|
+
"atomic_facts", "memories", "canonical_entities",
|
|
174
|
+
]
|
|
175
|
+
for table in tables:
|
|
176
|
+
rows = self._db.execute(
|
|
177
|
+
f"SELECT COUNT(*) AS c FROM {table} WHERE profile_id = ?",
|
|
178
|
+
(profile_id,),
|
|
179
|
+
)
|
|
180
|
+
counts[table] = int(dict(rows[0])["c"]) if rows else 0
|
|
181
|
+
self._db.execute(
|
|
182
|
+
f"DELETE FROM {table} WHERE profile_id = ?", (profile_id,)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Delete entity aliases (orphan PII via entity relationships)
|
|
186
|
+
self._db.execute(
|
|
187
|
+
"DELETE FROM entity_aliases WHERE entity_id IN "
|
|
188
|
+
"(SELECT entity_id FROM canonical_entities WHERE profile_id = ?)",
|
|
189
|
+
(profile_id,),
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Delete profile itself
|
|
193
|
+
self._db.execute(
|
|
194
|
+
"DELETE FROM profiles WHERE profile_id = ?", (profile_id,)
|
|
195
|
+
)
|
|
196
|
+
counts["profiles"] = 1
|
|
197
|
+
|
|
198
|
+
# Erase learning database (separate DB file)
|
|
199
|
+
try:
|
|
200
|
+
from superlocalmemory.learning.database import LearningDatabase
|
|
201
|
+
from superlocalmemory.core.config import DEFAULT_BASE_DIR
|
|
202
|
+
learning_db = LearningDatabase(DEFAULT_BASE_DIR / "learning.db")
|
|
203
|
+
learning_db.reset(profile_id)
|
|
204
|
+
counts["learning_db"] = 1
|
|
205
|
+
except Exception:
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
# VACUUM to remove deleted data from physical file
|
|
209
|
+
try:
|
|
210
|
+
self._db.execute("VACUUM")
|
|
211
|
+
except Exception:
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
logger.info("GDPR erasure for '%s': %s", profile_id, counts)
|
|
215
|
+
return counts
|
|
216
|
+
|
|
217
|
+
def forget_entity(self, entity_name: str, profile_id: str) -> dict:
|
|
218
|
+
"""Delete all data related to a specific entity.
|
|
219
|
+
|
|
220
|
+
Removes facts mentioning the entity, edges, temporal events,
|
|
221
|
+
and the entity itself. For targeted erasure requests.
|
|
222
|
+
"""
|
|
223
|
+
self._audit("delete", "entity", entity_name,
|
|
224
|
+
f"GDPR entity erasure in profile {profile_id}",
|
|
225
|
+
profile_id=profile_id)
|
|
226
|
+
|
|
227
|
+
entity = self._db.get_entity_by_name(entity_name, profile_id)
|
|
228
|
+
if entity is None:
|
|
229
|
+
return {"deleted": 0, "entity": entity_name, "found": False}
|
|
230
|
+
|
|
231
|
+
eid = entity.entity_id
|
|
232
|
+
counts: dict[str, int] = {}
|
|
233
|
+
|
|
234
|
+
# Delete facts mentioning this entity
|
|
235
|
+
rows = self._db.execute(
|
|
236
|
+
"SELECT fact_id FROM atomic_facts WHERE profile_id = ? "
|
|
237
|
+
"AND canonical_entities_json LIKE ?",
|
|
238
|
+
(profile_id, f'%"{eid}"%'),
|
|
239
|
+
)
|
|
240
|
+
fact_ids = [dict(r)["fact_id"] for r in rows]
|
|
241
|
+
for fid in fact_ids:
|
|
242
|
+
self._db.delete_fact(fid)
|
|
243
|
+
counts["facts"] = len(fact_ids)
|
|
244
|
+
|
|
245
|
+
# Delete temporal events
|
|
246
|
+
self._db.execute(
|
|
247
|
+
"DELETE FROM temporal_events WHERE entity_id = ? AND profile_id = ?",
|
|
248
|
+
(eid, profile_id),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Delete entity profile
|
|
252
|
+
self._db.execute(
|
|
253
|
+
"DELETE FROM entity_profiles WHERE entity_id = ? AND profile_id = ?",
|
|
254
|
+
(eid, profile_id),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Delete aliases + entity
|
|
258
|
+
self._db.execute("DELETE FROM entity_aliases WHERE entity_id = ?", (eid,))
|
|
259
|
+
self._db.execute("DELETE FROM canonical_entities WHERE entity_id = ?", (eid,))
|
|
260
|
+
counts["entity"] = 1
|
|
261
|
+
|
|
262
|
+
logger.info("Entity erasure '%s' in '%s': %s", entity_name, profile_id, counts)
|
|
263
|
+
return counts
|
|
264
|
+
|
|
265
|
+
# -- Audit Trail -------------------------------------------------------
|
|
266
|
+
|
|
267
|
+
def get_audit_trail(
|
|
268
|
+
self, profile_id: str, limit: int = 100
|
|
269
|
+
) -> list[dict]:
|
|
270
|
+
"""Get compliance audit trail for a profile."""
|
|
271
|
+
rows = self._db.execute(
|
|
272
|
+
"SELECT * FROM compliance_audit WHERE profile_id = ? "
|
|
273
|
+
"ORDER BY timestamp DESC LIMIT ?",
|
|
274
|
+
(profile_id, limit),
|
|
275
|
+
)
|
|
276
|
+
return [dict(r) for r in rows]
|
|
277
|
+
|
|
278
|
+
def _audit(
|
|
279
|
+
self, action: str, target_type: str, target_id: str, details: str,
|
|
280
|
+
profile_id: str | None = None,
|
|
281
|
+
) -> None:
|
|
282
|
+
"""Log a compliance action."""
|
|
283
|
+
from superlocalmemory.storage.models import _new_id
|
|
284
|
+
pid = profile_id if profile_id is not None else target_id
|
|
285
|
+
self._db.execute(
|
|
286
|
+
"INSERT INTO compliance_audit "
|
|
287
|
+
"(audit_id, profile_id, action, target_type, target_id, details, timestamp) "
|
|
288
|
+
"VALUES (?,?,?,?,?,?,?)",
|
|
289
|
+
(_new_id(), pid, action, target_type, target_id, details, _now()),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _now() -> str:
|
|
294
|
+
return datetime.now(UTC).isoformat()
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under the MIT License - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""SuperLocalMemory V3 — Memory Lifecycle Management.
|
|
6
|
+
|
|
7
|
+
Implements Active → Warm → Cold → Archived state machine.
|
|
8
|
+
Coupled with Langevin dynamics: positions naturally create lifecycle states.
|
|
9
|
+
|
|
10
|
+
Ported from V2.8 with Langevin coupling (Innovation's unique feature).
|
|
11
|
+
|
|
12
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from datetime import UTC, datetime, timedelta
|
|
19
|
+
|
|
20
|
+
from superlocalmemory.storage.models import AtomicFact, MemoryLifecycle
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
# Lifecycle transition thresholds (days since last access)
|
|
25
|
+
_ACTIVE_MAX_DAYS = 7 # Active for 7 days after last access
|
|
26
|
+
_WARM_MAX_DAYS = 30 # Warm for 30 days
|
|
27
|
+
_COLD_MAX_DAYS = 90 # Cold for 90 days, then archived
|
|
28
|
+
|
|
29
|
+
# Langevin weight thresholds (if Langevin is available)
|
|
30
|
+
_ACTIVE_WEIGHT_MIN = 0.7
|
|
31
|
+
_WARM_WEIGHT_MIN = 0.4
|
|
32
|
+
_COLD_WEIGHT_MIN = 0.1
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class LifecycleManager:
|
|
36
|
+
"""Manage memory lifecycle states.
|
|
37
|
+
|
|
38
|
+
Two complementary strategies:
|
|
39
|
+
1. Time-based: days since last access → state transition
|
|
40
|
+
2. Langevin-based: position on Poincaré ball → lifecycle weight → state
|
|
41
|
+
|
|
42
|
+
When Langevin is available, it takes precedence (more nuanced).
|
|
43
|
+
Time-based is the fallback for Mode A (no dynamics).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, db, langevin=None) -> None:
|
|
47
|
+
self._db = db
|
|
48
|
+
self._langevin = langevin
|
|
49
|
+
|
|
50
|
+
def get_lifecycle_state(self, fact: AtomicFact) -> MemoryLifecycle:
|
|
51
|
+
"""Determine current lifecycle state for a fact."""
|
|
52
|
+
# Strategy 1: Langevin-based (if available and position exists)
|
|
53
|
+
if self._langevin is not None and fact.langevin_position:
|
|
54
|
+
weight = self._langevin.compute_lifecycle_weight(fact.langevin_position)
|
|
55
|
+
return self._langevin.get_lifecycle_state(weight)
|
|
56
|
+
|
|
57
|
+
# Strategy 2: Time-based fallback
|
|
58
|
+
return self._time_based_state(fact)
|
|
59
|
+
|
|
60
|
+
def update_lifecycle(self, fact_id: str, profile_id: str) -> MemoryLifecycle:
|
|
61
|
+
"""Recompute and persist lifecycle state for a fact."""
|
|
62
|
+
rows = self._db.execute(
|
|
63
|
+
"SELECT lifecycle, access_count, created_at FROM atomic_facts "
|
|
64
|
+
"WHERE fact_id = ? AND profile_id = ?",
|
|
65
|
+
(fact_id, profile_id),
|
|
66
|
+
)
|
|
67
|
+
if not rows:
|
|
68
|
+
return MemoryLifecycle.ACTIVE
|
|
69
|
+
|
|
70
|
+
d = dict(rows[0])
|
|
71
|
+
days = self._days_since(d.get("created_at", ""))
|
|
72
|
+
access = d.get("access_count", 0)
|
|
73
|
+
|
|
74
|
+
if access > 10 or days < _ACTIVE_MAX_DAYS:
|
|
75
|
+
state = MemoryLifecycle.ACTIVE
|
|
76
|
+
elif days < _WARM_MAX_DAYS:
|
|
77
|
+
state = MemoryLifecycle.WARM
|
|
78
|
+
elif days < _COLD_MAX_DAYS:
|
|
79
|
+
state = MemoryLifecycle.COLD
|
|
80
|
+
else:
|
|
81
|
+
state = MemoryLifecycle.ARCHIVED
|
|
82
|
+
|
|
83
|
+
current = d.get("lifecycle", "active")
|
|
84
|
+
if current != state.value:
|
|
85
|
+
self._db.update_fact(fact_id, {"lifecycle": state})
|
|
86
|
+
logger.debug("Fact %s: %s → %s", fact_id, current, state.value)
|
|
87
|
+
|
|
88
|
+
return state
|
|
89
|
+
|
|
90
|
+
def run_maintenance(self, profile_id: str) -> dict[str, int]:
|
|
91
|
+
"""Run lifecycle maintenance on all facts in a profile.
|
|
92
|
+
|
|
93
|
+
If Langevin is available, evolve positions and update states.
|
|
94
|
+
Otherwise, use time-based transitions.
|
|
95
|
+
"""
|
|
96
|
+
facts = self._db.get_all_facts(profile_id)
|
|
97
|
+
counts: dict[str, int] = {s.value: 0 for s in MemoryLifecycle}
|
|
98
|
+
transitions = 0
|
|
99
|
+
|
|
100
|
+
for fact in facts:
|
|
101
|
+
old_state = fact.lifecycle
|
|
102
|
+
|
|
103
|
+
if self._langevin is not None and fact.langevin_position:
|
|
104
|
+
# Langevin step
|
|
105
|
+
age = self._days_since(fact.created_at)
|
|
106
|
+
new_pos, weight = self._langevin.step(
|
|
107
|
+
fact.langevin_position, fact.access_count, age, fact.importance
|
|
108
|
+
)
|
|
109
|
+
new_state = self._langevin.get_lifecycle_state(weight)
|
|
110
|
+
# Persist position + state
|
|
111
|
+
self._db.update_fact(fact.fact_id, {
|
|
112
|
+
"langevin_position": new_pos,
|
|
113
|
+
"lifecycle": new_state,
|
|
114
|
+
})
|
|
115
|
+
else:
|
|
116
|
+
new_state = self._time_based_state(fact)
|
|
117
|
+
if new_state != old_state:
|
|
118
|
+
self._db.update_fact(fact.fact_id, {"lifecycle": new_state})
|
|
119
|
+
|
|
120
|
+
counts[new_state.value] = counts.get(new_state.value, 0) + 1
|
|
121
|
+
if new_state != old_state:
|
|
122
|
+
transitions += 1
|
|
123
|
+
|
|
124
|
+
counts["transitions"] = transitions
|
|
125
|
+
logger.info("Lifecycle maintenance for '%s': %s", profile_id, counts)
|
|
126
|
+
return counts
|
|
127
|
+
|
|
128
|
+
def get_archived_facts(self, profile_id: str) -> list[AtomicFact]:
|
|
129
|
+
"""Get all archived facts (candidates for deletion/export)."""
|
|
130
|
+
rows = self._db.execute(
|
|
131
|
+
"SELECT * FROM atomic_facts WHERE profile_id = ? AND lifecycle = 'archived'",
|
|
132
|
+
(profile_id,),
|
|
133
|
+
)
|
|
134
|
+
return [self._db._row_to_fact(r) for r in rows]
|
|
135
|
+
|
|
136
|
+
# -- Internal ----------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
def _time_based_state(self, fact: AtomicFact) -> MemoryLifecycle:
|
|
139
|
+
"""Determine lifecycle state from time since creation + access count."""
|
|
140
|
+
days = self._days_since(fact.created_at)
|
|
141
|
+
if fact.access_count > 10 or days < _ACTIVE_MAX_DAYS:
|
|
142
|
+
return MemoryLifecycle.ACTIVE
|
|
143
|
+
if days < _WARM_MAX_DAYS:
|
|
144
|
+
return MemoryLifecycle.WARM
|
|
145
|
+
if days < _COLD_MAX_DAYS:
|
|
146
|
+
return MemoryLifecycle.COLD
|
|
147
|
+
return MemoryLifecycle.ARCHIVED
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def _days_since(iso_date: str) -> float:
|
|
151
|
+
"""Days since an ISO date string."""
|
|
152
|
+
if not iso_date:
|
|
153
|
+
return 0.0
|
|
154
|
+
try:
|
|
155
|
+
dt = datetime.fromisoformat(iso_date)
|
|
156
|
+
return (datetime.now(UTC) - dt).total_seconds() / 86400.0
|
|
157
|
+
except (ValueError, TypeError):
|
|
158
|
+
return 0.0
|