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,1190 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
-
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
-
|
|
4
|
-
"""Daemon-owned bridge from journaled remember requests to one SQLite writer.
|
|
5
|
-
|
|
6
|
-
The public HTTP boundary authenticates and runs trust policy before it calls
|
|
7
|
-
this module. This runtime then uses a separate FULL-synchronous journal to
|
|
8
|
-
make the request replayable, and submits a typed command to the sole daemon
|
|
9
|
-
writer. The command transaction creates only the M018 operation plus its
|
|
10
|
-
immediately-queryable projection; model and enrichment work remain with the
|
|
11
|
-
existing background materializer.
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from __future__ import annotations
|
|
15
|
-
|
|
16
|
-
import hashlib
|
|
17
|
-
import json
|
|
18
|
-
import logging
|
|
19
|
-
import re
|
|
20
|
-
import threading
|
|
21
|
-
import uuid
|
|
22
|
-
from collections.abc import Callable, Mapping
|
|
23
|
-
from datetime import datetime, timezone
|
|
24
|
-
from pathlib import Path
|
|
25
|
-
from typing import Any
|
|
26
|
-
|
|
27
|
-
from superlocalmemory.core.ingestion_command import (
|
|
28
|
-
IngestionCommand,
|
|
29
|
-
IngestionOperation,
|
|
30
|
-
IngestionOperationRepository,
|
|
31
|
-
IngestionRejectedError,
|
|
32
|
-
IngestionRequest,
|
|
33
|
-
MaterializationResult,
|
|
34
|
-
)
|
|
35
|
-
from superlocalmemory.core.remember_admission import (
|
|
36
|
-
RememberAdmissionCommand,
|
|
37
|
-
RememberReceipt,
|
|
38
|
-
RememberService,
|
|
39
|
-
)
|
|
40
|
-
from superlocalmemory.core.transactions.concrete_owners import (
|
|
41
|
-
REQUIRED_ADMISSION_OWNERS,
|
|
42
|
-
)
|
|
43
|
-
from superlocalmemory.core.transactions.obligations import ObligationLedger
|
|
44
|
-
from superlocalmemory.core.transactions.owners import (
|
|
45
|
-
ObligationKind,
|
|
46
|
-
OperationContext,
|
|
47
|
-
)
|
|
48
|
-
from superlocalmemory.storage.admission_codec import MachineKeyCommandCodec
|
|
49
|
-
from superlocalmemory.storage.admission_journal import (
|
|
50
|
-
Actor,
|
|
51
|
-
AdmissionEntry,
|
|
52
|
-
AdmissionJournal,
|
|
53
|
-
AdmissionJournalUnavailable,
|
|
54
|
-
AdmissionPayloadError,
|
|
55
|
-
RememberRequest,
|
|
56
|
-
TerminalAdmissionError,
|
|
57
|
-
)
|
|
58
|
-
from superlocalmemory.storage.database import DatabaseManager
|
|
59
|
-
from superlocalmemory.storage.generation_fence import (
|
|
60
|
-
admitted_epoch,
|
|
61
|
-
clear_admission_epoch,
|
|
62
|
-
record_admission_epoch,
|
|
63
|
-
)
|
|
64
|
-
from superlocalmemory.storage.write_coordinator import (
|
|
65
|
-
CommandConflictError,
|
|
66
|
-
CommandKind,
|
|
67
|
-
CommandRejectedError,
|
|
68
|
-
OwnershipRequiredError,
|
|
69
|
-
WriteCommand,
|
|
70
|
-
WriteCoordinator,
|
|
71
|
-
WriteCoordinatorError,
|
|
72
|
-
WriteResult,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
QueryableWriter = Callable[[IngestionRequest, str], list[str]]
|
|
76
|
-
Materializer = Callable[
|
|
77
|
-
[IngestionOperation],
|
|
78
|
-
list[str] | tuple[str, ...] | MaterializationResult,
|
|
79
|
-
]
|
|
80
|
-
|
|
81
|
-
logger = logging.getLogger("superlocalmemory.core.remember_runtime")
|
|
82
|
-
_OBLIGATION_LEDGER = ObligationLedger()
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _obligation_schema_present(conn) -> bool:
|
|
86
|
-
row = conn.execute(
|
|
87
|
-
"SELECT 1 FROM sqlite_master WHERE type='table' AND name='projection_obligations'"
|
|
88
|
-
).fetchone()
|
|
89
|
-
if row is None:
|
|
90
|
-
return False
|
|
91
|
-
columns = {r[1] for r in conn.execute("PRAGMA table_info(projection_obligations)")}
|
|
92
|
-
return "context_digest" in columns
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
class CanonicalRememberUnavailable(RuntimeError):
|
|
96
|
-
"""The daemon cannot accept a bounded canonical remember request."""
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class DaemonAlreadyServing(RuntimeError):
|
|
100
|
-
"""A healthy SLM daemon is already serving; this instance should exit 0.
|
|
101
|
-
|
|
102
|
-
Raised by ``CanonicalRememberRuntime.start()`` (H2) when the writer claim
|
|
103
|
-
fails AND a health-verified daemon is responding on the configured port.
|
|
104
|
-
Caught by ``unified_daemon.py`` lifespan → ``sys.exit(0)``.
|
|
105
|
-
"""
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class CanonicalMutationConflict(WriteCoordinatorError, ValueError):
|
|
109
|
-
"""A mutation retry key was reused for different immutable input."""
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
_MUTATION_IDEMPOTENCY_KEY = re.compile(r"^[A-Za-z0-9._:-]{1,256}$")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def validate_deterministic_admission(
|
|
116
|
-
content: str,
|
|
117
|
-
*,
|
|
118
|
-
max_verbatim_chars: int = 24_000,
|
|
119
|
-
max_ingest_bytes: int = 1_048_576,
|
|
120
|
-
) -> None:
|
|
121
|
-
"""Reject deterministic non-evidence before it can enter the journal.
|
|
122
|
-
|
|
123
|
-
The immediate writer checks these gates as a defence in depth measure. At
|
|
124
|
-
the daemon boundary they must run first: a rejected payload has no
|
|
125
|
-
queryable receipt, so recording it as dispatched would create replay work
|
|
126
|
-
that can never commit.
|
|
127
|
-
"""
|
|
128
|
-
from superlocalmemory.core.engine_ingestion import content_passes_admission
|
|
129
|
-
from superlocalmemory.core.ingest_gate import apply_ingest_gate
|
|
130
|
-
|
|
131
|
-
if not content_passes_admission(content):
|
|
132
|
-
raise AdmissionPayloadError("content rejected by deterministic admission policy")
|
|
133
|
-
if apply_ingest_gate(
|
|
134
|
-
content,
|
|
135
|
-
max_verbatim_chars=max_verbatim_chars,
|
|
136
|
-
max_ingest_bytes=max_ingest_bytes,
|
|
137
|
-
).rejected:
|
|
138
|
-
raise AdmissionPayloadError("content rejected by deterministic ingest policy")
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# ---------------------------------------------------------------------------
|
|
142
|
-
# H2 — graceful single-instance helpers (keep all I/O in stdlib; no imports
|
|
143
|
-
# from the server layer to avoid circular dependencies with core).
|
|
144
|
-
# ---------------------------------------------------------------------------
|
|
145
|
-
|
|
146
|
-
def _get_daemon_port() -> int:
|
|
147
|
-
"""Return the HTTP port this daemon instance listens on.
|
|
148
|
-
|
|
149
|
-
Reads ``SLM_DAEMON_PORT`` from the environment (set by ``unified_daemon``
|
|
150
|
-
at startup) and falls back to the conventional default of 8765.
|
|
151
|
-
"""
|
|
152
|
-
import os
|
|
153
|
-
|
|
154
|
-
raw = os.environ.get("SLM_DAEMON_PORT", "")
|
|
155
|
-
try:
|
|
156
|
-
return int(raw)
|
|
157
|
-
except (ValueError, TypeError):
|
|
158
|
-
return 8765
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
def _slm_health_check(port: int) -> bool:
|
|
162
|
-
"""Return True iff a healthy SLM daemon responds on *port* within 2 s."""
|
|
163
|
-
import urllib.request
|
|
164
|
-
|
|
165
|
-
try:
|
|
166
|
-
with urllib.request.urlopen(
|
|
167
|
-
f"http://127.0.0.1:{port}/health", timeout=2
|
|
168
|
-
) as resp:
|
|
169
|
-
return int(resp.status) == 200
|
|
170
|
-
except Exception:
|
|
171
|
-
return False
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def _boot_self_heal(data_dir: Path) -> None:
|
|
175
|
-
"""Run the H1 stale-artifact reaper. Fail-soft — never blocks startup."""
|
|
176
|
-
try:
|
|
177
|
-
from superlocalmemory.infra.self_heal import reap_stale_artifacts
|
|
178
|
-
|
|
179
|
-
report = reap_stale_artifacts(data_dir)
|
|
180
|
-
if report["removed"]:
|
|
181
|
-
logger.info(
|
|
182
|
-
"remember_runtime self-heal: removed %d stale artifact(s)",
|
|
183
|
-
len(report["removed"]),
|
|
184
|
-
)
|
|
185
|
-
except Exception as exc:
|
|
186
|
-
logger.debug("remember_runtime self-heal failed (non-fatal): %s", exc)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
class _CoordinatorAdapter:
|
|
190
|
-
"""Translate the journal service's narrow protocol into typed commands."""
|
|
191
|
-
|
|
192
|
-
def __init__(self, coordinator: WriteCoordinator) -> None:
|
|
193
|
-
self._coordinator = coordinator
|
|
194
|
-
|
|
195
|
-
def submit(
|
|
196
|
-
self, command: RememberAdmissionCommand, *, wait_ms: int,
|
|
197
|
-
) -> Mapping[str, Any]:
|
|
198
|
-
payload = {
|
|
199
|
-
"journal_id": command.journal_id,
|
|
200
|
-
"request_hash": command.request_hash,
|
|
201
|
-
"profile_id": command.profile_id,
|
|
202
|
-
"idempotency_key": command.idempotency_key,
|
|
203
|
-
"request": command.request.canonical_payload(),
|
|
204
|
-
}
|
|
205
|
-
try:
|
|
206
|
-
result = self._coordinator.submit(
|
|
207
|
-
WriteCommand.create(
|
|
208
|
-
CommandKind.ADMISSION,
|
|
209
|
-
payload,
|
|
210
|
-
command_id=command.journal_id,
|
|
211
|
-
),
|
|
212
|
-
timeout=max(0.001, wait_ms / 1000),
|
|
213
|
-
)
|
|
214
|
-
except CommandRejectedError as exc:
|
|
215
|
-
raise TerminalAdmissionError(exc.error_code) from exc
|
|
216
|
-
return {"state": "committed", "receipt": dict(result.receipt)}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
class CanonicalRememberRuntime:
|
|
220
|
-
"""One daemon lifetime of journal-first, coordinator-owned admission."""
|
|
221
|
-
|
|
222
|
-
def __init__(
|
|
223
|
-
self,
|
|
224
|
-
*,
|
|
225
|
-
db: DatabaseManager,
|
|
226
|
-
profile_id: str,
|
|
227
|
-
writer: QueryableWriter,
|
|
228
|
-
journal_path: str | Path,
|
|
229
|
-
materialize: Materializer | None = None,
|
|
230
|
-
owner_id: str | None = None,
|
|
231
|
-
) -> None:
|
|
232
|
-
if not profile_id:
|
|
233
|
-
raise ValueError("profile_id is required")
|
|
234
|
-
self._db = db
|
|
235
|
-
self._profile_id = profile_id
|
|
236
|
-
self._writer = writer
|
|
237
|
-
self._materialize = materialize or _materialization_is_not_available
|
|
238
|
-
self._binding_lock = threading.RLock()
|
|
239
|
-
self._generation = 0
|
|
240
|
-
self.coordinator = WriteCoordinator(db.db_path, owner_id=owner_id)
|
|
241
|
-
self.journal = AdmissionJournal(
|
|
242
|
-
journal_path,
|
|
243
|
-
codec=MachineKeyCommandCodec(Path(journal_path).with_name("admission-key.bin")),
|
|
244
|
-
)
|
|
245
|
-
self._service = RememberService(self.journal, _CoordinatorAdapter(self.coordinator))
|
|
246
|
-
self._started = False
|
|
247
|
-
self._obligation_schema_ok: bool | None = None
|
|
248
|
-
|
|
249
|
-
@classmethod
|
|
250
|
-
def for_engine(cls, engine: Any) -> "CanonicalRememberRuntime":
|
|
251
|
-
"""Create the daemon boundary from an initialized engine only."""
|
|
252
|
-
from superlocalmemory.core.engine_ingestion import build_immediate_admission_handler
|
|
253
|
-
from superlocalmemory.infra.data_root import state_path
|
|
254
|
-
|
|
255
|
-
db = engine._db
|
|
256
|
-
store_config = getattr(engine._config, "store", None)
|
|
257
|
-
return cls(
|
|
258
|
-
db=db,
|
|
259
|
-
profile_id=engine._profile_id,
|
|
260
|
-
writer=build_immediate_admission_handler(
|
|
261
|
-
db,
|
|
262
|
-
profile_id=engine._profile_id,
|
|
263
|
-
max_verbatim_chars=getattr(store_config, "max_verbatim_chars", 24_000),
|
|
264
|
-
max_ingest_bytes=getattr(store_config, "max_ingest_bytes", 1_048_576),
|
|
265
|
-
),
|
|
266
|
-
journal_path=state_path("admission_journal.db"),
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
def start(self) -> None:
|
|
270
|
-
"""Claim writer ownership, install the handler, then recover journal work.
|
|
271
|
-
|
|
272
|
-
H2 / G-01+G-05 — bounded graceful single-instance:
|
|
273
|
-
|
|
274
|
-
When ``claim_ownership()`` fails (another process holds the portalocker
|
|
275
|
-
flock), we enter a bounded retry loop (≤ 5 attempts, ~1 s between each,
|
|
276
|
-
total ≤ ~6 s) rather than immediately crashing or silently giving up.
|
|
277
|
-
This handles the race between a healthy daemon's HTTP-server bind and our
|
|
278
|
-
health check — the holder may be alive but not yet responding.
|
|
279
|
-
|
|
280
|
-
Per-iteration logic:
|
|
281
|
-
(a) ``claim_ownership()`` succeeds → break and proceed (holder died).
|
|
282
|
-
(b) ``_slm_health_check(port)`` is True → raise ``DaemonAlreadyServing``
|
|
283
|
-
(caught by ``unified_daemon.py`` lifespan → ``sys.exit(0)``).
|
|
284
|
-
(c) First iteration only → run ``_boot_self_heal()`` to clear
|
|
285
|
-
provably-dead metadata artifacts, then continue.
|
|
286
|
-
|
|
287
|
-
After the loop exhausts all attempts without claiming the lock, we know
|
|
288
|
-
a live process is holding the portalocker flock. Raise
|
|
289
|
-
``DaemonAlreadyServing`` (NOT ``CanonicalRememberUnavailable``) so the
|
|
290
|
-
daemon exits cleanly rather than crashing with a traceback.
|
|
291
|
-
|
|
292
|
-
INVARIANT: ``claim_ownership()`` (portalocker OS flock) is the sole
|
|
293
|
-
writer-integrity gate. We never bypass it, never signal or kill a live
|
|
294
|
-
process.
|
|
295
|
-
"""
|
|
296
|
-
import time as _time
|
|
297
|
-
|
|
298
|
-
if self._started:
|
|
299
|
-
return
|
|
300
|
-
if not self.coordinator.claim_ownership():
|
|
301
|
-
port = _get_daemon_port()
|
|
302
|
-
_self_heal_done = False
|
|
303
|
-
_MAX_ATTEMPTS = 5
|
|
304
|
-
|
|
305
|
-
for _attempt in range(_MAX_ATTEMPTS):
|
|
306
|
-
if _attempt > 0:
|
|
307
|
-
_time.sleep(1.0)
|
|
308
|
-
|
|
309
|
-
# (a) Re-try the claim — holder may have died in the gap.
|
|
310
|
-
if self.coordinator.claim_ownership():
|
|
311
|
-
break # claimed → proceed to handler registration
|
|
312
|
-
|
|
313
|
-
# (b) Health-verified owner → exit gracefully.
|
|
314
|
-
if _slm_health_check(port):
|
|
315
|
-
raise DaemonAlreadyServing(
|
|
316
|
-
f"another healthy SLM daemon is already serving"
|
|
317
|
-
f" on port {port}"
|
|
318
|
-
)
|
|
319
|
-
|
|
320
|
-
# (c) First iteration only: clear provably-dead stale artifacts.
|
|
321
|
-
if not _self_heal_done:
|
|
322
|
-
logger.info(
|
|
323
|
-
"remember_runtime: writer claim failed, no healthy"
|
|
324
|
-
" daemon on port %d; running self-heal (attempt %d/%d)",
|
|
325
|
-
port, _attempt + 1, _MAX_ATTEMPTS,
|
|
326
|
-
)
|
|
327
|
-
_boot_self_heal(self.coordinator.db_path.parent)
|
|
328
|
-
_self_heal_done = True
|
|
329
|
-
else:
|
|
330
|
-
# Loop exhausted: a live process holds the portalocker flock.
|
|
331
|
-
# Exit cleanly — never crash with a traceback.
|
|
332
|
-
raise DaemonAlreadyServing(
|
|
333
|
-
f"another SLM daemon holds the writer lock after"
|
|
334
|
-
f" {_MAX_ATTEMPTS} attempts on port {port}"
|
|
335
|
-
)
|
|
336
|
-
try:
|
|
337
|
-
self.coordinator.register_handler(CommandKind.ADMISSION, self._handle_admission)
|
|
338
|
-
self.coordinator.register_handler(CommandKind.DELETE_FACT, self._handle_mutation)
|
|
339
|
-
self.coordinator.register_handler(CommandKind.UPDATE_FACT, self._handle_mutation)
|
|
340
|
-
self.coordinator.register_handler(CommandKind.PROPOSE_CORRECTION, self._handle_mutation)
|
|
341
|
-
self.coordinator.register_handler(CommandKind.APPLY_CORRECTION, self._handle_mutation)
|
|
342
|
-
self.coordinator.register_handler(CommandKind.REJECT_CORRECTION, self._handle_mutation)
|
|
343
|
-
self.coordinator.register_handler(
|
|
344
|
-
CommandKind.ROLLBACK_CORRECTION, self._handle_mutation,
|
|
345
|
-
)
|
|
346
|
-
self.coordinator.register_handler(CommandKind.ARCHIVE_FACT, self._handle_mutation)
|
|
347
|
-
self.coordinator.register_handler(CommandKind.MERGE_FACT, self._handle_mutation)
|
|
348
|
-
self.coordinator.register_handler(CommandKind.SET_FACT_SCOPE, self._handle_mutation)
|
|
349
|
-
self.coordinator.start()
|
|
350
|
-
self.replay_pending()
|
|
351
|
-
except BaseException:
|
|
352
|
-
self.coordinator.release_ownership()
|
|
353
|
-
raise
|
|
354
|
-
self._started = True
|
|
355
|
-
|
|
356
|
-
@property
|
|
357
|
-
def ready(self) -> bool:
|
|
358
|
-
worker = self.coordinator._worker
|
|
359
|
-
return bool(
|
|
360
|
-
self._started
|
|
361
|
-
and self.coordinator._ownership_context is not None
|
|
362
|
-
and worker is not None
|
|
363
|
-
and worker.is_alive()
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
def stop(self) -> None:
|
|
367
|
-
"""Release the daemon writer lease after callers and workers have stopped."""
|
|
368
|
-
self._started = False
|
|
369
|
-
self.coordinator.release_ownership()
|
|
370
|
-
|
|
371
|
-
def rebind_engine(self, engine: Any) -> None:
|
|
372
|
-
"""Atomically follow a drained daemon mode/profile transition."""
|
|
373
|
-
from superlocalmemory.core.engine_ingestion import build_immediate_admission_handler
|
|
374
|
-
|
|
375
|
-
db = engine._db
|
|
376
|
-
if db.db_path.expanduser().resolve() != self.coordinator.db_path:
|
|
377
|
-
raise CanonicalRememberUnavailable(
|
|
378
|
-
"reconfigured engine targets a different canonical database"
|
|
379
|
-
)
|
|
380
|
-
profile_id = str(engine._profile_id)
|
|
381
|
-
if not profile_id:
|
|
382
|
-
raise CanonicalRememberUnavailable("reconfigured engine has no profile")
|
|
383
|
-
store_config = getattr(getattr(engine, "_config", None), "store", None)
|
|
384
|
-
writer = build_immediate_admission_handler(
|
|
385
|
-
db,
|
|
386
|
-
profile_id=profile_id,
|
|
387
|
-
max_verbatim_chars=getattr(store_config, "max_verbatim_chars", 24_000),
|
|
388
|
-
max_ingest_bytes=getattr(store_config, "max_ingest_bytes", 1_048_576),
|
|
389
|
-
)
|
|
390
|
-
with self._binding_lock:
|
|
391
|
-
previous = (self._db, self._profile_id, self._writer)
|
|
392
|
-
self._db = db
|
|
393
|
-
self._profile_id = profile_id
|
|
394
|
-
self._writer = writer
|
|
395
|
-
self._generation += 1
|
|
396
|
-
try:
|
|
397
|
-
self.replay_pending()
|
|
398
|
-
except BaseException:
|
|
399
|
-
with self._binding_lock:
|
|
400
|
-
self._db, self._profile_id, self._writer = previous
|
|
401
|
-
self._generation -= 1
|
|
402
|
-
raise
|
|
403
|
-
|
|
404
|
-
def remember(
|
|
405
|
-
self, request: RememberRequest, actor: Actor, *, deadline_ms: int = 2_000,
|
|
406
|
-
) -> RememberReceipt:
|
|
407
|
-
"""Journal then commit one bounded queryable admission receipt."""
|
|
408
|
-
if not self._started:
|
|
409
|
-
raise CanonicalRememberUnavailable("canonical remember writer is not ready")
|
|
410
|
-
if deadline_ms < 1 or deadline_ms > 2_000:
|
|
411
|
-
raise ValueError("deadline_ms must be between 1 and 2000")
|
|
412
|
-
with self._binding_lock:
|
|
413
|
-
admitted = self._generation
|
|
414
|
-
record_admission_epoch(request.profile_id, request.idempotency_key, admitted)
|
|
415
|
-
try:
|
|
416
|
-
return self._service.remember(request, actor, deadline_ms=deadline_ms)
|
|
417
|
-
except (
|
|
418
|
-
AdmissionJournalUnavailable,
|
|
419
|
-
OwnershipRequiredError,
|
|
420
|
-
WriteCoordinatorError,
|
|
421
|
-
) as exc:
|
|
422
|
-
# Name which of the three it was. They are not interchangeable and
|
|
423
|
-
# they call for different responses: an unavailable journal is I/O
|
|
424
|
-
# or lock contention and worth retrying, lost ownership means
|
|
425
|
-
# another writer holds the lease, and a coordinator error is the
|
|
426
|
-
# same type the generation fence raises to reject a stale epoch.
|
|
427
|
-
# Collapsing all three into one string makes a spurious fence
|
|
428
|
-
# rejection indistinguishable from a transient disk stall, for the
|
|
429
|
-
# operator reading a log and for a caller deciding whether to
|
|
430
|
-
# retry. Only the class name is included: it is the whole of the
|
|
431
|
-
# discriminating information and carries no request content.
|
|
432
|
-
raise CanonicalRememberUnavailable(
|
|
433
|
-
"canonical remember is temporarily unavailable "
|
|
434
|
-
f"({type(exc).__name__})"
|
|
435
|
-
) from exc
|
|
436
|
-
finally:
|
|
437
|
-
clear_admission_epoch(request.profile_id, request.idempotency_key)
|
|
438
|
-
|
|
439
|
-
def replay_pending(self) -> int:
|
|
440
|
-
"""Finish prepared/dispatched journal entries before publishing readiness."""
|
|
441
|
-
if self.coordinator._ownership_context is None:
|
|
442
|
-
raise CanonicalRememberUnavailable("canonical writer ownership is required")
|
|
443
|
-
|
|
444
|
-
def find(entry: AdmissionEntry) -> Mapping[str, Any] | None:
|
|
445
|
-
rows = self.coordinator.execute(
|
|
446
|
-
"SELECT request_hash, receipt_json FROM write_commits "
|
|
447
|
-
"WHERE profile_id=? AND idempotency_key=?",
|
|
448
|
-
(entry.profile_id, entry.idempotency_key),
|
|
449
|
-
timeout=1.0,
|
|
450
|
-
)
|
|
451
|
-
if not rows:
|
|
452
|
-
return None
|
|
453
|
-
if rows[0]["request_hash"] != entry.request_hash:
|
|
454
|
-
raise TerminalAdmissionError("IDEMPOTENCY_CONFLICT")
|
|
455
|
-
receipt = json.loads(rows[0]["receipt_json"])
|
|
456
|
-
return receipt if isinstance(receipt, dict) else None
|
|
457
|
-
|
|
458
|
-
def dispatch(entry, request: RememberRequest) -> Mapping[str, Any]:
|
|
459
|
-
command = RememberAdmissionCommand(
|
|
460
|
-
journal_id=entry.journal_id,
|
|
461
|
-
request_hash=entry.request_hash,
|
|
462
|
-
request=request,
|
|
463
|
-
profile_id=entry.profile_id,
|
|
464
|
-
idempotency_key=entry.idempotency_key,
|
|
465
|
-
)
|
|
466
|
-
return _CoordinatorAdapter(self.coordinator).submit(command, wait_ms=2_000)["receipt"]
|
|
467
|
-
|
|
468
|
-
try:
|
|
469
|
-
return self.journal.replay_pending(
|
|
470
|
-
find,
|
|
471
|
-
dispatch,
|
|
472
|
-
profile_id=self._profile_id,
|
|
473
|
-
)
|
|
474
|
-
except (WriteCoordinatorError, ValueError, json.JSONDecodeError) as exc:
|
|
475
|
-
raise CanonicalRememberUnavailable("pending remember recovery failed") from exc
|
|
476
|
-
|
|
477
|
-
def delete_fact(
|
|
478
|
-
self, profile_id: str, fact_id: str, *, idempotency_key: str | None = None,
|
|
479
|
-
) -> Mapping[str, Any]:
|
|
480
|
-
"""Hard-delete one profile-owned fact through the sole writer."""
|
|
481
|
-
return self._submit_mutation(
|
|
482
|
-
CommandKind.DELETE_FACT,
|
|
483
|
-
profile_id,
|
|
484
|
-
{"fact_id": fact_id},
|
|
485
|
-
idempotency_key=idempotency_key,
|
|
486
|
-
)
|
|
487
|
-
|
|
488
|
-
def update_fact(
|
|
489
|
-
self,
|
|
490
|
-
profile_id: str,
|
|
491
|
-
fact_id: str,
|
|
492
|
-
updates: Mapping[str, Any],
|
|
493
|
-
*,
|
|
494
|
-
idempotency_key: str | None = None,
|
|
495
|
-
) -> Mapping[str, Any]:
|
|
496
|
-
"""Apply deterministic fact fields after policy/model work completes."""
|
|
497
|
-
return self._submit_mutation(
|
|
498
|
-
CommandKind.UPDATE_FACT,
|
|
499
|
-
profile_id,
|
|
500
|
-
{"fact_id": fact_id, "updates": _json_roundtrip(updates)},
|
|
501
|
-
idempotency_key=idempotency_key,
|
|
502
|
-
)
|
|
503
|
-
|
|
504
|
-
def create_correction_successor(
|
|
505
|
-
self,
|
|
506
|
-
profile_id: str,
|
|
507
|
-
fact_id: str,
|
|
508
|
-
successor_fact_id: str,
|
|
509
|
-
content: str,
|
|
510
|
-
*,
|
|
511
|
-
embedding: list[float] | None = None,
|
|
512
|
-
fisher_mean: list[float] | None = None,
|
|
513
|
-
fisher_variance: list[float] | None = None,
|
|
514
|
-
trusted_actor_id: str = "canonical-runtime",
|
|
515
|
-
idempotency_key: str | None = None,
|
|
516
|
-
) -> Mapping[str, Any]:
|
|
517
|
-
"""Atomically create an immutable, review-required successor case."""
|
|
518
|
-
return self._submit_mutation(
|
|
519
|
-
CommandKind.PROPOSE_CORRECTION,
|
|
520
|
-
profile_id,
|
|
521
|
-
{
|
|
522
|
-
"fact_id": fact_id,
|
|
523
|
-
"successor_fact_id": successor_fact_id,
|
|
524
|
-
"content": content,
|
|
525
|
-
"embedding": _json_roundtrip({"value": embedding})["value"],
|
|
526
|
-
"fisher_mean": _json_roundtrip({"value": fisher_mean})["value"],
|
|
527
|
-
"fisher_variance": _json_roundtrip({"value": fisher_variance})["value"],
|
|
528
|
-
"trusted_actor_id": trusted_actor_id,
|
|
529
|
-
},
|
|
530
|
-
idempotency_key=idempotency_key,
|
|
531
|
-
)
|
|
532
|
-
|
|
533
|
-
def transition_correction(
|
|
534
|
-
self,
|
|
535
|
-
profile_id: str,
|
|
536
|
-
case_id: str,
|
|
537
|
-
*,
|
|
538
|
-
action: str,
|
|
539
|
-
expected_version: int,
|
|
540
|
-
actor_id: str,
|
|
541
|
-
event_valid_until: str | None = None,
|
|
542
|
-
idempotency_key: str | None = None,
|
|
543
|
-
) -> Mapping[str, Any]:
|
|
544
|
-
"""Apply, reject, or roll back one server-authorized correction case."""
|
|
545
|
-
command = {
|
|
546
|
-
"apply": CommandKind.APPLY_CORRECTION,
|
|
547
|
-
"reject": CommandKind.REJECT_CORRECTION,
|
|
548
|
-
"rollback": CommandKind.ROLLBACK_CORRECTION,
|
|
549
|
-
}.get(action)
|
|
550
|
-
if command is None:
|
|
551
|
-
raise ValueError("correction action must be apply, reject, or rollback")
|
|
552
|
-
return self._submit_mutation(
|
|
553
|
-
command,
|
|
554
|
-
profile_id,
|
|
555
|
-
{
|
|
556
|
-
"case_id": case_id,
|
|
557
|
-
"expected_version": expected_version,
|
|
558
|
-
"trusted_actor_id": actor_id,
|
|
559
|
-
"event_valid_until": event_valid_until,
|
|
560
|
-
},
|
|
561
|
-
idempotency_key=idempotency_key,
|
|
562
|
-
)
|
|
563
|
-
|
|
564
|
-
def archive_fact(
|
|
565
|
-
self, profile_id: str, fact_id: str, *, idempotency_key: str | None = None,
|
|
566
|
-
) -> Mapping[str, Any]:
|
|
567
|
-
"""Archive a fact and its restore payload in one bounded transaction."""
|
|
568
|
-
return self._submit_mutation(
|
|
569
|
-
CommandKind.ARCHIVE_FACT,
|
|
570
|
-
profile_id,
|
|
571
|
-
{"fact_id": fact_id},
|
|
572
|
-
idempotency_key=idempotency_key,
|
|
573
|
-
)
|
|
574
|
-
|
|
575
|
-
def merge_fact(
|
|
576
|
-
self,
|
|
577
|
-
profile_id: str,
|
|
578
|
-
fact_id: str,
|
|
579
|
-
kept_fact_id: str,
|
|
580
|
-
*,
|
|
581
|
-
idempotency_key: str | None = None,
|
|
582
|
-
) -> Mapping[str, Any]:
|
|
583
|
-
"""Record and apply a same-profile merge through the sole writer."""
|
|
584
|
-
return self._submit_mutation(
|
|
585
|
-
CommandKind.MERGE_FACT,
|
|
586
|
-
profile_id,
|
|
587
|
-
{
|
|
588
|
-
"fact_id": fact_id,
|
|
589
|
-
"kept_fact_id": kept_fact_id,
|
|
590
|
-
},
|
|
591
|
-
idempotency_key=idempotency_key,
|
|
592
|
-
)
|
|
593
|
-
|
|
594
|
-
def set_fact_scope(
|
|
595
|
-
self,
|
|
596
|
-
profile_id: str,
|
|
597
|
-
fact_id: str,
|
|
598
|
-
scope: str,
|
|
599
|
-
shared_with: list[str],
|
|
600
|
-
*,
|
|
601
|
-
idempotency_key: str | None = None,
|
|
602
|
-
) -> Mapping[str, Any]:
|
|
603
|
-
"""Set a validated scope without bypassing profile isolation."""
|
|
604
|
-
return self._submit_mutation(
|
|
605
|
-
CommandKind.SET_FACT_SCOPE,
|
|
606
|
-
profile_id,
|
|
607
|
-
{"fact_id": fact_id, "scope": scope, "shared_with": shared_with},
|
|
608
|
-
idempotency_key=idempotency_key,
|
|
609
|
-
)
|
|
610
|
-
|
|
611
|
-
def _submit_mutation(
|
|
612
|
-
self,
|
|
613
|
-
kind: CommandKind,
|
|
614
|
-
profile_id: str,
|
|
615
|
-
payload: Mapping[str, Any],
|
|
616
|
-
*,
|
|
617
|
-
idempotency_key: str | None,
|
|
618
|
-
) -> Mapping[str, Any]:
|
|
619
|
-
if not self._started:
|
|
620
|
-
raise CanonicalRememberUnavailable("canonical mutation writer is not ready")
|
|
621
|
-
if not profile_id:
|
|
622
|
-
raise ValueError("profile_id is required")
|
|
623
|
-
key = idempotency_key or str(uuid.uuid4())
|
|
624
|
-
if not _MUTATION_IDEMPOTENCY_KEY.fullmatch(key):
|
|
625
|
-
raise ValueError("idempotency key must be 1-256 safe characters")
|
|
626
|
-
canonical = _json_roundtrip(payload)
|
|
627
|
-
request_hash = hashlib.sha256(
|
|
628
|
-
json.dumps(canonical, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
629
|
-
).hexdigest()
|
|
630
|
-
scoped_key = (
|
|
631
|
-
f"mutation:{kind.value}:"
|
|
632
|
-
+ hashlib.sha256(
|
|
633
|
-
f"{profile_id}\0{key}".encode("utf-8")
|
|
634
|
-
).hexdigest()
|
|
635
|
-
)
|
|
636
|
-
command = WriteCommand(
|
|
637
|
-
command_id=scoped_key,
|
|
638
|
-
kind=kind,
|
|
639
|
-
payload={
|
|
640
|
-
**canonical,
|
|
641
|
-
"journal_id": scoped_key,
|
|
642
|
-
"request_hash": request_hash,
|
|
643
|
-
"profile_id": profile_id,
|
|
644
|
-
"idempotency_key": scoped_key,
|
|
645
|
-
},
|
|
646
|
-
)
|
|
647
|
-
try:
|
|
648
|
-
return dict(self.coordinator.submit(command, timeout=2.0).receipt)
|
|
649
|
-
except CanonicalMutationConflict:
|
|
650
|
-
# A deterministic lifecycle conflict is not a writer outage. Let
|
|
651
|
-
# HTTP/CLI/MCP report the actionable 409/validation result.
|
|
652
|
-
raise
|
|
653
|
-
except CommandConflictError as exc:
|
|
654
|
-
raise CanonicalMutationConflict(
|
|
655
|
-
"idempotency key belongs to a different mutation request"
|
|
656
|
-
) from exc
|
|
657
|
-
except (OwnershipRequiredError, WriteCoordinatorError) as exc:
|
|
658
|
-
raise CanonicalRememberUnavailable(
|
|
659
|
-
"canonical mutation is temporarily unavailable"
|
|
660
|
-
) from exc
|
|
661
|
-
|
|
662
|
-
def _handle_admission(
|
|
663
|
-
self,
|
|
664
|
-
conn,
|
|
665
|
-
capability,
|
|
666
|
-
command: WriteCommand,
|
|
667
|
-
) -> WriteResult:
|
|
668
|
-
"""Project a journal command under the coordinator's sole transaction."""
|
|
669
|
-
payload = command.payload
|
|
670
|
-
raw_request = payload.get("request")
|
|
671
|
-
if not isinstance(raw_request, Mapping):
|
|
672
|
-
raise ValueError("admission command request is missing")
|
|
673
|
-
request = RememberRequest.from_payload(raw_request)
|
|
674
|
-
with self._binding_lock:
|
|
675
|
-
db = self._db
|
|
676
|
-
if request.profile_id != self._profile_id:
|
|
677
|
-
raise ValueError("admission command targets a different profile")
|
|
678
|
-
expected = admitted_epoch(request.profile_id, request.idempotency_key)
|
|
679
|
-
if expected is not None and expected != self._generation:
|
|
680
|
-
raise ValueError("admission command epoch is stale")
|
|
681
|
-
ingestion_request = IngestionRequest(
|
|
682
|
-
content=request.content,
|
|
683
|
-
profile_id=request.profile_id,
|
|
684
|
-
source_type=request.source_type,
|
|
685
|
-
idempotency_key=request.idempotency_key,
|
|
686
|
-
metadata=dict(request.metadata),
|
|
687
|
-
scope=request.scope,
|
|
688
|
-
shared_with=request.shared_with,
|
|
689
|
-
trusted_actor_id=request.trusted_actor_id,
|
|
690
|
-
session_id=request.session_id,
|
|
691
|
-
session_date=request.session_date,
|
|
692
|
-
speaker=request.speaker,
|
|
693
|
-
role=request.role,
|
|
694
|
-
)
|
|
695
|
-
# There is deliberately no validate_admission argument here. The
|
|
696
|
-
# HTTP trust hook ran before journal.prepare, and no hook/model or
|
|
697
|
-
# projection code can enter the coordinator transaction.
|
|
698
|
-
with db._bind_coordinator_connection(conn, capability):
|
|
699
|
-
command_impl = IngestionCommand(
|
|
700
|
-
IngestionOperationRepository(db),
|
|
701
|
-
write_queryable=self._writer,
|
|
702
|
-
materialize=self._materialize,
|
|
703
|
-
)
|
|
704
|
-
try:
|
|
705
|
-
receipt = command_impl.submit(ingestion_request)
|
|
706
|
-
except IngestionRejectedError as exc:
|
|
707
|
-
raise CommandRejectedError() from exc
|
|
708
|
-
self._record_projection_obligations(conn, request, receipt)
|
|
709
|
-
return WriteResult.from_receipt(
|
|
710
|
-
command,
|
|
711
|
-
{
|
|
712
|
-
"operation_id": receipt.operation_id,
|
|
713
|
-
"pending_id": receipt.operation_id,
|
|
714
|
-
"fact_ids": list(receipt.fact_ids),
|
|
715
|
-
"count": len(receipt.fact_ids),
|
|
716
|
-
"status": "queryable",
|
|
717
|
-
"materialization_state": receipt.state.value,
|
|
718
|
-
},
|
|
719
|
-
)
|
|
720
|
-
|
|
721
|
-
def _record_projection_obligations(self, conn, request, receipt) -> None:
|
|
722
|
-
"""Record per-owner APPLY obligations for the admitted facts.
|
|
723
|
-
|
|
724
|
-
Fail-closed: raises RuntimeError if M033 is absent. This is
|
|
725
|
-
intentional — a successful remember() without a corresponding
|
|
726
|
-
obligation record would make projection audits permanently incomplete.
|
|
727
|
-
Back-compat: installs without M033 must run migrations first.
|
|
728
|
-
|
|
729
|
-
Schema negative cache: ``_obligation_schema_ok`` is re-checked on
|
|
730
|
-
every call while False so that hot migrations (M033 applied while the
|
|
731
|
-
runtime is running) are detected without a restart. Once the schema
|
|
732
|
-
is confirmed present the value is cached True for the lifetime of the
|
|
733
|
-
runtime instance.
|
|
734
|
-
|
|
735
|
-
Cross-process erasure fence: distributed, multi-writer erasure fencing
|
|
736
|
-
is intentionally SCOPED OUT of V4. Single-process SQLite WAL provides
|
|
737
|
-
adequate isolation for the current deployment model. See
|
|
738
|
-
docs/architecture/erasure-fence-deferred.md for the deferred design.
|
|
739
|
-
"""
|
|
740
|
-
fact_ids = tuple(getattr(receipt, "fact_ids", ()) or ())
|
|
741
|
-
if not fact_ids:
|
|
742
|
-
return
|
|
743
|
-
# Re-check while False so a hot M033 migration is picked up without
|
|
744
|
-
# requiring a daemon restart. Cache True permanently once confirmed.
|
|
745
|
-
if not self._obligation_schema_ok:
|
|
746
|
-
self._obligation_schema_ok = _obligation_schema_present(conn)
|
|
747
|
-
if not self._obligation_schema_ok:
|
|
748
|
-
raise RuntimeError(
|
|
749
|
-
"projection_obligations schema is absent; "
|
|
750
|
-
"run migrations (M033) before ingesting facts"
|
|
751
|
-
)
|
|
752
|
-
context = OperationContext(
|
|
753
|
-
operation_id=receipt.operation_id,
|
|
754
|
-
profile_id=request.profile_id,
|
|
755
|
-
subject_id=receipt.operation_id,
|
|
756
|
-
fact_ids=fact_ids,
|
|
757
|
-
)
|
|
758
|
-
_OBLIGATION_LEDGER.record_many(
|
|
759
|
-
conn, context, REQUIRED_ADMISSION_OWNERS, ObligationKind.APPLY,
|
|
760
|
-
)
|
|
761
|
-
|
|
762
|
-
def _handle_mutation(self, conn, capability, command: WriteCommand) -> WriteResult:
|
|
763
|
-
"""Run only deterministic SQLite mutation statements under the writer."""
|
|
764
|
-
payload = command.payload
|
|
765
|
-
profile_id = _payload_text(payload, "profile_id")
|
|
766
|
-
with self._binding_lock:
|
|
767
|
-
if profile_id != self._profile_id:
|
|
768
|
-
raise ValueError("mutation command targets a different profile")
|
|
769
|
-
with self._db._bind_coordinator_connection(conn, capability):
|
|
770
|
-
receipt = _execute_mutation(
|
|
771
|
-
self._db, command.kind, profile_id, payload, connection=conn
|
|
772
|
-
)
|
|
773
|
-
receipt["operation_id"] = f"mutation:{command.kind.value}:{command.command_id}"
|
|
774
|
-
return WriteResult.from_receipt(command, receipt)
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
def _materialization_is_not_available(_operation: IngestionOperation) -> list[str]:
|
|
778
|
-
"""Guard against accidental inline enrichment on the canonical path."""
|
|
779
|
-
raise RuntimeError("canonical remember materialization belongs to the background worker")
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
_FACT_UPDATE_COLUMNS = frozenset({"content", "embedding", "fisher_mean", "fisher_variance"})
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
def _json_roundtrip(value: Mapping[str, Any]) -> dict[str, Any]:
|
|
786
|
-
"""Reject non-command data before it reaches the writer thread."""
|
|
787
|
-
try:
|
|
788
|
-
decoded = json.loads(json.dumps(value, sort_keys=True, ensure_ascii=False))
|
|
789
|
-
except (TypeError, ValueError) as exc:
|
|
790
|
-
raise ValueError("mutation payload must be JSON-compatible") from exc
|
|
791
|
-
if not isinstance(decoded, dict): # pragma: no cover - Mapping input is enforced
|
|
792
|
-
raise ValueError("mutation payload must be an object")
|
|
793
|
-
return decoded
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
def _payload_text(payload: Mapping[str, Any], key: str) -> str:
|
|
797
|
-
value = payload.get(key)
|
|
798
|
-
if not isinstance(value, str) or not value:
|
|
799
|
-
raise ValueError(f"mutation command is missing {key}")
|
|
800
|
-
return value
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
def _fact_row(db: DatabaseManager, fact_id: str, profile_id: str) -> Mapping[str, Any] | None:
|
|
804
|
-
rows = db.execute(
|
|
805
|
-
"SELECT * FROM atomic_facts WHERE fact_id = ? AND profile_id = ? LIMIT 1",
|
|
806
|
-
(fact_id, profile_id),
|
|
807
|
-
)
|
|
808
|
-
return dict(rows[0]) if rows else None
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
def _execute_mutation(
|
|
812
|
-
db: DatabaseManager,
|
|
813
|
-
kind: CommandKind,
|
|
814
|
-
profile_id: str,
|
|
815
|
-
payload: Mapping[str, Any],
|
|
816
|
-
*,
|
|
817
|
-
connection: Any,
|
|
818
|
-
) -> dict[str, Any]:
|
|
819
|
-
"""Dispatch the finite mutation set; no policy, hooks, models, or I/O."""
|
|
820
|
-
if kind is CommandKind.PROPOSE_CORRECTION:
|
|
821
|
-
return _propose_correction_successor(db, profile_id, payload, connection=connection)
|
|
822
|
-
if kind in {
|
|
823
|
-
CommandKind.APPLY_CORRECTION,
|
|
824
|
-
CommandKind.REJECT_CORRECTION,
|
|
825
|
-
CommandKind.ROLLBACK_CORRECTION,
|
|
826
|
-
}:
|
|
827
|
-
return _transition_correction(db, kind, profile_id, payload, connection=connection)
|
|
828
|
-
fact_id = _payload_text(payload, "fact_id")
|
|
829
|
-
if kind is CommandKind.DELETE_FACT:
|
|
830
|
-
return _delete_fact(db, fact_id, profile_id)
|
|
831
|
-
if kind is CommandKind.UPDATE_FACT:
|
|
832
|
-
return _update_fact(db, fact_id, profile_id, payload)
|
|
833
|
-
if kind is CommandKind.ARCHIVE_FACT:
|
|
834
|
-
return _archive_fact(db, fact_id, profile_id, payload)
|
|
835
|
-
if kind is CommandKind.MERGE_FACT:
|
|
836
|
-
return _merge_fact(db, fact_id, profile_id, payload)
|
|
837
|
-
if kind is CommandKind.SET_FACT_SCOPE:
|
|
838
|
-
return _set_fact_scope(db, fact_id, profile_id, payload)
|
|
839
|
-
raise ValueError(f"unsupported mutation command {kind.value}")
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
def _delete_fact(db: DatabaseManager, fact_id: str, profile_id: str) -> dict[str, Any]:
|
|
843
|
-
row = _fact_row(db, fact_id, profile_id)
|
|
844
|
-
if row is None:
|
|
845
|
-
return {"ok": False, "operation_id": f"delete:{fact_id}", "fact_id": fact_id}
|
|
846
|
-
# M042 deliberately retains immutable predecessor/successor history.
|
|
847
|
-
# SQLite's FK would reject a direct delete, but exposing that as a generic
|
|
848
|
-
# writer outage turns a real lifecycle conflict into a misleading 503.
|
|
849
|
-
# A dedicated erasure workflow owns ledger removal; ordinary forget never
|
|
850
|
-
# deletes a fact that is part of immutable correction history.
|
|
851
|
-
try:
|
|
852
|
-
protected = db.execute(
|
|
853
|
-
"SELECT 1 FROM correction_cases "
|
|
854
|
-
"WHERE profile_id=? AND (predecessor_fact_id=? OR successor_fact_id=?) LIMIT 1",
|
|
855
|
-
(profile_id, fact_id, fact_id),
|
|
856
|
-
)
|
|
857
|
-
except Exception as exc:
|
|
858
|
-
if "no such table" in str(exc).lower():
|
|
859
|
-
protected = []
|
|
860
|
-
else:
|
|
861
|
-
raise
|
|
862
|
-
if protected:
|
|
863
|
-
raise CanonicalMutationConflict(
|
|
864
|
-
"fact is protected by correction history; resolve the correction before forgetting it"
|
|
865
|
-
)
|
|
866
|
-
db.delete_fact(fact_id, profile_id=profile_id)
|
|
867
|
-
return {
|
|
868
|
-
"ok": True,
|
|
869
|
-
"operation_id": f"delete:{fact_id}",
|
|
870
|
-
"deleted": fact_id,
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
def _update_fact(
|
|
875
|
-
db: DatabaseManager, fact_id: str, profile_id: str, payload: Mapping[str, Any],
|
|
876
|
-
) -> dict[str, Any]:
|
|
877
|
-
updates = payload.get("updates")
|
|
878
|
-
if not isinstance(updates, Mapping) or not isinstance(updates.get("content"), str):
|
|
879
|
-
raise ValueError("update command requires content")
|
|
880
|
-
row = _fact_row(db, fact_id, profile_id)
|
|
881
|
-
if row is None:
|
|
882
|
-
return {"ok": False, "operation_id": f"update:{fact_id}", "fact_id": fact_id}
|
|
883
|
-
safe = {
|
|
884
|
-
key: _thaw_command_value(value)
|
|
885
|
-
for key, value in updates.items()
|
|
886
|
-
if key in _FACT_UPDATE_COLUMNS
|
|
887
|
-
}
|
|
888
|
-
if set(safe) != set(updates):
|
|
889
|
-
raise ValueError("update command contains unsupported fields")
|
|
890
|
-
db.update_fact(fact_id, safe, profile_id=profile_id)
|
|
891
|
-
return {
|
|
892
|
-
"ok": True,
|
|
893
|
-
"operation_id": f"update:{fact_id}",
|
|
894
|
-
"fact_id": fact_id,
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
def _propose_correction_successor(
|
|
899
|
-
db: DatabaseManager,
|
|
900
|
-
profile_id: str,
|
|
901
|
-
payload: Mapping[str, Any],
|
|
902
|
-
*,
|
|
903
|
-
connection: Any,
|
|
904
|
-
) -> dict[str, Any]:
|
|
905
|
-
"""Create successor and M042 proposal in one canonical writer transaction.
|
|
906
|
-
|
|
907
|
-
System time is represented by the new fact's ``created_at`` and temporal
|
|
908
|
-
knowledge anchor. Event-time fields are copied exactly from the
|
|
909
|
-
predecessor because an edit payload has no trustworthy event-time claim.
|
|
910
|
-
"""
|
|
911
|
-
fact_id = _payload_text(payload, "fact_id")
|
|
912
|
-
source = _fact_row(db, fact_id, profile_id)
|
|
913
|
-
if source is None:
|
|
914
|
-
return {"ok": False, "operation_id": f"correction:{fact_id}", "fact_id": fact_id}
|
|
915
|
-
successor_id = _payload_text(payload, "successor_fact_id")
|
|
916
|
-
content = _payload_text(payload, "content").strip()
|
|
917
|
-
if not content:
|
|
918
|
-
raise ValueError("correction successor content cannot be empty")
|
|
919
|
-
if successor_id == fact_id:
|
|
920
|
-
raise ValueError("correction successor must differ from predecessor")
|
|
921
|
-
|
|
922
|
-
from superlocalmemory.storage.models import AtomicFact, FactType, MemoryLifecycle, SignalType
|
|
923
|
-
|
|
924
|
-
def _list_payload(key: str) -> list[float] | None:
|
|
925
|
-
value = payload.get(key)
|
|
926
|
-
if value is None:
|
|
927
|
-
return None
|
|
928
|
-
if not isinstance(value, (list, tuple)) or not all(
|
|
929
|
-
isinstance(v, (int, float)) for v in value
|
|
930
|
-
):
|
|
931
|
-
raise ValueError(f"correction successor {key} must be numeric or null")
|
|
932
|
-
return [float(v) for v in value]
|
|
933
|
-
|
|
934
|
-
now = datetime.now(timezone.utc).isoformat()
|
|
935
|
-
successor = AtomicFact(
|
|
936
|
-
fact_id=successor_id,
|
|
937
|
-
memory_id=str(source.get("memory_id") or ""),
|
|
938
|
-
profile_id=profile_id,
|
|
939
|
-
scope=str(source.get("scope") or "personal"),
|
|
940
|
-
shared_with=json.loads(source["shared_with"]) if source.get("shared_with") else None,
|
|
941
|
-
content=content,
|
|
942
|
-
fact_type=FactType(str(source.get("fact_type") or "semantic")),
|
|
943
|
-
entities=json.loads(source["entities_json"]) if source.get("entities_json") else [],
|
|
944
|
-
canonical_entities=(
|
|
945
|
-
json.loads(source["canonical_entities_json"])
|
|
946
|
-
if source.get("canonical_entities_json") else []
|
|
947
|
-
),
|
|
948
|
-
observation_date=source.get("observation_date"),
|
|
949
|
-
referenced_date=source.get("referenced_date"),
|
|
950
|
-
interval_start=source.get("interval_start"),
|
|
951
|
-
interval_end=source.get("interval_end"),
|
|
952
|
-
confidence=float(source.get("confidence") or 0.0),
|
|
953
|
-
importance=float(source.get("importance") or 0.0),
|
|
954
|
-
evidence_count=1,
|
|
955
|
-
access_count=0,
|
|
956
|
-
source_turn_ids=(
|
|
957
|
-
json.loads(source["source_turn_ids_json"])
|
|
958
|
-
if source.get("source_turn_ids_json") else []
|
|
959
|
-
),
|
|
960
|
-
session_id=str(source.get("session_id") or ""),
|
|
961
|
-
embedding=_list_payload("embedding"),
|
|
962
|
-
fisher_mean=_list_payload("fisher_mean"),
|
|
963
|
-
fisher_variance=_list_payload("fisher_variance"),
|
|
964
|
-
lifecycle=MemoryLifecycle.ACTIVE,
|
|
965
|
-
langevin_position=None,
|
|
966
|
-
emotional_valence=float(source.get("emotional_valence") or 0.0),
|
|
967
|
-
emotional_arousal=float(source.get("emotional_arousal") or 0.0),
|
|
968
|
-
signal_type=SignalType(str(source.get("signal_type") or "factual")),
|
|
969
|
-
created_at=now,
|
|
970
|
-
)
|
|
971
|
-
persisted_id = db.insert_fact_immutable(successor)
|
|
972
|
-
from superlocalmemory.storage.correction_cases import (
|
|
973
|
-
CorrectionActor,
|
|
974
|
-
propose_on_connection,
|
|
975
|
-
)
|
|
976
|
-
|
|
977
|
-
trusted_actor_id = _payload_text(payload, "trusted_actor_id")
|
|
978
|
-
actor = CorrectionActor(
|
|
979
|
-
actor_id=trusted_actor_id,
|
|
980
|
-
actor_kind="host_authenticated",
|
|
981
|
-
trust_tier="trusted",
|
|
982
|
-
)
|
|
983
|
-
case_id = uuid.uuid5(
|
|
984
|
-
uuid.NAMESPACE_URL,
|
|
985
|
-
f"slm-correction:{profile_id}:{fact_id}:{persisted_id}",
|
|
986
|
-
).hex
|
|
987
|
-
case = propose_on_connection(
|
|
988
|
-
connection,
|
|
989
|
-
case_id=case_id,
|
|
990
|
-
profile_id=profile_id,
|
|
991
|
-
scope=str(source.get("scope") or "personal"),
|
|
992
|
-
predecessor_fact_id=fact_id,
|
|
993
|
-
successor_fact_id=persisted_id,
|
|
994
|
-
reason_code="direct_content_correction",
|
|
995
|
-
actor=actor,
|
|
996
|
-
idempotency_key=_payload_text(payload, "idempotency_key"),
|
|
997
|
-
is_profile_active=lambda candidate: candidate == profile_id,
|
|
998
|
-
is_actor_trusted=lambda candidate: candidate == actor,
|
|
999
|
-
)
|
|
1000
|
-
return {
|
|
1001
|
-
"ok": True,
|
|
1002
|
-
"operation_id": f"correction:{fact_id}:{persisted_id}",
|
|
1003
|
-
"predecessor_fact_id": fact_id,
|
|
1004
|
-
"successor_fact_id": persisted_id,
|
|
1005
|
-
"case_id": case.case_id,
|
|
1006
|
-
"status": case.status,
|
|
1007
|
-
"version": case.version,
|
|
1008
|
-
"created_at": now,
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
def _transition_correction(
|
|
1013
|
-
db: DatabaseManager,
|
|
1014
|
-
kind: CommandKind,
|
|
1015
|
-
profile_id: str,
|
|
1016
|
-
payload: Mapping[str, Any],
|
|
1017
|
-
*,
|
|
1018
|
-
connection: Any,
|
|
1019
|
-
) -> dict[str, Any]:
|
|
1020
|
-
"""Advance one correction case under the same receipt transaction."""
|
|
1021
|
-
from superlocalmemory.storage.correction_cases import (
|
|
1022
|
-
CorrectionActor,
|
|
1023
|
-
transition_on_connection,
|
|
1024
|
-
)
|
|
1025
|
-
|
|
1026
|
-
case_id = _payload_text(payload, "case_id")
|
|
1027
|
-
version = payload.get("expected_version")
|
|
1028
|
-
if not isinstance(version, int) or isinstance(version, bool) or version < 0:
|
|
1029
|
-
raise ValueError("correction expected_version must be a non-negative integer")
|
|
1030
|
-
actor = CorrectionActor(
|
|
1031
|
-
actor_id=_payload_text(payload, "trusted_actor_id"),
|
|
1032
|
-
actor_kind="host_authenticated",
|
|
1033
|
-
trust_tier="trusted",
|
|
1034
|
-
)
|
|
1035
|
-
transitions = {
|
|
1036
|
-
CommandKind.APPLY_CORRECTION: ("proposed", "applied", True),
|
|
1037
|
-
CommandKind.REJECT_CORRECTION: ("proposed", "rejected", False),
|
|
1038
|
-
CommandKind.ROLLBACK_CORRECTION: ("applied", "rolled_back", True),
|
|
1039
|
-
}
|
|
1040
|
-
from_status, to_status, mutate_temporal = transitions[kind]
|
|
1041
|
-
case = transition_on_connection(
|
|
1042
|
-
connection,
|
|
1043
|
-
case_id=case_id,
|
|
1044
|
-
expected_version=version,
|
|
1045
|
-
actor=actor,
|
|
1046
|
-
operation_id=_payload_text(payload, "idempotency_key"),
|
|
1047
|
-
from_status=from_status,
|
|
1048
|
-
to_status=to_status,
|
|
1049
|
-
mutate_temporal=mutate_temporal,
|
|
1050
|
-
event_valid_until=payload.get("event_valid_until"),
|
|
1051
|
-
is_profile_active=lambda candidate: candidate == profile_id,
|
|
1052
|
-
is_actor_trusted=lambda candidate: candidate == actor,
|
|
1053
|
-
)
|
|
1054
|
-
return {
|
|
1055
|
-
"ok": True,
|
|
1056
|
-
"operation_id": f"correction:{to_status}:{case.case_id}",
|
|
1057
|
-
"case_id": case.case_id,
|
|
1058
|
-
"predecessor_fact_id": case.predecessor_fact_id,
|
|
1059
|
-
"successor_fact_id": case.successor_fact_id,
|
|
1060
|
-
"status": case.status,
|
|
1061
|
-
"version": case.version,
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
def _archive_fact(
|
|
1066
|
-
db: DatabaseManager, fact_id: str, profile_id: str, payload: Mapping[str, Any],
|
|
1067
|
-
) -> dict[str, Any]:
|
|
1068
|
-
row = _fact_row(db, fact_id, profile_id)
|
|
1069
|
-
if row is None:
|
|
1070
|
-
return {"ok": False, "operation_id": f"archive:{fact_id}", "fact_id": fact_id}
|
|
1071
|
-
command_key = _payload_text(payload, "idempotency_key")
|
|
1072
|
-
archive_id = str(uuid.uuid5(uuid.NAMESPACE_URL, f"{command_key}:archive"))
|
|
1073
|
-
archived_at = datetime.now(timezone.utc).isoformat()
|
|
1074
|
-
archive_payload = {
|
|
1075
|
-
key: row.get(key)
|
|
1076
|
-
for key in (
|
|
1077
|
-
"fact_id",
|
|
1078
|
-
"content",
|
|
1079
|
-
"canonical_entities_json",
|
|
1080
|
-
"importance",
|
|
1081
|
-
"confidence",
|
|
1082
|
-
"created_at",
|
|
1083
|
-
)
|
|
1084
|
-
}
|
|
1085
|
-
db.execute(
|
|
1086
|
-
"INSERT INTO memory_archive "
|
|
1087
|
-
"(archive_id, fact_id, profile_id, payload_json, archived_at, reason) "
|
|
1088
|
-
"VALUES (?, ?, ?, ?, ?, ?)",
|
|
1089
|
-
(
|
|
1090
|
-
archive_id,
|
|
1091
|
-
fact_id,
|
|
1092
|
-
profile_id,
|
|
1093
|
-
json.dumps(archive_payload),
|
|
1094
|
-
archived_at,
|
|
1095
|
-
"user_forget_dashboard",
|
|
1096
|
-
),
|
|
1097
|
-
)
|
|
1098
|
-
db.execute(
|
|
1099
|
-
"UPDATE atomic_facts SET archive_status = 'archived' "
|
|
1100
|
-
"WHERE fact_id = ? AND profile_id = ?",
|
|
1101
|
-
(fact_id, profile_id),
|
|
1102
|
-
)
|
|
1103
|
-
return {
|
|
1104
|
-
"ok": True,
|
|
1105
|
-
"operation_id": f"archive:{fact_id}",
|
|
1106
|
-
"fact_id": fact_id,
|
|
1107
|
-
"archived_at": archived_at,
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
def _merged_fact_ids(
|
|
1112
|
-
db: DatabaseManager, fact_id: str, kept: str, profile_id: str,
|
|
1113
|
-
) -> set[str]:
|
|
1114
|
-
rows = db.execute(
|
|
1115
|
-
"SELECT fact_id FROM atomic_facts "
|
|
1116
|
-
"WHERE fact_id IN (?, ?) AND profile_id = ?",
|
|
1117
|
-
(fact_id, kept, profile_id),
|
|
1118
|
-
)
|
|
1119
|
-
return {row["fact_id"] for row in rows}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
def _merge_fact(
|
|
1123
|
-
db: DatabaseManager, fact_id: str, profile_id: str, payload: Mapping[str, Any],
|
|
1124
|
-
) -> dict[str, Any]:
|
|
1125
|
-
kept = _payload_text(payload, "kept_fact_id")
|
|
1126
|
-
if kept == fact_id:
|
|
1127
|
-
raise ValueError("cannot merge a fact into itself")
|
|
1128
|
-
found = _merged_fact_ids(db, fact_id, kept, profile_id)
|
|
1129
|
-
if fact_id not in found or kept not in found:
|
|
1130
|
-
return {"ok": False, "operation_id": f"merge:{fact_id}:{kept}", "fact_id": fact_id}
|
|
1131
|
-
command_key = _payload_text(payload, "idempotency_key")
|
|
1132
|
-
merge_id = str(uuid.uuid5(uuid.NAMESPACE_URL, f"{command_key}:merge"))
|
|
1133
|
-
merged_at = datetime.now(timezone.utc).isoformat()
|
|
1134
|
-
db.execute(
|
|
1135
|
-
"INSERT INTO memory_merge_log "
|
|
1136
|
-
"(merge_id, profile_id, canonical_fact_id, merged_fact_id, "
|
|
1137
|
-
"cosine_sim, entity_jaccard, merged_at, reversible) "
|
|
1138
|
-
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
|
1139
|
-
(merge_id, profile_id, kept, fact_id, None, None, merged_at, 1),
|
|
1140
|
-
)
|
|
1141
|
-
db.execute(
|
|
1142
|
-
"UPDATE atomic_facts SET merged_into = ?, archive_status = 'merged', "
|
|
1143
|
-
"archive_reason = 'user_merge_dashboard' "
|
|
1144
|
-
"WHERE fact_id = ? AND profile_id = ?",
|
|
1145
|
-
(kept, fact_id, profile_id),
|
|
1146
|
-
)
|
|
1147
|
-
return {
|
|
1148
|
-
"ok": True,
|
|
1149
|
-
"operation_id": f"merge:{fact_id}:{kept}",
|
|
1150
|
-
"merged": fact_id,
|
|
1151
|
-
"into": kept,
|
|
1152
|
-
"merged_at": merged_at,
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
def _set_fact_scope(
|
|
1157
|
-
db: DatabaseManager, fact_id: str, profile_id: str, payload: Mapping[str, Any],
|
|
1158
|
-
) -> dict[str, Any]:
|
|
1159
|
-
scope = _payload_text(payload, "scope")
|
|
1160
|
-
shared_with = payload.get("shared_with")
|
|
1161
|
-
if scope not in {"personal", "shared", "global"} or not isinstance(shared_with, tuple):
|
|
1162
|
-
raise ValueError("invalid scope command")
|
|
1163
|
-
if _fact_row(db, fact_id, profile_id) is None:
|
|
1164
|
-
return {"ok": False, "operation_id": f"scope:{fact_id}", "fact_id": fact_id}
|
|
1165
|
-
values = [str(item) for item in shared_with]
|
|
1166
|
-
db.update_fact(fact_id, {"scope": scope, "shared_with": values}, profile_id=profile_id)
|
|
1167
|
-
return {
|
|
1168
|
-
"ok": True,
|
|
1169
|
-
"operation_id": f"scope:{fact_id}",
|
|
1170
|
-
"fact_id": fact_id,
|
|
1171
|
-
"scope": scope,
|
|
1172
|
-
"shared_with": values,
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
def _thaw_command_value(value: Any) -> Any:
|
|
1177
|
-
"""Restore coordinator-frozen JSON lists before DatabaseManager serializes them."""
|
|
1178
|
-
if isinstance(value, Mapping):
|
|
1179
|
-
return {key: _thaw_command_value(item) for key, item in value.items()}
|
|
1180
|
-
if isinstance(value, tuple):
|
|
1181
|
-
return [_thaw_command_value(item) for item in value]
|
|
1182
|
-
return value
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
__all__ = [
|
|
1186
|
-
"CanonicalRememberRuntime",
|
|
1187
|
-
"CanonicalRememberUnavailable",
|
|
1188
|
-
"DaemonAlreadyServing",
|
|
1189
|
-
"validate_deterministic_admission",
|
|
1190
|
-
]
|