superlocalmemory 4.1.13 → 4.1.14
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 +48 -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,939 +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
|
-
"""SuperLocalMemory V3.4.11 "Scale-Ready" — Graph Pruning Engine.
|
|
6
|
-
|
|
7
|
-
Reduces graph_edges count without losing meaningful connections:
|
|
8
|
-
1. Orphan removal: edges where source/target no longer exists
|
|
9
|
-
2. Self-loop removal: edges where source == target
|
|
10
|
-
3. Duplicate removal: keeps highest-weight edge per (source, target, type)
|
|
11
|
-
4. Supersedes chain collapse: A→B→C becomes A→B + A→C (B→C removed)
|
|
12
|
-
|
|
13
|
-
CRITICAL: Never deletes facts. Only prunes graph EDGES.
|
|
14
|
-
All operations are profile-scoped and idempotent.
|
|
15
|
-
|
|
16
|
-
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
from __future__ import annotations
|
|
20
|
-
|
|
21
|
-
import logging
|
|
22
|
-
import sqlite3
|
|
23
|
-
import time
|
|
24
|
-
import uuid
|
|
25
|
-
from datetime import datetime, UTC
|
|
26
|
-
from pathlib import Path
|
|
27
|
-
from typing import TYPE_CHECKING, Union
|
|
28
|
-
|
|
29
|
-
if TYPE_CHECKING:
|
|
30
|
-
from superlocalmemory.storage.database import DatabaseManager
|
|
31
|
-
|
|
32
|
-
logger = logging.getLogger("superlocalmemory.graph_pruner")
|
|
33
|
-
|
|
34
|
-
_CHAIN_BATCH_LIMIT = 10_000
|
|
35
|
-
# v3.4.59: Nodes with more than this many total edges (in+out) are hub nodes.
|
|
36
|
-
# SA and entity_graph channels cap fan-out at 30/node, so edges beyond this
|
|
37
|
-
# threshold provide zero additional recall signal while bloating the graph.
|
|
38
|
-
_MAX_DEGREE_PER_NODE: int = 100
|
|
39
|
-
_HUB_PRUNE_BATCH: int = 500 # delete edges in batches to avoid giant IN clauses
|
|
40
|
-
|
|
41
|
-
# Fix A: maximum rows to DELETE per transaction (keeps lock-hold < 10 ms).
|
|
42
|
-
_BATCH_SIZE: int = 1_000
|
|
43
|
-
# Brief sleep between batch-delete transactions so other writers get a turn.
|
|
44
|
-
_BATCH_YIELD_S: float = 0.005
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def prune_graph(
|
|
48
|
-
db_or_path: "Union[DatabaseManager, str, Path]",
|
|
49
|
-
profile_id: str = "default",
|
|
50
|
-
dry_run: bool = False,
|
|
51
|
-
cap_degree: bool = True,
|
|
52
|
-
max_degree: int = _MAX_DEGREE_PER_NODE,
|
|
53
|
-
min_edge_weight: float = 0.0,
|
|
54
|
-
) -> dict:
|
|
55
|
-
"""Run all graph pruning strategies for a specific profile.
|
|
56
|
-
|
|
57
|
-
Fix A: accepts a DatabaseManager as the first argument (preferred) to
|
|
58
|
-
route all writes through the process-level serialisation lock, eliminating
|
|
59
|
-
the lock-storm contribution from the legacy open-own-connection pattern.
|
|
60
|
-
|
|
61
|
-
For backward compatibility a ``str | Path`` is also accepted: a temporary
|
|
62
|
-
DatabaseManager is constructed from it with a deprecation warning.
|
|
63
|
-
|
|
64
|
-
Each DELETE strategy now runs in its own short transaction (≤ _BATCH_SIZE
|
|
65
|
-
rows) with a brief yield between batches. This caps the WAL write-lock
|
|
66
|
-
hold to < 10 ms per batch instead of potentially 30+ seconds for the
|
|
67
|
-
full prune.
|
|
68
|
-
|
|
69
|
-
v3.8.4-G (#84): two new parameters for config-driven thinning:
|
|
70
|
-
- ``max_degree``: per-node in/out degree cap (default = _MAX_DEGREE_PER_NODE=100).
|
|
71
|
-
- ``min_edge_weight``: edges with weight strictly below this floor are
|
|
72
|
-
discarded before any other step. Default 0.0 = no floor (current behaviour).
|
|
73
|
-
|
|
74
|
-
NOTE (Flaw 2 from CRIT): orphan IDs are read once upfront, then deleted
|
|
75
|
-
in batches. New orphan edges created by the materialiser DURING a prune
|
|
76
|
-
run are NOT in the initial ID list and will be pruned on the NEXT cycle.
|
|
77
|
-
The ``total_after`` stat therefore reflects reality after the last batch
|
|
78
|
-
completes, which may be slightly different from total_before - removed.
|
|
79
|
-
This is acceptable — no data loss, just a stat nuance.
|
|
80
|
-
|
|
81
|
-
Returns stats dict with counts for each strategy.
|
|
82
|
-
"""
|
|
83
|
-
from superlocalmemory.storage.database import DatabaseManager
|
|
84
|
-
|
|
85
|
-
if isinstance(db_or_path, DatabaseManager):
|
|
86
|
-
db = db_or_path
|
|
87
|
-
else:
|
|
88
|
-
logger.warning(
|
|
89
|
-
"prune_graph: passing a db_path is deprecated — pass a "
|
|
90
|
-
"DatabaseManager instead (Fix A backward-compat shim active)"
|
|
91
|
-
)
|
|
92
|
-
db = DatabaseManager(db_or_path)
|
|
93
|
-
|
|
94
|
-
stats: dict = {
|
|
95
|
-
"orphans_removed": 0,
|
|
96
|
-
"supersedes_collapsed": 0,
|
|
97
|
-
"self_loops_removed": 0,
|
|
98
|
-
"duplicates_removed": 0,
|
|
99
|
-
"hub_edges_removed": 0,
|
|
100
|
-
"association_orphans_removed": 0,
|
|
101
|
-
"low_weight_removed": 0,
|
|
102
|
-
"total_before": 0,
|
|
103
|
-
"total_after": 0,
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
try:
|
|
107
|
-
before = db.execute(
|
|
108
|
-
"SELECT COUNT(*) AS cnt FROM graph_edges WHERE profile_id = ?",
|
|
109
|
-
(profile_id,),
|
|
110
|
-
)
|
|
111
|
-
stats["total_before"] = int(before[0]["cnt"]) if before else 0
|
|
112
|
-
|
|
113
|
-
start = time.time()
|
|
114
|
-
|
|
115
|
-
if not dry_run:
|
|
116
|
-
# v3.8.4-G: min_edge_weight floor — run FIRST so subsequent steps
|
|
117
|
-
# operate on the already-thinned edge set.
|
|
118
|
-
if min_edge_weight > 0.0:
|
|
119
|
-
stats["low_weight_removed"] = _remove_low_weight_edges_batched(
|
|
120
|
-
db, profile_id, min_edge_weight,
|
|
121
|
-
)
|
|
122
|
-
stats["orphans_removed"] = _remove_orphan_edges_batched(
|
|
123
|
-
db, profile_id,
|
|
124
|
-
)
|
|
125
|
-
stats["self_loops_removed"] = _remove_self_loops_batched(
|
|
126
|
-
db, profile_id,
|
|
127
|
-
)
|
|
128
|
-
stats["duplicates_removed"] = _remove_duplicate_edges_batched(
|
|
129
|
-
db, profile_id,
|
|
130
|
-
)
|
|
131
|
-
stats["supersedes_collapsed"] = _collapse_supersedes_chains_batched(
|
|
132
|
-
db, profile_id,
|
|
133
|
-
)
|
|
134
|
-
if cap_degree:
|
|
135
|
-
stats["hub_edges_removed"] = _cap_node_degree_batched(
|
|
136
|
-
db, profile_id, max_degree,
|
|
137
|
-
)
|
|
138
|
-
stats["association_orphans_removed"] = (
|
|
139
|
-
_remove_orphan_association_edges_batched(db, profile_id)
|
|
140
|
-
)
|
|
141
|
-
else:
|
|
142
|
-
# Dry-run: read-only estimates via a temporary connection
|
|
143
|
-
# (DatabaseManager is not used here — no writes)
|
|
144
|
-
stats.update(
|
|
145
|
-
_dry_run_counts(db.db_path, profile_id, cap_degree)
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
after = db.execute(
|
|
149
|
-
"SELECT COUNT(*) AS cnt FROM graph_edges WHERE profile_id = ?",
|
|
150
|
-
(profile_id,),
|
|
151
|
-
)
|
|
152
|
-
stats["total_after"] = int(after[0]["cnt"]) if after else 0
|
|
153
|
-
|
|
154
|
-
elapsed = time.time() - start
|
|
155
|
-
total_removed = stats["total_before"] - stats["total_after"]
|
|
156
|
-
pct = round(total_removed / max(stats["total_before"], 1) * 100, 1)
|
|
157
|
-
|
|
158
|
-
prefix = "(dry-run) " if dry_run else ""
|
|
159
|
-
logger.info(
|
|
160
|
-
"%sGraph pruning: removed %d edges (%.1f%%) in %.1fs — "
|
|
161
|
-
"orphans=%d, supersedes=%d, self_loops=%d, duplicates=%d, hub_cap=%d",
|
|
162
|
-
prefix, total_removed, pct, elapsed,
|
|
163
|
-
stats["orphans_removed"], stats["supersedes_collapsed"],
|
|
164
|
-
stats["self_loops_removed"], stats["duplicates_removed"],
|
|
165
|
-
stats["hub_edges_removed"],
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
except Exception as exc:
|
|
169
|
-
logger.error("Graph pruning failed: %s", exc, exc_info=True)
|
|
170
|
-
stats["error"] = str(exc)
|
|
171
|
-
|
|
172
|
-
return stats
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
# ---------------------------------------------------------------------------
|
|
176
|
-
# Batched delete helpers (Fix A — short transactions, bounded lock hold)
|
|
177
|
-
# ---------------------------------------------------------------------------
|
|
178
|
-
|
|
179
|
-
def _batch_delete_by_ids(
|
|
180
|
-
db: "DatabaseManager",
|
|
181
|
-
table: str,
|
|
182
|
-
ids: list,
|
|
183
|
-
id_col: str = "edge_id",
|
|
184
|
-
) -> int:
|
|
185
|
-
"""Delete rows in ``table`` where ``id_col`` IN ``ids``, _BATCH_SIZE at a time.
|
|
186
|
-
|
|
187
|
-
Each batch runs in its own transaction so the write lock is released
|
|
188
|
-
between batches. Returns total rows deleted.
|
|
189
|
-
"""
|
|
190
|
-
removed = 0
|
|
191
|
-
for start in range(0, len(ids), _BATCH_SIZE):
|
|
192
|
-
batch = ids[start:start + _BATCH_SIZE]
|
|
193
|
-
ph = ",".join("?" * len(batch))
|
|
194
|
-
endpoints = _endpoints_of(db, table, id_col, batch)
|
|
195
|
-
with db.transaction():
|
|
196
|
-
db.execute(
|
|
197
|
-
f"DELETE FROM {table} WHERE {id_col} IN ({ph})",
|
|
198
|
-
tuple(batch),
|
|
199
|
-
)
|
|
200
|
-
# Inside the same transaction as the delete. The graph also lives in
|
|
201
|
-
# a second store, which no SQLite transaction can reach, so the
|
|
202
|
-
# durable record of "these facts need re-projecting" has to be as
|
|
203
|
-
# durable as the delete itself. Without it the second store keeps
|
|
204
|
-
# serving edges this function just removed, and a search walks a hop
|
|
205
|
-
# that no longer exists.
|
|
206
|
-
_enqueue_reprojection(db, endpoints)
|
|
207
|
-
removed += len(batch)
|
|
208
|
-
if start + _BATCH_SIZE < len(ids):
|
|
209
|
-
time.sleep(_BATCH_YIELD_S)
|
|
210
|
-
return removed
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
#: The two columns naming an edge's ends, per table. ``association_edges``
|
|
214
|
-
#: calls them ``source_fact_id``/``target_fact_id``, so a helper that assumed
|
|
215
|
-
#: ``source_id``/``target_id`` raised there, returned nothing, and every
|
|
216
|
-
#: association-edge deletion went unannounced to the graph store -- silently,
|
|
217
|
-
#: because the failure looked exactly like "this table has no endpoints".
|
|
218
|
-
_ENDPOINT_COLUMNS: dict[str, tuple[str, str]] = {
|
|
219
|
-
"graph_edges": ("source_id", "target_id"),
|
|
220
|
-
"association_edges": ("source_fact_id", "target_fact_id"),
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
def _endpoints_of(
|
|
225
|
-
db: "DatabaseManager", table: str, id_col: str, ids: list,
|
|
226
|
-
) -> list[tuple[str, str]]:
|
|
227
|
-
"""``(fact_id, profile_id)`` for both ends of the rows about to be deleted.
|
|
228
|
-
|
|
229
|
-
Read before the delete, because afterwards there is nothing to read. Only
|
|
230
|
-
the ends that are facts matter: an entity id here is projected as part of
|
|
231
|
-
whichever facts reference it, and those facts are re-derived anyway.
|
|
232
|
-
"""
|
|
233
|
-
if not ids:
|
|
234
|
-
return []
|
|
235
|
-
columns = _ENDPOINT_COLUMNS.get(table)
|
|
236
|
-
if columns is None:
|
|
237
|
-
# A table nobody has named the endpoints of is one this pass must not
|
|
238
|
-
# guess at. Loudly, because guessing wrong is what produced the silent
|
|
239
|
-
# hole above.
|
|
240
|
-
logger.warning(
|
|
241
|
-
"prune: %s has no declared endpoint columns, so its deletions "
|
|
242
|
-
"cannot be announced to the graph projection", table,
|
|
243
|
-
)
|
|
244
|
-
return []
|
|
245
|
-
ph = ",".join("?" * len(ids))
|
|
246
|
-
try:
|
|
247
|
-
rows = db.execute(
|
|
248
|
-
f"SELECT {columns[0]}, {columns[1]}, profile_id FROM {table} "
|
|
249
|
-
f"WHERE {id_col} IN ({ph})",
|
|
250
|
-
tuple(ids),
|
|
251
|
-
)
|
|
252
|
-
except Exception as exc: # noqa: BLE001
|
|
253
|
-
logger.warning("prune: cannot read endpoints from %s: %s", table, exc)
|
|
254
|
-
return []
|
|
255
|
-
seen: dict[tuple[str, str], None] = {}
|
|
256
|
-
for row in rows:
|
|
257
|
-
record = dict(row)
|
|
258
|
-
profile_id = str(record.get("profile_id") or "default")
|
|
259
|
-
for column in columns:
|
|
260
|
-
value = record.get(column)
|
|
261
|
-
if value:
|
|
262
|
-
seen[(str(value), profile_id)] = None
|
|
263
|
-
return list(seen)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
def _enqueue_reprojection(
|
|
267
|
-
db: "DatabaseManager", endpoints: list[tuple[str, str]],
|
|
268
|
-
) -> None:
|
|
269
|
-
"""Queue an upsert for each fact whose edges just changed.
|
|
270
|
-
|
|
271
|
-
The queue coalesces on fact id and the worker re-reads the fact's current
|
|
272
|
-
edges from SQLite, so queueing an endpoint twice, or queueing one whose
|
|
273
|
-
edges were already correct, costs one row and converges on the same answer.
|
|
274
|
-
An id that is an entity rather than a fact is filtered out here rather than
|
|
275
|
-
left for the worker, which would otherwise spend a lookup discovering the
|
|
276
|
-
same thing on every cycle.
|
|
277
|
-
"""
|
|
278
|
-
if not endpoints:
|
|
279
|
-
return
|
|
280
|
-
try:
|
|
281
|
-
from superlocalmemory.storage import projection_outbox
|
|
282
|
-
except Exception as exc: # noqa: BLE001
|
|
283
|
-
logger.debug("prune: no projection queue module: %s", exc)
|
|
284
|
-
return
|
|
285
|
-
try:
|
|
286
|
-
if not projection_outbox.is_available(db):
|
|
287
|
-
return
|
|
288
|
-
except Exception as exc: # noqa: BLE001
|
|
289
|
-
logger.debug("prune: projection queue unavailable: %s", exc)
|
|
290
|
-
return
|
|
291
|
-
by_profile: dict[str, list[str]] = {}
|
|
292
|
-
for fact_id, profile_id in endpoints:
|
|
293
|
-
by_profile.setdefault(profile_id, []).append(fact_id)
|
|
294
|
-
for profile_id, fact_ids in by_profile.items():
|
|
295
|
-
ph = ",".join("?" * len(fact_ids))
|
|
296
|
-
try:
|
|
297
|
-
rows = db.execute(
|
|
298
|
-
f"SELECT fact_id FROM atomic_facts WHERE profile_id = ? "
|
|
299
|
-
f"AND fact_id IN ({ph})",
|
|
300
|
-
(profile_id, *fact_ids),
|
|
301
|
-
)
|
|
302
|
-
real = [dict(row)["fact_id"] for row in rows]
|
|
303
|
-
except Exception as exc: # noqa: BLE001
|
|
304
|
-
logger.debug("prune: cannot confirm endpoints are facts: %s", exc)
|
|
305
|
-
continue
|
|
306
|
-
if not real:
|
|
307
|
-
continue
|
|
308
|
-
# Deliberately allowed to raise. This runs inside the same transaction
|
|
309
|
-
# as the delete, so a failure here rolls the delete back and the batch
|
|
310
|
-
# is simply retried on the next pass -- nothing is lost and nothing
|
|
311
|
-
# diverges.
|
|
312
|
-
#
|
|
313
|
-
# Swallowing it, as this once did, committed the delete with no record
|
|
314
|
-
# that the graph needed telling. The queue would then be empty, which
|
|
315
|
-
# is exactly what "the graph is up to date" looks like, and the graph
|
|
316
|
-
# would serve a link the store had removed with nothing anywhere
|
|
317
|
-
# recording that it happened. That is the failure this queue exists to
|
|
318
|
-
# make impossible, and it is the module's own stated policy: "a
|
|
319
|
-
# durability mechanism that silently degrades to best-effort is the
|
|
320
|
-
# defect it exists to remove."
|
|
321
|
-
projection_outbox.enqueue_many(
|
|
322
|
-
db, real, profile_id, op=projection_outbox.OP_UPSERT,
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
def _remove_orphan_edges_batched(
|
|
327
|
-
db: "DatabaseManager",
|
|
328
|
-
profile_id: str,
|
|
329
|
-
) -> int:
|
|
330
|
-
"""Remove graph_edges whose source OR target no longer exists in facts/entities.
|
|
331
|
-
|
|
332
|
-
Read all orphan IDs upfront, then batch-delete. New orphans created
|
|
333
|
-
during the run will be handled in the next maintenance cycle.
|
|
334
|
-
"""
|
|
335
|
-
rows = db.execute(
|
|
336
|
-
"""
|
|
337
|
-
SELECT edge_id FROM graph_edges
|
|
338
|
-
WHERE profile_id = ?
|
|
339
|
-
AND (
|
|
340
|
-
(source_id NOT IN (SELECT fact_id FROM atomic_facts)
|
|
341
|
-
AND source_id NOT IN (SELECT entity_id FROM canonical_entities))
|
|
342
|
-
OR
|
|
343
|
-
(target_id NOT IN (SELECT fact_id FROM atomic_facts)
|
|
344
|
-
AND target_id NOT IN (SELECT entity_id FROM canonical_entities))
|
|
345
|
-
)
|
|
346
|
-
""",
|
|
347
|
-
(profile_id,),
|
|
348
|
-
)
|
|
349
|
-
ids = [dict(r)["edge_id"] for r in rows]
|
|
350
|
-
if not ids:
|
|
351
|
-
return 0
|
|
352
|
-
return _batch_delete_by_ids(db, "graph_edges", ids)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
def _remove_self_loops_batched(
|
|
356
|
-
db: "DatabaseManager",
|
|
357
|
-
profile_id: str,
|
|
358
|
-
) -> int:
|
|
359
|
-
"""Remove self-loop edges (source_id == target_id)."""
|
|
360
|
-
rows = db.execute(
|
|
361
|
-
"SELECT edge_id FROM graph_edges "
|
|
362
|
-
"WHERE source_id = target_id AND profile_id = ?",
|
|
363
|
-
(profile_id,),
|
|
364
|
-
)
|
|
365
|
-
ids = [dict(r)["edge_id"] for r in rows]
|
|
366
|
-
if not ids:
|
|
367
|
-
return 0
|
|
368
|
-
return _batch_delete_by_ids(db, "graph_edges", ids)
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
def _remove_duplicate_edges_batched(
|
|
372
|
-
db: "DatabaseManager",
|
|
373
|
-
profile_id: str,
|
|
374
|
-
) -> int:
|
|
375
|
-
"""Remove duplicate edges (same source+target+type), keeping highest weight."""
|
|
376
|
-
rows = db.execute(
|
|
377
|
-
"""
|
|
378
|
-
SELECT edge_id FROM graph_edges ge_outer
|
|
379
|
-
WHERE profile_id = ?
|
|
380
|
-
AND edge_id NOT IN (
|
|
381
|
-
SELECT edge_id FROM graph_edges ge1
|
|
382
|
-
WHERE profile_id = ?
|
|
383
|
-
AND weight = (
|
|
384
|
-
SELECT MAX(weight) FROM graph_edges ge2
|
|
385
|
-
WHERE ge2.source_id = ge1.source_id
|
|
386
|
-
AND ge2.target_id = ge1.target_id
|
|
387
|
-
AND ge2.edge_type = ge1.edge_type
|
|
388
|
-
AND ge2.profile_id = ge1.profile_id
|
|
389
|
-
)
|
|
390
|
-
GROUP BY source_id, target_id, edge_type
|
|
391
|
-
)
|
|
392
|
-
""",
|
|
393
|
-
(profile_id, profile_id),
|
|
394
|
-
)
|
|
395
|
-
ids = [dict(r)["edge_id"] for r in rows]
|
|
396
|
-
if not ids:
|
|
397
|
-
return 0
|
|
398
|
-
return _batch_delete_by_ids(db, "graph_edges", ids)
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
def _collapse_supersedes_chains_batched(
|
|
402
|
-
db: "DatabaseManager",
|
|
403
|
-
profile_id: str,
|
|
404
|
-
) -> int:
|
|
405
|
-
"""Collapse supersedes chains: A→B, B→C becomes A→C (B→C removed)."""
|
|
406
|
-
rows = db.execute(
|
|
407
|
-
"""
|
|
408
|
-
SELECT e1.edge_id AS e1_id, e1.source_id AS a, e1.target_id AS b,
|
|
409
|
-
e1.weight AS e1_weight,
|
|
410
|
-
e2.edge_id AS e2_id, e2.target_id AS c
|
|
411
|
-
FROM graph_edges e1
|
|
412
|
-
JOIN graph_edges e2 ON e1.target_id = e2.source_id
|
|
413
|
-
WHERE e1.edge_type = 'supersedes'
|
|
414
|
-
AND e2.edge_type = 'supersedes'
|
|
415
|
-
AND e1.profile_id = ?
|
|
416
|
-
AND e2.profile_id = ?
|
|
417
|
-
LIMIT ?
|
|
418
|
-
""",
|
|
419
|
-
(profile_id, profile_id, _CHAIN_BATCH_LIMIT),
|
|
420
|
-
)
|
|
421
|
-
chains = [dict(r) for r in rows]
|
|
422
|
-
if not chains:
|
|
423
|
-
return 0
|
|
424
|
-
|
|
425
|
-
if len(chains) >= _CHAIN_BATCH_LIMIT:
|
|
426
|
-
logger.warning(
|
|
427
|
-
"Supersedes chain collapse hit limit (%d). "
|
|
428
|
-
"More chains may exist — will process in next cycle.",
|
|
429
|
-
_CHAIN_BATCH_LIMIT,
|
|
430
|
-
)
|
|
431
|
-
|
|
432
|
-
now = datetime.now(UTC).isoformat()
|
|
433
|
-
delete_ids: list[str] = []
|
|
434
|
-
insert_rows: list[tuple] = []
|
|
435
|
-
|
|
436
|
-
for chain in chains:
|
|
437
|
-
delete_ids.append(chain["e2_id"])
|
|
438
|
-
new_edge_id = uuid.uuid4().hex[:16]
|
|
439
|
-
insert_rows.append((
|
|
440
|
-
new_edge_id, profile_id,
|
|
441
|
-
chain["a"], chain["c"],
|
|
442
|
-
"supersedes", chain["e1_weight"] or 1.0, now,
|
|
443
|
-
))
|
|
444
|
-
|
|
445
|
-
# Delete B→C edges in batches, then insert A→C shortcuts
|
|
446
|
-
removed = _batch_delete_by_ids(db, "graph_edges", delete_ids)
|
|
447
|
-
|
|
448
|
-
for start in range(0, len(insert_rows), _BATCH_SIZE):
|
|
449
|
-
batch = insert_rows[start:start + _BATCH_SIZE]
|
|
450
|
-
with db.transaction():
|
|
451
|
-
for row in batch:
|
|
452
|
-
db.execute(
|
|
453
|
-
"INSERT OR IGNORE INTO graph_edges "
|
|
454
|
-
"(edge_id, profile_id, source_id, target_id, "
|
|
455
|
-
" edge_type, weight, created_at) "
|
|
456
|
-
"VALUES (?,?,?,?,?,?,?)",
|
|
457
|
-
row,
|
|
458
|
-
)
|
|
459
|
-
if start + _BATCH_SIZE < len(insert_rows):
|
|
460
|
-
time.sleep(_BATCH_YIELD_S)
|
|
461
|
-
|
|
462
|
-
return removed
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
def _cap_node_degree_batched(
|
|
466
|
-
db: "DatabaseManager",
|
|
467
|
-
profile_id: str,
|
|
468
|
-
max_degree: int,
|
|
469
|
-
) -> int:
|
|
470
|
-
"""Remove low-weight edges from hub nodes (degree > max_degree).
|
|
471
|
-
|
|
472
|
-
Uses the same window-function approach as the legacy cursor version but
|
|
473
|
-
selects IDs into Python then batch-deletes through DatabaseManager.
|
|
474
|
-
"""
|
|
475
|
-
rows = db.execute(
|
|
476
|
-
"""
|
|
477
|
-
SELECT edge_id FROM (
|
|
478
|
-
SELECT edge_id,
|
|
479
|
-
ROW_NUMBER() OVER (
|
|
480
|
-
PARTITION BY source_id ORDER BY weight DESC
|
|
481
|
-
) AS out_rn,
|
|
482
|
-
ROW_NUMBER() OVER (
|
|
483
|
-
PARTITION BY target_id ORDER BY weight DESC
|
|
484
|
-
) AS in_rn
|
|
485
|
-
FROM graph_edges
|
|
486
|
-
WHERE profile_id = ?
|
|
487
|
-
) WHERE out_rn > ? OR in_rn > ?
|
|
488
|
-
""",
|
|
489
|
-
(profile_id, max_degree, max_degree),
|
|
490
|
-
)
|
|
491
|
-
ids = [dict(r)["edge_id"] for r in rows]
|
|
492
|
-
if not ids:
|
|
493
|
-
return 0
|
|
494
|
-
removed = _batch_delete_by_ids(db, "graph_edges", ids)
|
|
495
|
-
logger.info(
|
|
496
|
-
"_cap_node_degree_batched: removed %d low-weight edges (max_degree=%d)",
|
|
497
|
-
removed, max_degree,
|
|
498
|
-
)
|
|
499
|
-
return removed
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
def _remove_low_weight_edges_batched(
|
|
503
|
-
db: "DatabaseManager",
|
|
504
|
-
profile_id: str,
|
|
505
|
-
min_weight: float,
|
|
506
|
-
) -> int:
|
|
507
|
-
"""Remove graph_edges with weight strictly below ``min_weight``.
|
|
508
|
-
|
|
509
|
-
v3.8.4-G (#84): implements the ``min_edge_weight`` floor from
|
|
510
|
-
``GraphPruningConfig``. Runs before orphan/duplicate/degree pruning so
|
|
511
|
-
subsequent steps operate on the already-thinned set.
|
|
512
|
-
|
|
513
|
-
A ``min_weight`` of 0.0 means no floor — no edges are touched. The
|
|
514
|
-
caller in ``prune_graph()`` guards the call with ``if min_edge_weight > 0.0``
|
|
515
|
-
so this function is only invoked when there is an actual floor to enforce.
|
|
516
|
-
"""
|
|
517
|
-
rows = db.execute(
|
|
518
|
-
"SELECT edge_id FROM graph_edges WHERE profile_id = ? AND weight < ?",
|
|
519
|
-
(profile_id, min_weight),
|
|
520
|
-
)
|
|
521
|
-
ids = [dict(r)["edge_id"] for r in rows]
|
|
522
|
-
if not ids:
|
|
523
|
-
return 0
|
|
524
|
-
removed = _batch_delete_by_ids(db, "graph_edges", ids)
|
|
525
|
-
logger.info(
|
|
526
|
-
"_remove_low_weight_edges_batched: removed %d edges (min_weight=%.4f)",
|
|
527
|
-
removed, min_weight,
|
|
528
|
-
)
|
|
529
|
-
return removed
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
def _remove_orphan_association_edges_batched(
|
|
533
|
-
db: "DatabaseManager",
|
|
534
|
-
profile_id: str,
|
|
535
|
-
) -> int:
|
|
536
|
-
"""Remove association_edges whose source/target fact no longer exists.
|
|
537
|
-
|
|
538
|
-
Fix: PK column is ``edge_id``, not ``id`` (schema_v32.py line 177).
|
|
539
|
-
"""
|
|
540
|
-
rows = db.execute(
|
|
541
|
-
"""
|
|
542
|
-
SELECT edge_id FROM association_edges
|
|
543
|
-
WHERE profile_id = ?
|
|
544
|
-
AND (
|
|
545
|
-
source_fact_id NOT IN (
|
|
546
|
-
SELECT fact_id FROM atomic_facts WHERE profile_id = ?
|
|
547
|
-
)
|
|
548
|
-
OR target_fact_id NOT IN (
|
|
549
|
-
SELECT fact_id FROM atomic_facts WHERE profile_id = ?
|
|
550
|
-
)
|
|
551
|
-
)
|
|
552
|
-
""",
|
|
553
|
-
(profile_id, profile_id, profile_id),
|
|
554
|
-
)
|
|
555
|
-
ids = [dict(r)["edge_id"] for r in rows]
|
|
556
|
-
if not ids:
|
|
557
|
-
return 0
|
|
558
|
-
return _batch_delete_by_ids(db, "association_edges", ids, id_col="edge_id")
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
# ---------------------------------------------------------------------------
|
|
562
|
-
# Dry-run estimates (read-only; uses a direct connection to avoid side effects)
|
|
563
|
-
# ---------------------------------------------------------------------------
|
|
564
|
-
|
|
565
|
-
def _dry_run_counts(
|
|
566
|
-
db_path: Path,
|
|
567
|
-
profile_id: str,
|
|
568
|
-
cap_degree: bool,
|
|
569
|
-
) -> dict:
|
|
570
|
-
"""Return estimated removal counts without modifying anything."""
|
|
571
|
-
counts: dict = {
|
|
572
|
-
"orphans_removed": 0,
|
|
573
|
-
"supersedes_collapsed": 0,
|
|
574
|
-
"self_loops_removed": 0,
|
|
575
|
-
"duplicates_removed": 0,
|
|
576
|
-
"hub_edges_removed": 0,
|
|
577
|
-
"association_orphans_removed": 0,
|
|
578
|
-
}
|
|
579
|
-
try:
|
|
580
|
-
conn = sqlite3.connect(str(db_path))
|
|
581
|
-
conn.execute("PRAGMA busy_timeout=10000")
|
|
582
|
-
conn.row_factory = sqlite3.Row
|
|
583
|
-
c = conn.cursor()
|
|
584
|
-
|
|
585
|
-
c.execute(
|
|
586
|
-
"""
|
|
587
|
-
SELECT COUNT(*) AS cnt FROM graph_edges
|
|
588
|
-
WHERE profile_id = ?
|
|
589
|
-
AND (
|
|
590
|
-
(source_id NOT IN (SELECT fact_id FROM atomic_facts)
|
|
591
|
-
AND source_id NOT IN (SELECT entity_id FROM canonical_entities))
|
|
592
|
-
OR
|
|
593
|
-
(target_id NOT IN (SELECT fact_id FROM atomic_facts)
|
|
594
|
-
AND target_id NOT IN (SELECT entity_id FROM canonical_entities))
|
|
595
|
-
)
|
|
596
|
-
""",
|
|
597
|
-
(profile_id,),
|
|
598
|
-
)
|
|
599
|
-
counts["orphans_removed"] = c.fetchone()["cnt"]
|
|
600
|
-
|
|
601
|
-
c.execute(
|
|
602
|
-
"SELECT COUNT(*) AS cnt FROM graph_edges "
|
|
603
|
-
"WHERE source_id = target_id AND profile_id = ?",
|
|
604
|
-
(profile_id,),
|
|
605
|
-
)
|
|
606
|
-
counts["self_loops_removed"] = c.fetchone()["cnt"]
|
|
607
|
-
|
|
608
|
-
c.execute(
|
|
609
|
-
"""
|
|
610
|
-
SELECT
|
|
611
|
-
(SELECT COUNT(*) FROM graph_edges WHERE profile_id = ?) -
|
|
612
|
-
(SELECT COUNT(*) FROM (
|
|
613
|
-
SELECT source_id, target_id, edge_type
|
|
614
|
-
FROM graph_edges WHERE profile_id = ?
|
|
615
|
-
GROUP BY source_id, target_id, edge_type
|
|
616
|
-
)) AS cnt
|
|
617
|
-
""",
|
|
618
|
-
(profile_id, profile_id),
|
|
619
|
-
)
|
|
620
|
-
counts["duplicates_removed"] = max(c.fetchone()["cnt"] or 0, 0)
|
|
621
|
-
|
|
622
|
-
if cap_degree:
|
|
623
|
-
c.execute(
|
|
624
|
-
"""
|
|
625
|
-
SELECT COUNT(*) AS cnt FROM (
|
|
626
|
-
SELECT edge_id,
|
|
627
|
-
ROW_NUMBER() OVER (
|
|
628
|
-
PARTITION BY source_id ORDER BY weight DESC
|
|
629
|
-
) AS out_rn,
|
|
630
|
-
ROW_NUMBER() OVER (
|
|
631
|
-
PARTITION BY target_id ORDER BY weight DESC
|
|
632
|
-
) AS in_rn
|
|
633
|
-
FROM graph_edges
|
|
634
|
-
WHERE profile_id = ?
|
|
635
|
-
) WHERE out_rn > ? OR in_rn > ?
|
|
636
|
-
""",
|
|
637
|
-
(profile_id, _MAX_DEGREE_PER_NODE, _MAX_DEGREE_PER_NODE),
|
|
638
|
-
)
|
|
639
|
-
counts["hub_edges_removed"] = c.fetchone()["cnt"]
|
|
640
|
-
|
|
641
|
-
conn.close()
|
|
642
|
-
except Exception as exc:
|
|
643
|
-
logger.warning("dry-run counts failed: %s", exc)
|
|
644
|
-
return counts
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
def _remove_orphan_association_edges(
|
|
648
|
-
c: sqlite3.Cursor,
|
|
649
|
-
profile_id: str,
|
|
650
|
-
dry_run: bool,
|
|
651
|
-
) -> int:
|
|
652
|
-
"""gi-04: remove association_edges whose source/target fact no longer
|
|
653
|
-
exists in atomic_facts.
|
|
654
|
-
|
|
655
|
-
prune_graph historically only touched graph_edges despite its docstring
|
|
656
|
-
claiming "all graph pruning". Hard-deleted facts leave orphaned
|
|
657
|
-
association_edges (the FK cascade only fires under FK-on connections),
|
|
658
|
-
which spreading_activation then has to scan. Returns rows removed.
|
|
659
|
-
"""
|
|
660
|
-
where = (
|
|
661
|
-
"profile_id = ? AND ("
|
|
662
|
-
"source_fact_id NOT IN (SELECT fact_id FROM atomic_facts WHERE profile_id = ?) "
|
|
663
|
-
"OR target_fact_id NOT IN (SELECT fact_id FROM atomic_facts WHERE profile_id = ?))"
|
|
664
|
-
)
|
|
665
|
-
c.execute(f"SELECT COUNT(*) AS cnt FROM association_edges WHERE {where}",
|
|
666
|
-
(profile_id, profile_id, profile_id))
|
|
667
|
-
n = c.fetchone()["cnt"]
|
|
668
|
-
if dry_run or not n:
|
|
669
|
-
return n
|
|
670
|
-
c.execute(f"DELETE FROM association_edges WHERE {where}",
|
|
671
|
-
(profile_id, profile_id, profile_id))
|
|
672
|
-
return c.rowcount
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
def _remove_orphan_edges(
|
|
676
|
-
c: sqlite3.Cursor,
|
|
677
|
-
profile_id: str,
|
|
678
|
-
dry_run: bool,
|
|
679
|
-
) -> int:
|
|
680
|
-
"""Remove edges where source OR target no longer exists in facts/entities.
|
|
681
|
-
|
|
682
|
-
Scoped to profile_id. Checks both source_id and target_id.
|
|
683
|
-
"""
|
|
684
|
-
orphan_sql = """
|
|
685
|
-
SELECT COUNT(*) as cnt FROM graph_edges
|
|
686
|
-
WHERE profile_id = ?
|
|
687
|
-
AND (
|
|
688
|
-
(source_id NOT IN (SELECT fact_id FROM atomic_facts)
|
|
689
|
-
AND source_id NOT IN (SELECT entity_id FROM canonical_entities))
|
|
690
|
-
OR
|
|
691
|
-
(target_id NOT IN (SELECT fact_id FROM atomic_facts)
|
|
692
|
-
AND target_id NOT IN (SELECT entity_id FROM canonical_entities))
|
|
693
|
-
)
|
|
694
|
-
"""
|
|
695
|
-
|
|
696
|
-
if dry_run:
|
|
697
|
-
c.execute(orphan_sql, (profile_id,))
|
|
698
|
-
return c.fetchone()["cnt"]
|
|
699
|
-
|
|
700
|
-
c.execute("""
|
|
701
|
-
DELETE FROM graph_edges
|
|
702
|
-
WHERE profile_id = ?
|
|
703
|
-
AND (
|
|
704
|
-
(source_id NOT IN (SELECT fact_id FROM atomic_facts)
|
|
705
|
-
AND source_id NOT IN (SELECT entity_id FROM canonical_entities))
|
|
706
|
-
OR
|
|
707
|
-
(target_id NOT IN (SELECT fact_id FROM atomic_facts)
|
|
708
|
-
AND target_id NOT IN (SELECT entity_id FROM canonical_entities))
|
|
709
|
-
)
|
|
710
|
-
""", (profile_id,))
|
|
711
|
-
return c.rowcount
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
def _remove_self_loops(
|
|
715
|
-
c: sqlite3.Cursor,
|
|
716
|
-
profile_id: str,
|
|
717
|
-
dry_run: bool,
|
|
718
|
-
) -> int:
|
|
719
|
-
"""Remove edges where source equals target. Scoped to profile_id."""
|
|
720
|
-
if dry_run:
|
|
721
|
-
c.execute(
|
|
722
|
-
"SELECT COUNT(*) as cnt FROM graph_edges "
|
|
723
|
-
"WHERE source_id = target_id AND profile_id = ?",
|
|
724
|
-
(profile_id,),
|
|
725
|
-
)
|
|
726
|
-
return c.fetchone()["cnt"]
|
|
727
|
-
|
|
728
|
-
c.execute(
|
|
729
|
-
"DELETE FROM graph_edges WHERE source_id = target_id AND profile_id = ?",
|
|
730
|
-
(profile_id,),
|
|
731
|
-
)
|
|
732
|
-
return c.rowcount
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
def _remove_duplicate_edges(
|
|
736
|
-
c: sqlite3.Cursor,
|
|
737
|
-
profile_id: str,
|
|
738
|
-
dry_run: bool,
|
|
739
|
-
) -> int:
|
|
740
|
-
"""Remove duplicate edges (same source+target+type), keeping highest weight.
|
|
741
|
-
|
|
742
|
-
Uses correlated subquery for SQLite 3.22+ compatibility (no window functions).
|
|
743
|
-
"""
|
|
744
|
-
if dry_run:
|
|
745
|
-
# Count actual edges to be deleted (total - groups = excess edges)
|
|
746
|
-
c.execute("""
|
|
747
|
-
SELECT
|
|
748
|
-
(SELECT COUNT(*) FROM graph_edges WHERE profile_id = ?) -
|
|
749
|
-
(SELECT COUNT(*) FROM (
|
|
750
|
-
SELECT source_id, target_id, edge_type
|
|
751
|
-
FROM graph_edges WHERE profile_id = ?
|
|
752
|
-
GROUP BY source_id, target_id, edge_type
|
|
753
|
-
)) as cnt
|
|
754
|
-
""", (profile_id, profile_id))
|
|
755
|
-
return max(c.fetchone()["cnt"], 0)
|
|
756
|
-
|
|
757
|
-
# Keep the edge with highest weight per (source, target, type).
|
|
758
|
-
# Portable: no ROW_NUMBER() OVER, works on SQLite 3.22+.
|
|
759
|
-
c.execute("""
|
|
760
|
-
DELETE FROM graph_edges
|
|
761
|
-
WHERE profile_id = ?
|
|
762
|
-
AND edge_id NOT IN (
|
|
763
|
-
SELECT edge_id FROM graph_edges ge1
|
|
764
|
-
WHERE profile_id = ?
|
|
765
|
-
AND weight = (
|
|
766
|
-
SELECT MAX(weight) FROM graph_edges ge2
|
|
767
|
-
WHERE ge2.source_id = ge1.source_id
|
|
768
|
-
AND ge2.target_id = ge1.target_id
|
|
769
|
-
AND ge2.edge_type = ge1.edge_type
|
|
770
|
-
AND ge2.profile_id = ge1.profile_id
|
|
771
|
-
)
|
|
772
|
-
GROUP BY source_id, target_id, edge_type
|
|
773
|
-
)
|
|
774
|
-
""", (profile_id, profile_id))
|
|
775
|
-
return c.rowcount
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
def _collapse_supersedes_chains(
|
|
779
|
-
c: sqlite3.Cursor,
|
|
780
|
-
profile_id: str,
|
|
781
|
-
dry_run: bool,
|
|
782
|
-
) -> int:
|
|
783
|
-
"""Collapse supersedes chains: if A supersedes B and B supersedes C,
|
|
784
|
-
remove B→C edge AND create A→C shortcut edge.
|
|
785
|
-
|
|
786
|
-
Preserves reachability: A can still reach C via the new direct edge.
|
|
787
|
-
"""
|
|
788
|
-
c.execute("""
|
|
789
|
-
SELECT e1.edge_id as e1_id, e1.source_id as a, e1.target_id as b,
|
|
790
|
-
e1.weight as e1_weight,
|
|
791
|
-
e2.edge_id as e2_id, e2.target_id as c
|
|
792
|
-
FROM graph_edges e1
|
|
793
|
-
JOIN graph_edges e2 ON e1.target_id = e2.source_id
|
|
794
|
-
WHERE e1.edge_type = 'supersedes'
|
|
795
|
-
AND e2.edge_type = 'supersedes'
|
|
796
|
-
AND e1.profile_id = ?
|
|
797
|
-
AND e2.profile_id = ?
|
|
798
|
-
LIMIT ?
|
|
799
|
-
""", (profile_id, profile_id, _CHAIN_BATCH_LIMIT))
|
|
800
|
-
|
|
801
|
-
chains = c.fetchall()
|
|
802
|
-
if not chains:
|
|
803
|
-
return 0
|
|
804
|
-
|
|
805
|
-
if len(chains) >= _CHAIN_BATCH_LIMIT:
|
|
806
|
-
logger.warning(
|
|
807
|
-
"Supersedes chain collapse hit limit (%d). "
|
|
808
|
-
"More chains may exist — will process in next cycle.",
|
|
809
|
-
_CHAIN_BATCH_LIMIT,
|
|
810
|
-
)
|
|
811
|
-
|
|
812
|
-
if dry_run:
|
|
813
|
-
return len(chains)
|
|
814
|
-
|
|
815
|
-
# Collect IDs for batch operations
|
|
816
|
-
delete_ids: list[str] = []
|
|
817
|
-
insert_rows: list[tuple] = []
|
|
818
|
-
now = datetime.now(UTC).isoformat()
|
|
819
|
-
|
|
820
|
-
for chain in chains:
|
|
821
|
-
a_id = chain["a"]
|
|
822
|
-
c_id = chain["c"]
|
|
823
|
-
e2_id = chain["e2_id"]
|
|
824
|
-
weight = chain["e1_weight"] or 1.0
|
|
825
|
-
|
|
826
|
-
delete_ids.append(e2_id)
|
|
827
|
-
|
|
828
|
-
# Create A→C shortcut edge (preserves reachability)
|
|
829
|
-
new_edge_id = uuid.uuid4().hex[:16]
|
|
830
|
-
insert_rows.append((
|
|
831
|
-
new_edge_id, profile_id, a_id, c_id,
|
|
832
|
-
"supersedes", weight, now,
|
|
833
|
-
))
|
|
834
|
-
|
|
835
|
-
# Batch DELETE: remove all B→C intermediate edges
|
|
836
|
-
for i in range(0, len(delete_ids), 500):
|
|
837
|
-
batch = delete_ids[i:i + 500]
|
|
838
|
-
placeholders = ",".join("?" * len(batch))
|
|
839
|
-
c.execute(
|
|
840
|
-
f"DELETE FROM graph_edges WHERE edge_id IN ({placeholders})",
|
|
841
|
-
batch,
|
|
842
|
-
)
|
|
843
|
-
|
|
844
|
-
# Batch INSERT: add all A→C shortcut edges
|
|
845
|
-
c.executemany(
|
|
846
|
-
"INSERT OR IGNORE INTO graph_edges "
|
|
847
|
-
"(edge_id, profile_id, source_id, target_id, edge_type, weight, created_at) "
|
|
848
|
-
"VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
849
|
-
insert_rows,
|
|
850
|
-
)
|
|
851
|
-
|
|
852
|
-
return len(delete_ids)
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
def _cap_node_degree(
|
|
856
|
-
c: sqlite3.Cursor,
|
|
857
|
-
profile_id: str,
|
|
858
|
-
max_degree: int,
|
|
859
|
-
dry_run: bool,
|
|
860
|
-
) -> int:
|
|
861
|
-
"""Remove low-weight edges from hub nodes (nodes with degree > max_degree).
|
|
862
|
-
|
|
863
|
-
v3.4.59: At 17K facts, popular entities (SLM, Claude, AgentAssert) caused
|
|
864
|
-
1.4M+ entity edges — avg 121 per node, some nodes with 5000+. SA fan-out
|
|
865
|
-
is capped at 30/node during traversal, so edges beyond max_degree add zero
|
|
866
|
-
recall signal while making every graph query scan millions of rows.
|
|
867
|
-
|
|
868
|
-
Algorithm (single-pass window function — no Python loops):
|
|
869
|
-
1. ROW_NUMBER() OVER (PARTITION BY source_id ORDER BY weight DESC) ranks
|
|
870
|
-
every edge per node in one full table scan.
|
|
871
|
-
2. Excess edge IDs are collected in a reusable temp table.
|
|
872
|
-
3. A single DELETE statement removes them; rowcount is returned.
|
|
873
|
-
4. The temp table is created once and cleared via DELETE (not DROP) —
|
|
874
|
-
DROP TABLE acquires an EXCLUSIVE lock that conflicts with concurrent
|
|
875
|
-
writers, causing "database is locked". Using CREATE...IF NOT EXISTS
|
|
876
|
-
plus DELETE FROM avoids that conflict while preserving rowcount.
|
|
877
|
-
"""
|
|
878
|
-
# gi-03: cap BOTH out-degree (PARTITION BY source_id) AND in-degree
|
|
879
|
-
# (PARTITION BY target_id). Previously only out-degree was capped, so hub
|
|
880
|
-
# nodes accumulated unbounded in-degree (observed up to 1457), inflating
|
|
881
|
-
# entity-channel fan-in cost. An edge is removed if it exceeds max_degree
|
|
882
|
-
# in EITHER direction (low-weight to both its endpoints). Computed in one
|
|
883
|
-
# window-function pass, no Python loops.
|
|
884
|
-
if dry_run:
|
|
885
|
-
c.execute(
|
|
886
|
-
"""
|
|
887
|
-
SELECT COUNT(*) as cnt FROM (
|
|
888
|
-
SELECT edge_id,
|
|
889
|
-
ROW_NUMBER() OVER (PARTITION BY source_id ORDER BY weight DESC) as out_rn,
|
|
890
|
-
ROW_NUMBER() OVER (PARTITION BY target_id ORDER BY weight DESC) as in_rn
|
|
891
|
-
FROM graph_edges
|
|
892
|
-
WHERE profile_id = ?
|
|
893
|
-
) WHERE out_rn > ? OR in_rn > ?
|
|
894
|
-
""",
|
|
895
|
-
(profile_id, max_degree, max_degree),
|
|
896
|
-
)
|
|
897
|
-
excess = c.fetchone()["cnt"]
|
|
898
|
-
logger.info(
|
|
899
|
-
"(dry-run) _cap_node_degree: ~%d edges would be removed (max_degree=%d, in+out)",
|
|
900
|
-
excess, max_degree,
|
|
901
|
-
)
|
|
902
|
-
return excess
|
|
903
|
-
|
|
904
|
-
# CREATE IF NOT EXISTS + DELETE FROM instead of DROP + CREATE.
|
|
905
|
-
# DROP TABLE acquires EXCLUSIVE which conflicts with concurrent writers.
|
|
906
|
-
# CREATE IF NOT EXISTS is idempotent; DELETE FROM clears prior contents.
|
|
907
|
-
c.execute(
|
|
908
|
-
"CREATE TEMP TABLE IF NOT EXISTS _slm_cap_del (edge_id TEXT PRIMARY KEY)"
|
|
909
|
-
)
|
|
910
|
-
c.execute("DELETE FROM _slm_cap_del")
|
|
911
|
-
c.execute(
|
|
912
|
-
"""
|
|
913
|
-
INSERT OR IGNORE INTO _slm_cap_del (edge_id)
|
|
914
|
-
SELECT edge_id FROM (
|
|
915
|
-
SELECT edge_id,
|
|
916
|
-
ROW_NUMBER() OVER (PARTITION BY source_id ORDER BY weight DESC) as out_rn,
|
|
917
|
-
ROW_NUMBER() OVER (PARTITION BY target_id ORDER BY weight DESC) as in_rn
|
|
918
|
-
FROM graph_edges
|
|
919
|
-
WHERE profile_id = ?
|
|
920
|
-
) WHERE out_rn > ? OR in_rn > ?
|
|
921
|
-
""",
|
|
922
|
-
(profile_id, max_degree, max_degree),
|
|
923
|
-
)
|
|
924
|
-
|
|
925
|
-
c.execute(
|
|
926
|
-
"""
|
|
927
|
-
DELETE FROM graph_edges
|
|
928
|
-
WHERE profile_id = ?
|
|
929
|
-
AND edge_id IN (SELECT edge_id FROM _slm_cap_del)
|
|
930
|
-
""",
|
|
931
|
-
(profile_id,),
|
|
932
|
-
)
|
|
933
|
-
deleted = c.rowcount
|
|
934
|
-
|
|
935
|
-
logger.info(
|
|
936
|
-
"_cap_node_degree: deleted %d low-weight edges (max_degree=%d, in+out capped)",
|
|
937
|
-
deleted, max_degree,
|
|
938
|
-
)
|
|
939
|
-
return deleted
|