superlocalmemory 2.8.6 → 3.0.1
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/LICENSE +9 -1
- package/NOTICE +63 -0
- package/README.md +165 -480
- package/bin/slm +17 -449
- package/bin/slm-npm +62 -48
- 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/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/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 -1808
- 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 -286
- /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
- /package/{install.ps1 → scripts/install.ps1} +0 -0
- /package/{install.sh → scripts/install.sh} +0 -0
|
@@ -0,0 +1,317 @@
|
|
|
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
|
+
"""V2 to V3 database migration.
|
|
6
|
+
|
|
7
|
+
Detects V2 installations, backs up data, extends schema with V3 tables,
|
|
8
|
+
and creates backward-compatible symlinks.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
import shutil
|
|
15
|
+
import sqlite3
|
|
16
|
+
from datetime import datetime, UTC
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
V2_BASE = Path.home() / ".claude-memory"
|
|
22
|
+
V3_BASE = Path.home() / ".superlocalmemory"
|
|
23
|
+
V2_DB_NAME = "memory.db"
|
|
24
|
+
BACKUP_NAME = "memory-v2-backup.db"
|
|
25
|
+
|
|
26
|
+
# V3 tables to add during migration
|
|
27
|
+
V3_TABLES_SQL = [
|
|
28
|
+
"""CREATE TABLE IF NOT EXISTS semantic_facts (
|
|
29
|
+
fact_id TEXT PRIMARY KEY,
|
|
30
|
+
memory_id TEXT,
|
|
31
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
32
|
+
content TEXT NOT NULL,
|
|
33
|
+
fact_type TEXT DEFAULT 'world',
|
|
34
|
+
confidence REAL DEFAULT 0.7,
|
|
35
|
+
speaker TEXT DEFAULT '',
|
|
36
|
+
embedding BLOB,
|
|
37
|
+
fisher_mean BLOB,
|
|
38
|
+
fisher_variance BLOB,
|
|
39
|
+
access_count INTEGER DEFAULT 0,
|
|
40
|
+
observation_date TEXT,
|
|
41
|
+
referenced_date TEXT,
|
|
42
|
+
interval_start TEXT,
|
|
43
|
+
interval_end TEXT,
|
|
44
|
+
canonical_entities TEXT DEFAULT '[]',
|
|
45
|
+
created_at TEXT,
|
|
46
|
+
updated_at TEXT
|
|
47
|
+
)""",
|
|
48
|
+
"""CREATE TABLE IF NOT EXISTS kg_nodes (
|
|
49
|
+
node_id TEXT PRIMARY KEY,
|
|
50
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
51
|
+
entity_name TEXT NOT NULL,
|
|
52
|
+
entity_type TEXT DEFAULT 'unknown',
|
|
53
|
+
aliases TEXT DEFAULT '[]',
|
|
54
|
+
fact_count INTEGER DEFAULT 0,
|
|
55
|
+
created_at TEXT
|
|
56
|
+
)""",
|
|
57
|
+
"""CREATE TABLE IF NOT EXISTS memory_edges (
|
|
58
|
+
edge_id TEXT PRIMARY KEY,
|
|
59
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
60
|
+
source_id TEXT NOT NULL,
|
|
61
|
+
target_id TEXT NOT NULL,
|
|
62
|
+
edge_type TEXT DEFAULT 'semantic',
|
|
63
|
+
weight REAL DEFAULT 1.0,
|
|
64
|
+
created_at TEXT
|
|
65
|
+
)""",
|
|
66
|
+
"""CREATE TABLE IF NOT EXISTS memory_scenes (
|
|
67
|
+
scene_id TEXT PRIMARY KEY,
|
|
68
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
69
|
+
label TEXT DEFAULT '',
|
|
70
|
+
fact_ids TEXT DEFAULT '[]',
|
|
71
|
+
created_at TEXT
|
|
72
|
+
)""",
|
|
73
|
+
"""CREATE TABLE IF NOT EXISTS bm25_tokens (
|
|
74
|
+
fact_id TEXT NOT NULL,
|
|
75
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
76
|
+
tokens TEXT NOT NULL,
|
|
77
|
+
doc_length INTEGER DEFAULT 0,
|
|
78
|
+
PRIMARY KEY (fact_id, profile_id)
|
|
79
|
+
)""",
|
|
80
|
+
"""CREATE TABLE IF NOT EXISTS temporal_events (
|
|
81
|
+
event_id TEXT PRIMARY KEY,
|
|
82
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
83
|
+
entity_id TEXT,
|
|
84
|
+
fact_id TEXT,
|
|
85
|
+
observation_date TEXT,
|
|
86
|
+
referenced_date TEXT,
|
|
87
|
+
interval_start TEXT,
|
|
88
|
+
interval_end TEXT,
|
|
89
|
+
description TEXT DEFAULT ''
|
|
90
|
+
)""",
|
|
91
|
+
"""CREATE TABLE IF NOT EXISTS memory_observations (
|
|
92
|
+
obs_id TEXT PRIMARY KEY,
|
|
93
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
94
|
+
entity_id TEXT NOT NULL,
|
|
95
|
+
observation TEXT NOT NULL,
|
|
96
|
+
source_fact_id TEXT,
|
|
97
|
+
created_at TEXT
|
|
98
|
+
)""",
|
|
99
|
+
"""CREATE TABLE IF NOT EXISTS contradictions (
|
|
100
|
+
contradiction_id TEXT PRIMARY KEY,
|
|
101
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
102
|
+
fact_id_a TEXT NOT NULL,
|
|
103
|
+
fact_id_b TEXT NOT NULL,
|
|
104
|
+
severity REAL DEFAULT 0.5,
|
|
105
|
+
resolved INTEGER DEFAULT 0,
|
|
106
|
+
created_at TEXT
|
|
107
|
+
)""",
|
|
108
|
+
"""CREATE TABLE IF NOT EXISTS langevin_state (
|
|
109
|
+
fact_id TEXT PRIMARY KEY,
|
|
110
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
111
|
+
position REAL DEFAULT 0.5,
|
|
112
|
+
velocity REAL DEFAULT 0.0,
|
|
113
|
+
updated_at TEXT
|
|
114
|
+
)""",
|
|
115
|
+
"""CREATE TABLE IF NOT EXISTS sheaf_sections (
|
|
116
|
+
section_id TEXT PRIMARY KEY,
|
|
117
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
118
|
+
fact_id TEXT NOT NULL,
|
|
119
|
+
section_data BLOB,
|
|
120
|
+
created_at TEXT
|
|
121
|
+
)""",
|
|
122
|
+
"""CREATE TABLE IF NOT EXISTS v3_config (
|
|
123
|
+
key TEXT PRIMARY KEY,
|
|
124
|
+
value TEXT NOT NULL,
|
|
125
|
+
updated_at TEXT
|
|
126
|
+
)""",
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
# Indexes for V3 tables
|
|
130
|
+
V3_INDEXES_SQL = [
|
|
131
|
+
"CREATE INDEX IF NOT EXISTS idx_facts_profile ON semantic_facts(profile_id)",
|
|
132
|
+
"CREATE INDEX IF NOT EXISTS idx_facts_memory ON semantic_facts(memory_id)",
|
|
133
|
+
"CREATE INDEX IF NOT EXISTS idx_nodes_profile ON kg_nodes(profile_id)",
|
|
134
|
+
"CREATE INDEX IF NOT EXISTS idx_edges_source ON memory_edges(source_id)",
|
|
135
|
+
"CREATE INDEX IF NOT EXISTS idx_edges_target ON memory_edges(target_id)",
|
|
136
|
+
"CREATE INDEX IF NOT EXISTS idx_scenes_profile ON memory_scenes(profile_id)",
|
|
137
|
+
"CREATE INDEX IF NOT EXISTS idx_temporal_entity ON temporal_events(entity_id)",
|
|
138
|
+
"CREATE INDEX IF NOT EXISTS idx_observations_entity ON memory_observations(entity_id)",
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class V2Migrator:
|
|
143
|
+
"""Migrate V2 database to V3 schema."""
|
|
144
|
+
|
|
145
|
+
def __init__(self, home: Path | None = None):
|
|
146
|
+
self._home = home or Path.home()
|
|
147
|
+
self._v2_base = self._home / ".claude-memory"
|
|
148
|
+
self._v3_base = self._home / ".superlocalmemory"
|
|
149
|
+
self._v2_db = self._v2_base / V2_DB_NAME
|
|
150
|
+
self._v3_db = self._v3_base / V2_DB_NAME
|
|
151
|
+
self._backup_db = self._v3_base / BACKUP_NAME
|
|
152
|
+
|
|
153
|
+
def detect_v2(self) -> bool:
|
|
154
|
+
"""Check if a V2 installation exists."""
|
|
155
|
+
return self._v2_db.exists() and self._v2_db.is_file()
|
|
156
|
+
|
|
157
|
+
def is_already_migrated(self) -> bool:
|
|
158
|
+
"""Check if migration has already been performed."""
|
|
159
|
+
if not self._v3_db.exists():
|
|
160
|
+
return False
|
|
161
|
+
try:
|
|
162
|
+
conn = sqlite3.connect(str(self._v3_db))
|
|
163
|
+
try:
|
|
164
|
+
tables = [r[0] for r in conn.execute(
|
|
165
|
+
"SELECT name FROM sqlite_master WHERE type='table'"
|
|
166
|
+
).fetchall()]
|
|
167
|
+
return "semantic_facts" in tables and "v3_config" in tables
|
|
168
|
+
finally:
|
|
169
|
+
conn.close()
|
|
170
|
+
except Exception:
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
def get_v2_stats(self) -> dict:
|
|
174
|
+
"""Get statistics about the V2 database."""
|
|
175
|
+
if not self.detect_v2():
|
|
176
|
+
return {"exists": False}
|
|
177
|
+
conn = None
|
|
178
|
+
try:
|
|
179
|
+
conn = sqlite3.connect(str(self._v2_db))
|
|
180
|
+
memory_count = conn.execute("SELECT COUNT(*) FROM memories").fetchone()[0]
|
|
181
|
+
tables = [r[0] for r in conn.execute(
|
|
182
|
+
"SELECT name FROM sqlite_master WHERE type='table'"
|
|
183
|
+
).fetchall()]
|
|
184
|
+
# Check for profiles
|
|
185
|
+
profile_count = 1
|
|
186
|
+
try:
|
|
187
|
+
profiles = conn.execute(
|
|
188
|
+
"SELECT DISTINCT profile FROM memories WHERE profile IS NOT NULL"
|
|
189
|
+
).fetchall()
|
|
190
|
+
profile_count = max(len(profiles), 1)
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
193
|
+
return {
|
|
194
|
+
"exists": True,
|
|
195
|
+
"memory_count": memory_count,
|
|
196
|
+
"profile_count": profile_count,
|
|
197
|
+
"table_count": len(tables),
|
|
198
|
+
"db_path": str(self._v2_db),
|
|
199
|
+
"db_size_mb": round(self._v2_db.stat().st_size / 1024 / 1024, 2),
|
|
200
|
+
}
|
|
201
|
+
except Exception as exc:
|
|
202
|
+
return {"exists": True, "error": str(exc)}
|
|
203
|
+
finally:
|
|
204
|
+
if conn is not None:
|
|
205
|
+
conn.close()
|
|
206
|
+
|
|
207
|
+
def migrate(self) -> dict:
|
|
208
|
+
"""Run the full V2 to V3 migration.
|
|
209
|
+
|
|
210
|
+
Steps:
|
|
211
|
+
1. Create V3 directory
|
|
212
|
+
2. Backup V2 database
|
|
213
|
+
3. Copy database to V3 location
|
|
214
|
+
4. Extend schema with V3 tables
|
|
215
|
+
5. Create symlink for backward compat
|
|
216
|
+
6. Mark migration complete
|
|
217
|
+
|
|
218
|
+
Returns dict with migration stats.
|
|
219
|
+
"""
|
|
220
|
+
if not self.detect_v2():
|
|
221
|
+
return {"success": False, "error": "No V2 installation found"}
|
|
222
|
+
|
|
223
|
+
if self.is_already_migrated():
|
|
224
|
+
return {"success": True, "message": "Already migrated"}
|
|
225
|
+
|
|
226
|
+
stats = {"steps": []}
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
# Step 1: Create V3 directory
|
|
230
|
+
self._v3_base.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
(self._v3_base / "embeddings").mkdir(exist_ok=True)
|
|
232
|
+
(self._v3_base / "models").mkdir(exist_ok=True)
|
|
233
|
+
stats["steps"].append("Created V3 directory")
|
|
234
|
+
|
|
235
|
+
# Step 2: Backup
|
|
236
|
+
shutil.copy2(str(self._v2_db), str(self._backup_db))
|
|
237
|
+
stats["steps"].append(f"Backed up to {self._backup_db}")
|
|
238
|
+
|
|
239
|
+
# Step 3: Copy to V3 location
|
|
240
|
+
shutil.copy2(str(self._v2_db), str(self._v3_db))
|
|
241
|
+
stats["steps"].append("Copied database to V3 location")
|
|
242
|
+
|
|
243
|
+
# Step 4: Extend schema
|
|
244
|
+
conn = sqlite3.connect(str(self._v3_db))
|
|
245
|
+
for sql in V3_TABLES_SQL:
|
|
246
|
+
conn.execute(sql)
|
|
247
|
+
for sql in V3_INDEXES_SQL:
|
|
248
|
+
conn.execute(sql)
|
|
249
|
+
# Mark migration
|
|
250
|
+
conn.execute(
|
|
251
|
+
"INSERT OR REPLACE INTO v3_config (key, value, updated_at) VALUES (?, ?, ?)",
|
|
252
|
+
("migration_date", datetime.now(UTC).isoformat(), datetime.now(UTC).isoformat()),
|
|
253
|
+
)
|
|
254
|
+
conn.execute(
|
|
255
|
+
"INSERT OR REPLACE INTO v3_config (key, value, updated_at) VALUES (?, ?, ?)",
|
|
256
|
+
("migration_version", "3.0.0", datetime.now(UTC).isoformat()),
|
|
257
|
+
)
|
|
258
|
+
conn.commit()
|
|
259
|
+
conn.close()
|
|
260
|
+
stats["steps"].append(f"Extended schema ({len(V3_TABLES_SQL)} tables, {len(V3_INDEXES_SQL)} indexes)")
|
|
261
|
+
|
|
262
|
+
# Step 5: Symlink (only if .claude-memory is not already a symlink)
|
|
263
|
+
if not self._v2_base.is_symlink():
|
|
264
|
+
# Rename original to .claude-memory-v2-original
|
|
265
|
+
original_backup = self._home / ".claude-memory-v2-original"
|
|
266
|
+
if not original_backup.exists():
|
|
267
|
+
self._v2_base.rename(original_backup)
|
|
268
|
+
self._v2_base.symlink_to(self._v3_base)
|
|
269
|
+
stats["steps"].append("Created symlink: .claude-memory -> .superlocalmemory")
|
|
270
|
+
else:
|
|
271
|
+
stats["steps"].append("Symlink skipped (backup dir already exists)")
|
|
272
|
+
else:
|
|
273
|
+
stats["steps"].append("Symlink already exists")
|
|
274
|
+
|
|
275
|
+
stats["success"] = True
|
|
276
|
+
stats["v3_db"] = str(self._v3_db)
|
|
277
|
+
stats["backup_db"] = str(self._backup_db)
|
|
278
|
+
|
|
279
|
+
except Exception as exc:
|
|
280
|
+
stats["success"] = False
|
|
281
|
+
stats["error"] = str(exc)
|
|
282
|
+
logger.error("Migration failed: %s", exc)
|
|
283
|
+
|
|
284
|
+
return stats
|
|
285
|
+
|
|
286
|
+
def rollback(self) -> dict:
|
|
287
|
+
"""Rollback migration -- restore V2 state.
|
|
288
|
+
|
|
289
|
+
Returns dict with rollback stats.
|
|
290
|
+
"""
|
|
291
|
+
stats = {"steps": []}
|
|
292
|
+
|
|
293
|
+
try:
|
|
294
|
+
# Remove symlink
|
|
295
|
+
if self._v2_base.is_symlink():
|
|
296
|
+
self._v2_base.unlink()
|
|
297
|
+
stats["steps"].append("Removed symlink")
|
|
298
|
+
|
|
299
|
+
# Restore original V2 directory
|
|
300
|
+
original_backup = self._home / ".claude-memory-v2-original"
|
|
301
|
+
if original_backup.exists():
|
|
302
|
+
if not self._v2_base.exists():
|
|
303
|
+
original_backup.rename(self._v2_base)
|
|
304
|
+
stats["steps"].append("Restored original .claude-memory")
|
|
305
|
+
elif self._backup_db.exists():
|
|
306
|
+
# Restore from backup
|
|
307
|
+
self._v2_base.mkdir(parents=True, exist_ok=True)
|
|
308
|
+
shutil.copy2(str(self._backup_db), str(self._v2_db))
|
|
309
|
+
stats["steps"].append("Restored database from backup")
|
|
310
|
+
|
|
311
|
+
stats["success"] = True
|
|
312
|
+
|
|
313
|
+
except Exception as exc:
|
|
314
|
+
stats["success"] = False
|
|
315
|
+
stats["error"] = str(exc)
|
|
316
|
+
|
|
317
|
+
return stats
|
|
File without changes
|
|
@@ -0,0 +1,130 @@
|
|
|
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 — Trust Gate (Pre-Operation Checks).
|
|
6
|
+
|
|
7
|
+
Enforces minimum trust thresholds before allowing write/delete operations.
|
|
8
|
+
Read operations always pass but are logged for audit purposes.
|
|
9
|
+
|
|
10
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from superlocalmemory.trust.scorer import TrustScorer
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TrustError(PermissionError):
|
|
25
|
+
"""Raised when an agent fails a trust check.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
agent_id: The agent that failed the check.
|
|
29
|
+
trust_score: The agent's current trust score.
|
|
30
|
+
threshold: The minimum required trust score.
|
|
31
|
+
operation: The operation that was attempted.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
agent_id: str,
|
|
37
|
+
trust_score: float,
|
|
38
|
+
threshold: float,
|
|
39
|
+
operation: str,
|
|
40
|
+
) -> None:
|
|
41
|
+
self.agent_id = agent_id
|
|
42
|
+
self.trust_score = trust_score
|
|
43
|
+
self.threshold = threshold
|
|
44
|
+
self.operation = operation
|
|
45
|
+
super().__init__(
|
|
46
|
+
f"Agent '{agent_id}' trust {trust_score:.3f} below "
|
|
47
|
+
f"{operation} threshold {threshold:.3f}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TrustGate:
|
|
52
|
+
"""Pre-operation trust checks.
|
|
53
|
+
|
|
54
|
+
Operations are gated by minimum trust thresholds:
|
|
55
|
+
- write: agent must have trust >= write_threshold (default 0.3)
|
|
56
|
+
- delete: agent must have trust >= delete_threshold (default 0.5)
|
|
57
|
+
- read: always passes (logged for audit trail)
|
|
58
|
+
|
|
59
|
+
Raises TrustError if the agent's trust is too low.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
scorer: TrustScorer,
|
|
65
|
+
write_threshold: float = 0.3,
|
|
66
|
+
delete_threshold: float = 0.5,
|
|
67
|
+
) -> None:
|
|
68
|
+
if write_threshold < 0 or write_threshold > 1:
|
|
69
|
+
raise ValueError("write_threshold must be in [0, 1]")
|
|
70
|
+
if delete_threshold < 0 or delete_threshold > 1:
|
|
71
|
+
raise ValueError("delete_threshold must be in [0, 1]")
|
|
72
|
+
|
|
73
|
+
self._scorer = scorer
|
|
74
|
+
self._write_threshold = write_threshold
|
|
75
|
+
self._delete_threshold = delete_threshold
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def write_threshold(self) -> float:
|
|
79
|
+
return self._write_threshold
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def delete_threshold(self) -> float:
|
|
83
|
+
return self._delete_threshold
|
|
84
|
+
|
|
85
|
+
def check_write(self, agent_id: str, profile_id: str) -> None:
|
|
86
|
+
"""Check if agent is trusted enough to write.
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
TrustError: If agent trust is below write_threshold.
|
|
90
|
+
"""
|
|
91
|
+
score = self._scorer.get_agent_trust(agent_id, profile_id)
|
|
92
|
+
logger.debug(
|
|
93
|
+
"trust gate write: agent=%s trust=%.3f threshold=%.3f",
|
|
94
|
+
agent_id, score, self._write_threshold,
|
|
95
|
+
)
|
|
96
|
+
if score < self._write_threshold:
|
|
97
|
+
raise TrustError(
|
|
98
|
+
agent_id, score, self._write_threshold, "write"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def check_delete(self, agent_id: str, profile_id: str) -> None:
|
|
102
|
+
"""Check if agent is trusted enough to delete.
|
|
103
|
+
|
|
104
|
+
Delete requires higher trust than write because it is destructive.
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
TrustError: If agent trust is below delete_threshold.
|
|
108
|
+
"""
|
|
109
|
+
score = self._scorer.get_agent_trust(agent_id, profile_id)
|
|
110
|
+
logger.debug(
|
|
111
|
+
"trust gate delete: agent=%s trust=%.3f threshold=%.3f",
|
|
112
|
+
agent_id, score, self._delete_threshold,
|
|
113
|
+
)
|
|
114
|
+
if score < self._delete_threshold:
|
|
115
|
+
raise TrustError(
|
|
116
|
+
agent_id, score, self._delete_threshold, "delete"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def check_read(self, agent_id: str, profile_id: str) -> None:
|
|
120
|
+
"""Read check — always passes. Logged for audit trail.
|
|
121
|
+
|
|
122
|
+
Reads are never blocked because denying read access could break
|
|
123
|
+
agent functionality. However, logging read access enables
|
|
124
|
+
anomaly detection and compliance auditing.
|
|
125
|
+
"""
|
|
126
|
+
score = self._scorer.get_agent_trust(agent_id, profile_id)
|
|
127
|
+
logger.debug(
|
|
128
|
+
"trust gate read (always pass): agent=%s trust=%.3f",
|
|
129
|
+
agent_id, score,
|
|
130
|
+
)
|
|
@@ -0,0 +1,124 @@
|
|
|
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 — Provenance Tracking.
|
|
6
|
+
|
|
7
|
+
Records who/what created each memory and how.
|
|
8
|
+
V1 had provenance as WRITE-ONLY (stored but never read).
|
|
9
|
+
Innovation wires reads into trust scoring and compliance audit.
|
|
10
|
+
|
|
11
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
from datetime import UTC, datetime
|
|
18
|
+
|
|
19
|
+
from superlocalmemory.storage.models import ProvenanceRecord
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ProvenanceTracker:
|
|
25
|
+
"""Track provenance of all stored facts.
|
|
26
|
+
|
|
27
|
+
Every fact gets a provenance record at creation time.
|
|
28
|
+
Provenance feeds into:
|
|
29
|
+
- Trust scoring (sources with high-quality provenance get trust boost)
|
|
30
|
+
- Compliance audit (GDPR right-to-know: who stored what data?)
|
|
31
|
+
- Debugging (which extraction pipeline created this fact?)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, db) -> None:
|
|
35
|
+
self._db = db
|
|
36
|
+
|
|
37
|
+
def record(
|
|
38
|
+
self,
|
|
39
|
+
fact_id: str,
|
|
40
|
+
profile_id: str,
|
|
41
|
+
source_type: str,
|
|
42
|
+
source_id: str = "",
|
|
43
|
+
created_by: str = "",
|
|
44
|
+
) -> ProvenanceRecord:
|
|
45
|
+
"""Record provenance for a newly stored fact.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
fact_id: The fact being tracked.
|
|
49
|
+
profile_id: Active profile.
|
|
50
|
+
source_type: "conversation", "import", "consolidation", "migration".
|
|
51
|
+
source_id: Session ID, import batch ID, etc.
|
|
52
|
+
created_by: Agent ID or user identifier.
|
|
53
|
+
"""
|
|
54
|
+
record = ProvenanceRecord(
|
|
55
|
+
profile_id=profile_id,
|
|
56
|
+
fact_id=fact_id,
|
|
57
|
+
source_type=source_type,
|
|
58
|
+
source_id=source_id,
|
|
59
|
+
created_by=created_by,
|
|
60
|
+
timestamp=datetime.now(UTC).isoformat(),
|
|
61
|
+
)
|
|
62
|
+
self._db.execute(
|
|
63
|
+
"INSERT INTO provenance "
|
|
64
|
+
"(provenance_id, profile_id, fact_id, source_type, source_id, "
|
|
65
|
+
"created_by, timestamp) VALUES (?,?,?,?,?,?,?)",
|
|
66
|
+
(record.provenance_id, record.profile_id, record.fact_id,
|
|
67
|
+
record.source_type, record.source_id, record.created_by,
|
|
68
|
+
record.timestamp),
|
|
69
|
+
)
|
|
70
|
+
return record
|
|
71
|
+
|
|
72
|
+
def get_provenance(self, fact_id: str, profile_id: str) -> ProvenanceRecord | None:
|
|
73
|
+
"""Get provenance for a specific fact."""
|
|
74
|
+
rows = self._db.execute(
|
|
75
|
+
"SELECT * FROM provenance WHERE fact_id = ? AND profile_id = ?",
|
|
76
|
+
(fact_id, profile_id),
|
|
77
|
+
)
|
|
78
|
+
if not rows:
|
|
79
|
+
return None
|
|
80
|
+
d = dict(rows[0])
|
|
81
|
+
return ProvenanceRecord(
|
|
82
|
+
provenance_id=d["provenance_id"],
|
|
83
|
+
profile_id=d["profile_id"],
|
|
84
|
+
fact_id=d["fact_id"],
|
|
85
|
+
source_type=d["source_type"],
|
|
86
|
+
source_id=d.get("source_id", ""),
|
|
87
|
+
created_by=d.get("created_by", ""),
|
|
88
|
+
timestamp=d["timestamp"],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def get_facts_by_source(
|
|
92
|
+
self, source_type: str, profile_id: str, limit: int = 100
|
|
93
|
+
) -> list[ProvenanceRecord]:
|
|
94
|
+
"""Get all facts from a specific source type."""
|
|
95
|
+
rows = self._db.execute(
|
|
96
|
+
"SELECT * FROM provenance WHERE source_type = ? AND profile_id = ? "
|
|
97
|
+
"ORDER BY timestamp DESC LIMIT ?",
|
|
98
|
+
(source_type, profile_id, limit),
|
|
99
|
+
)
|
|
100
|
+
return [self._row_to_record(r) for r in rows]
|
|
101
|
+
|
|
102
|
+
def get_provenance_for_profile(
|
|
103
|
+
self, profile_id: str, limit: int = 100
|
|
104
|
+
) -> list[ProvenanceRecord]:
|
|
105
|
+
"""Get all provenance records for a profile (compliance audit)."""
|
|
106
|
+
rows = self._db.execute(
|
|
107
|
+
"SELECT * FROM provenance WHERE profile_id = ? "
|
|
108
|
+
"ORDER BY timestamp DESC LIMIT ?",
|
|
109
|
+
(profile_id, limit),
|
|
110
|
+
)
|
|
111
|
+
return [self._row_to_record(r) for r in rows]
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _row_to_record(row) -> ProvenanceRecord:
|
|
115
|
+
d = dict(row)
|
|
116
|
+
return ProvenanceRecord(
|
|
117
|
+
provenance_id=d["provenance_id"],
|
|
118
|
+
profile_id=d["profile_id"],
|
|
119
|
+
fact_id=d["fact_id"],
|
|
120
|
+
source_type=d["source_type"],
|
|
121
|
+
source_id=d.get("source_id", ""),
|
|
122
|
+
created_by=d.get("created_by", ""),
|
|
123
|
+
timestamp=d["timestamp"],
|
|
124
|
+
)
|