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,915 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
-
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
-
|
|
4
|
-
"""Staged lifecycle for optional CozoDB and LanceDB projections.
|
|
5
|
-
|
|
6
|
-
SQLite and sqlite-vec remain the canonical store. The Scale Engine is a
|
|
7
|
-
derived projection: it is prepared outside the active paths, checked against
|
|
8
|
-
the canonical store, and only then promoted. This prevents an installed
|
|
9
|
-
optional dependency from mutating an existing user's data root at daemon
|
|
10
|
-
startup.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
import hashlib
|
|
16
|
-
import json
|
|
17
|
-
import os
|
|
18
|
-
import shutil
|
|
19
|
-
import sqlite3
|
|
20
|
-
import uuid
|
|
21
|
-
from datetime import UTC, datetime
|
|
22
|
-
from pathlib import Path
|
|
23
|
-
from typing import Any, Callable
|
|
24
|
-
|
|
25
|
-
from superlocalmemory.storage.logical_edges import count_logical_edges, iter_logical_edges
|
|
26
|
-
from superlocalmemory.storage.sqlite_vectors import (
|
|
27
|
-
CanonicalVectorError,
|
|
28
|
-
count_canonical_vectors,
|
|
29
|
-
iter_canonical_vectors,
|
|
30
|
-
load_sqlite_vec_extension,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class ScaleEngineError(RuntimeError):
|
|
35
|
-
"""A scale projection cannot safely advance to its next lifecycle state."""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _utc_now() -> str:
|
|
39
|
-
return datetime.now(UTC).isoformat()
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class ScaleEngineManager:
|
|
43
|
-
"""Prepare, verify, promote, and roll back derived scale projections.
|
|
44
|
-
|
|
45
|
-
``backend_factory`` is intentionally injectable. It keeps lifecycle tests
|
|
46
|
-
independent from native optional packages while production uses the real
|
|
47
|
-
CozoDB and LanceDB wrappers.
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
MANIFEST_NAME = "scale-engine.json"
|
|
51
|
-
SCHEMA_VERSION = 1
|
|
52
|
-
LIFECYCLE_LOCK = "scale-engine.lifecycle.lock"
|
|
53
|
-
PROMOTION_JOURNAL = "scale-engine.promotion.json"
|
|
54
|
-
|
|
55
|
-
def __init__(
|
|
56
|
-
self,
|
|
57
|
-
config: Any,
|
|
58
|
-
*,
|
|
59
|
-
backend_factory: Callable[[Path, Path], tuple[Any, Any]] | None = None,
|
|
60
|
-
profile_id: str | None = None,
|
|
61
|
-
) -> None:
|
|
62
|
-
self.config = config
|
|
63
|
-
self.data_dir = Path(getattr(config, "data_dir", None) or config.base_dir)
|
|
64
|
-
self.db_path = Path(getattr(config, "db_path", None) or self.data_dir / "memory.db")
|
|
65
|
-
self.profile_id = profile_id or getattr(config, "active_profile", "default")
|
|
66
|
-
self._backend_factory = backend_factory or self._real_backend_factory
|
|
67
|
-
|
|
68
|
-
@property
|
|
69
|
-
def staging_root(self) -> Path:
|
|
70
|
-
return self.data_dir / "scale-staging"
|
|
71
|
-
|
|
72
|
-
@property
|
|
73
|
-
def backup_root(self) -> Path:
|
|
74
|
-
return self.data_dir / "scale-backups"
|
|
75
|
-
|
|
76
|
-
@property
|
|
77
|
-
def active_paths(self) -> tuple[Path, Path]:
|
|
78
|
-
return self.data_dir / "cozo", self.data_dir / "lance"
|
|
79
|
-
|
|
80
|
-
@property
|
|
81
|
-
def lifecycle_lock_path(self) -> Path:
|
|
82
|
-
return self.data_dir / self.LIFECYCLE_LOCK
|
|
83
|
-
|
|
84
|
-
@property
|
|
85
|
-
def promotion_journal_path(self) -> Path:
|
|
86
|
-
return self.data_dir / self.PROMOTION_JOURNAL
|
|
87
|
-
|
|
88
|
-
def status(self) -> dict[str, Any]:
|
|
89
|
-
manifests: list[dict[str, Any]] = []
|
|
90
|
-
if self.staging_root.exists():
|
|
91
|
-
for path in sorted(self.staging_root.glob(f"*/{self.MANIFEST_NAME}")):
|
|
92
|
-
try:
|
|
93
|
-
manifests.append(json.loads(path.read_text()))
|
|
94
|
-
except (OSError, json.JSONDecodeError):
|
|
95
|
-
manifests.append({"stage_id": path.parent.name, "state": "corrupt"})
|
|
96
|
-
backups = (
|
|
97
|
-
sorted(p.name for p in self.backup_root.glob("*") if p.is_dir())
|
|
98
|
-
if self.backup_root.exists()
|
|
99
|
-
else []
|
|
100
|
-
)
|
|
101
|
-
paths_present = {
|
|
102
|
-
"cozo": self.active_paths[0].exists(),
|
|
103
|
-
"lance": self.active_paths[1].exists(),
|
|
104
|
-
}
|
|
105
|
-
state = getattr(self.config, "scale_engine_state", "local_core")
|
|
106
|
-
legacy_projection_candidate = (
|
|
107
|
-
state == "local_core"
|
|
108
|
-
and all(paths_present.values())
|
|
109
|
-
and not backups
|
|
110
|
-
and not self.promotion_journal_path.exists()
|
|
111
|
-
and self._has_legacy_projection_layout()
|
|
112
|
-
)
|
|
113
|
-
runtime = self._runtime_backend_status()
|
|
114
|
-
return {
|
|
115
|
-
"state": state,
|
|
116
|
-
# This command reads persisted state, not the live daemon. Never
|
|
117
|
-
# turn a last-known backend row into a present-tense routing claim.
|
|
118
|
-
"active": {"cozo": False, "lance": False},
|
|
119
|
-
# LOW-2 (3.7.9): after promote the daemon must restart before the
|
|
120
|
-
# backends actually serve; surface that explicitly so `status` isn't
|
|
121
|
-
# mistaken for "promotion failed".
|
|
122
|
-
"daemon_must_restart_to_activate": (
|
|
123
|
-
state == "promoted"
|
|
124
|
-
and not any(v == "active" for v in runtime.values())
|
|
125
|
-
),
|
|
126
|
-
"last_daemon_observation": runtime,
|
|
127
|
-
"paths_present": paths_present,
|
|
128
|
-
"retrieval_routing": (
|
|
129
|
-
"daemon_runtime_check_required" if state == "promoted" else "canonical_sqlite"
|
|
130
|
-
),
|
|
131
|
-
"legacy_projection_candidate": legacy_projection_candidate,
|
|
132
|
-
"legacy_candidate_requires_confirmation": legacy_projection_candidate,
|
|
133
|
-
"migration_repair_required": (
|
|
134
|
-
self.promotion_journal_path.exists()
|
|
135
|
-
or any(
|
|
136
|
-
manifest.get("state") == "promoted" and state != "promoted"
|
|
137
|
-
for manifest in manifests
|
|
138
|
-
)
|
|
139
|
-
or (
|
|
140
|
-
state == "promoted"
|
|
141
|
-
and any(
|
|
142
|
-
manifest.get("state") in {"prepared", "verified"}
|
|
143
|
-
for manifest in manifests
|
|
144
|
-
)
|
|
145
|
-
)
|
|
146
|
-
),
|
|
147
|
-
"stages": manifests,
|
|
148
|
-
"backups": backups,
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
def adopt_legacy_projection(self) -> dict[str, Any] | None:
|
|
152
|
-
"""Safely adopt a v3.5-era projection into the staged lifecycle.
|
|
153
|
-
|
|
154
|
-
A legacy projection proves only that an older runtime created files.
|
|
155
|
-
It cannot establish parity with today's canonical SQLite database.
|
|
156
|
-
Rebuild a fresh stage, verify it while the canonical database is
|
|
157
|
-
stable, and promote it atomically; the legacy directories become the
|
|
158
|
-
explicit rollback copy.
|
|
159
|
-
"""
|
|
160
|
-
if not self.status()["legacy_projection_candidate"]:
|
|
161
|
-
return None
|
|
162
|
-
lock_path = self._acquire_lifecycle_lock()
|
|
163
|
-
prepared: dict[str, Any] | None = None
|
|
164
|
-
try:
|
|
165
|
-
# Re-check inside the lock. A concurrent command may have
|
|
166
|
-
# completed promotion while this caller waited to acquire it.
|
|
167
|
-
current_status = self.status()
|
|
168
|
-
if not current_status["legacy_projection_candidate"]:
|
|
169
|
-
return None
|
|
170
|
-
retry_payloads = [
|
|
171
|
-
manifest["stage_id"]
|
|
172
|
-
for manifest in current_status["stages"]
|
|
173
|
-
if manifest.get("state") in {"prepared", "verified"}
|
|
174
|
-
and manifest.get("stage_id")
|
|
175
|
-
]
|
|
176
|
-
prepared = self._prepare()
|
|
177
|
-
self._verify(prepared["stage_id"])
|
|
178
|
-
promoted = self._promote(prepared["stage_id"])
|
|
179
|
-
retired: list[str] = []
|
|
180
|
-
retirement_failures: dict[str, str] = {}
|
|
181
|
-
for stage_id in retry_payloads:
|
|
182
|
-
try:
|
|
183
|
-
self._retire_superseded_stage(stage_id)
|
|
184
|
-
retired.append(stage_id)
|
|
185
|
-
except Exception as cleanup_exc:
|
|
186
|
-
retirement_failures[stage_id] = str(cleanup_exc)
|
|
187
|
-
return {
|
|
188
|
-
**promoted,
|
|
189
|
-
"retired_stages": retired,
|
|
190
|
-
"retirement_failures": retirement_failures,
|
|
191
|
-
}
|
|
192
|
-
except Exception as exc:
|
|
193
|
-
# A failed adoption must leave the canonical path selected. The
|
|
194
|
-
# manifest is retained for inspection, but its replaceable Cozo
|
|
195
|
-
# and Lance payloads are retired so repeated retries cannot grow
|
|
196
|
-
# the data root without bound.
|
|
197
|
-
retirement_error: Exception | None = None
|
|
198
|
-
unresolved_promotion = self.promotion_journal_path.exists()
|
|
199
|
-
if prepared is not None and not unresolved_promotion:
|
|
200
|
-
try:
|
|
201
|
-
self._retire_rejected_stage(prepared["stage_id"], exc)
|
|
202
|
-
except Exception as cleanup_exc:
|
|
203
|
-
retirement_error = cleanup_exc
|
|
204
|
-
self.config.scale_engine_state = (
|
|
205
|
-
"verified" if unresolved_promotion else "local_core"
|
|
206
|
-
)
|
|
207
|
-
self.config.graph_backend = "auto"
|
|
208
|
-
self.config.vector_backend = "auto"
|
|
209
|
-
self._save_config()
|
|
210
|
-
if retirement_error is not None:
|
|
211
|
-
raise ScaleEngineError(
|
|
212
|
-
f"{exc}; rejected stage retirement failed: {retirement_error}"
|
|
213
|
-
) from exc
|
|
214
|
-
raise
|
|
215
|
-
finally:
|
|
216
|
-
self._release_lifecycle_lock(lock_path)
|
|
217
|
-
|
|
218
|
-
def _retire_rejected_stage(self, stage_id: str, error: Exception) -> None:
|
|
219
|
-
"""Keep rejection evidence while removing replaceable projection data."""
|
|
220
|
-
self._retire_stage_payload(
|
|
221
|
-
stage_id,
|
|
222
|
-
{
|
|
223
|
-
"state": "rejected",
|
|
224
|
-
"rejected_at": _utc_now(),
|
|
225
|
-
"failure": f"{type(error).__name__}: {error}",
|
|
226
|
-
},
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
def _retire_superseded_stage(self, stage_id: str) -> None:
|
|
230
|
-
"""Retain an old retry manifest after a newer projection is promoted."""
|
|
231
|
-
self._retire_stage_payload(
|
|
232
|
-
stage_id,
|
|
233
|
-
{"state": "superseded", "superseded_at": _utc_now()},
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
def _retire_stage_payload(
|
|
237
|
-
self, stage_id: str, manifest_updates: dict[str, Any]
|
|
238
|
-
) -> None:
|
|
239
|
-
"""Remove derived stage bytes while retaining its durable manifest."""
|
|
240
|
-
stage_dir, manifest = self._load_stage(stage_id)
|
|
241
|
-
for payload in (stage_dir / "cozo", stage_dir / "lance"):
|
|
242
|
-
if payload.exists():
|
|
243
|
-
shutil.rmtree(payload)
|
|
244
|
-
self._fsync_directory(stage_dir)
|
|
245
|
-
manifest.update(manifest_updates)
|
|
246
|
-
self._write_manifest(stage_dir, manifest)
|
|
247
|
-
|
|
248
|
-
def prepare(self) -> dict[str, Any]:
|
|
249
|
-
"""Build a new projection in a private staging directory."""
|
|
250
|
-
lock_path = self._acquire_lifecycle_lock()
|
|
251
|
-
try:
|
|
252
|
-
self._recover_interrupted_promotion()
|
|
253
|
-
return self._prepare()
|
|
254
|
-
finally:
|
|
255
|
-
self._release_lifecycle_lock(lock_path)
|
|
256
|
-
|
|
257
|
-
def _prepare(self) -> dict[str, Any]:
|
|
258
|
-
"""Build a new projection while the caller owns the lifecycle lock."""
|
|
259
|
-
self._require_default_profile()
|
|
260
|
-
self._require_canonical_db()
|
|
261
|
-
stage_id = f"{datetime.now(UTC).strftime('%Y%m%dT%H%M%SZ')}-{uuid.uuid4().hex[:8]}"
|
|
262
|
-
stage_dir = self.staging_root / stage_id
|
|
263
|
-
cozo_dir, lance_dir = stage_dir / "cozo", stage_dir / "lance"
|
|
264
|
-
self._mkdir_durable(stage_dir, exist_ok=False)
|
|
265
|
-
cozo = lance = None
|
|
266
|
-
try:
|
|
267
|
-
cozo, lance = self._backend_factory(cozo_dir, lance_dir)
|
|
268
|
-
with self._readonly_connection() as conn:
|
|
269
|
-
cozo.bulk_import_from_sqlite(conn, self.profile_id)
|
|
270
|
-
lance.bulk_import_from_sqlite(conn, self.profile_id)
|
|
271
|
-
canonical = self._canonical_counts(conn)
|
|
272
|
-
source_fingerprint = self._projection_fingerprint(conn, canonical)
|
|
273
|
-
observed = self._observed_counts(cozo, lance)
|
|
274
|
-
manifest = {
|
|
275
|
-
"schema_version": self.SCHEMA_VERSION,
|
|
276
|
-
"stage_id": stage_id,
|
|
277
|
-
"state": "prepared",
|
|
278
|
-
"created_at": _utc_now(),
|
|
279
|
-
"profile_id": self.profile_id,
|
|
280
|
-
"canonical": canonical,
|
|
281
|
-
"observed": observed,
|
|
282
|
-
"source_fingerprint": source_fingerprint,
|
|
283
|
-
}
|
|
284
|
-
self._write_manifest(stage_dir, manifest)
|
|
285
|
-
self.config.scale_engine_state = "prepared"
|
|
286
|
-
self._save_config()
|
|
287
|
-
return manifest
|
|
288
|
-
except CanonicalVectorError as exc:
|
|
289
|
-
shutil.rmtree(stage_dir, ignore_errors=True)
|
|
290
|
-
raise ScaleEngineError(f"canonical vector projection failed: {exc}") from exc
|
|
291
|
-
except Exception:
|
|
292
|
-
shutil.rmtree(stage_dir, ignore_errors=True)
|
|
293
|
-
raise
|
|
294
|
-
finally:
|
|
295
|
-
self._close(cozo)
|
|
296
|
-
self._close(lance)
|
|
297
|
-
|
|
298
|
-
def verify(self, stage_id: str) -> dict[str, Any]:
|
|
299
|
-
"""Prove a staged projection matches the current canonical SQLite data."""
|
|
300
|
-
lock_path = self._acquire_lifecycle_lock()
|
|
301
|
-
try:
|
|
302
|
-
self._recover_interrupted_promotion()
|
|
303
|
-
return self._verify(stage_id)
|
|
304
|
-
except CanonicalVectorError as exc:
|
|
305
|
-
raise ScaleEngineError(f"canonical vector projection failed: {exc}") from exc
|
|
306
|
-
finally:
|
|
307
|
-
self._release_lifecycle_lock(lock_path)
|
|
308
|
-
|
|
309
|
-
def _verify(self, stage_id: str) -> dict[str, Any]:
|
|
310
|
-
"""Verify while the caller owns the lifecycle lock."""
|
|
311
|
-
stage_dir, manifest = self._load_stage(stage_id)
|
|
312
|
-
self._validate_manifest(manifest, state="prepared")
|
|
313
|
-
self._require_default_profile()
|
|
314
|
-
cozo = lance = None
|
|
315
|
-
try:
|
|
316
|
-
cozo, lance = self._backend_factory(stage_dir / "cozo", stage_dir / "lance")
|
|
317
|
-
with self._readonly_connection() as conn:
|
|
318
|
-
canonical = self._canonical_counts(conn)
|
|
319
|
-
source_fingerprint = self._projection_fingerprint(conn, canonical)
|
|
320
|
-
observed = self._observed_counts(cozo, lance)
|
|
321
|
-
if manifest["source_fingerprint"] != source_fingerprint:
|
|
322
|
-
raise ScaleEngineError(
|
|
323
|
-
"canonical SQLite changed after preparation; prepare a new stage"
|
|
324
|
-
)
|
|
325
|
-
if canonical != manifest["canonical"] or observed != canonical:
|
|
326
|
-
raise ScaleEngineError(
|
|
327
|
-
f"projection parity failed: canonical={canonical}, observed={observed}"
|
|
328
|
-
)
|
|
329
|
-
with self._readonly_connection() as conn:
|
|
330
|
-
self._verify_content_sample(conn, cozo, canonical)
|
|
331
|
-
manifest.update({"state": "verified", "verified_at": _utc_now(), "observed": observed})
|
|
332
|
-
self._write_manifest(stage_dir, manifest)
|
|
333
|
-
self.config.scale_engine_state = "verified"
|
|
334
|
-
self._save_config()
|
|
335
|
-
return manifest
|
|
336
|
-
finally:
|
|
337
|
-
self._close(cozo)
|
|
338
|
-
self._close(lance)
|
|
339
|
-
|
|
340
|
-
def promote(self, stage_id: str) -> dict[str, Any]:
|
|
341
|
-
"""Move a verified stage into active paths, preserving a rollback copy."""
|
|
342
|
-
lock_path = self._acquire_lifecycle_lock()
|
|
343
|
-
try:
|
|
344
|
-
self._recover_interrupted_promotion()
|
|
345
|
-
return self._promote(stage_id)
|
|
346
|
-
except CanonicalVectorError as exc:
|
|
347
|
-
raise ScaleEngineError(f"canonical vector projection failed: {exc}") from exc
|
|
348
|
-
finally:
|
|
349
|
-
self._release_lifecycle_lock(lock_path)
|
|
350
|
-
|
|
351
|
-
def _promote(self, stage_id: str) -> dict[str, Any]:
|
|
352
|
-
"""Promote while the caller owns the lifecycle lock.
|
|
353
|
-
|
|
354
|
-
Concurrency fix (v3.8.4): the original code held a SQLite
|
|
355
|
-
BEGIN IMMEDIATE lock across multiple fsync/rename filesystem operations
|
|
356
|
-
(mkdir_durable, replace_durable, write_promotion_journal) which could
|
|
357
|
-
starve other memory.db writers for 30+ seconds.
|
|
358
|
-
|
|
359
|
-
Fix: the fingerprint check runs inside a short memory_read() block
|
|
360
|
-
(consistent WAL snapshot, no write lock). All filesystem operations
|
|
361
|
-
happen AFTER that block with no SQLite lock held. The lifecycle lock
|
|
362
|
-
(file-based, acquired by the caller) already serialises concurrent
|
|
363
|
-
promote/rollback calls.
|
|
364
|
-
|
|
365
|
-
Lock-ordering invariant preserved: get_write_lock is OUTERMOST; no
|
|
366
|
-
SQLite write lock is held here at all (only a read snapshot).
|
|
367
|
-
"""
|
|
368
|
-
from superlocalmemory.storage.memory_write import memory_read
|
|
369
|
-
|
|
370
|
-
stage_dir, manifest = self._load_stage(stage_id)
|
|
371
|
-
self._validate_manifest(manifest, state="verified")
|
|
372
|
-
staged = (stage_dir / "cozo", stage_dir / "lance")
|
|
373
|
-
if not all(path.exists() for path in staged):
|
|
374
|
-
raise ScaleEngineError("verified stage is incomplete; prepare a new stage")
|
|
375
|
-
backup_dir = self.backup_root / f"{stage_id}-{uuid.uuid4().hex[:6]}"
|
|
376
|
-
active = self.active_paths
|
|
377
|
-
|
|
378
|
-
# ── Phase 1: fingerprint check (short read snapshot, no write lock) ──
|
|
379
|
-
# memory_read() gives a consistent WAL snapshot and closes the
|
|
380
|
-
# connection immediately on exit — no lock held after this block.
|
|
381
|
-
try:
|
|
382
|
-
with memory_read(self.db_path) as gate:
|
|
383
|
-
canonical = self._canonical_counts(gate)
|
|
384
|
-
if manifest["source_fingerprint"] != self._projection_fingerprint(
|
|
385
|
-
gate, canonical
|
|
386
|
-
):
|
|
387
|
-
raise ScaleEngineError(
|
|
388
|
-
"canonical SQLite changed after verification; prepare a new stage"
|
|
389
|
-
)
|
|
390
|
-
except ScaleEngineError:
|
|
391
|
-
raise
|
|
392
|
-
except Exception as exc:
|
|
393
|
-
raise ScaleEngineError(
|
|
394
|
-
f"fingerprint check failed: {exc}"
|
|
395
|
-
) from exc
|
|
396
|
-
|
|
397
|
-
# ── Phase 2: filesystem operations — NO SQLite lock held ─────────────
|
|
398
|
-
# The lifecycle lock (file-based, held by the caller) serialises
|
|
399
|
-
# concurrent promotions; no SQLite fence is required for the swaps.
|
|
400
|
-
try:
|
|
401
|
-
self._mkdir_durable(self.backup_root)
|
|
402
|
-
journal = {
|
|
403
|
-
"schema_version": self.SCHEMA_VERSION,
|
|
404
|
-
"operation": "promotion",
|
|
405
|
-
"state": "intent",
|
|
406
|
-
"stage_id": stage_id,
|
|
407
|
-
"backup_id": backup_dir.name,
|
|
408
|
-
"moves": [],
|
|
409
|
-
}
|
|
410
|
-
self._write_promotion_journal(journal)
|
|
411
|
-
self._mkdir_durable(backup_dir, exist_ok=False)
|
|
412
|
-
for name, source, destination in zip(("cozo", "lance"), active, staged):
|
|
413
|
-
if source.exists():
|
|
414
|
-
target = backup_dir / name
|
|
415
|
-
move = {"name": name, "kind": "active_to_backup", "state": "intent"}
|
|
416
|
-
journal["moves"].append(move)
|
|
417
|
-
self._write_promotion_journal(journal)
|
|
418
|
-
self._replace_durable(source, target)
|
|
419
|
-
move["state"] = "complete"
|
|
420
|
-
self._write_promotion_journal(journal)
|
|
421
|
-
move = {"name": name, "kind": "stage_to_active", "state": "intent"}
|
|
422
|
-
journal["moves"].append(move)
|
|
423
|
-
self._write_promotion_journal(journal)
|
|
424
|
-
self._replace_durable(destination, source)
|
|
425
|
-
move["state"] = "complete"
|
|
426
|
-
self._write_promotion_journal(journal)
|
|
427
|
-
manifest.update(
|
|
428
|
-
{
|
|
429
|
-
"state": "promoted",
|
|
430
|
-
"promoted_at": _utc_now(),
|
|
431
|
-
"backup_id": backup_dir.name,
|
|
432
|
-
}
|
|
433
|
-
)
|
|
434
|
-
self._write_manifest(stage_dir, manifest)
|
|
435
|
-
self.config.scale_engine_state = "promoted"
|
|
436
|
-
self.config.graph_backend = "cozo"
|
|
437
|
-
self.config.vector_backend = "lancedb"
|
|
438
|
-
self._save_config()
|
|
439
|
-
journal["state"] = "committed"
|
|
440
|
-
self._write_promotion_journal(journal)
|
|
441
|
-
self.promotion_journal_path.unlink(missing_ok=True)
|
|
442
|
-
return manifest
|
|
443
|
-
except Exception as exc:
|
|
444
|
-
try:
|
|
445
|
-
recovery = self._recover_interrupted_promotion()
|
|
446
|
-
except Exception as recovery_error:
|
|
447
|
-
raise ScaleEngineError(
|
|
448
|
-
f"promotion interrupted; automatic recovery needs repair: {recovery_error}"
|
|
449
|
-
) from exc
|
|
450
|
-
if recovery == "finalized_committed_promotion":
|
|
451
|
-
_, recovered_manifest = self._load_stage(stage_id)
|
|
452
|
-
return recovered_manifest
|
|
453
|
-
raise ScaleEngineError(f"promotion rolled back: {exc}") from exc
|
|
454
|
-
|
|
455
|
-
def rollback(self, backup_id: str) -> dict[str, Any]:
|
|
456
|
-
"""Restore an explicitly named pre-promotion backup."""
|
|
457
|
-
lock_path = self._acquire_lifecycle_lock()
|
|
458
|
-
try:
|
|
459
|
-
self._recover_interrupted_promotion()
|
|
460
|
-
return self._rollback(backup_id)
|
|
461
|
-
finally:
|
|
462
|
-
self._release_lifecycle_lock(lock_path)
|
|
463
|
-
|
|
464
|
-
def _rollback(self, backup_id: str) -> dict[str, Any]:
|
|
465
|
-
"""Roll back while the caller owns the lifecycle lock."""
|
|
466
|
-
backup_dir = self.backup_root / backup_id
|
|
467
|
-
if not backup_dir.is_dir():
|
|
468
|
-
raise ScaleEngineError(f"backup does not exist: {backup_id}")
|
|
469
|
-
backup_paths = (backup_dir / "cozo", backup_dir / "lance")
|
|
470
|
-
active = self.active_paths
|
|
471
|
-
displaced = self.backup_root / f"rollback-displaced-{uuid.uuid4().hex[:8]}"
|
|
472
|
-
try:
|
|
473
|
-
journal = {
|
|
474
|
-
"schema_version": self.SCHEMA_VERSION,
|
|
475
|
-
"operation": "rollback",
|
|
476
|
-
"state": "intent",
|
|
477
|
-
"backup_id": backup_id,
|
|
478
|
-
"displaced_id": displaced.name,
|
|
479
|
-
"moves": [],
|
|
480
|
-
}
|
|
481
|
-
self._write_promotion_journal(journal)
|
|
482
|
-
for name, source, target in zip(("cozo", "lance"), active, backup_paths):
|
|
483
|
-
if source.exists():
|
|
484
|
-
self._mkdir_durable(displaced)
|
|
485
|
-
move = {"name": name, "kind": "active_to_displaced", "state": "intent"}
|
|
486
|
-
journal["moves"].append(move)
|
|
487
|
-
self._write_promotion_journal(journal)
|
|
488
|
-
self._replace_durable(source, displaced / name)
|
|
489
|
-
move["state"] = "complete"
|
|
490
|
-
self._write_promotion_journal(journal)
|
|
491
|
-
if target.exists():
|
|
492
|
-
move = {"name": name, "kind": "backup_to_active", "state": "intent"}
|
|
493
|
-
journal["moves"].append(move)
|
|
494
|
-
self._write_promotion_journal(journal)
|
|
495
|
-
self._replace_durable(target, source)
|
|
496
|
-
move["state"] = "complete"
|
|
497
|
-
self._write_promotion_journal(journal)
|
|
498
|
-
self.config.scale_engine_state = "local_core"
|
|
499
|
-
self.config.graph_backend = "auto"
|
|
500
|
-
self.config.vector_backend = "auto"
|
|
501
|
-
self._save_config()
|
|
502
|
-
journal["state"] = "committed"
|
|
503
|
-
self._write_promotion_journal(journal)
|
|
504
|
-
self.promotion_journal_path.unlink(missing_ok=True)
|
|
505
|
-
return {
|
|
506
|
-
"state": "local_core",
|
|
507
|
-
"restored_backup": backup_id,
|
|
508
|
-
"displaced": displaced.name,
|
|
509
|
-
}
|
|
510
|
-
except Exception as exc:
|
|
511
|
-
try:
|
|
512
|
-
self._recover_interrupted_promotion()
|
|
513
|
-
except ScaleEngineError as recovery_error:
|
|
514
|
-
raise ScaleEngineError(
|
|
515
|
-
f"rollback interrupted; automatic recovery needs repair: {recovery_error}"
|
|
516
|
-
) from exc
|
|
517
|
-
raise ScaleEngineError(f"rollback recovered: {exc}") from exc
|
|
518
|
-
|
|
519
|
-
def _real_backend_factory(self, cozo_dir: Path, lance_dir: Path) -> tuple[Any, Any]:
|
|
520
|
-
from superlocalmemory.graph.cozo_backend import CozoDBGraphBackend
|
|
521
|
-
from superlocalmemory.vector.lancedb_backend import LanceDBVectorBackend
|
|
522
|
-
# v3.7.6 (#72): promote the configured embedding width into the new
|
|
523
|
-
# LanceDB store so a 1024d (or other) custom endpoint survives promotion.
|
|
524
|
-
dimension = getattr(
|
|
525
|
-
getattr(self.config, "embedding", None), "dimension", None
|
|
526
|
-
)
|
|
527
|
-
return (
|
|
528
|
-
CozoDBGraphBackend(str(cozo_dir / "graph")),
|
|
529
|
-
LanceDBVectorBackend(str(lance_dir), dimension=dimension),
|
|
530
|
-
)
|
|
531
|
-
|
|
532
|
-
def _readonly_connection(self) -> sqlite3.Connection:
|
|
533
|
-
conn = sqlite3.connect(f"file:{self.db_path}?mode=ro", uri=True)
|
|
534
|
-
conn.execute("PRAGMA query_only=ON")
|
|
535
|
-
load_sqlite_vec_extension(conn)
|
|
536
|
-
return conn
|
|
537
|
-
|
|
538
|
-
def _canonical_counts(self, conn: sqlite3.Connection) -> dict[str, int]:
|
|
539
|
-
nodes = conn.execute(
|
|
540
|
-
"SELECT COUNT(*) FROM canonical_entities WHERE profile_id=?",
|
|
541
|
-
(self.profile_id,),
|
|
542
|
-
).fetchone()[0]
|
|
543
|
-
edges = count_logical_edges(conn, self.profile_id)
|
|
544
|
-
vectors = count_canonical_vectors(conn, self.profile_id)
|
|
545
|
-
fact_entity = self._count_fact_entity_links(conn)
|
|
546
|
-
return {
|
|
547
|
-
"entities": int(nodes),
|
|
548
|
-
"edges": int(edges),
|
|
549
|
-
"vectors": int(vectors),
|
|
550
|
-
"fact_entity": int(fact_entity),
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
def _count_fact_entity_links(self, conn: sqlite3.Connection) -> int:
|
|
554
|
-
"""Count fact->entity bridge rows exactly as ``bulk_import_from_sqlite``
|
|
555
|
-
projects them: dedup entity IDs per fact, drop empties. This bridge is
|
|
556
|
-
what lets Cozo map a query seed into the fact graph; if its import
|
|
557
|
-
silently fails, count parity on entities/edges/vectors still passes but
|
|
558
|
-
entity recall returns empty — so it must be verified explicitly.
|
|
559
|
-
|
|
560
|
-
Withheld and archived facts are excluded, because the import excludes
|
|
561
|
-
them. On this store that is not a rounding difference: 1,291 withheld
|
|
562
|
-
facts hold 379,591 of the 395,524 bridge rows — about 294 entity
|
|
563
|
-
references each, against roughly 4 for an ordinary memory. That is the
|
|
564
|
-
"pooled entity list" the retrieval channel refuses to let into its entity
|
|
565
|
-
map, and it is why a withheld row out-ranks a real one. Counting them
|
|
566
|
-
here made the parity check demand a projection the import must not build,
|
|
567
|
-
and promotion failed on it."""
|
|
568
|
-
from superlocalmemory.storage.database import (
|
|
569
|
-
visible_fact_clause_for_connection,
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
total = 0
|
|
573
|
-
for (raw,) in conn.execute(
|
|
574
|
-
"SELECT canonical_entities_json FROM atomic_facts WHERE profile_id=?"
|
|
575
|
-
+ visible_fact_clause_for_connection(conn),
|
|
576
|
-
(self.profile_id,),
|
|
577
|
-
):
|
|
578
|
-
try:
|
|
579
|
-
entity_ids = json.loads(raw or "[]")
|
|
580
|
-
except (TypeError, ValueError, json.JSONDecodeError):
|
|
581
|
-
continue
|
|
582
|
-
total += sum(1 for eid in dict.fromkeys(entity_ids) if eid)
|
|
583
|
-
return total
|
|
584
|
-
|
|
585
|
-
def _observed_counts(self, cozo: Any, lance: Any) -> dict[str, int]:
|
|
586
|
-
graph = cozo.health_check()
|
|
587
|
-
vector = lance.health_check()
|
|
588
|
-
if graph.get("status") != "active" or vector.get("status") != "active":
|
|
589
|
-
raise ScaleEngineError(f"projection health failed: cozo={graph}, lancedb={vector}")
|
|
590
|
-
return {
|
|
591
|
-
"entities": int(graph["entities"]),
|
|
592
|
-
"edges": int(graph["edges"]),
|
|
593
|
-
"vectors": int(vector["vectors"]),
|
|
594
|
-
"fact_entity": int(graph.get("fact_entity", 0)),
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
def _verify_content_sample(
|
|
598
|
-
self, conn: sqlite3.Connection, cozo: Any, canonical: dict[str, int]
|
|
599
|
-
) -> None:
|
|
600
|
-
"""Content parity: count equality does not prove the import reproduced
|
|
601
|
-
the correct rows. Compare the projected entity-ID set against canonical
|
|
602
|
-
SQLite (bounded), catching a projection that has the right entity count
|
|
603
|
-
but the wrong identities."""
|
|
604
|
-
getter = getattr(cozo, "entity_ids", None)
|
|
605
|
-
n = int(canonical.get("entities", 0))
|
|
606
|
-
if not callable(getter) or n == 0:
|
|
607
|
-
return # backend cannot sample, or nothing to compare
|
|
608
|
-
observed_ids = set(getter(limit=n))
|
|
609
|
-
rows = conn.execute(
|
|
610
|
-
"SELECT entity_id FROM canonical_entities WHERE profile_id=?",
|
|
611
|
-
(self.profile_id,),
|
|
612
|
-
).fetchall()
|
|
613
|
-
expected_ids = {r[0] for r in rows}
|
|
614
|
-
if expected_ids != observed_ids:
|
|
615
|
-
missing = sorted(expected_ids - observed_ids)[:5]
|
|
616
|
-
extra = sorted(observed_ids - expected_ids)[:5]
|
|
617
|
-
raise ScaleEngineError(
|
|
618
|
-
"projection content parity failed: entity IDs diverged "
|
|
619
|
-
f"(missing sample={missing}, unexpected sample={extra})"
|
|
620
|
-
)
|
|
621
|
-
|
|
622
|
-
def _projection_fingerprint(
|
|
623
|
-
self, conn: sqlite3.Connection, counts: dict[str, int]
|
|
624
|
-
) -> str:
|
|
625
|
-
"""Hash the exact projection source rows inside one SQLite snapshot."""
|
|
626
|
-
digest = hashlib.sha256()
|
|
627
|
-
digest.update(json.dumps(counts, sort_keys=True).encode())
|
|
628
|
-
tables = (
|
|
629
|
-
(
|
|
630
|
-
"canonical_entities",
|
|
631
|
-
"entity_id, canonical_name, entity_type, first_seen, last_seen, "
|
|
632
|
-
"fact_count, profile_id",
|
|
633
|
-
"entity_id",
|
|
634
|
-
),
|
|
635
|
-
("atomic_facts", "fact_id, canonical_entities_json, lifecycle, profile_id", "fact_id"),
|
|
636
|
-
)
|
|
637
|
-
for table, columns, ordering in tables:
|
|
638
|
-
try:
|
|
639
|
-
rows = conn.execute(
|
|
640
|
-
f"SELECT {columns} FROM {table} WHERE profile_id=? ORDER BY {ordering}",
|
|
641
|
-
(self.profile_id,),
|
|
642
|
-
)
|
|
643
|
-
for row in rows:
|
|
644
|
-
self._digest_row(digest, table, row)
|
|
645
|
-
except sqlite3.OperationalError as exc:
|
|
646
|
-
raise ScaleEngineError(f"canonical SQLite missing required {table} table") from exc
|
|
647
|
-
try:
|
|
648
|
-
for row in iter_logical_edges(conn, self.profile_id):
|
|
649
|
-
self._digest_row(digest, "graph_edges", row)
|
|
650
|
-
except sqlite3.OperationalError as exc:
|
|
651
|
-
raise ScaleEngineError("canonical SQLite missing required graph_edges table") from exc
|
|
652
|
-
for row in iter_canonical_vectors(conn, self.profile_id):
|
|
653
|
-
self._digest_row(digest, "fact_embeddings", row)
|
|
654
|
-
return digest.hexdigest()
|
|
655
|
-
|
|
656
|
-
@staticmethod
|
|
657
|
-
def _digest_row(digest: Any, table: str, row: Any) -> None:
|
|
658
|
-
digest.update(table.encode())
|
|
659
|
-
digest.update(json.dumps(list(row), default=str, separators=(",", ":")).encode())
|
|
660
|
-
|
|
661
|
-
def _has_legacy_projection_layout(self) -> bool:
|
|
662
|
-
cozo, lance = self.active_paths
|
|
663
|
-
return (cozo / "graph").is_dir() and (lance / "embeddings.lance").exists()
|
|
664
|
-
|
|
665
|
-
def _runtime_backend_status(self) -> dict[str, str]:
|
|
666
|
-
status = {"cozo": "unknown", "lance": "unknown"}
|
|
667
|
-
try:
|
|
668
|
-
with self._readonly_connection() as conn:
|
|
669
|
-
rows = conn.execute(
|
|
670
|
-
"SELECT backend_name, status FROM backend_status "
|
|
671
|
-
"WHERE backend_name IN ('cozo', 'lancedb')"
|
|
672
|
-
)
|
|
673
|
-
for name, value in rows:
|
|
674
|
-
if name == "cozo":
|
|
675
|
-
status["cozo"] = str(value)
|
|
676
|
-
elif name == "lancedb":
|
|
677
|
-
status["lance"] = str(value)
|
|
678
|
-
except sqlite3.OperationalError:
|
|
679
|
-
pass
|
|
680
|
-
return status
|
|
681
|
-
|
|
682
|
-
def _load_stage(self, stage_id: str) -> tuple[Path, dict[str, Any]]:
|
|
683
|
-
stage_dir = self.staging_root / stage_id
|
|
684
|
-
try:
|
|
685
|
-
manifest = json.loads((stage_dir / self.MANIFEST_NAME).read_text())
|
|
686
|
-
except (OSError, json.JSONDecodeError) as exc:
|
|
687
|
-
raise ScaleEngineError(f"invalid scale stage: {stage_id}") from exc
|
|
688
|
-
return stage_dir, manifest
|
|
689
|
-
|
|
690
|
-
def _write_manifest(self, stage_dir: Path, manifest: dict[str, Any]) -> None:
|
|
691
|
-
target = stage_dir / self.MANIFEST_NAME
|
|
692
|
-
temporary = target.with_suffix(".tmp")
|
|
693
|
-
temporary.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n")
|
|
694
|
-
os.replace(temporary, target)
|
|
695
|
-
|
|
696
|
-
def _validate_manifest(self, manifest: dict[str, Any], *, state: str) -> None:
|
|
697
|
-
if manifest.get("schema_version") != self.SCHEMA_VERSION or manifest.get("state") != state:
|
|
698
|
-
raise ScaleEngineError(f"stage must be {state}, got {manifest.get('state')!r}")
|
|
699
|
-
|
|
700
|
-
def _require_default_profile(self) -> None:
|
|
701
|
-
if self.profile_id != "default":
|
|
702
|
-
raise ScaleEngineError(
|
|
703
|
-
"Scale Engine promotion currently supports the default profile only"
|
|
704
|
-
)
|
|
705
|
-
|
|
706
|
-
def _require_canonical_db(self) -> None:
|
|
707
|
-
if not self.db_path.exists():
|
|
708
|
-
raise ScaleEngineError(f"canonical SQLite database not found: {self.db_path}")
|
|
709
|
-
|
|
710
|
-
def _acquire_lifecycle_lock(self) -> Path:
|
|
711
|
-
"""Serialize every mutating lifecycle command across processes."""
|
|
712
|
-
lock_path = self.lifecycle_lock_path
|
|
713
|
-
descriptor = None
|
|
714
|
-
for attempt in range(2):
|
|
715
|
-
try:
|
|
716
|
-
descriptor = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
|
|
717
|
-
break
|
|
718
|
-
except FileExistsError as exc:
|
|
719
|
-
if attempt == 0 and self._clear_dead_legacy_adoption_lock(lock_path):
|
|
720
|
-
continue
|
|
721
|
-
raise ScaleEngineError(
|
|
722
|
-
"Scale Engine lifecycle operation already in progress; retry after it completes"
|
|
723
|
-
) from exc
|
|
724
|
-
if descriptor is None:
|
|
725
|
-
raise ScaleEngineError("could not acquire Scale Engine lifecycle lock")
|
|
726
|
-
try:
|
|
727
|
-
with os.fdopen(descriptor, "w") as lock_file:
|
|
728
|
-
json.dump({"pid": os.getpid(), "started_at": _utc_now()}, lock_file)
|
|
729
|
-
except Exception:
|
|
730
|
-
lock_path.unlink(missing_ok=True)
|
|
731
|
-
raise
|
|
732
|
-
return lock_path
|
|
733
|
-
|
|
734
|
-
@staticmethod
|
|
735
|
-
def _release_lifecycle_lock(lock_path: Path) -> None:
|
|
736
|
-
lock_path.unlink(missing_ok=True)
|
|
737
|
-
|
|
738
|
-
@staticmethod
|
|
739
|
-
def _clear_dead_legacy_adoption_lock(lock_path: Path) -> bool:
|
|
740
|
-
"""Recover only a lock whose recorded process no longer exists."""
|
|
741
|
-
try:
|
|
742
|
-
owner = json.loads(lock_path.read_text())
|
|
743
|
-
pid = owner.get("pid")
|
|
744
|
-
if not isinstance(pid, int) or pid <= 0:
|
|
745
|
-
return False
|
|
746
|
-
os.kill(pid, 0)
|
|
747
|
-
except ProcessLookupError:
|
|
748
|
-
lock_path.unlink(missing_ok=True)
|
|
749
|
-
return True
|
|
750
|
-
except (OSError, ValueError, json.JSONDecodeError):
|
|
751
|
-
return False
|
|
752
|
-
return False
|
|
753
|
-
|
|
754
|
-
def recover_interrupted_promotion(self) -> str | None:
|
|
755
|
-
"""Recover a durable promotion journal before opening projection paths."""
|
|
756
|
-
lock_path = self._acquire_lifecycle_lock()
|
|
757
|
-
try:
|
|
758
|
-
return self._recover_interrupted_promotion()
|
|
759
|
-
finally:
|
|
760
|
-
self._release_lifecycle_lock(lock_path)
|
|
761
|
-
|
|
762
|
-
def _recover_interrupted_promotion(self) -> str | None:
|
|
763
|
-
"""Finalize or reverse an interrupted directory swap under lifecycle lock."""
|
|
764
|
-
if not self.promotion_journal_path.exists():
|
|
765
|
-
return None
|
|
766
|
-
try:
|
|
767
|
-
journal = json.loads(self.promotion_journal_path.read_text())
|
|
768
|
-
backup_id = str(journal["backup_id"])
|
|
769
|
-
state = str(journal["state"])
|
|
770
|
-
except (OSError, KeyError, TypeError, json.JSONDecodeError) as exc:
|
|
771
|
-
raise ScaleEngineError("invalid promotion journal; manual repair required") from exc
|
|
772
|
-
operation = str(journal.get("operation", "promotion"))
|
|
773
|
-
if state == "committed":
|
|
774
|
-
if operation == "promotion":
|
|
775
|
-
self.config.scale_engine_state = "promoted"
|
|
776
|
-
self.config.graph_backend = "cozo"
|
|
777
|
-
self.config.vector_backend = "lancedb"
|
|
778
|
-
elif operation == "rollback":
|
|
779
|
-
self.config.scale_engine_state = "local_core"
|
|
780
|
-
self.config.graph_backend = "auto"
|
|
781
|
-
self.config.vector_backend = "auto"
|
|
782
|
-
else:
|
|
783
|
-
raise ScaleEngineError(f"unknown journal operation: {operation!r}")
|
|
784
|
-
self._save_config()
|
|
785
|
-
self.promotion_journal_path.unlink(missing_ok=True)
|
|
786
|
-
return f"finalized_committed_{operation}"
|
|
787
|
-
if state != "intent":
|
|
788
|
-
raise ScaleEngineError(f"unknown promotion journal state: {state!r}")
|
|
789
|
-
backup_dir = self.backup_root / backup_id
|
|
790
|
-
active = dict(zip(("cozo", "lance"), self.active_paths))
|
|
791
|
-
stage_dir = self.staging_root / str(journal.get("stage_id", ""))
|
|
792
|
-
displaced_dir = self.backup_root / str(journal.get("displaced_id", ""))
|
|
793
|
-
moves = journal.get("moves")
|
|
794
|
-
if not isinstance(moves, list):
|
|
795
|
-
# Compatibility for journals written by the initial v3.7.3
|
|
796
|
-
# candidate before per-rename intents existed.
|
|
797
|
-
moves = [
|
|
798
|
-
{"name": name, "kind": "active_to_backup", "state": "complete"}
|
|
799
|
-
for name in journal.get("moved_active", [])
|
|
800
|
-
] + [
|
|
801
|
-
{"name": name, "kind": "stage_to_active", "state": "complete"}
|
|
802
|
-
for name in journal.get("moved_stage", [])
|
|
803
|
-
]
|
|
804
|
-
for move in reversed(moves):
|
|
805
|
-
self._reverse_journal_move(move, active, stage_dir, backup_dir, displaced_dir)
|
|
806
|
-
if operation == "promotion":
|
|
807
|
-
if backup_dir.exists() and not any(backup_dir.iterdir()):
|
|
808
|
-
backup_dir.rmdir()
|
|
809
|
-
self.config.scale_engine_state = "local_core"
|
|
810
|
-
self.config.graph_backend = "auto"
|
|
811
|
-
self.config.vector_backend = "auto"
|
|
812
|
-
elif operation == "rollback":
|
|
813
|
-
if displaced_dir.exists() and not any(displaced_dir.iterdir()):
|
|
814
|
-
displaced_dir.rmdir()
|
|
815
|
-
self.config.scale_engine_state = "promoted"
|
|
816
|
-
self.config.graph_backend = "cozo"
|
|
817
|
-
self.config.vector_backend = "lancedb"
|
|
818
|
-
else:
|
|
819
|
-
raise ScaleEngineError(f"unknown journal operation: {operation!r}")
|
|
820
|
-
self._save_config()
|
|
821
|
-
self.promotion_journal_path.unlink(missing_ok=True)
|
|
822
|
-
return f"reversed_interrupted_{operation}"
|
|
823
|
-
|
|
824
|
-
@staticmethod
|
|
825
|
-
def _reverse_journal_move(
|
|
826
|
-
move: Any,
|
|
827
|
-
active: dict[str, Path],
|
|
828
|
-
stage_dir: Path,
|
|
829
|
-
backup_dir: Path,
|
|
830
|
-
displaced_dir: Path,
|
|
831
|
-
) -> None:
|
|
832
|
-
"""Reverse one planned rename based on actual paths, not journal timing."""
|
|
833
|
-
if not isinstance(move, dict):
|
|
834
|
-
raise ScaleEngineError("invalid promotion journal move")
|
|
835
|
-
name = move.get("name")
|
|
836
|
-
kind = move.get("kind")
|
|
837
|
-
active_path = active.get(name)
|
|
838
|
-
if active_path is None:
|
|
839
|
-
raise ScaleEngineError(f"invalid promotion journal backend: {name!r}")
|
|
840
|
-
if kind == "stage_to_active":
|
|
841
|
-
source, target = active_path, stage_dir / name
|
|
842
|
-
elif kind == "active_to_backup":
|
|
843
|
-
source, target = backup_dir / name, active_path
|
|
844
|
-
elif kind == "active_to_displaced":
|
|
845
|
-
source, target = displaced_dir / name, active_path
|
|
846
|
-
elif kind == "backup_to_active":
|
|
847
|
-
source, target = active_path, backup_dir / name
|
|
848
|
-
else:
|
|
849
|
-
raise ScaleEngineError(f"invalid promotion journal move kind: {kind!r}")
|
|
850
|
-
if source.exists() and not target.exists():
|
|
851
|
-
ScaleEngineManager._replace_durable(source, target)
|
|
852
|
-
elif target.exists() and not source.exists():
|
|
853
|
-
return
|
|
854
|
-
else:
|
|
855
|
-
raise ScaleEngineError(f"cannot safely reconcile {kind} for {name}")
|
|
856
|
-
|
|
857
|
-
def _write_promotion_journal(self, journal: dict[str, Any]) -> None:
|
|
858
|
-
self._write_json_durable(self.promotion_journal_path, journal)
|
|
859
|
-
|
|
860
|
-
@staticmethod
|
|
861
|
-
def _write_json_durable(target: Path, payload: dict[str, Any]) -> None:
|
|
862
|
-
temporary = target.with_suffix(target.suffix + ".tmp")
|
|
863
|
-
with temporary.open("w") as handle:
|
|
864
|
-
json.dump(payload, handle, indent=2, sort_keys=True)
|
|
865
|
-
handle.write("\n")
|
|
866
|
-
handle.flush()
|
|
867
|
-
os.fsync(handle.fileno())
|
|
868
|
-
os.replace(temporary, target)
|
|
869
|
-
ScaleEngineManager._fsync_directory(target.parent)
|
|
870
|
-
|
|
871
|
-
@staticmethod
|
|
872
|
-
def _replace_durable(source: Path, target: Path) -> None:
|
|
873
|
-
"""Rename a projection path and persist both directory entries."""
|
|
874
|
-
os.replace(source, target)
|
|
875
|
-
ScaleEngineManager._fsync_directory(source.parent)
|
|
876
|
-
if target.parent != source.parent:
|
|
877
|
-
ScaleEngineManager._fsync_directory(target.parent)
|
|
878
|
-
|
|
879
|
-
@staticmethod
|
|
880
|
-
def _mkdir_durable(path: Path, *, exist_ok: bool = True) -> None:
|
|
881
|
-
"""Create a directory and persist every new parent entry before rename."""
|
|
882
|
-
missing: list[Path] = []
|
|
883
|
-
ancestor = path
|
|
884
|
-
while not ancestor.exists():
|
|
885
|
-
missing.append(ancestor)
|
|
886
|
-
ancestor = ancestor.parent
|
|
887
|
-
path.mkdir(parents=True, exist_ok=exist_ok)
|
|
888
|
-
for created in reversed(missing):
|
|
889
|
-
ScaleEngineManager._fsync_directory(created.parent)
|
|
890
|
-
|
|
891
|
-
@staticmethod
|
|
892
|
-
def _fsync_directory(directory_path: Path) -> None:
|
|
893
|
-
"""Best-effort directory-entry durability across local filesystems."""
|
|
894
|
-
try:
|
|
895
|
-
directory = os.open(directory_path, os.O_RDONLY)
|
|
896
|
-
try:
|
|
897
|
-
os.fsync(directory)
|
|
898
|
-
finally:
|
|
899
|
-
os.close(directory)
|
|
900
|
-
except OSError:
|
|
901
|
-
# The file itself is already durable where directory fsync is not
|
|
902
|
-
# supported by the local filesystem (notably some Windows setups).
|
|
903
|
-
pass
|
|
904
|
-
|
|
905
|
-
def _save_config(self) -> None:
|
|
906
|
-
save = getattr(self.config, "save", None)
|
|
907
|
-
if callable(save):
|
|
908
|
-
save()
|
|
909
|
-
|
|
910
|
-
@staticmethod
|
|
911
|
-
def _close(resource: Any) -> None:
|
|
912
|
-
if resource is not None:
|
|
913
|
-
close = getattr(resource, "close", None)
|
|
914
|
-
if callable(close):
|
|
915
|
-
close()
|