superlocalmemory 4.1.13 → 4.1.15
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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +103 -0
- package/README.md +3 -3
- package/package.json +1 -8
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/agents/slm-memory-advisor.md +1 -1
- package/plugin-src/agents/slm-optimize-advisor.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-governance/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-loop/SKILL.md +1 -1
- package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
- package/plugin-src/skills/slm-profile/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +1 -1
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-scope/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/scripts/postinstall.js +71 -2
- package/pyproject.toml +0 -250
- package/src/superlocalmemory/__init__.py +0 -82
- package/src/superlocalmemory/access/__init__.py +0 -3
- package/src/superlocalmemory/access/rbac.py +0 -575
- package/src/superlocalmemory/attribution/__init__.py +0 -9
- package/src/superlocalmemory/attribution/signer.py +0 -173
- package/src/superlocalmemory/attribution/watermark.py +0 -189
- package/src/superlocalmemory/brain/__init__.py +0 -5
- package/src/superlocalmemory/brain/truth.py +0 -418
- package/src/superlocalmemory/cli/__init__.py +0 -5
- package/src/superlocalmemory/cli/__main__.py +0 -17
- package/src/superlocalmemory/cli/_lazy_init.py +0 -115
- package/src/superlocalmemory/cli/cache_cmd.py +0 -198
- package/src/superlocalmemory/cli/commands.py +0 -4710
- package/src/superlocalmemory/cli/compress_cmd.py +0 -151
- package/src/superlocalmemory/cli/context_commands.py +0 -193
- package/src/superlocalmemory/cli/daemon.py +0 -909
- package/src/superlocalmemory/cli/db_migrate.py +0 -150
- package/src/superlocalmemory/cli/diagnostics_cmd.py +0 -101
- package/src/superlocalmemory/cli/escape_hatch.py +0 -220
- package/src/superlocalmemory/cli/evidence_cmd.py +0 -103
- package/src/superlocalmemory/cli/gdpr_cmd.py +0 -792
- package/src/superlocalmemory/cli/gdpr_io.py +0 -109
- package/src/superlocalmemory/cli/help_cmd.py +0 -197
- package/src/superlocalmemory/cli/host_upgrades.py +0 -189
- package/src/superlocalmemory/cli/ingest_cmd.py +0 -327
- package/src/superlocalmemory/cli/json_output.py +0 -81
- package/src/superlocalmemory/cli/loop_cmd.py +0 -187
- package/src/superlocalmemory/cli/main.py +0 -1145
- package/src/superlocalmemory/cli/mesh_cmd.py +0 -38
- package/src/superlocalmemory/cli/migrate_cmd.py +0 -55
- package/src/superlocalmemory/cli/ops_cmd.py +0 -281
- package/src/superlocalmemory/cli/optimize_cmd.py +0 -179
- package/src/superlocalmemory/cli/optimize_constants.py +0 -31
- package/src/superlocalmemory/cli/pending_store.py +0 -296
- package/src/superlocalmemory/cli/proxy_cmd.py +0 -108
- package/src/superlocalmemory/cli/scale_engine_cmd.py +0 -56
- package/src/superlocalmemory/cli/service_installer.py +0 -373
- package/src/superlocalmemory/cli/setup_wizard.py +0 -1162
- package/src/superlocalmemory/cli/summary_cmd.py +0 -215
- package/src/superlocalmemory/cli/version_banner.py +0 -202
- package/src/superlocalmemory/cli/wizard_v3426_options.py +0 -129
- package/src/superlocalmemory/code_graph/__init__.py +0 -46
- package/src/superlocalmemory/code_graph/blast_radius.py +0 -177
- package/src/superlocalmemory/code_graph/bridge/__init__.py +0 -36
- package/src/superlocalmemory/code_graph/bridge/entity_resolver.py +0 -490
- package/src/superlocalmemory/code_graph/bridge/event_listeners.py +0 -206
- package/src/superlocalmemory/code_graph/bridge/fact_enricher.py +0 -159
- package/src/superlocalmemory/code_graph/bridge/hebbian_linker.py +0 -170
- package/src/superlocalmemory/code_graph/bridge/maintenance.py +0 -220
- package/src/superlocalmemory/code_graph/bridge/temporal_checker.py +0 -152
- package/src/superlocalmemory/code_graph/changes.py +0 -363
- package/src/superlocalmemory/code_graph/communities.py +0 -299
- package/src/superlocalmemory/code_graph/config.py +0 -154
- package/src/superlocalmemory/code_graph/database.py +0 -526
- package/src/superlocalmemory/code_graph/extractors/__init__.py +0 -95
- package/src/superlocalmemory/code_graph/extractors/python.py +0 -413
- package/src/superlocalmemory/code_graph/extractors/typescript.py +0 -556
- package/src/superlocalmemory/code_graph/flows.py +0 -350
- package/src/superlocalmemory/code_graph/git_hooks.py +0 -226
- package/src/superlocalmemory/code_graph/graph_engine.py +0 -295
- package/src/superlocalmemory/code_graph/graph_store.py +0 -335
- package/src/superlocalmemory/code_graph/incremental.py +0 -200
- package/src/superlocalmemory/code_graph/models.py +0 -130
- package/src/superlocalmemory/code_graph/parser.py +0 -687
- package/src/superlocalmemory/code_graph/resolver.py +0 -321
- package/src/superlocalmemory/code_graph/search.py +0 -460
- package/src/superlocalmemory/code_graph/service.py +0 -95
- package/src/superlocalmemory/code_graph/watcher.py +0 -207
- package/src/superlocalmemory/compliance/__init__.py +0 -0
- package/src/superlocalmemory/compliance/abac.py +0 -204
- package/src/superlocalmemory/compliance/audit.py +0 -385
- package/src/superlocalmemory/compliance/eu_ai_act.py +0 -101
- package/src/superlocalmemory/compliance/gdpr.py +0 -1479
- package/src/superlocalmemory/compliance/lifecycle.py +0 -158
- package/src/superlocalmemory/compliance/retention.py +0 -415
- package/src/superlocalmemory/compliance/scheduler.py +0 -217
- package/src/superlocalmemory/contracts/__init__.py +0 -1
- package/src/superlocalmemory/contracts/schemas/agent-experience-v1.schema.json +0 -92
- package/src/superlocalmemory/contracts/schemas/agent-integration-contract-v2.schema.json +0 -46
- package/src/superlocalmemory/contracts/schemas/cognitive-turn-receipt-v1.schema.json +0 -59
- package/src/superlocalmemory/contracts/v402.py +0 -62
- package/src/superlocalmemory/core/__init__.py +0 -0
- package/src/superlocalmemory/core/actor_context.py +0 -166
- package/src/superlocalmemory/core/admission.py +0 -769
- package/src/superlocalmemory/core/backend_orchestrator.py +0 -637
- package/src/superlocalmemory/core/block_hygiene.py +0 -147
- package/src/superlocalmemory/core/community_summary.py +0 -267
- package/src/superlocalmemory/core/component_healer.py +0 -144
- package/src/superlocalmemory/core/component_registry.py +0 -514
- package/src/superlocalmemory/core/config.py +0 -2204
- package/src/superlocalmemory/core/consolidation_engine.py +0 -983
- package/src/superlocalmemory/core/context_cache.py +0 -574
- package/src/superlocalmemory/core/derivation_lineage.py +0 -246
- package/src/superlocalmemory/core/embedding_worker.py +0 -208
- package/src/superlocalmemory/core/embeddings.py +0 -1052
- package/src/superlocalmemory/core/engine.py +0 -1395
- package/src/superlocalmemory/core/engine_capabilities.py +0 -24
- package/src/superlocalmemory/core/engine_ingestion.py +0 -983
- package/src/superlocalmemory/core/engine_lock.py +0 -75
- package/src/superlocalmemory/core/engine_wiring.py +0 -776
- package/src/superlocalmemory/core/entity_community.py +0 -178
- package/src/superlocalmemory/core/error_envelope.py +0 -60
- package/src/superlocalmemory/core/evidence_bundle.py +0 -528
- package/src/superlocalmemory/core/fact_consolidator.py +0 -812
- package/src/superlocalmemory/core/file_lock.py +0 -92
- package/src/superlocalmemory/core/graph_analyzer.py +0 -456
- package/src/superlocalmemory/core/graph_metrics.py +0 -597
- package/src/superlocalmemory/core/graph_pruner.py +0 -939
- package/src/superlocalmemory/core/health_monitor.py +0 -338
- package/src/superlocalmemory/core/hooks.py +0 -65
- package/src/superlocalmemory/core/ingest_gate.py +0 -133
- package/src/superlocalmemory/core/ingest_policy.py +0 -38
- package/src/superlocalmemory/core/ingestion_command.py +0 -1042
- package/src/superlocalmemory/core/injection.py +0 -434
- package/src/superlocalmemory/core/install_detector.py +0 -131
- package/src/superlocalmemory/core/key_expander.py +0 -138
- package/src/superlocalmemory/core/lifecycle_state.py +0 -153
- package/src/superlocalmemory/core/maintenance.py +0 -777
- package/src/superlocalmemory/core/maintenance_scheduler.py +0 -507
- package/src/superlocalmemory/core/materialization_control.py +0 -20
- package/src/superlocalmemory/core/mcp_embedder_proxy.py +0 -89
- package/src/superlocalmemory/core/memory_health.py +0 -266
- package/src/superlocalmemory/core/mode_capability.py +0 -111
- package/src/superlocalmemory/core/modes.py +0 -168
- package/src/superlocalmemory/core/mutations.py +0 -688
- package/src/superlocalmemory/core/ollama_embedder.py +0 -266
- package/src/superlocalmemory/core/ollama_validator.py +0 -315
- package/src/superlocalmemory/core/operation_policy.py +0 -92
- package/src/superlocalmemory/core/operation_policy_registry.py +0 -542
- package/src/superlocalmemory/core/operation_request.py +0 -127
- package/src/superlocalmemory/core/ops_remediation.py +0 -542
- package/src/superlocalmemory/core/pii.py +0 -105
- package/src/superlocalmemory/core/platform_utils.py +0 -138
- package/src/superlocalmemory/core/profiles.py +0 -234
- package/src/superlocalmemory/core/progressive_abstraction.py +0 -208
- package/src/superlocalmemory/core/projection_drain.py +0 -380
- package/src/superlocalmemory/core/queue_consumer.py +0 -168
- package/src/superlocalmemory/core/ram_lock.py +0 -160
- package/src/superlocalmemory/core/rate_limit.py +0 -151
- package/src/superlocalmemory/core/recall_gate.py +0 -95
- package/src/superlocalmemory/core/recall_pipeline.py +0 -1337
- package/src/superlocalmemory/core/recall_queue.py +0 -377
- package/src/superlocalmemory/core/recall_worker.py +0 -414
- package/src/superlocalmemory/core/registry.py +0 -121
- package/src/superlocalmemory/core/remember_admission.py +0 -161
- package/src/superlocalmemory/core/remember_runtime.py +0 -1190
- package/src/superlocalmemory/core/remote_mode.py +0 -214
- package/src/superlocalmemory/core/reranker_worker.py +0 -338
- package/src/superlocalmemory/core/safe_fs.py +0 -108
- package/src/superlocalmemory/core/scale_autopromote.py +0 -196
- package/src/superlocalmemory/core/scale_engine.py +0 -915
- package/src/superlocalmemory/core/score_contract.py +0 -82
- package/src/superlocalmemory/core/security_primitives.py +0 -672
- package/src/superlocalmemory/core/session_identity.py +0 -98
- package/src/superlocalmemory/core/shadow_router.py +0 -319
- package/src/superlocalmemory/core/slm_disabled.py +0 -85
- package/src/superlocalmemory/core/status_contract.py +0 -108
- package/src/superlocalmemory/core/store_pipeline.py +0 -1404
- package/src/superlocalmemory/core/summarizer.py +0 -200
- package/src/superlocalmemory/core/tier_manager.py +0 -461
- package/src/superlocalmemory/core/topic_signature.py +0 -156
- package/src/superlocalmemory/core/transactions/__init__.py +0 -78
- package/src/superlocalmemory/core/transactions/concrete_owners.py +0 -604
- package/src/superlocalmemory/core/transactions/erasure.py +0 -825
- package/src/superlocalmemory/core/transactions/manifest.py +0 -255
- package/src/superlocalmemory/core/transactions/manifest_key.py +0 -155
- package/src/superlocalmemory/core/transactions/obligations.py +0 -272
- package/src/superlocalmemory/core/transactions/owners.py +0 -114
- package/src/superlocalmemory/core/transactions/reconciler.py +0 -285
- package/src/superlocalmemory/core/transactions/service.py +0 -330
- package/src/superlocalmemory/core/worker_pool.py +0 -377
- package/src/superlocalmemory/core/working_memory.py +0 -288
- package/src/superlocalmemory/dynamics/__init__.py +0 -0
- package/src/superlocalmemory/dynamics/activation_guided_quantization.py +0 -374
- package/src/superlocalmemory/dynamics/eap_scheduler.py +0 -294
- package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +0 -171
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +0 -227
- package/src/superlocalmemory/encoding/__init__.py +0 -0
- package/src/superlocalmemory/encoding/auto_linker.py +0 -308
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +0 -899
- package/src/superlocalmemory/encoding/consolidator.py +0 -472
- package/src/superlocalmemory/encoding/context_generator.py +0 -175
- package/src/superlocalmemory/encoding/emotional.py +0 -189
- package/src/superlocalmemory/encoding/entity_reflexion.py +0 -200
- package/src/superlocalmemory/encoding/entity_resolver.py +0 -687
- package/src/superlocalmemory/encoding/entropy_gate.py +0 -101
- package/src/superlocalmemory/encoding/fact_extractor.py +0 -877
- package/src/superlocalmemory/encoding/foresight.py +0 -93
- package/src/superlocalmemory/encoding/graph_builder.py +0 -346
- package/src/superlocalmemory/encoding/observation_builder.py +0 -177
- package/src/superlocalmemory/encoding/prospective_markers.py +0 -262
- package/src/superlocalmemory/encoding/scene_builder.py +0 -410
- package/src/superlocalmemory/encoding/signal_inference.py +0 -90
- package/src/superlocalmemory/encoding/temporal_parser.py +0 -432
- package/src/superlocalmemory/encoding/temporal_validator.py +0 -572
- package/src/superlocalmemory/encoding/type_router.py +0 -237
- package/src/superlocalmemory/evaluation/__init__.py +0 -13
- package/src/superlocalmemory/evaluation/calibration.py +0 -308
- package/src/superlocalmemory/evolution/__init__.py +0 -29
- package/src/superlocalmemory/evolution/blind_verifier.py +0 -122
- package/src/superlocalmemory/evolution/budget.py +0 -356
- package/src/superlocalmemory/evolution/evolution_store.py +0 -619
- package/src/superlocalmemory/evolution/llm_dispatch.py +0 -559
- package/src/superlocalmemory/evolution/model_selection.py +0 -175
- package/src/superlocalmemory/evolution/mutation_generator.py +0 -226
- package/src/superlocalmemory/evolution/skill_activator.py +0 -270
- package/src/superlocalmemory/evolution/skill_evolver.py +0 -928
- package/src/superlocalmemory/evolution/triggers.py +0 -376
- package/src/superlocalmemory/evolution/types.py +0 -114
- package/src/superlocalmemory/graph/__init__.py +0 -9
- package/src/superlocalmemory/graph/cozo_adjacency.py +0 -122
- package/src/superlocalmemory/graph/cozo_backend.py +0 -751
- package/src/superlocalmemory/hooks/__init__.py +0 -3
- package/src/superlocalmemory/hooks/_outcome_common.py +0 -523
- package/src/superlocalmemory/hooks/adapter_base.py +0 -347
- package/src/superlocalmemory/hooks/antigravity_adapter.py +0 -171
- package/src/superlocalmemory/hooks/auto_capture.py +0 -133
- package/src/superlocalmemory/hooks/auto_invoker.py +0 -521
- package/src/superlocalmemory/hooks/auto_parameterize.py +0 -147
- package/src/superlocalmemory/hooks/auto_recall.py +0 -191
- package/src/superlocalmemory/hooks/auto_recall_hook.py +0 -251
- package/src/superlocalmemory/hooks/before_web_hook.py +0 -131
- package/src/superlocalmemory/hooks/claude_code_hooks.py +0 -637
- package/src/superlocalmemory/hooks/codex_assets.py +0 -251
- package/src/superlocalmemory/hooks/codex_hooks.py +0 -186
- package/src/superlocalmemory/hooks/context_payload.py +0 -311
- package/src/superlocalmemory/hooks/copilot_adapter.py +0 -208
- package/src/superlocalmemory/hooks/cross_platform_connector.py +0 -90
- package/src/superlocalmemory/hooks/cursor_adapter.py +0 -173
- package/src/superlocalmemory/hooks/hook_daemon.py +0 -295
- package/src/superlocalmemory/hooks/hook_handlers.py +0 -822
- package/src/superlocalmemory/hooks/ide_connector.py +0 -246
- package/src/superlocalmemory/hooks/memory_protocol.py +0 -158
- package/src/superlocalmemory/hooks/portable_kit.py +0 -755
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +0 -183
- package/src/superlocalmemory/hooks/post_tool_outcome_hook.py +0 -351
- package/src/superlocalmemory/hooks/prewarm_auth.py +0 -187
- package/src/superlocalmemory/hooks/rules_engine.py +0 -99
- package/src/superlocalmemory/hooks/session_registry.py +0 -330
- package/src/superlocalmemory/hooks/stop_outcome_hook.py +0 -138
- package/src/superlocalmemory/hooks/sync_loop.py +0 -114
- package/src/superlocalmemory/hooks/topic_shift_hook.py +0 -302
- package/src/superlocalmemory/hooks/user_prompt_hook.py +0 -131
- package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +0 -210
- package/src/superlocalmemory/infra/__init__.py +0 -3
- package/src/superlocalmemory/infra/auth_middleware.py +0 -145
- package/src/superlocalmemory/infra/backup.py +0 -974
- package/src/superlocalmemory/infra/backup_obligations.py +0 -423
- package/src/superlocalmemory/infra/cache_manager.py +0 -267
- package/src/superlocalmemory/infra/cloud_backup.py +0 -788
- package/src/superlocalmemory/infra/daemon_identity.py +0 -300
- package/src/superlocalmemory/infra/data_root.py +0 -238
- package/src/superlocalmemory/infra/event_bus.py +0 -637
- package/src/superlocalmemory/infra/local_diagnostics.py +0 -327
- package/src/superlocalmemory/infra/pid_manager.py +0 -193
- package/src/superlocalmemory/infra/process_identity.py +0 -180
- package/src/superlocalmemory/infra/process_reaper.py +0 -624
- package/src/superlocalmemory/infra/rate_limiter.py +0 -228
- package/src/superlocalmemory/infra/self_heal.py +0 -401
- package/src/superlocalmemory/infra/version_integrity.py +0 -229
- package/src/superlocalmemory/ingestion/__init__.py +0 -13
- package/src/superlocalmemory/ingestion/adapter_manager.py +0 -255
- package/src/superlocalmemory/ingestion/base_adapter.py +0 -171
- package/src/superlocalmemory/ingestion/calendar_adapter.py +0 -349
- package/src/superlocalmemory/ingestion/credentials.py +0 -125
- package/src/superlocalmemory/ingestion/gmail_adapter.py +0 -378
- package/src/superlocalmemory/ingestion/parsers.py +0 -100
- package/src/superlocalmemory/ingestion/transcript_adapter.py +0 -161
- package/src/superlocalmemory/integrations/__init__.py +0 -1
- package/src/superlocalmemory/integrations/bounded_loops_mcp.py +0 -431
- package/src/superlocalmemory/integrations/bounded_loops_v051.py +0 -236
- package/src/superlocalmemory/learning/__init__.py +0 -0
- package/src/superlocalmemory/learning/adaptive.py +0 -172
- package/src/superlocalmemory/learning/arm_catalog.py +0 -97
- package/src/superlocalmemory/learning/assertion_miner.py +0 -403
- package/src/superlocalmemory/learning/bandit.py +0 -654
- package/src/superlocalmemory/learning/bandit_cache.py +0 -131
- package/src/superlocalmemory/learning/behavioral.py +0 -542
- package/src/superlocalmemory/learning/bootstrap.py +0 -298
- package/src/superlocalmemory/learning/consolidation_cycle.py +0 -398
- package/src/superlocalmemory/learning/consolidation_quantization_worker.py +0 -115
- package/src/superlocalmemory/learning/consolidation_worker.py +0 -261
- package/src/superlocalmemory/learning/cross_project.py +0 -408
- package/src/superlocalmemory/learning/database.py +0 -698
- package/src/superlocalmemory/learning/dedup_hnsw.py +0 -413
- package/src/superlocalmemory/learning/engagement.py +0 -487
- package/src/superlocalmemory/learning/engagement_features.py +0 -279
- package/src/superlocalmemory/learning/ensemble.py +0 -309
- package/src/superlocalmemory/learning/entity_compiler.py +0 -356
- package/src/superlocalmemory/learning/fact_outcome_joins.py +0 -207
- package/src/superlocalmemory/learning/features.py +0 -138
- package/src/superlocalmemory/learning/feedback.py +0 -724
- package/src/superlocalmemory/learning/forgetting_scheduler.py +0 -375
- package/src/superlocalmemory/learning/hnsw_dedup.py +0 -69
- package/src/superlocalmemory/learning/labeler.py +0 -85
- package/src/superlocalmemory/learning/legacy_migration.py +0 -316
- package/src/superlocalmemory/learning/lightgbm_subprocess.py +0 -236
- package/src/superlocalmemory/learning/memory_merge.py +0 -175
- package/src/superlocalmemory/learning/model_cache.py +0 -267
- package/src/superlocalmemory/learning/model_rollback.py +0 -281
- package/src/superlocalmemory/learning/outcome_queue.py +0 -306
- package/src/superlocalmemory/learning/outcomes.py +0 -286
- package/src/superlocalmemory/learning/pattern_miner.py +0 -465
- package/src/superlocalmemory/learning/pattern_miner_constants.py +0 -90
- package/src/superlocalmemory/learning/pcos.py +0 -291
- package/src/superlocalmemory/learning/project_context.py +0 -366
- package/src/superlocalmemory/learning/propensity.py +0 -131
- package/src/superlocalmemory/learning/ranker.py +0 -300
- package/src/superlocalmemory/learning/ranker_common.py +0 -163
- package/src/superlocalmemory/learning/ranker_retrain_legacy.py +0 -210
- package/src/superlocalmemory/learning/ranker_retrain_online.py +0 -423
- package/src/superlocalmemory/learning/reward.py +0 -888
- package/src/superlocalmemory/learning/reward_archive.py +0 -223
- package/src/superlocalmemory/learning/reward_boost.py +0 -211
- package/src/superlocalmemory/learning/reward_from_outcomes.py +0 -365
- package/src/superlocalmemory/learning/reward_model.py +0 -144
- package/src/superlocalmemory/learning/reward_proxy.py +0 -578
- package/src/superlocalmemory/learning/shadow_test.py +0 -524
- package/src/superlocalmemory/learning/signal_kinds.py +0 -79
- package/src/superlocalmemory/learning/signal_worker.py +0 -268
- package/src/superlocalmemory/learning/signals.py +0 -646
- package/src/superlocalmemory/learning/skill_performance_miner.py +0 -422
- package/src/superlocalmemory/learning/source_quality.py +0 -828
- package/src/superlocalmemory/learning/trigram_index.py +0 -548
- package/src/superlocalmemory/learning/workflows.py +0 -309
- package/src/superlocalmemory/llm/__init__.py +0 -0
- package/src/superlocalmemory/llm/backbone.py +0 -364
- package/src/superlocalmemory/loops/__init__.py +0 -58
- package/src/superlocalmemory/loops/budget.py +0 -58
- package/src/superlocalmemory/loops/engine.py +0 -174
- package/src/superlocalmemory/loops/ledger.py +0 -298
- package/src/superlocalmemory/loops/models.py +0 -152
- package/src/superlocalmemory/loops/rules.py +0 -52
- package/src/superlocalmemory/math/__init__.py +0 -0
- package/src/superlocalmemory/math/ebbinghaus.py +0 -352
- package/src/superlocalmemory/math/fisher.py +0 -356
- package/src/superlocalmemory/math/fisher_quantized.py +0 -255
- package/src/superlocalmemory/math/hopfield.py +0 -282
- package/src/superlocalmemory/math/langevin.py +0 -411
- package/src/superlocalmemory/math/polar_quant.py +0 -414
- package/src/superlocalmemory/math/qjl.py +0 -115
- package/src/superlocalmemory/math/sheaf.py +0 -261
- package/src/superlocalmemory/math/turbo_quant.py +0 -318
- package/src/superlocalmemory/mcp/__init__.py +0 -0
- package/src/superlocalmemory/mcp/_daemon_proxy.py +0 -203
- package/src/superlocalmemory/mcp/_pool_adapter.py +0 -181
- package/src/superlocalmemory/mcp/_stdin_guard.py +0 -60
- package/src/superlocalmemory/mcp/agent_context.py +0 -115
- package/src/superlocalmemory/mcp/cli_fallback.py +0 -602
- package/src/superlocalmemory/mcp/http_transport.py +0 -85
- package/src/superlocalmemory/mcp/profiles.py +0 -154
- package/src/superlocalmemory/mcp/resources.py +0 -281
- package/src/superlocalmemory/mcp/server.py +0 -480
- package/src/superlocalmemory/mcp/session_binding.py +0 -98
- package/src/superlocalmemory/mcp/shared.py +0 -112
- package/src/superlocalmemory/mcp/tools.py +0 -18
- package/src/superlocalmemory/mcp/tools_active.py +0 -958
- package/src/superlocalmemory/mcp/tools_brain.py +0 -298
- package/src/superlocalmemory/mcp/tools_code_graph.py +0 -1717
- package/src/superlocalmemory/mcp/tools_context.py +0 -239
- package/src/superlocalmemory/mcp/tools_core.py +0 -1112
- package/src/superlocalmemory/mcp/tools_evolution.py +0 -343
- package/src/superlocalmemory/mcp/tools_learning.py +0 -393
- package/src/superlocalmemory/mcp/tools_loops.py +0 -345
- package/src/superlocalmemory/mcp/tools_mesh.py +0 -429
- package/src/superlocalmemory/mcp/tools_ops.py +0 -115
- package/src/superlocalmemory/mcp/tools_optimize.py +0 -322
- package/src/superlocalmemory/mcp/tools_summaries.py +0 -147
- package/src/superlocalmemory/mcp/tools_v28.py +0 -292
- package/src/superlocalmemory/mcp/tools_v3.py +0 -398
- package/src/superlocalmemory/mcp/tools_v33.py +0 -507
- package/src/superlocalmemory/mesh/__init__.py +0 -12
- package/src/superlocalmemory/mesh/broker.py +0 -812
- package/src/superlocalmemory/mesh/broker_security.py +0 -470
- package/src/superlocalmemory/mesh/discovery.py +0 -365
- package/src/superlocalmemory/mesh/lock_protocol.py +0 -313
- package/src/superlocalmemory/mesh/node_identity.py +0 -97
- package/src/superlocalmemory/mesh/outbox_remote.py +0 -429
- package/src/superlocalmemory/mesh/remote_sync.py +0 -829
- package/src/superlocalmemory/mesh/state_sync.py +0 -286
- package/src/superlocalmemory/migrations/__init__.py +0 -5
- package/src/superlocalmemory/migrations/v3_4_25_to_v3_4_26.py +0 -144
- package/src/superlocalmemory/optimize/NOTICE +0 -6
- package/src/superlocalmemory/optimize/__init__.py +0 -0
- package/src/superlocalmemory/optimize/adapters/__init__.py +0 -68
- package/src/superlocalmemory/optimize/adapters/_agent_registry.py +0 -120
- package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +0 -112
- package/src/superlocalmemory/optimize/adapters/openai_adapter.py +0 -122
- package/src/superlocalmemory/optimize/adapters/wrap.py +0 -228
- package/src/superlocalmemory/optimize/cache/__init__.py +0 -31
- package/src/superlocalmemory/optimize/cache/boundary_store.py +0 -488
- package/src/superlocalmemory/optimize/cache/centroid_store.py +0 -199
- package/src/superlocalmemory/optimize/cache/context_key.py +0 -67
- package/src/superlocalmemory/optimize/cache/exact.py +0 -88
- package/src/superlocalmemory/optimize/cache/invalidation.py +0 -36
- package/src/superlocalmemory/optimize/cache/key_builder.py +0 -111
- package/src/superlocalmemory/optimize/cache/manager.py +0 -737
- package/src/superlocalmemory/optimize/cache/semantic.py +0 -637
- package/src/superlocalmemory/optimize/cache/stampede.py +0 -50
- package/src/superlocalmemory/optimize/compress/__init__.py +0 -17
- package/src/superlocalmemory/optimize/compress/align.py +0 -159
- package/src/superlocalmemory/optimize/compress/ccr.py +0 -116
- package/src/superlocalmemory/optimize/compress/prose_llmlingua.py +0 -71
- package/src/superlocalmemory/optimize/compress/router.py +0 -667
- package/src/superlocalmemory/optimize/config/__init__.py +0 -56
- package/src/superlocalmemory/optimize/config/defaults.py +0 -43
- package/src/superlocalmemory/optimize/config/schema.py +0 -327
- package/src/superlocalmemory/optimize/config/store.py +0 -270
- package/src/superlocalmemory/optimize/metrics/__init__.py +0 -8
- package/src/superlocalmemory/optimize/metrics/counters.py +0 -155
- package/src/superlocalmemory/optimize/metrics/estimator.py +0 -87
- package/src/superlocalmemory/optimize/metrics/exporters.py +0 -77
- package/src/superlocalmemory/optimize/metrics/persistence.py +0 -115
- package/src/superlocalmemory/optimize/proxy/__init__.py +0 -28
- package/src/superlocalmemory/optimize/proxy/_helpers.py +0 -730
- package/src/superlocalmemory/optimize/proxy/anthropic_surface.py +0 -375
- package/src/superlocalmemory/optimize/proxy/capture.py +0 -550
- package/src/superlocalmemory/optimize/proxy/gemini_surface.py +0 -528
- package/src/superlocalmemory/optimize/proxy/lifecycle.py +0 -126
- package/src/superlocalmemory/optimize/proxy/openai_surface.py +0 -465
- package/src/superlocalmemory/optimize/proxy/server.py +0 -199
- package/src/superlocalmemory/optimize/proxy/vertex_surface.py +0 -246
- package/src/superlocalmemory/optimize/storage/__init__.py +0 -0
- package/src/superlocalmemory/optimize/storage/db.py +0 -1185
- package/src/superlocalmemory/optimize/storage/schema.py +0 -205
- package/src/superlocalmemory/parameterization/__init__.py +0 -47
- package/src/superlocalmemory/parameterization/cross_project.py +0 -12
- package/src/superlocalmemory/parameterization/pattern_extractor.py +0 -584
- package/src/superlocalmemory/parameterization/pii_filter.py +0 -106
- package/src/superlocalmemory/parameterization/prompt_injector.py +0 -219
- package/src/superlocalmemory/parameterization/prompt_lifecycle.py +0 -281
- package/src/superlocalmemory/parameterization/soft_prompt_generator.py +0 -542
- package/src/superlocalmemory/parameterization/workflow_miner.py +0 -17
- package/src/superlocalmemory/reliability/__init__.py +0 -45
- package/src/superlocalmemory/reliability/join_liveness.py +0 -301
- package/src/superlocalmemory/reliability/prior_distance.py +0 -243
- package/src/superlocalmemory/retrieval/__init__.py +0 -0
- package/src/superlocalmemory/retrieval/agentic.py +0 -367
- package/src/superlocalmemory/retrieval/ann_index.py +0 -235
- package/src/superlocalmemory/retrieval/bm25_channel.py +0 -451
- package/src/superlocalmemory/retrieval/bridge_discovery.py +0 -253
- package/src/superlocalmemory/retrieval/channel_registry.py +0 -154
- package/src/superlocalmemory/retrieval/channel_status.py +0 -117
- package/src/superlocalmemory/retrieval/engine.py +0 -1615
- package/src/superlocalmemory/retrieval/entity_channel.py +0 -994
- package/src/superlocalmemory/retrieval/forgetting_filter.py +0 -160
- package/src/superlocalmemory/retrieval/fusion.py +0 -81
- package/src/superlocalmemory/retrieval/graph_adjacency.py +0 -219
- package/src/superlocalmemory/retrieval/hopfield_channel.py +0 -465
- package/src/superlocalmemory/retrieval/profile_channel.py +0 -105
- package/src/superlocalmemory/retrieval/quantization_aware_search.py +0 -147
- package/src/superlocalmemory/retrieval/remote_reranker.py +0 -758
- package/src/superlocalmemory/retrieval/reranker.py +0 -674
- package/src/superlocalmemory/retrieval/scope_policy.py +0 -126
- package/src/superlocalmemory/retrieval/semantic_channel.py +0 -638
- package/src/superlocalmemory/retrieval/spreading.py +0 -288
- package/src/superlocalmemory/retrieval/spreading_activation.py +0 -616
- package/src/superlocalmemory/retrieval/strategy.py +0 -248
- package/src/superlocalmemory/retrieval/temporal_channel.py +0 -433
- package/src/superlocalmemory/retrieval/temporal_frame.py +0 -102
- package/src/superlocalmemory/retrieval/temporal_utils.py +0 -122
- package/src/superlocalmemory/retrieval/temporal_validity_filter.py +0 -499
- package/src/superlocalmemory/retrieval/time_window.py +0 -181
- package/src/superlocalmemory/retrieval/vector_store.py +0 -863
- package/src/superlocalmemory/server/__init__.py +0 -1
- package/src/superlocalmemory/server/api.py +0 -310
- package/src/superlocalmemory/server/asset_versions.py +0 -171
- package/src/superlocalmemory/server/bandit_loops.py +0 -158
- package/src/superlocalmemory/server/config_file.py +0 -90
- package/src/superlocalmemory/server/consolidation_runner.py +0 -140
- package/src/superlocalmemory/server/egress_policy.py +0 -258
- package/src/superlocalmemory/server/loopback.py +0 -85
- package/src/superlocalmemory/server/middleware/__init__.py +0 -11
- package/src/superlocalmemory/server/middleware/security_headers.py +0 -144
- package/src/superlocalmemory/server/origin.py +0 -55
- package/src/superlocalmemory/server/profile_runtime.py +0 -515
- package/src/superlocalmemory/server/rbac_enforce.py +0 -194
- package/src/superlocalmemory/server/recall_health.py +0 -343
- package/src/superlocalmemory/server/recall_serializer.py +0 -320
- package/src/superlocalmemory/server/route_mutations.py +0 -104
- package/src/superlocalmemory/server/routes/__init__.py +0 -4
- package/src/superlocalmemory/server/routes/abstraction.py +0 -314
- package/src/superlocalmemory/server/routes/adapters.py +0 -63
- package/src/superlocalmemory/server/routes/agents.py +0 -303
- package/src/superlocalmemory/server/routes/backup.py +0 -869
- package/src/superlocalmemory/server/routes/behavioral.py +0 -659
- package/src/superlocalmemory/server/routes/brain.py +0 -1892
- package/src/superlocalmemory/server/routes/chat.py +0 -393
- package/src/superlocalmemory/server/routes/compliance.py +0 -533
- package/src/superlocalmemory/server/routes/config_api.py +0 -703
- package/src/superlocalmemory/server/routes/data_io.py +0 -329
- package/src/superlocalmemory/server/routes/entity.py +0 -237
- package/src/superlocalmemory/server/routes/events.py +0 -214
- package/src/superlocalmemory/server/routes/evolution.py +0 -510
- package/src/superlocalmemory/server/routes/helpers.py +0 -499
- package/src/superlocalmemory/server/routes/ingest.py +0 -137
- package/src/superlocalmemory/server/routes/insights.py +0 -366
- package/src/superlocalmemory/server/routes/learning.py +0 -834
- package/src/superlocalmemory/server/routes/learning_telemetry.py +0 -154
- package/src/superlocalmemory/server/routes/lifecycle.py +0 -184
- package/src/superlocalmemory/server/routes/memories.py +0 -1661
- package/src/superlocalmemory/server/routes/mesh.py +0 -517
- package/src/superlocalmemory/server/routes/mesh_lock.py +0 -54
- package/src/superlocalmemory/server/routes/mesh_state.py +0 -63
- package/src/superlocalmemory/server/routes/optimize.py +0 -197
- package/src/superlocalmemory/server/routes/prewarm.py +0 -173
- package/src/superlocalmemory/server/routes/profiles.py +0 -292
- package/src/superlocalmemory/server/routes/ratelimit.py +0 -132
- package/src/superlocalmemory/server/routes/rbac.py +0 -366
- package/src/superlocalmemory/server/routes/stats.py +0 -385
- package/src/superlocalmemory/server/routes/tiers.py +0 -222
- package/src/superlocalmemory/server/routes/timeline.py +0 -258
- package/src/superlocalmemory/server/routes/token.py +0 -90
- package/src/superlocalmemory/server/routes/v3_api.py +0 -3023
- package/src/superlocalmemory/server/routes/ws.py +0 -171
- package/src/superlocalmemory/server/security_middleware.py +0 -89
- package/src/superlocalmemory/server/ui.py +0 -354
- package/src/superlocalmemory/server/unified_daemon.py +0 -6326
- package/src/superlocalmemory/server/write_identity.py +0 -195
- package/src/superlocalmemory/storage/__init__.py +0 -0
- package/src/superlocalmemory/storage/_migration_internals.py +0 -638
- package/src/superlocalmemory/storage/_schema_version.py +0 -174
- package/src/superlocalmemory/storage/access_log.py +0 -170
- package/src/superlocalmemory/storage/admission_codec.py +0 -129
- package/src/superlocalmemory/storage/admission_journal.py +0 -843
- package/src/superlocalmemory/storage/agent_experience.py +0 -546
- package/src/superlocalmemory/storage/backup.py +0 -531
- package/src/superlocalmemory/storage/correction_cases.py +0 -670
- package/src/superlocalmemory/storage/database.py +0 -3180
- package/src/superlocalmemory/storage/deferred_writes.py +0 -209
- package/src/superlocalmemory/storage/embedding_codec.py +0 -200
- package/src/superlocalmemory/storage/embedding_migrator.py +0 -672
- package/src/superlocalmemory/storage/erasure_fence.py +0 -45
- package/src/superlocalmemory/storage/execution_learning.py +0 -285
- package/src/superlocalmemory/storage/external_evidence.py +0 -359
- package/src/superlocalmemory/storage/generation_fence.py +0 -63
- package/src/superlocalmemory/storage/lineage_retention.py +0 -236
- package/src/superlocalmemory/storage/logical_edges.py +0 -86
- package/src/superlocalmemory/storage/memory_write.py +0 -115
- package/src/superlocalmemory/storage/migration_runner.py +0 -895
- package/src/superlocalmemory/storage/migration_v33.py +0 -140
- package/src/superlocalmemory/storage/migrations/M001_add_signal_features_columns.py +0 -67
- package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +0 -107
- package/src/superlocalmemory/storage/migrations/M003_migration_log.py +0 -38
- package/src/superlocalmemory/storage/migrations/M004_cross_platform_sync_log.py +0 -46
- package/src/superlocalmemory/storage/migrations/M005_bandit_tables.py +0 -75
- package/src/superlocalmemory/storage/migrations/M006_action_outcomes_reward.py +0 -75
- package/src/superlocalmemory/storage/migrations/M007_pending_outcomes.py +0 -63
- package/src/superlocalmemory/storage/migrations/M009_model_lineage.py +0 -94
- package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +0 -80
- package/src/superlocalmemory/storage/migrations/M011_archive_and_merge.py +0 -87
- package/src/superlocalmemory/storage/migrations/M012_shadow_observations.py +0 -72
- package/src/superlocalmemory/storage/migrations/M013_bi_temporal_columns.py +0 -55
- package/src/superlocalmemory/storage/migrations/M014_v345_scale_ready.py +0 -45
- package/src/superlocalmemory/storage/migrations/M015_add_pinned_column.py +0 -58
- package/src/superlocalmemory/storage/migrations/M016_add_scope_support.py +0 -120
- package/src/superlocalmemory/storage/migrations/M017_ccq_scope_column.py +0 -79
- package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +0 -120
- package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +0 -54
- package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +0 -52
- package/src/superlocalmemory/storage/migrations/M021_ingestion_log_profile.py +0 -108
- package/src/superlocalmemory/storage/migrations/M022_entity_aliases_profile.py +0 -86
- package/src/superlocalmemory/storage/migrations/M023_mesh_profile_isolation.py +0 -194
- package/src/superlocalmemory/storage/migrations/M024_rbac_users_roles.py +0 -87
- package/src/superlocalmemory/storage/migrations/M025_perf_indexes.py +0 -90
- package/src/superlocalmemory/storage/migrations/M026_rbac_memberships_fk.py +0 -136
- package/src/superlocalmemory/storage/migrations/M027_transferable_patterns_profile.py +0 -163
- package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +0 -305
- package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +0 -137
- package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +0 -93
- package/src/superlocalmemory/storage/migrations/M031_dead_letter_operations.py +0 -80
- package/src/superlocalmemory/storage/migrations/M032_write_coordinator_admission.py +0 -188
- package/src/superlocalmemory/storage/migrations/M033_projection_transactions.py +0 -148
- package/src/superlocalmemory/storage/migrations/M034_obligation_integrity.py +0 -58
- package/src/superlocalmemory/storage/migrations/M035_erasure_receipts.py +0 -113
- package/src/superlocalmemory/storage/migrations/M036_vector_row_map.py +0 -107
- package/src/superlocalmemory/storage/migrations/M037_manifest_hmac_version.py +0 -162
- package/src/superlocalmemory/storage/migrations/M038_learning_feedback_channel.py +0 -77
- package/src/superlocalmemory/storage/migrations/M039_scene_fact_members.py +0 -137
- package/src/superlocalmemory/storage/migrations/M040_agent_experience_receipts.py +0 -254
- package/src/superlocalmemory/storage/migrations/M041_external_evidence_receipts.py +0 -189
- package/src/superlocalmemory/storage/migrations/M042_correction_case_ledger.py +0 -245
- package/src/superlocalmemory/storage/migrations/M043_quarantine_display_summaries.py +0 -512
- package/src/superlocalmemory/storage/migrations/M044_play_carries_its_own_evidence.py +0 -127
- package/src/superlocalmemory/storage/migrations/M045_fact_outcome_score.py +0 -158
- package/src/superlocalmemory/storage/migrations/M046_prospective_memory_has_its_own_name.py +0 -620
- package/src/superlocalmemory/storage/migrations/M047_fisher_vectors_are_stored_like_every_other_vector.py +0 -306
- package/src/superlocalmemory/storage/migrations/M048_upcoming_holds_only_what_is_upcoming.py +0 -229
- package/src/superlocalmemory/storage/migrations/M049_a_schema_version_marker_is_one_row.py +0 -201
- package/src/superlocalmemory/storage/migrations/M050_execution_learning_v2.py +0 -70
- package/src/superlocalmemory/storage/migrations/__init__.py +0 -103
- package/src/superlocalmemory/storage/migrations.py +0 -333
- package/src/superlocalmemory/storage/models.py +0 -500
- package/src/superlocalmemory/storage/projection_outbox.py +0 -346
- package/src/superlocalmemory/storage/quantized_store.py +0 -280
- package/src/superlocalmemory/storage/read_connection.py +0 -115
- package/src/superlocalmemory/storage/retention_policy.py +0 -860
- package/src/superlocalmemory/storage/schema.py +0 -1108
- package/src/superlocalmemory/storage/schema_code_graph.py +0 -282
- package/src/superlocalmemory/storage/schema_v32.py +0 -382
- package/src/superlocalmemory/storage/schema_v3410.py +0 -159
- package/src/superlocalmemory/storage/schema_v3411.py +0 -149
- package/src/superlocalmemory/storage/schema_v343.py +0 -315
- package/src/superlocalmemory/storage/schema_v345.py +0 -109
- package/src/superlocalmemory/storage/schema_v347.py +0 -140
- package/src/superlocalmemory/storage/sqlite_vectors.py +0 -169
- package/src/superlocalmemory/storage/v2_migrator.py +0 -466
- package/src/superlocalmemory/storage/write_coordinator.py +0 -949
- package/src/superlocalmemory/storage/write_lock.py +0 -88
- package/src/superlocalmemory/summaries/__init__.py +0 -37
- package/src/superlocalmemory/summaries/base.py +0 -267
- package/src/superlocalmemory/summaries/daily_reflection.py +0 -340
- package/src/superlocalmemory/summaries/non_answer.py +0 -223
- package/src/superlocalmemory/summaries/project_work_log.py +0 -440
- package/src/superlocalmemory/summaries/session_summary.py +0 -311
- package/src/superlocalmemory/trust/__init__.py +0 -0
- package/src/superlocalmemory/trust/gate.py +0 -171
- package/src/superlocalmemory/trust/provenance.py +0 -124
- package/src/superlocalmemory/trust/scorer.py +0 -413
- package/src/superlocalmemory/trust/signals.py +0 -153
- package/src/superlocalmemory/ui/assets/slm-icon-white.svg +0 -64
- package/src/superlocalmemory/ui/assets/slm-icon.svg +0 -36
- package/src/superlocalmemory/ui/css/brain.css +0 -409
- package/src/superlocalmemory/ui/css/design-system.css +0 -696
- package/src/superlocalmemory/ui/css/legacy-dashboard.css +0 -663
- package/src/superlocalmemory/ui/css/neural-glass.css +0 -1599
- package/src/superlocalmemory/ui/css/od-bridge.css +0 -158
- package/src/superlocalmemory/ui/favicon.svg +0 -36
- package/src/superlocalmemory/ui/index.html +0 -1638
- package/src/superlocalmemory/ui/js/agents.js +0 -192
- package/src/superlocalmemory/ui/js/auto-settings.js +0 -624
- package/src/superlocalmemory/ui/js/brain.js +0 -1400
- package/src/superlocalmemory/ui/js/clusters.js +0 -326
- package/src/superlocalmemory/ui/js/compliance.js +0 -307
- package/src/superlocalmemory/ui/js/core.js +0 -566
- package/src/superlocalmemory/ui/js/dashboard.js +0 -503
- package/src/superlocalmemory/ui/js/event-delegation.js +0 -113
- package/src/superlocalmemory/ui/js/events.js +0 -178
- package/src/superlocalmemory/ui/js/fact-detail.js +0 -142
- package/src/superlocalmemory/ui/js/feedback.js +0 -339
- package/src/superlocalmemory/ui/js/graph-event-bus.js +0 -83
- package/src/superlocalmemory/ui/js/graph-filters.js +0 -220
- package/src/superlocalmemory/ui/js/graph-ui.js +0 -214
- package/src/superlocalmemory/ui/js/ide-status.js +0 -115
- package/src/superlocalmemory/ui/js/init.js +0 -54
- package/src/superlocalmemory/ui/js/knowledge-graph.js +0 -945
- package/src/superlocalmemory/ui/js/lifecycle.js +0 -387
- package/src/superlocalmemory/ui/js/math-health.js +0 -114
- package/src/superlocalmemory/ui/js/memories.js +0 -394
- package/src/superlocalmemory/ui/js/memory-chat.js +0 -371
- package/src/superlocalmemory/ui/js/memory-timeline.js +0 -265
- package/src/superlocalmemory/ui/js/modal.js +0 -733
- package/src/superlocalmemory/ui/js/ng-entities.js +0 -298
- package/src/superlocalmemory/ui/js/ng-health.js +0 -208
- package/src/superlocalmemory/ui/js/ng-ingestion.js +0 -203
- package/src/superlocalmemory/ui/js/ng-mesh.js +0 -374
- package/src/superlocalmemory/ui/js/ng-shell.js +0 -524
- package/src/superlocalmemory/ui/js/ng-skills.js +0 -663
- package/src/superlocalmemory/ui/js/od-agents.js +0 -588
- package/src/superlocalmemory/ui/js/od-auth-gate.js +0 -257
- package/src/superlocalmemory/ui/js/od-backup.js +0 -878
- package/src/superlocalmemory/ui/js/od-boundedloops.js +0 -324
- package/src/superlocalmemory/ui/js/od-brain.js +0 -1095
- package/src/superlocalmemory/ui/js/od-compliance-ext.js +0 -301
- package/src/superlocalmemory/ui/js/od-components.js +0 -147
- package/src/superlocalmemory/ui/js/od-entities.js +0 -622
- package/src/superlocalmemory/ui/js/od-graph.js +0 -776
- package/src/superlocalmemory/ui/js/od-health.js +0 -579
- package/src/superlocalmemory/ui/js/od-mcp.js +0 -508
- package/src/superlocalmemory/ui/js/od-memories.js +0 -1499
- package/src/superlocalmemory/ui/js/od-mesh.js +0 -645
- package/src/superlocalmemory/ui/js/od-operations.js +0 -1268
- package/src/superlocalmemory/ui/js/od-ops-health.js +0 -417
- package/src/superlocalmemory/ui/js/od-optimize.js +0 -828
- package/src/superlocalmemory/ui/js/od-settings.js +0 -1275
- package/src/superlocalmemory/ui/js/od-shell.js +0 -819
- package/src/superlocalmemory/ui/js/od-skills.js +0 -600
- package/src/superlocalmemory/ui/js/od-team.js +0 -265
- package/src/superlocalmemory/ui/js/optimize.js +0 -191
- package/src/superlocalmemory/ui/js/profiles.js +0 -362
- package/src/superlocalmemory/ui/js/quick-actions.js +0 -334
- package/src/superlocalmemory/ui/js/recall-lab.js +0 -373
- package/src/superlocalmemory/ui/js/search.js +0 -86
- package/src/superlocalmemory/ui/js/settings.js +0 -556
- package/src/superlocalmemory/ui/js/timeline.js +0 -62
- package/src/superlocalmemory/ui/js/trust-dashboard.js +0 -225
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/bootstrap-icons.css +0 -2018
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/bootstrap.bundle.min.js +0 -7
- package/src/superlocalmemory/ui/vendor/bootstrap.min.css +0 -6
- package/src/superlocalmemory/ui/vendor/d3.v7.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/graphology-library.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/graphology.umd.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/inter-ui/inter-variable.min.css +0 -8
- package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable-Italic.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/sigma.min.js +0 -1
- package/src/superlocalmemory/vector/__init__.py +0 -9
- package/src/superlocalmemory/vector/lancedb_backend.py +0 -366
|
@@ -1,1404 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
-
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
-
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
-
|
|
5
|
-
"""Store pipeline — extracted free functions for MemoryEngine.store().
|
|
6
|
-
|
|
7
|
-
Direction: engine.py imports this module. This module NEVER imports engine.py.
|
|
8
|
-
|
|
9
|
-
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import hashlib
|
|
15
|
-
import json
|
|
16
|
-
import logging
|
|
17
|
-
import sqlite3
|
|
18
|
-
import uuid
|
|
19
|
-
from typing import TYPE_CHECKING, Any
|
|
20
|
-
|
|
21
|
-
if TYPE_CHECKING:
|
|
22
|
-
from superlocalmemory.core.config import SLMConfig
|
|
23
|
-
from superlocalmemory.core.hooks import HookRegistry
|
|
24
|
-
from superlocalmemory.storage.database import DatabaseManager
|
|
25
|
-
|
|
26
|
-
from superlocalmemory.storage.erasure_fence import is_erasing
|
|
27
|
-
from superlocalmemory.storage.models import (
|
|
28
|
-
AtomicFact,
|
|
29
|
-
FactType,
|
|
30
|
-
MemoryRecord,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
logger = logging.getLogger(__name__)
|
|
34
|
-
|
|
35
|
-
# Langevin initialization radius for new facts (ACTIVE zone < 0.3)
|
|
36
|
-
_INIT_LANGEVIN_RADIUS = 0.05
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _reraise_materialization_deferral(exc: Exception) -> None:
|
|
40
|
-
"""Keep explicit runtime preemption out of best-effort fallbacks."""
|
|
41
|
-
from superlocalmemory.core.materialization_control import MaterializationDeferred
|
|
42
|
-
|
|
43
|
-
if isinstance(exc, MaterializationDeferred):
|
|
44
|
-
raise exc
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _ingestion_effect_id(operation_id: str, *parts: object) -> str:
|
|
48
|
-
"""Return a stable ID for a relational effect owned by one ingestion."""
|
|
49
|
-
if not operation_id:
|
|
50
|
-
return uuid.uuid4().hex
|
|
51
|
-
payload = "\0".join((operation_id, *(str(part) for part in parts)))
|
|
52
|
-
return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:32]
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _record_correction_candidate(
|
|
56
|
-
db: DatabaseManager,
|
|
57
|
-
*,
|
|
58
|
-
operation_id: str,
|
|
59
|
-
profile_id: str,
|
|
60
|
-
scope: str,
|
|
61
|
-
predecessor_fact_id: str,
|
|
62
|
-
successor_fact_id: str,
|
|
63
|
-
reason_code: str,
|
|
64
|
-
trusted_actor_id: str,
|
|
65
|
-
) -> None:
|
|
66
|
-
"""Append a review candidate through the current canonical transaction.
|
|
67
|
-
|
|
68
|
-
This intentionally carries identifiers and a controlled reason code only.
|
|
69
|
-
It must not use detector prose because that can contain user memory text.
|
|
70
|
-
If the current path is bound to the canonical coordinator,
|
|
71
|
-
``raw_connection`` yields its already-open transaction; otherwise the
|
|
72
|
-
database manager owns the short transaction. Either path is atomic.
|
|
73
|
-
"""
|
|
74
|
-
if not trusted_actor_id or not predecessor_fact_id or not successor_fact_id:
|
|
75
|
-
return
|
|
76
|
-
if predecessor_fact_id == successor_fact_id:
|
|
77
|
-
return
|
|
78
|
-
from superlocalmemory.storage.correction_cases import (
|
|
79
|
-
CorrectionActor,
|
|
80
|
-
CorrectionCaseError,
|
|
81
|
-
propose_on_connection,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
case_id = _ingestion_effect_id(
|
|
85
|
-
operation_id, "correction-case", profile_id, predecessor_fact_id,
|
|
86
|
-
successor_fact_id, reason_code,
|
|
87
|
-
)
|
|
88
|
-
idempotency_key = _ingestion_effect_id(
|
|
89
|
-
operation_id, "correction-proposal", profile_id, predecessor_fact_id,
|
|
90
|
-
successor_fact_id, reason_code,
|
|
91
|
-
)
|
|
92
|
-
actor = CorrectionActor(
|
|
93
|
-
actor_id=trusted_actor_id,
|
|
94
|
-
actor_kind="host_attested",
|
|
95
|
-
trust_tier="canonical_writer",
|
|
96
|
-
)
|
|
97
|
-
try:
|
|
98
|
-
with db.raw_connection() as conn:
|
|
99
|
-
propose_on_connection(
|
|
100
|
-
conn,
|
|
101
|
-
case_id=case_id,
|
|
102
|
-
profile_id=profile_id,
|
|
103
|
-
scope=scope,
|
|
104
|
-
predecessor_fact_id=predecessor_fact_id,
|
|
105
|
-
successor_fact_id=successor_fact_id,
|
|
106
|
-
reason_code=reason_code,
|
|
107
|
-
actor=actor,
|
|
108
|
-
idempotency_key=idempotency_key,
|
|
109
|
-
# Candidate detection observes a possible correction. It does
|
|
110
|
-
# not claim an event-time boundary from heuristic evidence.
|
|
111
|
-
is_profile_active=lambda candidate_profile: candidate_profile == profile_id,
|
|
112
|
-
is_actor_trusted=lambda candidate_actor: candidate_actor == actor,
|
|
113
|
-
)
|
|
114
|
-
except (CorrectionCaseError, sqlite3.Error, ValueError) as exc:
|
|
115
|
-
# A missing/hot-upgrading M042 ledger must not make memory ingestion
|
|
116
|
-
# unavailable. The candidate is advisory and has no retrieval effect;
|
|
117
|
-
# the warning is the operational signal that an operator must inspect.
|
|
118
|
-
logger.warning("Correction candidate not recorded for %s: %s", successor_fact_id, exc)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
#: Entity id pattern for the per-profile placeholder a dated fact with no
|
|
122
|
-
#: resolved entity points its temporal event at. Per profile, not shared, so
|
|
123
|
-
#: profile deletion cascades it away with everything else.
|
|
124
|
-
_UNRESOLVED_ENTITY_PREFIX = "__slm_unresolved__:"
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def _ensure_unresolved_entity(db: Any, profile_id: str) -> str | None:
|
|
128
|
-
"""Return the placeholder entity id for ``profile_id``, creating it once.
|
|
129
|
-
|
|
130
|
-
``temporal_events.entity_id`` is NOT NULL with a foreign key to
|
|
131
|
-
``canonical_entities``, so an event for a fact with no recognised entity
|
|
132
|
-
needs something real to reference. This is that something.
|
|
133
|
-
|
|
134
|
-
It is deliberately inert as an entity:
|
|
135
|
-
|
|
136
|
-
* Its ``canonical_name`` is empty, and the only query that resolves an
|
|
137
|
-
entity by name (``retrieval/temporal_channel.py``, the "events for
|
|
138
|
-
entity X" lookup) is driven by names extracted from the user's
|
|
139
|
-
question, which are never empty.
|
|
140
|
-
* Nothing derives entity links from ``temporal_events``. The entity
|
|
141
|
-
channel builds its map from ``atomic_facts.canonical_entities_json``,
|
|
142
|
-
which this does not touch — so the placeholder cannot become the kind
|
|
143
|
-
of entity that links a quarter of the store and flattens entity
|
|
144
|
-
proximity. That failure mode is real (on the author's store 'State'
|
|
145
|
-
links 1,388 facts) and this is specifically shaped not to add to it.
|
|
146
|
-
* ``fact_count`` stays 0. It is a hook for a foreign key, not a concept.
|
|
147
|
-
|
|
148
|
-
Returns None if the row cannot be created, in which case the caller skips
|
|
149
|
-
the temporal event rather than failing the write — a missing temporal row
|
|
150
|
-
is a degraded memory, an aborted remember is a lost one.
|
|
151
|
-
"""
|
|
152
|
-
entity_id = f"{_UNRESOLVED_ENTITY_PREFIX}{profile_id}"
|
|
153
|
-
try:
|
|
154
|
-
db.execute(
|
|
155
|
-
"INSERT OR IGNORE INTO canonical_entities "
|
|
156
|
-
"(entity_id, profile_id, canonical_name, entity_type, fact_count) "
|
|
157
|
-
"VALUES (?, ?, '', 'unresolved', 0)",
|
|
158
|
-
(entity_id, profile_id),
|
|
159
|
-
)
|
|
160
|
-
return entity_id
|
|
161
|
-
except Exception as exc: # noqa: BLE001 — never fail a write over this
|
|
162
|
-
logger.debug(
|
|
163
|
-
"temporal placeholder entity unavailable for %s: %s",
|
|
164
|
-
profile_id, exc,
|
|
165
|
-
)
|
|
166
|
-
return None
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
def _record_fact_entity_association(
|
|
170
|
-
db: DatabaseManager,
|
|
171
|
-
*,
|
|
172
|
-
operation_id: str,
|
|
173
|
-
profile_id: str,
|
|
174
|
-
fact_id: str,
|
|
175
|
-
entity_id: str,
|
|
176
|
-
) -> None:
|
|
177
|
-
"""Apply one fact/entity count effect in O(1), exactly once."""
|
|
178
|
-
if not operation_id:
|
|
179
|
-
db.increment_entity_fact_count(entity_id, profile_id)
|
|
180
|
-
return
|
|
181
|
-
with db.transaction():
|
|
182
|
-
claimed = db.execute(
|
|
183
|
-
"INSERT INTO fact_entity_associations "
|
|
184
|
-
"(profile_id,fact_id,entity_id,first_operation_id,count_applied) "
|
|
185
|
-
"SELECT ?,?,?,?,"
|
|
186
|
-
"CASE WHEN fact.rowid > repair.target_fact_rowid THEN 1 ELSE 0 END "
|
|
187
|
-
"FROM canonical_entities AS entity "
|
|
188
|
-
"JOIN atomic_facts AS fact "
|
|
189
|
-
"ON fact.fact_id=? AND fact.profile_id=? "
|
|
190
|
-
"JOIN fact_entity_association_repair_state AS repair "
|
|
191
|
-
"ON repair.repair_key='historical-backfill' "
|
|
192
|
-
"WHERE entity.entity_id=? AND entity.profile_id=? "
|
|
193
|
-
"ON CONFLICT(profile_id,fact_id,entity_id) DO UPDATE SET "
|
|
194
|
-
"count_applied=excluded.count_applied,"
|
|
195
|
-
"first_operation_id=excluded.first_operation_id "
|
|
196
|
-
"WHERE fact_entity_associations.count_applied=0 "
|
|
197
|
-
"AND excluded.count_applied=1 "
|
|
198
|
-
"RETURNING count_applied",
|
|
199
|
-
(
|
|
200
|
-
profile_id,
|
|
201
|
-
fact_id,
|
|
202
|
-
entity_id,
|
|
203
|
-
operation_id,
|
|
204
|
-
fact_id,
|
|
205
|
-
profile_id,
|
|
206
|
-
entity_id,
|
|
207
|
-
profile_id,
|
|
208
|
-
),
|
|
209
|
-
)
|
|
210
|
-
if claimed and int(claimed[0]["count_applied"]) == 1:
|
|
211
|
-
db.execute(
|
|
212
|
-
"UPDATE canonical_entities SET fact_count=fact_count+1 "
|
|
213
|
-
"WHERE entity_id=? AND profile_id=?",
|
|
214
|
-
(entity_id, profile_id),
|
|
215
|
-
)
|
|
216
|
-
elif not claimed:
|
|
217
|
-
# An empty result has TWO causes: (a) the 'historical-backfill'
|
|
218
|
-
# repair-state row is missing — M028 DDL ran but its INSERT rolled
|
|
219
|
-
# back (partial migration) — so the JOIN returned zero rows and the
|
|
220
|
-
# main INSERT produced no output; or (b) the association already
|
|
221
|
-
# exists and the ON CONFLICT update was correctly skipped (an
|
|
222
|
-
# idempotent re-run). Only (a) needs the fallback insert; (b) is a
|
|
223
|
-
# no-op. Disambiguate by probing the repair-state table directly —
|
|
224
|
-
# a DIFFERENT table, so this stays a constant-query, idempotent
|
|
225
|
-
# effect and never re-fires the insert on an already-applied row.
|
|
226
|
-
repair_ready = db.execute(
|
|
227
|
-
"SELECT 1 FROM fact_entity_association_repair_state "
|
|
228
|
-
"WHERE repair_key='historical-backfill' LIMIT 1",
|
|
229
|
-
)
|
|
230
|
-
if repair_ready:
|
|
231
|
-
# (b) idempotent skip — the association was already applied.
|
|
232
|
-
return
|
|
233
|
-
# (a) partial migration: insert with count_applied=1 (no historical-
|
|
234
|
-
# rowid check needed — all associations in this state are
|
|
235
|
-
# post-migration). ON CONFLICT DO NOTHING makes this retry-safe.
|
|
236
|
-
fallback = db.execute(
|
|
237
|
-
"INSERT INTO fact_entity_associations "
|
|
238
|
-
"(profile_id,fact_id,entity_id,first_operation_id,count_applied) "
|
|
239
|
-
"SELECT ?,?,?,?,1 "
|
|
240
|
-
"FROM canonical_entities AS entity "
|
|
241
|
-
"JOIN atomic_facts AS fact "
|
|
242
|
-
"ON fact.fact_id=? AND fact.profile_id=? "
|
|
243
|
-
"WHERE entity.entity_id=? AND entity.profile_id=? "
|
|
244
|
-
"ON CONFLICT(profile_id,fact_id,entity_id) DO NOTHING "
|
|
245
|
-
"RETURNING count_applied",
|
|
246
|
-
(
|
|
247
|
-
profile_id, fact_id, entity_id, operation_id,
|
|
248
|
-
fact_id, profile_id,
|
|
249
|
-
entity_id, profile_id,
|
|
250
|
-
),
|
|
251
|
-
)
|
|
252
|
-
if fallback:
|
|
253
|
-
db.execute(
|
|
254
|
-
"UPDATE canonical_entities SET fact_count=fact_count+1 "
|
|
255
|
-
"WHERE entity_id=? AND profile_id=?",
|
|
256
|
-
(entity_id, profile_id),
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def _init_langevin_position(dim: int = 8) -> list[float]:
|
|
261
|
-
"""Initialize Langevin position near origin for a new fact.
|
|
262
|
-
|
|
263
|
-
Small random perturbation ensures each fact gets a unique position
|
|
264
|
-
while staying deep in the ACTIVE zone (radius < 0.3).
|
|
265
|
-
"""
|
|
266
|
-
import numpy as np
|
|
267
|
-
rng = np.random.default_rng()
|
|
268
|
-
direction = rng.standard_normal(dim)
|
|
269
|
-
norm = float(np.linalg.norm(direction))
|
|
270
|
-
if norm < 1e-8:
|
|
271
|
-
direction = np.ones(dim)
|
|
272
|
-
norm = float(np.linalg.norm(direction))
|
|
273
|
-
return (direction / norm * _INIT_LANGEVIN_RADIUS).tolist()
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
# ---------------------------------------------------------------------------
|
|
277
|
-
# enrich_fact (was MemoryEngine._enrich_fact)
|
|
278
|
-
# ---------------------------------------------------------------------------
|
|
279
|
-
|
|
280
|
-
def enrich_fact(
|
|
281
|
-
fact: AtomicFact,
|
|
282
|
-
record: MemoryRecord,
|
|
283
|
-
profile_id: str,
|
|
284
|
-
*,
|
|
285
|
-
embedder: Any,
|
|
286
|
-
entity_resolver: Any,
|
|
287
|
-
temporal_parser: Any,
|
|
288
|
-
) -> AtomicFact:
|
|
289
|
-
"""Enrich fact with embeddings, entities, temporal, emotional data."""
|
|
290
|
-
from superlocalmemory.encoding.emotional import emotional_importance_boost, tag_emotion
|
|
291
|
-
from superlocalmemory.encoding.signal_inference import infer_signal
|
|
292
|
-
|
|
293
|
-
# v3.8.4 D: if the fact already carries a sync-embedded vector (warm-guard
|
|
294
|
-
# path in store_fast), reuse it — avoids a redundant embed call in the
|
|
295
|
-
# materializer and keeps the vector consistent with the one indexed in the
|
|
296
|
-
# vector store at write time.
|
|
297
|
-
if fact.embedding is not None:
|
|
298
|
-
embedding = fact.embedding
|
|
299
|
-
fisher_mean = fact.fisher_mean
|
|
300
|
-
fisher_variance = fact.fisher_variance
|
|
301
|
-
# fisher_params are computed in the warm-guard path too, but guard
|
|
302
|
-
# against the edge case where they weren't (e.g. compute_fisher_params
|
|
303
|
-
# raised after embed succeeded).
|
|
304
|
-
if (fisher_mean is None or fisher_variance is None) and embedder and embedding:
|
|
305
|
-
fisher_mean, fisher_variance = embedder.compute_fisher_params(embedding)
|
|
306
|
-
else:
|
|
307
|
-
embedding = embedder.embed(fact.content) if embedder else None
|
|
308
|
-
fisher_mean, fisher_variance = (None, None)
|
|
309
|
-
if embedder and embedding:
|
|
310
|
-
fisher_mean, fisher_variance = embedder.compute_fisher_params(embedding)
|
|
311
|
-
|
|
312
|
-
canonical = {}
|
|
313
|
-
if entity_resolver and fact.entities:
|
|
314
|
-
canonical = entity_resolver.resolve(fact.entities, profile_id)
|
|
315
|
-
|
|
316
|
-
temporal = {}
|
|
317
|
-
if temporal_parser:
|
|
318
|
-
temporal = temporal_parser.extract_dates_from_text(fact.content)
|
|
319
|
-
|
|
320
|
-
emotion = tag_emotion(fact.content)
|
|
321
|
-
signal = infer_signal(fact.content)
|
|
322
|
-
|
|
323
|
-
# Strategy A: initialize Langevin position near origin (ACTIVE zone).
|
|
324
|
-
# New facts start as ACTIVE; dynamics will evolve them based on access patterns.
|
|
325
|
-
langevin_pos = _init_langevin_position(dim=8)
|
|
326
|
-
|
|
327
|
-
return AtomicFact(
|
|
328
|
-
fact_id=fact.fact_id, memory_id=record.memory_id,
|
|
329
|
-
profile_id=profile_id, content=fact.content,
|
|
330
|
-
fact_type=fact.fact_type, entities=fact.entities,
|
|
331
|
-
canonical_entities=list(canonical.values()),
|
|
332
|
-
observation_date=fact.observation_date or record.session_date,
|
|
333
|
-
referenced_date=fact.referenced_date or temporal.get("referenced_date"),
|
|
334
|
-
interval_start=fact.interval_start or temporal.get("interval_start"),
|
|
335
|
-
interval_end=fact.interval_end or temporal.get("interval_end"),
|
|
336
|
-
confidence=fact.confidence,
|
|
337
|
-
importance=min(1.0, fact.importance + emotional_importance_boost(emotion)),
|
|
338
|
-
evidence_count=fact.evidence_count,
|
|
339
|
-
source_turn_ids=fact.source_turn_ids, session_id=record.session_id,
|
|
340
|
-
embedding=embedding, fisher_mean=fisher_mean, fisher_variance=fisher_variance,
|
|
341
|
-
langevin_position=langevin_pos,
|
|
342
|
-
emotional_valence=emotion.valence, emotional_arousal=emotion.arousal,
|
|
343
|
-
signal_type=signal, created_at=fact.created_at,
|
|
344
|
-
pinned=getattr(fact, 'pinned', False),
|
|
345
|
-
# v3.6.15 multi-scope: scope is a per-MEMORY property — every fact
|
|
346
|
-
# derived from a memory inherits the memory's scope. The record is
|
|
347
|
-
# authoritative; fact-extractor output never carries scope, so reading
|
|
348
|
-
# it off the fact (as before) silently downgraded extracted facts to
|
|
349
|
-
# 'personal' and broke `--scope global` on the common extraction path.
|
|
350
|
-
scope=(getattr(record, 'scope', None)
|
|
351
|
-
or getattr(fact, 'scope', None) or 'personal'),
|
|
352
|
-
shared_with=(getattr(record, 'shared_with', None)
|
|
353
|
-
if getattr(record, 'scope', None) in ('shared', 'global')
|
|
354
|
-
else getattr(fact, 'shared_with', None)),
|
|
355
|
-
)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
# ---------------------------------------------------------------------------
|
|
359
|
-
# Vector dual-write helper (P1-2 / embeddings-vector-01)
|
|
360
|
-
# ---------------------------------------------------------------------------
|
|
361
|
-
|
|
362
|
-
def _upsert_fact_vectors(fact, profile_id, ann_index, vector_store, embedder=None) -> bool:
|
|
363
|
-
"""Dual-write a fact's embedding to the sqlite-vec projection and the ANN index.
|
|
364
|
-
|
|
365
|
-
Write order mirrors _attach_vector: the sqlite-vec projection is attempted
|
|
366
|
-
first because that is what a meaning-based search reads. The ANN index is
|
|
367
|
-
updated only when the projection accepts the vector. Returns True when the
|
|
368
|
-
fact is now findable by meaning (the projection accepted the vector).
|
|
369
|
-
|
|
370
|
-
Embeds on-demand when the fact has no embedding (e.g. consolidated
|
|
371
|
-
summary facts created without one), so UPDATE/SUPERSEDE and consolidated
|
|
372
|
-
facts remain visible to the semantic channel instead of having a row in
|
|
373
|
-
``atomic_facts`` but none in the vector store.
|
|
374
|
-
"""
|
|
375
|
-
if not getattr(fact, "embedding", None) and embedder is not None and fact.content:
|
|
376
|
-
try:
|
|
377
|
-
fact.embedding = embedder.embed(fact.content)
|
|
378
|
-
except Exception as _emb_exc: # pragma: no cover - defensive
|
|
379
|
-
_reraise_materialization_deferral(_emb_exc)
|
|
380
|
-
logger.debug("on-demand embed failed for %s: %s", fact.fact_id, _emb_exc)
|
|
381
|
-
return False
|
|
382
|
-
if not getattr(fact, "embedding", None):
|
|
383
|
-
return False
|
|
384
|
-
|
|
385
|
-
projected = False
|
|
386
|
-
# Projection first: what a meaning-based search reads.
|
|
387
|
-
if vector_store and getattr(vector_store, "available", False):
|
|
388
|
-
try:
|
|
389
|
-
projected = bool(vector_store.upsert(
|
|
390
|
-
fact_id=fact.fact_id,
|
|
391
|
-
profile_id=profile_id,
|
|
392
|
-
embedding=fact.embedding,
|
|
393
|
-
))
|
|
394
|
-
if not projected:
|
|
395
|
-
logger.warning(
|
|
396
|
-
"vector projection refused for %s — stored but not "
|
|
397
|
-
"findable by meaning", fact.fact_id[:12],
|
|
398
|
-
)
|
|
399
|
-
except Exception as _vs_exc:
|
|
400
|
-
logger.warning(
|
|
401
|
-
"vector projection failed for %s (%s: %s) — stored but not "
|
|
402
|
-
"findable by meaning",
|
|
403
|
-
fact.fact_id[:12], type(_vs_exc).__name__, _vs_exc,
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
# ANN index only when the projection accepted the vector so both
|
|
407
|
-
# representations stay consistent. Writing ANN without a vector-store
|
|
408
|
-
# entry creates a ghost that disappears on restart (ANN is rebuilt from
|
|
409
|
-
# the vector store at startup).
|
|
410
|
-
if projected and ann_index:
|
|
411
|
-
try:
|
|
412
|
-
ann_index.add(fact.fact_id, fact.embedding)
|
|
413
|
-
except Exception:
|
|
414
|
-
logger.warning(
|
|
415
|
-
"in-memory index rejected %s", fact.fact_id[:12], exc_info=True,
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
return projected
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
class _TombstoneReadError(Exception):
|
|
422
|
-
pass
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
def _fact_is_tombstoned(db: DatabaseManager, profile_id: str, fact_id: str) -> bool:
|
|
426
|
-
try:
|
|
427
|
-
rows = db.execute(
|
|
428
|
-
"SELECT 1 FROM projection_tombstones "
|
|
429
|
-
"WHERE profile_id = ? AND fact_id = ? LIMIT 1",
|
|
430
|
-
(profile_id, fact_id),
|
|
431
|
-
)
|
|
432
|
-
return bool(rows)
|
|
433
|
-
except Exception as exc:
|
|
434
|
-
raise _TombstoneReadError(
|
|
435
|
-
f"tombstone check failed for {fact_id[:16]}: {type(exc).__name__}: {exc}"
|
|
436
|
-
) from exc
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
def _residue_table_exists(db: DatabaseManager, name: str) -> bool:
|
|
440
|
-
try:
|
|
441
|
-
return bool(db.execute(
|
|
442
|
-
"SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1",
|
|
443
|
-
(name,),
|
|
444
|
-
))
|
|
445
|
-
except Exception:
|
|
446
|
-
# Cannot determine existence: treat as present so the residue probe runs
|
|
447
|
-
# and fails closed rather than silently reporting "no residue".
|
|
448
|
-
return True
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
def _read_tombstoned_with_retry(
|
|
452
|
-
db: DatabaseManager, profile_id: str, fact_id: str, attempts: int = 3,
|
|
453
|
-
) -> bool:
|
|
454
|
-
last: _TombstoneReadError | None = None
|
|
455
|
-
for _ in range(max(1, attempts)):
|
|
456
|
-
try:
|
|
457
|
-
return _fact_is_tombstoned(db, profile_id, fact_id)
|
|
458
|
-
except _TombstoneReadError as exc:
|
|
459
|
-
last = exc
|
|
460
|
-
raise last if last is not None else _TombstoneReadError("tombstone read failed")
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
def _vector_residue_present(db: DatabaseManager, fact_id: str) -> bool:
|
|
464
|
-
uncertain = False
|
|
465
|
-
for table in ("embedding_metadata", "vector_row_map"):
|
|
466
|
-
if not _residue_table_exists(db, table):
|
|
467
|
-
continue
|
|
468
|
-
try:
|
|
469
|
-
if db.execute(
|
|
470
|
-
f"SELECT 1 FROM {table} WHERE fact_id = ? LIMIT 1", (fact_id,)
|
|
471
|
-
):
|
|
472
|
-
return True
|
|
473
|
-
except Exception:
|
|
474
|
-
uncertain = True
|
|
475
|
-
return uncertain
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
def _drop_resurrected_facts(
|
|
479
|
-
db: DatabaseManager,
|
|
480
|
-
profile_id: str,
|
|
481
|
-
stored_ids: list[str],
|
|
482
|
-
vector_store: Any,
|
|
483
|
-
ann_index: Any,
|
|
484
|
-
retrieval_engine: Any,
|
|
485
|
-
) -> list[str]:
|
|
486
|
-
survivors: list[str] = []
|
|
487
|
-
for fid in stored_ids:
|
|
488
|
-
try:
|
|
489
|
-
tombstoned = _read_tombstoned_with_retry(db, profile_id, fid)
|
|
490
|
-
except _TombstoneReadError as exc:
|
|
491
|
-
if is_erasing(profile_id, fid):
|
|
492
|
-
# Tombstone unreadable but the in-process fence confirms an
|
|
493
|
-
# erasure is in flight — clean up rather than leave residue.
|
|
494
|
-
tombstoned = True
|
|
495
|
-
else:
|
|
496
|
-
logger.error(
|
|
497
|
-
"Tombstone read error in drop_resurrected for %s, deferring: %s",
|
|
498
|
-
fid[:16], exc,
|
|
499
|
-
)
|
|
500
|
-
continue
|
|
501
|
-
if not tombstoned:
|
|
502
|
-
survivors.append(fid)
|
|
503
|
-
continue
|
|
504
|
-
vec_store_ok = vector_store is not None and getattr(vector_store, "available", False)
|
|
505
|
-
try:
|
|
506
|
-
if vec_store_ok:
|
|
507
|
-
vector_store.delete(fid)
|
|
508
|
-
if ann_index is not None and hasattr(ann_index, "remove"):
|
|
509
|
-
ann_index.remove(fid)
|
|
510
|
-
bm25 = getattr(retrieval_engine, "_bm25", None) if retrieval_engine else None
|
|
511
|
-
if bm25 is not None and hasattr(bm25, "remove_fact"):
|
|
512
|
-
bm25.remove_fact(fid)
|
|
513
|
-
db.delete_bm25_tokens_for_fact(fid)
|
|
514
|
-
db.delete_fact(fid, profile_id=profile_id)
|
|
515
|
-
except Exception as exc:
|
|
516
|
-
logger.warning("resurrection undo failed for %s: %s", fid[:16], exc)
|
|
517
|
-
if _vector_residue_present(db, fid):
|
|
518
|
-
logger.error(
|
|
519
|
-
"resurrection undo incomplete for %s: vector residue remains "
|
|
520
|
-
"(vec_store_available=%s); tombstone preserved",
|
|
521
|
-
fid[:16], vec_store_ok,
|
|
522
|
-
)
|
|
523
|
-
return survivors
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
# ---------------------------------------------------------------------------
|
|
527
|
-
# run_store (was MemoryEngine.store)
|
|
528
|
-
# ---------------------------------------------------------------------------
|
|
529
|
-
|
|
530
|
-
def run_store(
|
|
531
|
-
content: str,
|
|
532
|
-
profile_id: str,
|
|
533
|
-
session_id: str = "",
|
|
534
|
-
session_date: str | None = None,
|
|
535
|
-
speaker: str = "",
|
|
536
|
-
role: str = "user",
|
|
537
|
-
metadata: dict[str, Any] | None = None,
|
|
538
|
-
*,
|
|
539
|
-
scope: str = "personal",
|
|
540
|
-
shared_with: list[str] | None = None,
|
|
541
|
-
config: SLMConfig,
|
|
542
|
-
db: DatabaseManager,
|
|
543
|
-
embedder: Any,
|
|
544
|
-
fact_extractor: Any,
|
|
545
|
-
entity_resolver: Any,
|
|
546
|
-
temporal_parser: Any,
|
|
547
|
-
type_router: Any,
|
|
548
|
-
graph_builder: Any,
|
|
549
|
-
consolidator: Any,
|
|
550
|
-
observation_builder: Any,
|
|
551
|
-
scene_builder: Any,
|
|
552
|
-
entropy_gate: Any,
|
|
553
|
-
ann_index: Any,
|
|
554
|
-
sheaf_checker: Any,
|
|
555
|
-
retrieval_engine: Any,
|
|
556
|
-
provenance: Any,
|
|
557
|
-
hooks: HookRegistry,
|
|
558
|
-
vector_store: Any = None,
|
|
559
|
-
temporal_validator: Any = None,
|
|
560
|
-
auto_linker: Any = None,
|
|
561
|
-
context_generator: Any = None,
|
|
562
|
-
consolidation_engine: Any = None,
|
|
563
|
-
existing_memory_id: str | None = None,
|
|
564
|
-
queryable_fact_ids: tuple[str, ...] = (),
|
|
565
|
-
trusted_actor_id: str = "",
|
|
566
|
-
pre_authorized: bool = False,
|
|
567
|
-
ingestion_source_type: str = "store",
|
|
568
|
-
ingestion_operation_id: str = "",
|
|
569
|
-
derivation_report: dict[str, bool] | None = None,
|
|
570
|
-
precompleted_derivation_stages: frozenset[str] = frozenset(),
|
|
571
|
-
materialization_progress: dict[str, Any] | None = None,
|
|
572
|
-
materialization_checkpoint: Any = None,
|
|
573
|
-
) -> list[str]:
|
|
574
|
-
"""Store content and extract structured facts. Returns fact_ids.
|
|
575
|
-
|
|
576
|
-
Multi-scope: ``scope`` sets visibility (personal/shared/global).
|
|
577
|
-
``shared_with`` is a list of profile_ids for shared scope.
|
|
578
|
-
"""
|
|
579
|
-
# Pre-operation hooks (trust gate, ABAC, rate limiter)
|
|
580
|
-
hook_ctx = {
|
|
581
|
-
"operation": "store",
|
|
582
|
-
"agent_id": (
|
|
583
|
-
trusted_actor_id
|
|
584
|
-
or (metadata.get("agent_id", "unknown") if metadata else "unknown")
|
|
585
|
-
),
|
|
586
|
-
"profile_id": profile_id,
|
|
587
|
-
"content_preview": content[:100],
|
|
588
|
-
}
|
|
589
|
-
if not pre_authorized:
|
|
590
|
-
hooks.run_pre("store", hook_ctx)
|
|
591
|
-
|
|
592
|
-
# Admission gates apply to FRESH submissions only. A materialization pass
|
|
593
|
-
# re-runs this pipeline for content whose queryable projection was already
|
|
594
|
-
# committed at submit (``queryable_fact_ids`` is set). Re-applying admission
|
|
595
|
-
# here — the entropy near-duplicate gate especially — would discard that
|
|
596
|
-
# already-committed fact (return []) and wedge the operation in an endless
|
|
597
|
-
# materialize retry loop ("materialization produced no final facts"). The
|
|
598
|
-
# near-duplicate verdict is expected at materialize: the submitted
|
|
599
|
-
# projection itself is in the gate's window. Admission was already decided
|
|
600
|
-
# at submit (store_fast enforces the same gates), so skip it here.
|
|
601
|
-
is_materialization = bool(queryable_fact_ids)
|
|
602
|
-
|
|
603
|
-
if entropy_gate and not is_materialization and not entropy_gate.should_pass(content):
|
|
604
|
-
return []
|
|
605
|
-
|
|
606
|
-
# v3.5.0: store-side quality gate (H3). Reject prompt-template leakage,
|
|
607
|
-
# empty placeholders, and other non-memory content BEFORE it enters the
|
|
608
|
-
# DB. Uses the shared is_low_quality from core/injection so both store
|
|
609
|
-
# AND injection filter by identical rules. Saves DB IO + recall pollution.
|
|
610
|
-
if not is_materialization:
|
|
611
|
-
try:
|
|
612
|
-
from superlocalmemory.core.injection import is_low_quality
|
|
613
|
-
if is_low_quality(content):
|
|
614
|
-
logger.debug("Store rejected (low-quality content): %s...",
|
|
615
|
-
content[:80].replace("\n", " "))
|
|
616
|
-
return []
|
|
617
|
-
except Exception:
|
|
618
|
-
pass # Best-effort gate; store succeeds if import fails
|
|
619
|
-
|
|
620
|
-
from superlocalmemory.encoding.temporal_parser import TemporalParser
|
|
621
|
-
parser = temporal_parser or TemporalParser()
|
|
622
|
-
parsed_date = parser.parse_session_date(session_date) if session_date else None
|
|
623
|
-
|
|
624
|
-
queryable_ids = frozenset(queryable_fact_ids)
|
|
625
|
-
queryable_facts: list[AtomicFact] = []
|
|
626
|
-
if existing_memory_id:
|
|
627
|
-
memory_rows = db.execute(
|
|
628
|
-
"SELECT * FROM memories WHERE memory_id=?",
|
|
629
|
-
(existing_memory_id,),
|
|
630
|
-
)
|
|
631
|
-
if not memory_rows:
|
|
632
|
-
raise ValueError("queryable ingestion memory does not exist")
|
|
633
|
-
memory = dict(memory_rows[0])
|
|
634
|
-
if memory["profile_id"] != profile_id:
|
|
635
|
-
raise ValueError("queryable ingestion memory profile mismatch")
|
|
636
|
-
if memory["content"] != content:
|
|
637
|
-
raise ValueError("queryable ingestion memory content mismatch")
|
|
638
|
-
stored_shared = json.loads(memory.get("shared_with") or "null")
|
|
639
|
-
if (memory.get("scope") or "personal") != scope:
|
|
640
|
-
raise ValueError("queryable ingestion memory scope mismatch")
|
|
641
|
-
if (stored_shared or None) != (shared_with or None):
|
|
642
|
-
raise ValueError("queryable ingestion shared scope mismatch")
|
|
643
|
-
queryable_facts = db.get_facts_by_ids(list(queryable_ids), profile_id)
|
|
644
|
-
if len(queryable_facts) != len(queryable_ids):
|
|
645
|
-
raise ValueError("queryable ingestion fact profile mismatch or missing fact")
|
|
646
|
-
if any(fact.memory_id != existing_memory_id for fact in queryable_facts):
|
|
647
|
-
raise ValueError("queryable ingestion fact belongs to another memory")
|
|
648
|
-
record = MemoryRecord(
|
|
649
|
-
memory_id=existing_memory_id,
|
|
650
|
-
profile_id=profile_id,
|
|
651
|
-
content=content,
|
|
652
|
-
session_id=memory.get("session_id") or session_id,
|
|
653
|
-
speaker=memory.get("speaker") or speaker,
|
|
654
|
-
role=memory.get("role") or role,
|
|
655
|
-
session_date=memory.get("session_date") or parsed_date,
|
|
656
|
-
created_at=memory["created_at"],
|
|
657
|
-
metadata=json.loads(memory.get("metadata_json") or "{}"),
|
|
658
|
-
scope=scope,
|
|
659
|
-
shared_with=shared_with,
|
|
660
|
-
)
|
|
661
|
-
else:
|
|
662
|
-
record = MemoryRecord(
|
|
663
|
-
profile_id=profile_id, content=content,
|
|
664
|
-
session_id=session_id, speaker=speaker, role=role,
|
|
665
|
-
session_date=parsed_date, metadata=metadata or {},
|
|
666
|
-
scope=scope, shared_with=shared_with,
|
|
667
|
-
)
|
|
668
|
-
db.store_memory(record)
|
|
669
|
-
|
|
670
|
-
extraction_complete = "extraction" in precompleted_derivation_stages
|
|
671
|
-
consolidation_complete = (
|
|
672
|
-
"consolidation" in precompleted_derivation_stages
|
|
673
|
-
or consolidator is not None
|
|
674
|
-
)
|
|
675
|
-
canonicalization_complete = (
|
|
676
|
-
"canonicalization" in precompleted_derivation_stages
|
|
677
|
-
or entity_resolver is not None
|
|
678
|
-
)
|
|
679
|
-
graph_complete = (
|
|
680
|
-
"graph" in precompleted_derivation_stages
|
|
681
|
-
or graph_builder is not None
|
|
682
|
-
)
|
|
683
|
-
temporal_complete = True
|
|
684
|
-
provenance_complete = provenance is not None
|
|
685
|
-
|
|
686
|
-
try:
|
|
687
|
-
facts = fact_extractor.extract_facts(
|
|
688
|
-
turns=[content], session_id=session_id,
|
|
689
|
-
session_date=parsed_date, speaker_a=speaker,
|
|
690
|
-
)
|
|
691
|
-
extraction_complete = facts is not None
|
|
692
|
-
except Exception as _extract_exc:
|
|
693
|
-
_reraise_materialization_deferral(_extract_exc)
|
|
694
|
-
# P0-1 (remember-write-04): an extractor EXCEPTION (transient LLM/embed
|
|
695
|
-
# backend error) must NOT orphan the already-committed memory. The None
|
|
696
|
-
# guard below only handled a None *return*, not a raise. Treat a raise
|
|
697
|
-
# as "no facts" so the verbatim/raw fallback persists the content.
|
|
698
|
-
logger.warning(
|
|
699
|
-
"extract_facts() raised — falling back to raw fact: %s", _extract_exc,
|
|
700
|
-
)
|
|
701
|
-
facts = None
|
|
702
|
-
|
|
703
|
-
# v3.4.38: Defensive None guard. extract_facts() returns None on transient
|
|
704
|
-
# failures (embedding worker timeout, LLM call fail). Without this guard,
|
|
705
|
-
# line 201's `{f.content for f in facts}` raises 'NoneType' object is not
|
|
706
|
-
# iterable, causing the caller to mark_failed permanently — even though
|
|
707
|
-
# the content is still recoverable. 18 memories were lost to this between
|
|
708
|
-
# April 15-26, 2026.
|
|
709
|
-
if facts is None:
|
|
710
|
-
facts = []
|
|
711
|
-
|
|
712
|
-
# V3.3.11: Also store raw content as a verbatim fact to preserve details
|
|
713
|
-
# that fact extraction may abstract away (dates, names, specifics).
|
|
714
|
-
# This ensures BM25 and semantic search can always find the original text.
|
|
715
|
-
# V3.3.12: Extract entities from verbatim content so entity channel + temporal
|
|
716
|
-
# channel can find it (was entities=[] which blinded the entity-graph and temporal signals).
|
|
717
|
-
# V3.3.20: Stronger verbatim filter — skip greetings, filler, short phrases.
|
|
718
|
-
# Verbatim facts with just "Hey! How are you?" dilute embeddings and add noise.
|
|
719
|
-
_MIN_VERBATIM_WORDS = 8
|
|
720
|
-
if (not queryable_facts
|
|
721
|
-
and content.strip()
|
|
722
|
-
and len(content.strip()) >= 40
|
|
723
|
-
and len(content.strip().split()) >= _MIN_VERBATIM_WORDS):
|
|
724
|
-
import re as _re
|
|
725
|
-
_verbatim_text = content.strip()
|
|
726
|
-
# Extract entities using the same regex as fact_extractor
|
|
727
|
-
_ent_re = _re.compile(r"\b([A-Z][a-z]+(?:\s[A-Z][a-z]+){0,3})\b")
|
|
728
|
-
_entity_set = {m.group(1) for m in _ent_re.finditer(_verbatim_text)}
|
|
729
|
-
# Also extract all-caps abbreviations (NYU, MIT, etc.) — dedup with first set
|
|
730
|
-
_entity_set |= {m.group(1) for m in _re.finditer(r'\b([A-Z]{2,})\b', _verbatim_text)}
|
|
731
|
-
_verbatim_entities = sorted(_entity_set)
|
|
732
|
-
verbatim = AtomicFact(
|
|
733
|
-
fact_id=uuid.uuid4().hex[:16],
|
|
734
|
-
content=_verbatim_text,
|
|
735
|
-
fact_type=FactType.EPISODIC,
|
|
736
|
-
entities=_verbatim_entities,
|
|
737
|
-
session_id=session_id,
|
|
738
|
-
observation_date=parsed_date,
|
|
739
|
-
confidence=0.9,
|
|
740
|
-
importance=0.5,
|
|
741
|
-
scope=scope,
|
|
742
|
-
shared_with=shared_with,
|
|
743
|
-
)
|
|
744
|
-
# Avoid duplicate if extraction already produced the exact same text
|
|
745
|
-
extracted_texts = {f.content.strip().lower() for f in facts}
|
|
746
|
-
if verbatim.content.strip().lower() not in extracted_texts:
|
|
747
|
-
facts.append(verbatim)
|
|
748
|
-
|
|
749
|
-
if queryable_facts:
|
|
750
|
-
# Replace any extractor-produced copy of the complete raw turn with the
|
|
751
|
-
# already-queryable projection, then promote that stable fact ID in
|
|
752
|
-
# place. Large inputs may have a clamped queryable projection, so the
|
|
753
|
-
# projection itself is authoritative for the verbatim retrieval unit.
|
|
754
|
-
raw_text = content.strip().lower()
|
|
755
|
-
facts = [
|
|
756
|
-
fact for fact in facts
|
|
757
|
-
if fact.content.strip().lower() != raw_text
|
|
758
|
-
and fact.fact_id not in queryable_ids
|
|
759
|
-
]
|
|
760
|
-
facts.extend(queryable_facts)
|
|
761
|
-
|
|
762
|
-
# V3.3.21: If fact extraction produced nothing (short input like "this is test"),
|
|
763
|
-
# store the raw content as a minimal fact. User explicitly called `slm remember` —
|
|
764
|
-
# their data should NEVER be silently dropped. The min-length and min-word filters
|
|
765
|
-
# are designed for automatic conversation extraction, not explicit user storage.
|
|
766
|
-
if not facts and content.strip():
|
|
767
|
-
facts = [AtomicFact(
|
|
768
|
-
fact_id=uuid.uuid4().hex[:16],
|
|
769
|
-
content=content.strip(),
|
|
770
|
-
fact_type=FactType.SEMANTIC,
|
|
771
|
-
entities=[],
|
|
772
|
-
session_id=session_id,
|
|
773
|
-
observation_date=parsed_date,
|
|
774
|
-
confidence=0.7,
|
|
775
|
-
importance=0.3,
|
|
776
|
-
scope=scope,
|
|
777
|
-
shared_with=shared_with,
|
|
778
|
-
)]
|
|
779
|
-
|
|
780
|
-
if not facts:
|
|
781
|
-
return []
|
|
782
|
-
|
|
783
|
-
if type_router:
|
|
784
|
-
facts = type_router.route_facts(facts)
|
|
785
|
-
|
|
786
|
-
stored_ids: list[str] = []
|
|
787
|
-
for fact in facts:
|
|
788
|
-
try:
|
|
789
|
-
if _fact_is_tombstoned(db, profile_id, fact.fact_id):
|
|
790
|
-
continue
|
|
791
|
-
except _TombstoneReadError as _tse:
|
|
792
|
-
logger.error(
|
|
793
|
-
"Tombstone read error for %s, deferring ingestion: %s",
|
|
794
|
-
fact.fact_id[:16], _tse,
|
|
795
|
-
)
|
|
796
|
-
continue
|
|
797
|
-
if is_erasing(profile_id, fact.fact_id):
|
|
798
|
-
continue
|
|
799
|
-
fact = enrich_fact(
|
|
800
|
-
fact, record, profile_id,
|
|
801
|
-
embedder=embedder,
|
|
802
|
-
entity_resolver=entity_resolver,
|
|
803
|
-
temporal_parser=temporal_parser,
|
|
804
|
-
)
|
|
805
|
-
|
|
806
|
-
if (
|
|
807
|
-
materialization_progress is not None
|
|
808
|
-
and not materialization_progress.get("relational_started", False)
|
|
809
|
-
):
|
|
810
|
-
if materialization_checkpoint is not None:
|
|
811
|
-
materialization_checkpoint(
|
|
812
|
-
"relational_started",
|
|
813
|
-
(),
|
|
814
|
-
{
|
|
815
|
-
"pipeline_started": True,
|
|
816
|
-
"relational_started": True,
|
|
817
|
-
"pipeline": False,
|
|
818
|
-
},
|
|
819
|
-
)
|
|
820
|
-
materialization_progress["relational_started"] = True
|
|
821
|
-
|
|
822
|
-
is_queryable_promotion = fact.fact_id in queryable_ids
|
|
823
|
-
# When a promoted fact is being materialised the embedding column is
|
|
824
|
-
# written in a separate step AFTER the projection attempt so that the
|
|
825
|
-
# two representations stay in the correct order: projection first,
|
|
826
|
-
# canonical column second (mirroring _attach_vector's invariant).
|
|
827
|
-
# Writing the column before the projection and then failing the
|
|
828
|
-
# projection leaves the fact with embedding IS NOT NULL but no entry
|
|
829
|
-
# in fact_embeddings — invisible to meaning-search and invisible to
|
|
830
|
-
# every repair pass that selects WHERE embedding IS NULL.
|
|
831
|
-
_deferred_canonical_embedding = None # set below if promotion path taken
|
|
832
|
-
if is_queryable_promotion:
|
|
833
|
-
db.update_fact(fact.fact_id, {
|
|
834
|
-
"content": fact.content,
|
|
835
|
-
"fact_type": fact.fact_type,
|
|
836
|
-
"entities_json": fact.entities,
|
|
837
|
-
"canonical_entities_json": fact.canonical_entities,
|
|
838
|
-
"observation_date": fact.observation_date,
|
|
839
|
-
"referenced_date": fact.referenced_date,
|
|
840
|
-
"interval_start": fact.interval_start,
|
|
841
|
-
"interval_end": fact.interval_end,
|
|
842
|
-
"confidence": fact.confidence,
|
|
843
|
-
"importance": fact.importance,
|
|
844
|
-
"evidence_count": fact.evidence_count,
|
|
845
|
-
"access_count": fact.access_count,
|
|
846
|
-
"source_turn_ids_json": fact.source_turn_ids,
|
|
847
|
-
"session_id": fact.session_id,
|
|
848
|
-
# embedding / fisher fields omitted here — written after
|
|
849
|
-
# the projection attempt below so canonical is never ahead
|
|
850
|
-
# of the projection.
|
|
851
|
-
"lifecycle": fact.lifecycle,
|
|
852
|
-
"langevin_position": fact.langevin_position,
|
|
853
|
-
"emotional_valence": fact.emotional_valence,
|
|
854
|
-
"emotional_arousal": fact.emotional_arousal,
|
|
855
|
-
"signal_type": fact.signal_type,
|
|
856
|
-
})
|
|
857
|
-
if fact.embedding:
|
|
858
|
-
_deferred_canonical_embedding = (
|
|
859
|
-
fact.fact_id,
|
|
860
|
-
fact.embedding,
|
|
861
|
-
fact.fisher_mean,
|
|
862
|
-
fact.fisher_variance,
|
|
863
|
-
)
|
|
864
|
-
if consolidator:
|
|
865
|
-
try:
|
|
866
|
-
action = consolidator.consolidate(
|
|
867
|
-
fact,
|
|
868
|
-
profile_id,
|
|
869
|
-
exclude_fact_ids=queryable_ids,
|
|
870
|
-
)
|
|
871
|
-
except Exception as _consolidate_exc:
|
|
872
|
-
# P0-1 (remember-write-03): a consolidate failure (e.g. LLM
|
|
873
|
-
# timeout) must NOT orphan the already-committed memory. Fall
|
|
874
|
-
# back to storing the raw enriched fact so the content stays
|
|
875
|
-
# retrievable across all channels.
|
|
876
|
-
logger.warning(
|
|
877
|
-
"consolidate() failed for fact %s — storing raw fact as "
|
|
878
|
-
"fallback: %s", fact.fact_id, _consolidate_exc,
|
|
879
|
-
)
|
|
880
|
-
consolidation_complete = False
|
|
881
|
-
action = None
|
|
882
|
-
|
|
883
|
-
if action is not None:
|
|
884
|
-
if action.action_type.value == "noop":
|
|
885
|
-
# A canonical ingestion projection already exists before
|
|
886
|
-
# enrichment. Reconcile it against pre-existing facts and
|
|
887
|
-
# remove it when consolidation proves it is a duplicate.
|
|
888
|
-
target_id = action.existing_fact_id
|
|
889
|
-
if is_queryable_promotion and target_id:
|
|
890
|
-
db.delete_fact(fact.fact_id)
|
|
891
|
-
# The original promoted fact was deleted; its deferred
|
|
892
|
-
# canonical embedding write must not happen.
|
|
893
|
-
_deferred_canonical_embedding = None
|
|
894
|
-
existing_fact = db.get_fact(target_id) if target_id else None
|
|
895
|
-
if existing_fact is None:
|
|
896
|
-
continue
|
|
897
|
-
fact = existing_fact
|
|
898
|
-
|
|
899
|
-
if action.action_type.value in ("update", "supersede"):
|
|
900
|
-
# A consolidator UPDATE/SUPERSEDE is a review-required
|
|
901
|
-
# proposal. It persists the incoming successor and does
|
|
902
|
-
# not mutate, delete, archive, or change trust on the
|
|
903
|
-
# matched predecessor. Continue materializing the
|
|
904
|
-
# incoming fact through the common projection pipeline.
|
|
905
|
-
proposed_fact = db.get_fact(action.new_fact_id)
|
|
906
|
-
if proposed_fact is None:
|
|
907
|
-
raise RuntimeError(
|
|
908
|
-
f"consolidation {action.action_type.value} proposal produced "
|
|
909
|
-
f"missing fact {action.new_fact_id}"
|
|
910
|
-
)
|
|
911
|
-
fact = proposed_fact
|
|
912
|
-
if action.existing_fact_id:
|
|
913
|
-
_record_correction_candidate(
|
|
914
|
-
db,
|
|
915
|
-
operation_id=ingestion_operation_id,
|
|
916
|
-
profile_id=profile_id,
|
|
917
|
-
scope=scope,
|
|
918
|
-
predecessor_fact_id=action.existing_fact_id,
|
|
919
|
-
successor_fact_id=action.new_fact_id,
|
|
920
|
-
reason_code=f"consolidation_{action.action_type.value}",
|
|
921
|
-
trusted_actor_id=trusted_actor_id,
|
|
922
|
-
)
|
|
923
|
-
# ADD case: consolidator already stored the fact (F8 fix)
|
|
924
|
-
# Fall through to post-processing below
|
|
925
|
-
else:
|
|
926
|
-
# Consolidate failed → store the raw fact ourselves so the
|
|
927
|
-
# memory is never left without a retrievable fact, then fall
|
|
928
|
-
# through to post-processing (embeddings, graph, context).
|
|
929
|
-
if not is_queryable_promotion:
|
|
930
|
-
db.store_fact(fact)
|
|
931
|
-
elif not is_queryable_promotion:
|
|
932
|
-
db.store_fact(fact)
|
|
933
|
-
|
|
934
|
-
if fact.fact_id not in stored_ids:
|
|
935
|
-
stored_ids.append(fact.fact_id)
|
|
936
|
-
if materialization_progress is not None:
|
|
937
|
-
materialization_progress["fact_ids"] = tuple(stored_ids)
|
|
938
|
-
|
|
939
|
-
# Projection first (vector store then ANN index, on-demand embed for
|
|
940
|
-
# consolidated facts that arrived without one).
|
|
941
|
-
_upsert_fact_vectors(fact, profile_id, ann_index, vector_store, embedder)
|
|
942
|
-
|
|
943
|
-
# Canonical embedding write: deferred to here from the queryable-
|
|
944
|
-
# promotion block above so the projection is always attempted first.
|
|
945
|
-
# The column is written regardless of whether the projection accepted
|
|
946
|
-
# the vector (the vector is real data; withholding it would force
|
|
947
|
-
# every repair pass to re-compute a model call that the same
|
|
948
|
-
# installation condition would refuse again anyway).
|
|
949
|
-
if _deferred_canonical_embedding is not None:
|
|
950
|
-
_dc_fid, _dc_emb, _dc_fmean, _dc_fvar = _deferred_canonical_embedding
|
|
951
|
-
try:
|
|
952
|
-
db.update_fact(_dc_fid, {
|
|
953
|
-
"embedding": _dc_emb,
|
|
954
|
-
"fisher_mean": _dc_fmean,
|
|
955
|
-
"fisher_variance": _dc_fvar,
|
|
956
|
-
})
|
|
957
|
-
except Exception as _dc_exc:
|
|
958
|
-
logger.warning(
|
|
959
|
-
"canonical embedding write failed for %s: %s",
|
|
960
|
-
_dc_fid[:12], _dc_exc,
|
|
961
|
-
)
|
|
962
|
-
_deferred_canonical_embedding = None
|
|
963
|
-
|
|
964
|
-
# Phase 2: Generate contextual description (after consolidator, before graph_builder)
|
|
965
|
-
if context_generator:
|
|
966
|
-
try:
|
|
967
|
-
import json as _json
|
|
968
|
-
ctx_result = context_generator.generate(fact, config.mode.value)
|
|
969
|
-
db.store_fact_context(
|
|
970
|
-
fact_id=fact.fact_id,
|
|
971
|
-
profile_id=profile_id,
|
|
972
|
-
contextual_description=ctx_result.description,
|
|
973
|
-
keywords=_json.dumps(ctx_result.keywords),
|
|
974
|
-
generated_by=ctx_result.generated_by,
|
|
975
|
-
)
|
|
976
|
-
except Exception as _ctx_exc:
|
|
977
|
-
logger.debug("Context generation skipped for %s: %s", fact.fact_id, _ctx_exc)
|
|
978
|
-
|
|
979
|
-
if graph_builder:
|
|
980
|
-
graph_builder.build_edges(fact, profile_id)
|
|
981
|
-
|
|
982
|
-
# Phase 3: AutoLinker creates association_edges (AFTER GraphBuilder)
|
|
983
|
-
if auto_linker is not None:
|
|
984
|
-
try:
|
|
985
|
-
auto_linker.link_new_fact(fact, profile_id)
|
|
986
|
-
except Exception as exc:
|
|
987
|
-
logger.debug("AutoLinker.link_new_fact: %s", exc)
|
|
988
|
-
|
|
989
|
-
# Sheaf consistency check (runs after edges exist)
|
|
990
|
-
if (sheaf_checker
|
|
991
|
-
and fact.embedding
|
|
992
|
-
and fact.canonical_entities):
|
|
993
|
-
from superlocalmemory.storage.models import EdgeType, GraphEdge
|
|
994
|
-
try:
|
|
995
|
-
edges_for_fact = db.get_edges_for_node(
|
|
996
|
-
fact.fact_id, profile_id,
|
|
997
|
-
)
|
|
998
|
-
if len(edges_for_fact) < config.math.sheaf_max_edges_per_check:
|
|
999
|
-
contradictions = sheaf_checker.check_consistency(
|
|
1000
|
-
fact, profile_id,
|
|
1001
|
-
)
|
|
1002
|
-
for c in contradictions:
|
|
1003
|
-
if c.severity > 0.45:
|
|
1004
|
-
edge = GraphEdge(
|
|
1005
|
-
profile_id=profile_id,
|
|
1006
|
-
source_id=fact.fact_id,
|
|
1007
|
-
target_id=c.fact_id_b,
|
|
1008
|
-
edge_type=EdgeType.SUPERSEDES,
|
|
1009
|
-
weight=c.severity,
|
|
1010
|
-
)
|
|
1011
|
-
db.store_edge(edge)
|
|
1012
|
-
except Exception as exc:
|
|
1013
|
-
logger.debug("Sheaf check skipped: %s", exc)
|
|
1014
|
-
|
|
1015
|
-
# Phase 4: Temporal validation and contradiction detection
|
|
1016
|
-
if temporal_validator:
|
|
1017
|
-
try:
|
|
1018
|
-
db.store_temporal_validity(
|
|
1019
|
-
fact_id=fact.fact_id,
|
|
1020
|
-
profile_id=profile_id,
|
|
1021
|
-
valid_from=fact.observation_date,
|
|
1022
|
-
valid_until=None,
|
|
1023
|
-
)
|
|
1024
|
-
invalidations = temporal_validator.validate_and_invalidate(
|
|
1025
|
-
new_fact=fact,
|
|
1026
|
-
profile_id=profile_id,
|
|
1027
|
-
)
|
|
1028
|
-
if invalidations:
|
|
1029
|
-
logger.info(
|
|
1030
|
-
"Temporal: %d facts invalidated by new fact %s",
|
|
1031
|
-
len(invalidations), fact.fact_id,
|
|
1032
|
-
)
|
|
1033
|
-
for candidate in invalidations:
|
|
1034
|
-
predecessor_fact_id = str(candidate.get("old_fact_id") or "")
|
|
1035
|
-
_record_correction_candidate(
|
|
1036
|
-
db,
|
|
1037
|
-
operation_id=ingestion_operation_id,
|
|
1038
|
-
profile_id=profile_id,
|
|
1039
|
-
scope=scope,
|
|
1040
|
-
predecessor_fact_id=predecessor_fact_id,
|
|
1041
|
-
successor_fact_id=fact.fact_id,
|
|
1042
|
-
reason_code="temporal_contradiction",
|
|
1043
|
-
trusted_actor_id=trusted_actor_id,
|
|
1044
|
-
)
|
|
1045
|
-
except Exception as exc:
|
|
1046
|
-
temporal_complete = False
|
|
1047
|
-
logger.debug(
|
|
1048
|
-
"Temporal validation skipped for fact %s: %s",
|
|
1049
|
-
fact.fact_id, exc,
|
|
1050
|
-
)
|
|
1051
|
-
|
|
1052
|
-
# Phase 4b: fact-augmented key expansion (T3b). Index entity aliases /
|
|
1053
|
-
# canonical names as BM25 alt-keys so paraphrased queries match. Mode A
|
|
1054
|
-
# (entity graph) is zero-LLM and safe on the hot store path; LLM
|
|
1055
|
-
# paraphrase enrichment (Mode B/C) is left to background consolidation.
|
|
1056
|
-
try:
|
|
1057
|
-
from superlocalmemory.core.key_expander import KeyExpander
|
|
1058
|
-
_alt_keys = KeyExpander(db).expand(fact, profile_id, mode="a")
|
|
1059
|
-
if _alt_keys:
|
|
1060
|
-
db.upsert_fact_expansion(fact.fact_id, _alt_keys)
|
|
1061
|
-
except Exception as exc:
|
|
1062
|
-
logger.debug("Key expansion skipped for %s: %s", fact.fact_id, exc)
|
|
1063
|
-
|
|
1064
|
-
if observation_builder:
|
|
1065
|
-
for eid in fact.canonical_entities:
|
|
1066
|
-
observation_builder.update_profile(eid, fact, profile_id)
|
|
1067
|
-
|
|
1068
|
-
# The normalized association key makes this O(1) and exactly-once
|
|
1069
|
-
# across retries, crashes, and separate operations that consolidate to
|
|
1070
|
-
# the same fact.
|
|
1071
|
-
for eid in fact.canonical_entities:
|
|
1072
|
-
_record_fact_entity_association(
|
|
1073
|
-
db,
|
|
1074
|
-
operation_id=ingestion_operation_id,
|
|
1075
|
-
profile_id=profile_id,
|
|
1076
|
-
fact_id=fact.fact_id,
|
|
1077
|
-
entity_id=eid,
|
|
1078
|
-
)
|
|
1079
|
-
if scene_builder:
|
|
1080
|
-
scene_builder.assign_to_scene(fact, profile_id)
|
|
1081
|
-
|
|
1082
|
-
# Populate temporal_events for temporal retrieval.
|
|
1083
|
-
#
|
|
1084
|
-
# A DATE IS A DATE WHETHER OR NOT AN ENTITY WAS RESOLVED. This used to
|
|
1085
|
-
# read `if fact.canonical_entities and has_dates`, so a fact carrying a
|
|
1086
|
-
# perfectly good date but no recognised entity got no temporal row at
|
|
1087
|
-
# all. Measured on the author's store: 967 of 3,894 genuine facts have
|
|
1088
|
-
# no canonical entity (24.8%), and 958 of those have no temporal event
|
|
1089
|
-
# either — a quarter of the store that the temporal channel could only
|
|
1090
|
-
# reach through its created_at recency fallback, i.e. by being recent
|
|
1091
|
-
# rather than by being about the right time.
|
|
1092
|
-
#
|
|
1093
|
-
# The date-window query does not need the entity: it joins
|
|
1094
|
-
# temporal_events to atomic_facts and nothing else
|
|
1095
|
-
# (retrieval/temporal_channel.py). Only the "events for entity X"
|
|
1096
|
-
# lookup joins canonical_entities, and that lookup is correctly
|
|
1097
|
-
# uninterested in a fact with no entity.
|
|
1098
|
-
#
|
|
1099
|
-
# temporal_events.entity_id is NOT NULL with an FK to
|
|
1100
|
-
# canonical_entities, so an entity-less event needs a row to point at.
|
|
1101
|
-
# It gets a per-profile sentinel rather than a shared one, and the
|
|
1102
|
-
# sentinel is invisible to ranking: the entity channel builds its map
|
|
1103
|
-
# from atomic_facts.canonical_entities_json, which is untouched here, so
|
|
1104
|
-
# this cannot create the kind of entity that links a quarter of the
|
|
1105
|
-
# store and destroys entity proximity.
|
|
1106
|
-
has_dates = (fact.observation_date or fact.referenced_date
|
|
1107
|
-
or fact.interval_start)
|
|
1108
|
-
if has_dates:
|
|
1109
|
-
from superlocalmemory.storage.models import TemporalEvent
|
|
1110
|
-
entity_ids = list(fact.canonical_entities)
|
|
1111
|
-
if not entity_ids:
|
|
1112
|
-
sentinel = _ensure_unresolved_entity(db, profile_id)
|
|
1113
|
-
entity_ids = [sentinel] if sentinel else []
|
|
1114
|
-
for eid in entity_ids:
|
|
1115
|
-
event = TemporalEvent(
|
|
1116
|
-
event_id=_ingestion_effect_id(
|
|
1117
|
-
ingestion_operation_id,
|
|
1118
|
-
"temporal",
|
|
1119
|
-
fact.fact_id,
|
|
1120
|
-
eid,
|
|
1121
|
-
"observed",
|
|
1122
|
-
),
|
|
1123
|
-
profile_id=profile_id, entity_id=eid,
|
|
1124
|
-
fact_id=fact.fact_id,
|
|
1125
|
-
scope=fact.scope,
|
|
1126
|
-
shared_with=fact.shared_with,
|
|
1127
|
-
observation_date=fact.observation_date,
|
|
1128
|
-
referenced_date=fact.referenced_date,
|
|
1129
|
-
interval_start=fact.interval_start,
|
|
1130
|
-
interval_end=fact.interval_end,
|
|
1131
|
-
description=fact.content[:200],
|
|
1132
|
-
)
|
|
1133
|
-
db.store_temporal_event(event)
|
|
1134
|
-
|
|
1135
|
-
# Foresight: extract time-bounded predictions
|
|
1136
|
-
try:
|
|
1137
|
-
from superlocalmemory.encoding.foresight import extract_foresight_signals
|
|
1138
|
-
from superlocalmemory.storage.models import TemporalEvent as _TE
|
|
1139
|
-
foresight_signals = extract_foresight_signals(fact)
|
|
1140
|
-
for signal_index, sig in enumerate(foresight_signals):
|
|
1141
|
-
f_event = _TE(
|
|
1142
|
-
event_id=_ingestion_effect_id(
|
|
1143
|
-
ingestion_operation_id,
|
|
1144
|
-
"temporal",
|
|
1145
|
-
fact.fact_id,
|
|
1146
|
-
sig.get("entity_id", ""),
|
|
1147
|
-
"foresight",
|
|
1148
|
-
signal_index,
|
|
1149
|
-
),
|
|
1150
|
-
profile_id=profile_id,
|
|
1151
|
-
entity_id=sig.get("entity_id", ""),
|
|
1152
|
-
fact_id=fact.fact_id,
|
|
1153
|
-
scope=fact.scope,
|
|
1154
|
-
shared_with=fact.shared_with,
|
|
1155
|
-
interval_start=sig.get("start_time"),
|
|
1156
|
-
interval_end=sig.get("end_time"),
|
|
1157
|
-
description=sig.get("description", ""),
|
|
1158
|
-
)
|
|
1159
|
-
db.store_temporal_event(f_event)
|
|
1160
|
-
except Exception as exc:
|
|
1161
|
-
logger.debug("Foresight extraction: %s", exc)
|
|
1162
|
-
|
|
1163
|
-
# Persist BM25 tokens at ingestion
|
|
1164
|
-
bm25 = getattr(retrieval_engine, '_bm25', None) if retrieval_engine else None
|
|
1165
|
-
if bm25:
|
|
1166
|
-
bm25.add(fact.fact_id, fact.content, profile_id)
|
|
1167
|
-
|
|
1168
|
-
# Record provenance for data lineage (EU AI Act Art. 10)
|
|
1169
|
-
if provenance:
|
|
1170
|
-
try:
|
|
1171
|
-
provenance.record(
|
|
1172
|
-
fact_id=fact.fact_id,
|
|
1173
|
-
profile_id=profile_id,
|
|
1174
|
-
source_type=ingestion_source_type,
|
|
1175
|
-
source_id=ingestion_operation_id or session_id,
|
|
1176
|
-
created_by=trusted_actor_id or speaker or "unknown",
|
|
1177
|
-
)
|
|
1178
|
-
except Exception:
|
|
1179
|
-
provenance_complete = False
|
|
1180
|
-
|
|
1181
|
-
stored_ids = _drop_resurrected_facts(
|
|
1182
|
-
db, profile_id, stored_ids, vector_store, ann_index, retrieval_engine,
|
|
1183
|
-
)
|
|
1184
|
-
|
|
1185
|
-
logger.info("Stored %d facts (session=%s)", len(stored_ids), session_id)
|
|
1186
|
-
|
|
1187
|
-
if derivation_report is not None:
|
|
1188
|
-
derivation_report.update({
|
|
1189
|
-
"extraction": extraction_complete,
|
|
1190
|
-
"canonicalization": canonicalization_complete,
|
|
1191
|
-
"consolidation": consolidation_complete,
|
|
1192
|
-
"graph": graph_complete,
|
|
1193
|
-
"temporal": temporal_complete,
|
|
1194
|
-
"provenance": provenance_complete,
|
|
1195
|
-
})
|
|
1196
|
-
|
|
1197
|
-
# Stage observations and emitted fact IDs are recorded before optional
|
|
1198
|
-
# post-hooks. A hook failure must not erase the durable retry boundary and
|
|
1199
|
-
# cause extraction/consolidation to run again.
|
|
1200
|
-
if materialization_progress is not None:
|
|
1201
|
-
materialization_progress["fact_ids"] = tuple(stored_ids)
|
|
1202
|
-
materialization_progress["relational_complete"] = True
|
|
1203
|
-
materialization_progress["post_hooks"] = False
|
|
1204
|
-
if materialization_checkpoint is not None and stored_ids:
|
|
1205
|
-
materialization_checkpoint(
|
|
1206
|
-
"relational_complete",
|
|
1207
|
-
tuple(stored_ids),
|
|
1208
|
-
{
|
|
1209
|
-
"pipeline_started": True,
|
|
1210
|
-
"relational_started": True,
|
|
1211
|
-
"pipeline": True,
|
|
1212
|
-
"post_hooks": False,
|
|
1213
|
-
"relational": True,
|
|
1214
|
-
**dict(derivation_report or {}),
|
|
1215
|
-
},
|
|
1216
|
-
)
|
|
1217
|
-
|
|
1218
|
-
# Post-operation hooks (audit, trust signal, event bus)
|
|
1219
|
-
hook_ctx["ingestion_operation_id"] = ingestion_operation_id
|
|
1220
|
-
hook_ctx["fact_ids"] = stored_ids
|
|
1221
|
-
hook_ctx["fact_count"] = len(stored_ids)
|
|
1222
|
-
hooks.run_post("store", hook_ctx)
|
|
1223
|
-
if materialization_progress is not None:
|
|
1224
|
-
materialization_progress["post_hooks"] = True
|
|
1225
|
-
|
|
1226
|
-
# Phase 5: Step-count trigger for lightweight consolidation (L7)
|
|
1227
|
-
if consolidation_engine is not None:
|
|
1228
|
-
try:
|
|
1229
|
-
consolidation_engine.increment_store_count(profile_id)
|
|
1230
|
-
except Exception as _cons_exc:
|
|
1231
|
-
logger.debug("Consolidation step-count trigger: %s", _cons_exc)
|
|
1232
|
-
|
|
1233
|
-
return stored_ids
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
# ---------------------------------------------------------------------------
|
|
1237
|
-
# run_store_fact_direct (was MemoryEngine.store_fact_direct)
|
|
1238
|
-
# ---------------------------------------------------------------------------
|
|
1239
|
-
|
|
1240
|
-
def run_store_fact_direct(
|
|
1241
|
-
fact: AtomicFact,
|
|
1242
|
-
profile_id: str,
|
|
1243
|
-
*,
|
|
1244
|
-
db: DatabaseManager,
|
|
1245
|
-
embedder: Any,
|
|
1246
|
-
entity_resolver: Any,
|
|
1247
|
-
ann_index: Any,
|
|
1248
|
-
graph_builder: Any,
|
|
1249
|
-
retrieval_engine: Any,
|
|
1250
|
-
vector_store: Any = None,
|
|
1251
|
-
) -> str:
|
|
1252
|
-
"""Store a pre-built fact with full enrichment.
|
|
1253
|
-
|
|
1254
|
-
Ensures embedding, Fisher params, canonical entities, BM25 tokens,
|
|
1255
|
-
and graph edges are all populated — even for auxiliary data.
|
|
1256
|
-
Creates a parent memory record to satisfy FK constraint.
|
|
1257
|
-
"""
|
|
1258
|
-
# remember-write-02: gate low-quality content (empty, bare category tags,
|
|
1259
|
-
# placeholder/template leakage) at the WRITE boundary, matching run_store's
|
|
1260
|
-
# gate. Previously this direct path had no filter, so junk entered the KB
|
|
1261
|
-
# and polluted evidence/stats/embeddings (the read-side filter only hid it).
|
|
1262
|
-
from superlocalmemory.core.injection import is_low_quality
|
|
1263
|
-
if is_low_quality(fact.content):
|
|
1264
|
-
logger.debug("run_store_fact_direct: skipping low-quality content")
|
|
1265
|
-
return fact.fact_id
|
|
1266
|
-
|
|
1267
|
-
# Create parent memory record (FK: atomic_facts.memory_id → memories.memory_id)
|
|
1268
|
-
if not fact.memory_id:
|
|
1269
|
-
record = MemoryRecord(
|
|
1270
|
-
profile_id=profile_id,
|
|
1271
|
-
content=fact.content[:500],
|
|
1272
|
-
session_id=fact.session_id,
|
|
1273
|
-
)
|
|
1274
|
-
db.store_memory(record)
|
|
1275
|
-
fact.memory_id = record.memory_id
|
|
1276
|
-
|
|
1277
|
-
if not fact.embedding and embedder:
|
|
1278
|
-
fact.embedding = embedder.embed(fact.content)
|
|
1279
|
-
if fact.embedding:
|
|
1280
|
-
fact.fisher_mean, fact.fisher_variance = (
|
|
1281
|
-
embedder.compute_fisher_params(fact.embedding)
|
|
1282
|
-
)
|
|
1283
|
-
if entity_resolver and fact.entities:
|
|
1284
|
-
canonical = entity_resolver.resolve(
|
|
1285
|
-
fact.entities, profile_id,
|
|
1286
|
-
)
|
|
1287
|
-
fact.canonical_entities = list(canonical.values())
|
|
1288
|
-
db.store_fact(fact)
|
|
1289
|
-
# Projection first (vector store), ANN only on success — matching
|
|
1290
|
-
# _attach_vector's invariant. The return value (projected) is not used
|
|
1291
|
-
# here because run_store_fact_direct is a fire-and-forget path with no
|
|
1292
|
-
# receipt to update; the caller is responsible for any enrichment status.
|
|
1293
|
-
_upsert_fact_vectors(fact, profile_id, ann_index, vector_store)
|
|
1294
|
-
if graph_builder:
|
|
1295
|
-
graph_builder.build_edges(fact, profile_id)
|
|
1296
|
-
# The graph projection must run after GraphBuilder: syncing immediately
|
|
1297
|
-
# after SQLite fact insertion omitted every new fact edge from Cozo.
|
|
1298
|
-
_sync_to_graph_backends(fact)
|
|
1299
|
-
# BM25 indexing
|
|
1300
|
-
bm25 = getattr(retrieval_engine, '_bm25', None) if retrieval_engine else None
|
|
1301
|
-
if bm25:
|
|
1302
|
-
bm25.add(fact.fact_id, fact.content, profile_id)
|
|
1303
|
-
return fact.fact_id
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
# ---------------------------------------------------------------------------
|
|
1307
|
-
# run_close_session (was MemoryEngine.close_session)
|
|
1308
|
-
# ---------------------------------------------------------------------------
|
|
1309
|
-
|
|
1310
|
-
def _session_already_summarised(
|
|
1311
|
-
db: DatabaseManager,
|
|
1312
|
-
profile_id: str,
|
|
1313
|
-
session_id: str,
|
|
1314
|
-
) -> bool:
|
|
1315
|
-
"""True if close_session already wrote temporal summaries for this session."""
|
|
1316
|
-
if not session_id:
|
|
1317
|
-
return False
|
|
1318
|
-
try:
|
|
1319
|
-
rows = db.execute(
|
|
1320
|
-
"SELECT 1 FROM temporal_events "
|
|
1321
|
-
"WHERE profile_id = ? AND description LIKE ? LIMIT 1",
|
|
1322
|
-
(profile_id, f"Session {session_id}:%"),
|
|
1323
|
-
)
|
|
1324
|
-
return bool(rows)
|
|
1325
|
-
except Exception:
|
|
1326
|
-
return False
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
def run_close_session(
|
|
1330
|
-
session_id: str,
|
|
1331
|
-
profile_id: str,
|
|
1332
|
-
*,
|
|
1333
|
-
db: DatabaseManager,
|
|
1334
|
-
) -> int:
|
|
1335
|
-
"""Create session-level temporal summary for session-level retrieval.
|
|
1336
|
-
|
|
1337
|
-
Aggregates facts from a completed session into temporal_events
|
|
1338
|
-
with session scope. Enables temporal queries like "What happened
|
|
1339
|
-
in session 3?"
|
|
1340
|
-
|
|
1341
|
-
Idempotent: if temporal summaries for this session already exist,
|
|
1342
|
-
returns 0 without writing duplicates.
|
|
1343
|
-
|
|
1344
|
-
Returns number of session summary events created.
|
|
1345
|
-
"""
|
|
1346
|
-
from superlocalmemory.storage.models import TemporalEvent
|
|
1347
|
-
|
|
1348
|
-
if not session_id:
|
|
1349
|
-
return 0
|
|
1350
|
-
|
|
1351
|
-
if _session_already_summarised(db, profile_id, session_id):
|
|
1352
|
-
logger.debug("Session %s already summarised — skip close", session_id)
|
|
1353
|
-
return 0
|
|
1354
|
-
|
|
1355
|
-
facts = db.get_all_facts(profile_id)
|
|
1356
|
-
session_facts = [f for f in facts if f.session_id == session_id]
|
|
1357
|
-
if not session_facts:
|
|
1358
|
-
return 0
|
|
1359
|
-
|
|
1360
|
-
# Group by entity for session-level summaries
|
|
1361
|
-
entity_facts: dict[str, list[AtomicFact]] = {}
|
|
1362
|
-
for f in session_facts:
|
|
1363
|
-
for eid in f.canonical_entities:
|
|
1364
|
-
entity_facts.setdefault(eid, []).append(f)
|
|
1365
|
-
|
|
1366
|
-
count = 0
|
|
1367
|
-
session_date = session_facts[0].observation_date or ""
|
|
1368
|
-
for eid, efacts in entity_facts.items():
|
|
1369
|
-
summary_parts = [f.content[:80] for f in efacts[:5]]
|
|
1370
|
-
summary = f"Session {session_id}: " + "; ".join(summary_parts)
|
|
1371
|
-
event = TemporalEvent(
|
|
1372
|
-
profile_id=profile_id,
|
|
1373
|
-
entity_id=eid,
|
|
1374
|
-
fact_id=efacts[0].fact_id,
|
|
1375
|
-
observation_date=session_date,
|
|
1376
|
-
description=summary[:500],
|
|
1377
|
-
)
|
|
1378
|
-
db.store_temporal_event(event)
|
|
1379
|
-
count += 1
|
|
1380
|
-
|
|
1381
|
-
logger.info(
|
|
1382
|
-
"Session %s closed: %d summary events for %d facts",
|
|
1383
|
-
session_id, count, len(session_facts),
|
|
1384
|
-
)
|
|
1385
|
-
return count
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
# ---------------------------------------------------------------------------
|
|
1389
|
-
# v3.4.5: Incremental sync to CozoDB/LanceDB (F-04)
|
|
1390
|
-
# ---------------------------------------------------------------------------
|
|
1391
|
-
|
|
1392
|
-
def _sync_to_graph_backends(fact: Any) -> None:
|
|
1393
|
-
"""Sync a newly stored fact to CozoDB/LanceDB.
|
|
1394
|
-
|
|
1395
|
-
Non-blocking, best-effort. Called after SQLite write.
|
|
1396
|
-
Failures are logged, not raised — SQLite is already committed.
|
|
1397
|
-
"""
|
|
1398
|
-
try:
|
|
1399
|
-
from superlocalmemory.core.backend_orchestrator import get_orchestrator
|
|
1400
|
-
orch = get_orchestrator()
|
|
1401
|
-
if orch is not None:
|
|
1402
|
-
orch.sync_new_fact(fact)
|
|
1403
|
-
except Exception:
|
|
1404
|
-
pass # Best-effort — daemon may not have initialized yet
|