superlocalmemory 4.1.13 → 4.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +103 -0
- package/README.md +3 -3
- package/package.json +1 -8
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/agents/slm-memory-advisor.md +1 -1
- package/plugin-src/agents/slm-optimize-advisor.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-governance/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-loop/SKILL.md +1 -1
- package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
- package/plugin-src/skills/slm-profile/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +1 -1
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-scope/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/scripts/postinstall.js +71 -2
- package/pyproject.toml +0 -250
- package/src/superlocalmemory/__init__.py +0 -82
- package/src/superlocalmemory/access/__init__.py +0 -3
- package/src/superlocalmemory/access/rbac.py +0 -575
- package/src/superlocalmemory/attribution/__init__.py +0 -9
- package/src/superlocalmemory/attribution/signer.py +0 -173
- package/src/superlocalmemory/attribution/watermark.py +0 -189
- package/src/superlocalmemory/brain/__init__.py +0 -5
- package/src/superlocalmemory/brain/truth.py +0 -418
- package/src/superlocalmemory/cli/__init__.py +0 -5
- package/src/superlocalmemory/cli/__main__.py +0 -17
- package/src/superlocalmemory/cli/_lazy_init.py +0 -115
- package/src/superlocalmemory/cli/cache_cmd.py +0 -198
- package/src/superlocalmemory/cli/commands.py +0 -4710
- package/src/superlocalmemory/cli/compress_cmd.py +0 -151
- package/src/superlocalmemory/cli/context_commands.py +0 -193
- package/src/superlocalmemory/cli/daemon.py +0 -909
- package/src/superlocalmemory/cli/db_migrate.py +0 -150
- package/src/superlocalmemory/cli/diagnostics_cmd.py +0 -101
- package/src/superlocalmemory/cli/escape_hatch.py +0 -220
- package/src/superlocalmemory/cli/evidence_cmd.py +0 -103
- package/src/superlocalmemory/cli/gdpr_cmd.py +0 -792
- package/src/superlocalmemory/cli/gdpr_io.py +0 -109
- package/src/superlocalmemory/cli/help_cmd.py +0 -197
- package/src/superlocalmemory/cli/host_upgrades.py +0 -189
- package/src/superlocalmemory/cli/ingest_cmd.py +0 -327
- package/src/superlocalmemory/cli/json_output.py +0 -81
- package/src/superlocalmemory/cli/loop_cmd.py +0 -187
- package/src/superlocalmemory/cli/main.py +0 -1145
- package/src/superlocalmemory/cli/mesh_cmd.py +0 -38
- package/src/superlocalmemory/cli/migrate_cmd.py +0 -55
- package/src/superlocalmemory/cli/ops_cmd.py +0 -281
- package/src/superlocalmemory/cli/optimize_cmd.py +0 -179
- package/src/superlocalmemory/cli/optimize_constants.py +0 -31
- package/src/superlocalmemory/cli/pending_store.py +0 -296
- package/src/superlocalmemory/cli/proxy_cmd.py +0 -108
- package/src/superlocalmemory/cli/scale_engine_cmd.py +0 -56
- package/src/superlocalmemory/cli/service_installer.py +0 -373
- package/src/superlocalmemory/cli/setup_wizard.py +0 -1162
- package/src/superlocalmemory/cli/summary_cmd.py +0 -215
- package/src/superlocalmemory/cli/version_banner.py +0 -202
- package/src/superlocalmemory/cli/wizard_v3426_options.py +0 -129
- package/src/superlocalmemory/code_graph/__init__.py +0 -46
- package/src/superlocalmemory/code_graph/blast_radius.py +0 -177
- package/src/superlocalmemory/code_graph/bridge/__init__.py +0 -36
- package/src/superlocalmemory/code_graph/bridge/entity_resolver.py +0 -490
- package/src/superlocalmemory/code_graph/bridge/event_listeners.py +0 -206
- package/src/superlocalmemory/code_graph/bridge/fact_enricher.py +0 -159
- package/src/superlocalmemory/code_graph/bridge/hebbian_linker.py +0 -170
- package/src/superlocalmemory/code_graph/bridge/maintenance.py +0 -220
- package/src/superlocalmemory/code_graph/bridge/temporal_checker.py +0 -152
- package/src/superlocalmemory/code_graph/changes.py +0 -363
- package/src/superlocalmemory/code_graph/communities.py +0 -299
- package/src/superlocalmemory/code_graph/config.py +0 -154
- package/src/superlocalmemory/code_graph/database.py +0 -526
- package/src/superlocalmemory/code_graph/extractors/__init__.py +0 -95
- package/src/superlocalmemory/code_graph/extractors/python.py +0 -413
- package/src/superlocalmemory/code_graph/extractors/typescript.py +0 -556
- package/src/superlocalmemory/code_graph/flows.py +0 -350
- package/src/superlocalmemory/code_graph/git_hooks.py +0 -226
- package/src/superlocalmemory/code_graph/graph_engine.py +0 -295
- package/src/superlocalmemory/code_graph/graph_store.py +0 -335
- package/src/superlocalmemory/code_graph/incremental.py +0 -200
- package/src/superlocalmemory/code_graph/models.py +0 -130
- package/src/superlocalmemory/code_graph/parser.py +0 -687
- package/src/superlocalmemory/code_graph/resolver.py +0 -321
- package/src/superlocalmemory/code_graph/search.py +0 -460
- package/src/superlocalmemory/code_graph/service.py +0 -95
- package/src/superlocalmemory/code_graph/watcher.py +0 -207
- package/src/superlocalmemory/compliance/__init__.py +0 -0
- package/src/superlocalmemory/compliance/abac.py +0 -204
- package/src/superlocalmemory/compliance/audit.py +0 -385
- package/src/superlocalmemory/compliance/eu_ai_act.py +0 -101
- package/src/superlocalmemory/compliance/gdpr.py +0 -1479
- package/src/superlocalmemory/compliance/lifecycle.py +0 -158
- package/src/superlocalmemory/compliance/retention.py +0 -415
- package/src/superlocalmemory/compliance/scheduler.py +0 -217
- package/src/superlocalmemory/contracts/__init__.py +0 -1
- package/src/superlocalmemory/contracts/schemas/agent-experience-v1.schema.json +0 -92
- package/src/superlocalmemory/contracts/schemas/agent-integration-contract-v2.schema.json +0 -46
- package/src/superlocalmemory/contracts/schemas/cognitive-turn-receipt-v1.schema.json +0 -59
- package/src/superlocalmemory/contracts/v402.py +0 -62
- package/src/superlocalmemory/core/__init__.py +0 -0
- package/src/superlocalmemory/core/actor_context.py +0 -166
- package/src/superlocalmemory/core/admission.py +0 -769
- package/src/superlocalmemory/core/backend_orchestrator.py +0 -637
- package/src/superlocalmemory/core/block_hygiene.py +0 -147
- package/src/superlocalmemory/core/community_summary.py +0 -267
- package/src/superlocalmemory/core/component_healer.py +0 -144
- package/src/superlocalmemory/core/component_registry.py +0 -514
- package/src/superlocalmemory/core/config.py +0 -2204
- package/src/superlocalmemory/core/consolidation_engine.py +0 -983
- package/src/superlocalmemory/core/context_cache.py +0 -574
- package/src/superlocalmemory/core/derivation_lineage.py +0 -246
- package/src/superlocalmemory/core/embedding_worker.py +0 -208
- package/src/superlocalmemory/core/embeddings.py +0 -1052
- package/src/superlocalmemory/core/engine.py +0 -1395
- package/src/superlocalmemory/core/engine_capabilities.py +0 -24
- package/src/superlocalmemory/core/engine_ingestion.py +0 -983
- package/src/superlocalmemory/core/engine_lock.py +0 -75
- package/src/superlocalmemory/core/engine_wiring.py +0 -776
- package/src/superlocalmemory/core/entity_community.py +0 -178
- package/src/superlocalmemory/core/error_envelope.py +0 -60
- package/src/superlocalmemory/core/evidence_bundle.py +0 -528
- package/src/superlocalmemory/core/fact_consolidator.py +0 -812
- package/src/superlocalmemory/core/file_lock.py +0 -92
- package/src/superlocalmemory/core/graph_analyzer.py +0 -456
- package/src/superlocalmemory/core/graph_metrics.py +0 -597
- package/src/superlocalmemory/core/graph_pruner.py +0 -939
- package/src/superlocalmemory/core/health_monitor.py +0 -338
- package/src/superlocalmemory/core/hooks.py +0 -65
- package/src/superlocalmemory/core/ingest_gate.py +0 -133
- package/src/superlocalmemory/core/ingest_policy.py +0 -38
- package/src/superlocalmemory/core/ingestion_command.py +0 -1042
- package/src/superlocalmemory/core/injection.py +0 -434
- package/src/superlocalmemory/core/install_detector.py +0 -131
- package/src/superlocalmemory/core/key_expander.py +0 -138
- package/src/superlocalmemory/core/lifecycle_state.py +0 -153
- package/src/superlocalmemory/core/maintenance.py +0 -777
- package/src/superlocalmemory/core/maintenance_scheduler.py +0 -507
- package/src/superlocalmemory/core/materialization_control.py +0 -20
- package/src/superlocalmemory/core/mcp_embedder_proxy.py +0 -89
- package/src/superlocalmemory/core/memory_health.py +0 -266
- package/src/superlocalmemory/core/mode_capability.py +0 -111
- package/src/superlocalmemory/core/modes.py +0 -168
- package/src/superlocalmemory/core/mutations.py +0 -688
- package/src/superlocalmemory/core/ollama_embedder.py +0 -266
- package/src/superlocalmemory/core/ollama_validator.py +0 -315
- package/src/superlocalmemory/core/operation_policy.py +0 -92
- package/src/superlocalmemory/core/operation_policy_registry.py +0 -542
- package/src/superlocalmemory/core/operation_request.py +0 -127
- package/src/superlocalmemory/core/ops_remediation.py +0 -542
- package/src/superlocalmemory/core/pii.py +0 -105
- package/src/superlocalmemory/core/platform_utils.py +0 -138
- package/src/superlocalmemory/core/profiles.py +0 -234
- package/src/superlocalmemory/core/progressive_abstraction.py +0 -208
- package/src/superlocalmemory/core/projection_drain.py +0 -380
- package/src/superlocalmemory/core/queue_consumer.py +0 -168
- package/src/superlocalmemory/core/ram_lock.py +0 -160
- package/src/superlocalmemory/core/rate_limit.py +0 -151
- package/src/superlocalmemory/core/recall_gate.py +0 -95
- package/src/superlocalmemory/core/recall_pipeline.py +0 -1337
- package/src/superlocalmemory/core/recall_queue.py +0 -377
- package/src/superlocalmemory/core/recall_worker.py +0 -414
- package/src/superlocalmemory/core/registry.py +0 -121
- package/src/superlocalmemory/core/remember_admission.py +0 -161
- package/src/superlocalmemory/core/remember_runtime.py +0 -1190
- package/src/superlocalmemory/core/remote_mode.py +0 -214
- package/src/superlocalmemory/core/reranker_worker.py +0 -338
- package/src/superlocalmemory/core/safe_fs.py +0 -108
- package/src/superlocalmemory/core/scale_autopromote.py +0 -196
- package/src/superlocalmemory/core/scale_engine.py +0 -915
- package/src/superlocalmemory/core/score_contract.py +0 -82
- package/src/superlocalmemory/core/security_primitives.py +0 -672
- package/src/superlocalmemory/core/session_identity.py +0 -98
- package/src/superlocalmemory/core/shadow_router.py +0 -319
- package/src/superlocalmemory/core/slm_disabled.py +0 -85
- package/src/superlocalmemory/core/status_contract.py +0 -108
- package/src/superlocalmemory/core/store_pipeline.py +0 -1404
- package/src/superlocalmemory/core/summarizer.py +0 -200
- package/src/superlocalmemory/core/tier_manager.py +0 -461
- package/src/superlocalmemory/core/topic_signature.py +0 -156
- package/src/superlocalmemory/core/transactions/__init__.py +0 -78
- package/src/superlocalmemory/core/transactions/concrete_owners.py +0 -604
- package/src/superlocalmemory/core/transactions/erasure.py +0 -825
- package/src/superlocalmemory/core/transactions/manifest.py +0 -255
- package/src/superlocalmemory/core/transactions/manifest_key.py +0 -155
- package/src/superlocalmemory/core/transactions/obligations.py +0 -272
- package/src/superlocalmemory/core/transactions/owners.py +0 -114
- package/src/superlocalmemory/core/transactions/reconciler.py +0 -285
- package/src/superlocalmemory/core/transactions/service.py +0 -330
- package/src/superlocalmemory/core/worker_pool.py +0 -377
- package/src/superlocalmemory/core/working_memory.py +0 -288
- package/src/superlocalmemory/dynamics/__init__.py +0 -0
- package/src/superlocalmemory/dynamics/activation_guided_quantization.py +0 -374
- package/src/superlocalmemory/dynamics/eap_scheduler.py +0 -294
- package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +0 -171
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +0 -227
- package/src/superlocalmemory/encoding/__init__.py +0 -0
- package/src/superlocalmemory/encoding/auto_linker.py +0 -308
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +0 -899
- package/src/superlocalmemory/encoding/consolidator.py +0 -472
- package/src/superlocalmemory/encoding/context_generator.py +0 -175
- package/src/superlocalmemory/encoding/emotional.py +0 -189
- package/src/superlocalmemory/encoding/entity_reflexion.py +0 -200
- package/src/superlocalmemory/encoding/entity_resolver.py +0 -687
- package/src/superlocalmemory/encoding/entropy_gate.py +0 -101
- package/src/superlocalmemory/encoding/fact_extractor.py +0 -877
- package/src/superlocalmemory/encoding/foresight.py +0 -93
- package/src/superlocalmemory/encoding/graph_builder.py +0 -346
- package/src/superlocalmemory/encoding/observation_builder.py +0 -177
- package/src/superlocalmemory/encoding/prospective_markers.py +0 -262
- package/src/superlocalmemory/encoding/scene_builder.py +0 -410
- package/src/superlocalmemory/encoding/signal_inference.py +0 -90
- package/src/superlocalmemory/encoding/temporal_parser.py +0 -432
- package/src/superlocalmemory/encoding/temporal_validator.py +0 -572
- package/src/superlocalmemory/encoding/type_router.py +0 -237
- package/src/superlocalmemory/evaluation/__init__.py +0 -13
- package/src/superlocalmemory/evaluation/calibration.py +0 -308
- package/src/superlocalmemory/evolution/__init__.py +0 -29
- package/src/superlocalmemory/evolution/blind_verifier.py +0 -122
- package/src/superlocalmemory/evolution/budget.py +0 -356
- package/src/superlocalmemory/evolution/evolution_store.py +0 -619
- package/src/superlocalmemory/evolution/llm_dispatch.py +0 -559
- package/src/superlocalmemory/evolution/model_selection.py +0 -175
- package/src/superlocalmemory/evolution/mutation_generator.py +0 -226
- package/src/superlocalmemory/evolution/skill_activator.py +0 -270
- package/src/superlocalmemory/evolution/skill_evolver.py +0 -928
- package/src/superlocalmemory/evolution/triggers.py +0 -376
- package/src/superlocalmemory/evolution/types.py +0 -114
- package/src/superlocalmemory/graph/__init__.py +0 -9
- package/src/superlocalmemory/graph/cozo_adjacency.py +0 -122
- package/src/superlocalmemory/graph/cozo_backend.py +0 -751
- package/src/superlocalmemory/hooks/__init__.py +0 -3
- package/src/superlocalmemory/hooks/_outcome_common.py +0 -523
- package/src/superlocalmemory/hooks/adapter_base.py +0 -347
- package/src/superlocalmemory/hooks/antigravity_adapter.py +0 -171
- package/src/superlocalmemory/hooks/auto_capture.py +0 -133
- package/src/superlocalmemory/hooks/auto_invoker.py +0 -521
- package/src/superlocalmemory/hooks/auto_parameterize.py +0 -147
- package/src/superlocalmemory/hooks/auto_recall.py +0 -191
- package/src/superlocalmemory/hooks/auto_recall_hook.py +0 -251
- package/src/superlocalmemory/hooks/before_web_hook.py +0 -131
- package/src/superlocalmemory/hooks/claude_code_hooks.py +0 -637
- package/src/superlocalmemory/hooks/codex_assets.py +0 -251
- package/src/superlocalmemory/hooks/codex_hooks.py +0 -186
- package/src/superlocalmemory/hooks/context_payload.py +0 -311
- package/src/superlocalmemory/hooks/copilot_adapter.py +0 -208
- package/src/superlocalmemory/hooks/cross_platform_connector.py +0 -90
- package/src/superlocalmemory/hooks/cursor_adapter.py +0 -173
- package/src/superlocalmemory/hooks/hook_daemon.py +0 -295
- package/src/superlocalmemory/hooks/hook_handlers.py +0 -822
- package/src/superlocalmemory/hooks/ide_connector.py +0 -246
- package/src/superlocalmemory/hooks/memory_protocol.py +0 -158
- package/src/superlocalmemory/hooks/portable_kit.py +0 -755
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +0 -183
- package/src/superlocalmemory/hooks/post_tool_outcome_hook.py +0 -351
- package/src/superlocalmemory/hooks/prewarm_auth.py +0 -187
- package/src/superlocalmemory/hooks/rules_engine.py +0 -99
- package/src/superlocalmemory/hooks/session_registry.py +0 -330
- package/src/superlocalmemory/hooks/stop_outcome_hook.py +0 -138
- package/src/superlocalmemory/hooks/sync_loop.py +0 -114
- package/src/superlocalmemory/hooks/topic_shift_hook.py +0 -302
- package/src/superlocalmemory/hooks/user_prompt_hook.py +0 -131
- package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +0 -210
- package/src/superlocalmemory/infra/__init__.py +0 -3
- package/src/superlocalmemory/infra/auth_middleware.py +0 -145
- package/src/superlocalmemory/infra/backup.py +0 -974
- package/src/superlocalmemory/infra/backup_obligations.py +0 -423
- package/src/superlocalmemory/infra/cache_manager.py +0 -267
- package/src/superlocalmemory/infra/cloud_backup.py +0 -788
- package/src/superlocalmemory/infra/daemon_identity.py +0 -300
- package/src/superlocalmemory/infra/data_root.py +0 -238
- package/src/superlocalmemory/infra/event_bus.py +0 -637
- package/src/superlocalmemory/infra/local_diagnostics.py +0 -327
- package/src/superlocalmemory/infra/pid_manager.py +0 -193
- package/src/superlocalmemory/infra/process_identity.py +0 -180
- package/src/superlocalmemory/infra/process_reaper.py +0 -624
- package/src/superlocalmemory/infra/rate_limiter.py +0 -228
- package/src/superlocalmemory/infra/self_heal.py +0 -401
- package/src/superlocalmemory/infra/version_integrity.py +0 -229
- package/src/superlocalmemory/ingestion/__init__.py +0 -13
- package/src/superlocalmemory/ingestion/adapter_manager.py +0 -255
- package/src/superlocalmemory/ingestion/base_adapter.py +0 -171
- package/src/superlocalmemory/ingestion/calendar_adapter.py +0 -349
- package/src/superlocalmemory/ingestion/credentials.py +0 -125
- package/src/superlocalmemory/ingestion/gmail_adapter.py +0 -378
- package/src/superlocalmemory/ingestion/parsers.py +0 -100
- package/src/superlocalmemory/ingestion/transcript_adapter.py +0 -161
- package/src/superlocalmemory/integrations/__init__.py +0 -1
- package/src/superlocalmemory/integrations/bounded_loops_mcp.py +0 -431
- package/src/superlocalmemory/integrations/bounded_loops_v051.py +0 -236
- package/src/superlocalmemory/learning/__init__.py +0 -0
- package/src/superlocalmemory/learning/adaptive.py +0 -172
- package/src/superlocalmemory/learning/arm_catalog.py +0 -97
- package/src/superlocalmemory/learning/assertion_miner.py +0 -403
- package/src/superlocalmemory/learning/bandit.py +0 -654
- package/src/superlocalmemory/learning/bandit_cache.py +0 -131
- package/src/superlocalmemory/learning/behavioral.py +0 -542
- package/src/superlocalmemory/learning/bootstrap.py +0 -298
- package/src/superlocalmemory/learning/consolidation_cycle.py +0 -398
- package/src/superlocalmemory/learning/consolidation_quantization_worker.py +0 -115
- package/src/superlocalmemory/learning/consolidation_worker.py +0 -261
- package/src/superlocalmemory/learning/cross_project.py +0 -408
- package/src/superlocalmemory/learning/database.py +0 -698
- package/src/superlocalmemory/learning/dedup_hnsw.py +0 -413
- package/src/superlocalmemory/learning/engagement.py +0 -487
- package/src/superlocalmemory/learning/engagement_features.py +0 -279
- package/src/superlocalmemory/learning/ensemble.py +0 -309
- package/src/superlocalmemory/learning/entity_compiler.py +0 -356
- package/src/superlocalmemory/learning/fact_outcome_joins.py +0 -207
- package/src/superlocalmemory/learning/features.py +0 -138
- package/src/superlocalmemory/learning/feedback.py +0 -724
- package/src/superlocalmemory/learning/forgetting_scheduler.py +0 -375
- package/src/superlocalmemory/learning/hnsw_dedup.py +0 -69
- package/src/superlocalmemory/learning/labeler.py +0 -85
- package/src/superlocalmemory/learning/legacy_migration.py +0 -316
- package/src/superlocalmemory/learning/lightgbm_subprocess.py +0 -236
- package/src/superlocalmemory/learning/memory_merge.py +0 -175
- package/src/superlocalmemory/learning/model_cache.py +0 -267
- package/src/superlocalmemory/learning/model_rollback.py +0 -281
- package/src/superlocalmemory/learning/outcome_queue.py +0 -306
- package/src/superlocalmemory/learning/outcomes.py +0 -286
- package/src/superlocalmemory/learning/pattern_miner.py +0 -465
- package/src/superlocalmemory/learning/pattern_miner_constants.py +0 -90
- package/src/superlocalmemory/learning/pcos.py +0 -291
- package/src/superlocalmemory/learning/project_context.py +0 -366
- package/src/superlocalmemory/learning/propensity.py +0 -131
- package/src/superlocalmemory/learning/ranker.py +0 -300
- package/src/superlocalmemory/learning/ranker_common.py +0 -163
- package/src/superlocalmemory/learning/ranker_retrain_legacy.py +0 -210
- package/src/superlocalmemory/learning/ranker_retrain_online.py +0 -423
- package/src/superlocalmemory/learning/reward.py +0 -888
- package/src/superlocalmemory/learning/reward_archive.py +0 -223
- package/src/superlocalmemory/learning/reward_boost.py +0 -211
- package/src/superlocalmemory/learning/reward_from_outcomes.py +0 -365
- package/src/superlocalmemory/learning/reward_model.py +0 -144
- package/src/superlocalmemory/learning/reward_proxy.py +0 -578
- package/src/superlocalmemory/learning/shadow_test.py +0 -524
- package/src/superlocalmemory/learning/signal_kinds.py +0 -79
- package/src/superlocalmemory/learning/signal_worker.py +0 -268
- package/src/superlocalmemory/learning/signals.py +0 -646
- package/src/superlocalmemory/learning/skill_performance_miner.py +0 -422
- package/src/superlocalmemory/learning/source_quality.py +0 -828
- package/src/superlocalmemory/learning/trigram_index.py +0 -548
- package/src/superlocalmemory/learning/workflows.py +0 -309
- package/src/superlocalmemory/llm/__init__.py +0 -0
- package/src/superlocalmemory/llm/backbone.py +0 -364
- package/src/superlocalmemory/loops/__init__.py +0 -58
- package/src/superlocalmemory/loops/budget.py +0 -58
- package/src/superlocalmemory/loops/engine.py +0 -174
- package/src/superlocalmemory/loops/ledger.py +0 -298
- package/src/superlocalmemory/loops/models.py +0 -152
- package/src/superlocalmemory/loops/rules.py +0 -52
- package/src/superlocalmemory/math/__init__.py +0 -0
- package/src/superlocalmemory/math/ebbinghaus.py +0 -352
- package/src/superlocalmemory/math/fisher.py +0 -356
- package/src/superlocalmemory/math/fisher_quantized.py +0 -255
- package/src/superlocalmemory/math/hopfield.py +0 -282
- package/src/superlocalmemory/math/langevin.py +0 -411
- package/src/superlocalmemory/math/polar_quant.py +0 -414
- package/src/superlocalmemory/math/qjl.py +0 -115
- package/src/superlocalmemory/math/sheaf.py +0 -261
- package/src/superlocalmemory/math/turbo_quant.py +0 -318
- package/src/superlocalmemory/mcp/__init__.py +0 -0
- package/src/superlocalmemory/mcp/_daemon_proxy.py +0 -203
- package/src/superlocalmemory/mcp/_pool_adapter.py +0 -181
- package/src/superlocalmemory/mcp/_stdin_guard.py +0 -60
- package/src/superlocalmemory/mcp/agent_context.py +0 -115
- package/src/superlocalmemory/mcp/cli_fallback.py +0 -602
- package/src/superlocalmemory/mcp/http_transport.py +0 -85
- package/src/superlocalmemory/mcp/profiles.py +0 -154
- package/src/superlocalmemory/mcp/resources.py +0 -281
- package/src/superlocalmemory/mcp/server.py +0 -480
- package/src/superlocalmemory/mcp/session_binding.py +0 -98
- package/src/superlocalmemory/mcp/shared.py +0 -112
- package/src/superlocalmemory/mcp/tools.py +0 -18
- package/src/superlocalmemory/mcp/tools_active.py +0 -958
- package/src/superlocalmemory/mcp/tools_brain.py +0 -298
- package/src/superlocalmemory/mcp/tools_code_graph.py +0 -1717
- package/src/superlocalmemory/mcp/tools_context.py +0 -239
- package/src/superlocalmemory/mcp/tools_core.py +0 -1112
- package/src/superlocalmemory/mcp/tools_evolution.py +0 -343
- package/src/superlocalmemory/mcp/tools_learning.py +0 -393
- package/src/superlocalmemory/mcp/tools_loops.py +0 -345
- package/src/superlocalmemory/mcp/tools_mesh.py +0 -429
- package/src/superlocalmemory/mcp/tools_ops.py +0 -115
- package/src/superlocalmemory/mcp/tools_optimize.py +0 -322
- package/src/superlocalmemory/mcp/tools_summaries.py +0 -147
- package/src/superlocalmemory/mcp/tools_v28.py +0 -292
- package/src/superlocalmemory/mcp/tools_v3.py +0 -398
- package/src/superlocalmemory/mcp/tools_v33.py +0 -507
- package/src/superlocalmemory/mesh/__init__.py +0 -12
- package/src/superlocalmemory/mesh/broker.py +0 -812
- package/src/superlocalmemory/mesh/broker_security.py +0 -470
- package/src/superlocalmemory/mesh/discovery.py +0 -365
- package/src/superlocalmemory/mesh/lock_protocol.py +0 -313
- package/src/superlocalmemory/mesh/node_identity.py +0 -97
- package/src/superlocalmemory/mesh/outbox_remote.py +0 -429
- package/src/superlocalmemory/mesh/remote_sync.py +0 -829
- package/src/superlocalmemory/mesh/state_sync.py +0 -286
- package/src/superlocalmemory/migrations/__init__.py +0 -5
- package/src/superlocalmemory/migrations/v3_4_25_to_v3_4_26.py +0 -144
- package/src/superlocalmemory/optimize/NOTICE +0 -6
- package/src/superlocalmemory/optimize/__init__.py +0 -0
- package/src/superlocalmemory/optimize/adapters/__init__.py +0 -68
- package/src/superlocalmemory/optimize/adapters/_agent_registry.py +0 -120
- package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +0 -112
- package/src/superlocalmemory/optimize/adapters/openai_adapter.py +0 -122
- package/src/superlocalmemory/optimize/adapters/wrap.py +0 -228
- package/src/superlocalmemory/optimize/cache/__init__.py +0 -31
- package/src/superlocalmemory/optimize/cache/boundary_store.py +0 -488
- package/src/superlocalmemory/optimize/cache/centroid_store.py +0 -199
- package/src/superlocalmemory/optimize/cache/context_key.py +0 -67
- package/src/superlocalmemory/optimize/cache/exact.py +0 -88
- package/src/superlocalmemory/optimize/cache/invalidation.py +0 -36
- package/src/superlocalmemory/optimize/cache/key_builder.py +0 -111
- package/src/superlocalmemory/optimize/cache/manager.py +0 -737
- package/src/superlocalmemory/optimize/cache/semantic.py +0 -637
- package/src/superlocalmemory/optimize/cache/stampede.py +0 -50
- package/src/superlocalmemory/optimize/compress/__init__.py +0 -17
- package/src/superlocalmemory/optimize/compress/align.py +0 -159
- package/src/superlocalmemory/optimize/compress/ccr.py +0 -116
- package/src/superlocalmemory/optimize/compress/prose_llmlingua.py +0 -71
- package/src/superlocalmemory/optimize/compress/router.py +0 -667
- package/src/superlocalmemory/optimize/config/__init__.py +0 -56
- package/src/superlocalmemory/optimize/config/defaults.py +0 -43
- package/src/superlocalmemory/optimize/config/schema.py +0 -327
- package/src/superlocalmemory/optimize/config/store.py +0 -270
- package/src/superlocalmemory/optimize/metrics/__init__.py +0 -8
- package/src/superlocalmemory/optimize/metrics/counters.py +0 -155
- package/src/superlocalmemory/optimize/metrics/estimator.py +0 -87
- package/src/superlocalmemory/optimize/metrics/exporters.py +0 -77
- package/src/superlocalmemory/optimize/metrics/persistence.py +0 -115
- package/src/superlocalmemory/optimize/proxy/__init__.py +0 -28
- package/src/superlocalmemory/optimize/proxy/_helpers.py +0 -730
- package/src/superlocalmemory/optimize/proxy/anthropic_surface.py +0 -375
- package/src/superlocalmemory/optimize/proxy/capture.py +0 -550
- package/src/superlocalmemory/optimize/proxy/gemini_surface.py +0 -528
- package/src/superlocalmemory/optimize/proxy/lifecycle.py +0 -126
- package/src/superlocalmemory/optimize/proxy/openai_surface.py +0 -465
- package/src/superlocalmemory/optimize/proxy/server.py +0 -199
- package/src/superlocalmemory/optimize/proxy/vertex_surface.py +0 -246
- package/src/superlocalmemory/optimize/storage/__init__.py +0 -0
- package/src/superlocalmemory/optimize/storage/db.py +0 -1185
- package/src/superlocalmemory/optimize/storage/schema.py +0 -205
- package/src/superlocalmemory/parameterization/__init__.py +0 -47
- package/src/superlocalmemory/parameterization/cross_project.py +0 -12
- package/src/superlocalmemory/parameterization/pattern_extractor.py +0 -584
- package/src/superlocalmemory/parameterization/pii_filter.py +0 -106
- package/src/superlocalmemory/parameterization/prompt_injector.py +0 -219
- package/src/superlocalmemory/parameterization/prompt_lifecycle.py +0 -281
- package/src/superlocalmemory/parameterization/soft_prompt_generator.py +0 -542
- package/src/superlocalmemory/parameterization/workflow_miner.py +0 -17
- package/src/superlocalmemory/reliability/__init__.py +0 -45
- package/src/superlocalmemory/reliability/join_liveness.py +0 -301
- package/src/superlocalmemory/reliability/prior_distance.py +0 -243
- package/src/superlocalmemory/retrieval/__init__.py +0 -0
- package/src/superlocalmemory/retrieval/agentic.py +0 -367
- package/src/superlocalmemory/retrieval/ann_index.py +0 -235
- package/src/superlocalmemory/retrieval/bm25_channel.py +0 -451
- package/src/superlocalmemory/retrieval/bridge_discovery.py +0 -253
- package/src/superlocalmemory/retrieval/channel_registry.py +0 -154
- package/src/superlocalmemory/retrieval/channel_status.py +0 -117
- package/src/superlocalmemory/retrieval/engine.py +0 -1615
- package/src/superlocalmemory/retrieval/entity_channel.py +0 -994
- package/src/superlocalmemory/retrieval/forgetting_filter.py +0 -160
- package/src/superlocalmemory/retrieval/fusion.py +0 -81
- package/src/superlocalmemory/retrieval/graph_adjacency.py +0 -219
- package/src/superlocalmemory/retrieval/hopfield_channel.py +0 -465
- package/src/superlocalmemory/retrieval/profile_channel.py +0 -105
- package/src/superlocalmemory/retrieval/quantization_aware_search.py +0 -147
- package/src/superlocalmemory/retrieval/remote_reranker.py +0 -758
- package/src/superlocalmemory/retrieval/reranker.py +0 -674
- package/src/superlocalmemory/retrieval/scope_policy.py +0 -126
- package/src/superlocalmemory/retrieval/semantic_channel.py +0 -638
- package/src/superlocalmemory/retrieval/spreading.py +0 -288
- package/src/superlocalmemory/retrieval/spreading_activation.py +0 -616
- package/src/superlocalmemory/retrieval/strategy.py +0 -248
- package/src/superlocalmemory/retrieval/temporal_channel.py +0 -433
- package/src/superlocalmemory/retrieval/temporal_frame.py +0 -102
- package/src/superlocalmemory/retrieval/temporal_utils.py +0 -122
- package/src/superlocalmemory/retrieval/temporal_validity_filter.py +0 -499
- package/src/superlocalmemory/retrieval/time_window.py +0 -181
- package/src/superlocalmemory/retrieval/vector_store.py +0 -863
- package/src/superlocalmemory/server/__init__.py +0 -1
- package/src/superlocalmemory/server/api.py +0 -310
- package/src/superlocalmemory/server/asset_versions.py +0 -171
- package/src/superlocalmemory/server/bandit_loops.py +0 -158
- package/src/superlocalmemory/server/config_file.py +0 -90
- package/src/superlocalmemory/server/consolidation_runner.py +0 -140
- package/src/superlocalmemory/server/egress_policy.py +0 -258
- package/src/superlocalmemory/server/loopback.py +0 -85
- package/src/superlocalmemory/server/middleware/__init__.py +0 -11
- package/src/superlocalmemory/server/middleware/security_headers.py +0 -144
- package/src/superlocalmemory/server/origin.py +0 -55
- package/src/superlocalmemory/server/profile_runtime.py +0 -515
- package/src/superlocalmemory/server/rbac_enforce.py +0 -194
- package/src/superlocalmemory/server/recall_health.py +0 -343
- package/src/superlocalmemory/server/recall_serializer.py +0 -320
- package/src/superlocalmemory/server/route_mutations.py +0 -104
- package/src/superlocalmemory/server/routes/__init__.py +0 -4
- package/src/superlocalmemory/server/routes/abstraction.py +0 -314
- package/src/superlocalmemory/server/routes/adapters.py +0 -63
- package/src/superlocalmemory/server/routes/agents.py +0 -303
- package/src/superlocalmemory/server/routes/backup.py +0 -869
- package/src/superlocalmemory/server/routes/behavioral.py +0 -659
- package/src/superlocalmemory/server/routes/brain.py +0 -1892
- package/src/superlocalmemory/server/routes/chat.py +0 -393
- package/src/superlocalmemory/server/routes/compliance.py +0 -533
- package/src/superlocalmemory/server/routes/config_api.py +0 -703
- package/src/superlocalmemory/server/routes/data_io.py +0 -329
- package/src/superlocalmemory/server/routes/entity.py +0 -237
- package/src/superlocalmemory/server/routes/events.py +0 -214
- package/src/superlocalmemory/server/routes/evolution.py +0 -510
- package/src/superlocalmemory/server/routes/helpers.py +0 -499
- package/src/superlocalmemory/server/routes/ingest.py +0 -137
- package/src/superlocalmemory/server/routes/insights.py +0 -366
- package/src/superlocalmemory/server/routes/learning.py +0 -834
- package/src/superlocalmemory/server/routes/learning_telemetry.py +0 -154
- package/src/superlocalmemory/server/routes/lifecycle.py +0 -184
- package/src/superlocalmemory/server/routes/memories.py +0 -1661
- package/src/superlocalmemory/server/routes/mesh.py +0 -517
- package/src/superlocalmemory/server/routes/mesh_lock.py +0 -54
- package/src/superlocalmemory/server/routes/mesh_state.py +0 -63
- package/src/superlocalmemory/server/routes/optimize.py +0 -197
- package/src/superlocalmemory/server/routes/prewarm.py +0 -173
- package/src/superlocalmemory/server/routes/profiles.py +0 -292
- package/src/superlocalmemory/server/routes/ratelimit.py +0 -132
- package/src/superlocalmemory/server/routes/rbac.py +0 -366
- package/src/superlocalmemory/server/routes/stats.py +0 -385
- package/src/superlocalmemory/server/routes/tiers.py +0 -222
- package/src/superlocalmemory/server/routes/timeline.py +0 -258
- package/src/superlocalmemory/server/routes/token.py +0 -90
- package/src/superlocalmemory/server/routes/v3_api.py +0 -3023
- package/src/superlocalmemory/server/routes/ws.py +0 -171
- package/src/superlocalmemory/server/security_middleware.py +0 -89
- package/src/superlocalmemory/server/ui.py +0 -354
- package/src/superlocalmemory/server/unified_daemon.py +0 -6326
- package/src/superlocalmemory/server/write_identity.py +0 -195
- package/src/superlocalmemory/storage/__init__.py +0 -0
- package/src/superlocalmemory/storage/_migration_internals.py +0 -638
- package/src/superlocalmemory/storage/_schema_version.py +0 -174
- package/src/superlocalmemory/storage/access_log.py +0 -170
- package/src/superlocalmemory/storage/admission_codec.py +0 -129
- package/src/superlocalmemory/storage/admission_journal.py +0 -843
- package/src/superlocalmemory/storage/agent_experience.py +0 -546
- package/src/superlocalmemory/storage/backup.py +0 -531
- package/src/superlocalmemory/storage/correction_cases.py +0 -670
- package/src/superlocalmemory/storage/database.py +0 -3180
- package/src/superlocalmemory/storage/deferred_writes.py +0 -209
- package/src/superlocalmemory/storage/embedding_codec.py +0 -200
- package/src/superlocalmemory/storage/embedding_migrator.py +0 -672
- package/src/superlocalmemory/storage/erasure_fence.py +0 -45
- package/src/superlocalmemory/storage/execution_learning.py +0 -285
- package/src/superlocalmemory/storage/external_evidence.py +0 -359
- package/src/superlocalmemory/storage/generation_fence.py +0 -63
- package/src/superlocalmemory/storage/lineage_retention.py +0 -236
- package/src/superlocalmemory/storage/logical_edges.py +0 -86
- package/src/superlocalmemory/storage/memory_write.py +0 -115
- package/src/superlocalmemory/storage/migration_runner.py +0 -895
- package/src/superlocalmemory/storage/migration_v33.py +0 -140
- package/src/superlocalmemory/storage/migrations/M001_add_signal_features_columns.py +0 -67
- package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +0 -107
- package/src/superlocalmemory/storage/migrations/M003_migration_log.py +0 -38
- package/src/superlocalmemory/storage/migrations/M004_cross_platform_sync_log.py +0 -46
- package/src/superlocalmemory/storage/migrations/M005_bandit_tables.py +0 -75
- package/src/superlocalmemory/storage/migrations/M006_action_outcomes_reward.py +0 -75
- package/src/superlocalmemory/storage/migrations/M007_pending_outcomes.py +0 -63
- package/src/superlocalmemory/storage/migrations/M009_model_lineage.py +0 -94
- package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +0 -80
- package/src/superlocalmemory/storage/migrations/M011_archive_and_merge.py +0 -87
- package/src/superlocalmemory/storage/migrations/M012_shadow_observations.py +0 -72
- package/src/superlocalmemory/storage/migrations/M013_bi_temporal_columns.py +0 -55
- package/src/superlocalmemory/storage/migrations/M014_v345_scale_ready.py +0 -45
- package/src/superlocalmemory/storage/migrations/M015_add_pinned_column.py +0 -58
- package/src/superlocalmemory/storage/migrations/M016_add_scope_support.py +0 -120
- package/src/superlocalmemory/storage/migrations/M017_ccq_scope_column.py +0 -79
- package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +0 -120
- package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +0 -54
- package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +0 -52
- package/src/superlocalmemory/storage/migrations/M021_ingestion_log_profile.py +0 -108
- package/src/superlocalmemory/storage/migrations/M022_entity_aliases_profile.py +0 -86
- package/src/superlocalmemory/storage/migrations/M023_mesh_profile_isolation.py +0 -194
- package/src/superlocalmemory/storage/migrations/M024_rbac_users_roles.py +0 -87
- package/src/superlocalmemory/storage/migrations/M025_perf_indexes.py +0 -90
- package/src/superlocalmemory/storage/migrations/M026_rbac_memberships_fk.py +0 -136
- package/src/superlocalmemory/storage/migrations/M027_transferable_patterns_profile.py +0 -163
- package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +0 -305
- package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +0 -137
- package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +0 -93
- package/src/superlocalmemory/storage/migrations/M031_dead_letter_operations.py +0 -80
- package/src/superlocalmemory/storage/migrations/M032_write_coordinator_admission.py +0 -188
- package/src/superlocalmemory/storage/migrations/M033_projection_transactions.py +0 -148
- package/src/superlocalmemory/storage/migrations/M034_obligation_integrity.py +0 -58
- package/src/superlocalmemory/storage/migrations/M035_erasure_receipts.py +0 -113
- package/src/superlocalmemory/storage/migrations/M036_vector_row_map.py +0 -107
- package/src/superlocalmemory/storage/migrations/M037_manifest_hmac_version.py +0 -162
- package/src/superlocalmemory/storage/migrations/M038_learning_feedback_channel.py +0 -77
- package/src/superlocalmemory/storage/migrations/M039_scene_fact_members.py +0 -137
- package/src/superlocalmemory/storage/migrations/M040_agent_experience_receipts.py +0 -254
- package/src/superlocalmemory/storage/migrations/M041_external_evidence_receipts.py +0 -189
- package/src/superlocalmemory/storage/migrations/M042_correction_case_ledger.py +0 -245
- package/src/superlocalmemory/storage/migrations/M043_quarantine_display_summaries.py +0 -512
- package/src/superlocalmemory/storage/migrations/M044_play_carries_its_own_evidence.py +0 -127
- package/src/superlocalmemory/storage/migrations/M045_fact_outcome_score.py +0 -158
- package/src/superlocalmemory/storage/migrations/M046_prospective_memory_has_its_own_name.py +0 -620
- package/src/superlocalmemory/storage/migrations/M047_fisher_vectors_are_stored_like_every_other_vector.py +0 -306
- package/src/superlocalmemory/storage/migrations/M048_upcoming_holds_only_what_is_upcoming.py +0 -229
- package/src/superlocalmemory/storage/migrations/M049_a_schema_version_marker_is_one_row.py +0 -201
- package/src/superlocalmemory/storage/migrations/M050_execution_learning_v2.py +0 -70
- package/src/superlocalmemory/storage/migrations/__init__.py +0 -103
- package/src/superlocalmemory/storage/migrations.py +0 -333
- package/src/superlocalmemory/storage/models.py +0 -500
- package/src/superlocalmemory/storage/projection_outbox.py +0 -346
- package/src/superlocalmemory/storage/quantized_store.py +0 -280
- package/src/superlocalmemory/storage/read_connection.py +0 -115
- package/src/superlocalmemory/storage/retention_policy.py +0 -860
- package/src/superlocalmemory/storage/schema.py +0 -1108
- package/src/superlocalmemory/storage/schema_code_graph.py +0 -282
- package/src/superlocalmemory/storage/schema_v32.py +0 -382
- package/src/superlocalmemory/storage/schema_v3410.py +0 -159
- package/src/superlocalmemory/storage/schema_v3411.py +0 -149
- package/src/superlocalmemory/storage/schema_v343.py +0 -315
- package/src/superlocalmemory/storage/schema_v345.py +0 -109
- package/src/superlocalmemory/storage/schema_v347.py +0 -140
- package/src/superlocalmemory/storage/sqlite_vectors.py +0 -169
- package/src/superlocalmemory/storage/v2_migrator.py +0 -466
- package/src/superlocalmemory/storage/write_coordinator.py +0 -949
- package/src/superlocalmemory/storage/write_lock.py +0 -88
- package/src/superlocalmemory/summaries/__init__.py +0 -37
- package/src/superlocalmemory/summaries/base.py +0 -267
- package/src/superlocalmemory/summaries/daily_reflection.py +0 -340
- package/src/superlocalmemory/summaries/non_answer.py +0 -223
- package/src/superlocalmemory/summaries/project_work_log.py +0 -440
- package/src/superlocalmemory/summaries/session_summary.py +0 -311
- package/src/superlocalmemory/trust/__init__.py +0 -0
- package/src/superlocalmemory/trust/gate.py +0 -171
- package/src/superlocalmemory/trust/provenance.py +0 -124
- package/src/superlocalmemory/trust/scorer.py +0 -413
- package/src/superlocalmemory/trust/signals.py +0 -153
- package/src/superlocalmemory/ui/assets/slm-icon-white.svg +0 -64
- package/src/superlocalmemory/ui/assets/slm-icon.svg +0 -36
- package/src/superlocalmemory/ui/css/brain.css +0 -409
- package/src/superlocalmemory/ui/css/design-system.css +0 -696
- package/src/superlocalmemory/ui/css/legacy-dashboard.css +0 -663
- package/src/superlocalmemory/ui/css/neural-glass.css +0 -1599
- package/src/superlocalmemory/ui/css/od-bridge.css +0 -158
- package/src/superlocalmemory/ui/favicon.svg +0 -36
- package/src/superlocalmemory/ui/index.html +0 -1638
- package/src/superlocalmemory/ui/js/agents.js +0 -192
- package/src/superlocalmemory/ui/js/auto-settings.js +0 -624
- package/src/superlocalmemory/ui/js/brain.js +0 -1400
- package/src/superlocalmemory/ui/js/clusters.js +0 -326
- package/src/superlocalmemory/ui/js/compliance.js +0 -307
- package/src/superlocalmemory/ui/js/core.js +0 -566
- package/src/superlocalmemory/ui/js/dashboard.js +0 -503
- package/src/superlocalmemory/ui/js/event-delegation.js +0 -113
- package/src/superlocalmemory/ui/js/events.js +0 -178
- package/src/superlocalmemory/ui/js/fact-detail.js +0 -142
- package/src/superlocalmemory/ui/js/feedback.js +0 -339
- package/src/superlocalmemory/ui/js/graph-event-bus.js +0 -83
- package/src/superlocalmemory/ui/js/graph-filters.js +0 -220
- package/src/superlocalmemory/ui/js/graph-ui.js +0 -214
- package/src/superlocalmemory/ui/js/ide-status.js +0 -115
- package/src/superlocalmemory/ui/js/init.js +0 -54
- package/src/superlocalmemory/ui/js/knowledge-graph.js +0 -945
- package/src/superlocalmemory/ui/js/lifecycle.js +0 -387
- package/src/superlocalmemory/ui/js/math-health.js +0 -114
- package/src/superlocalmemory/ui/js/memories.js +0 -394
- package/src/superlocalmemory/ui/js/memory-chat.js +0 -371
- package/src/superlocalmemory/ui/js/memory-timeline.js +0 -265
- package/src/superlocalmemory/ui/js/modal.js +0 -733
- package/src/superlocalmemory/ui/js/ng-entities.js +0 -298
- package/src/superlocalmemory/ui/js/ng-health.js +0 -208
- package/src/superlocalmemory/ui/js/ng-ingestion.js +0 -203
- package/src/superlocalmemory/ui/js/ng-mesh.js +0 -374
- package/src/superlocalmemory/ui/js/ng-shell.js +0 -524
- package/src/superlocalmemory/ui/js/ng-skills.js +0 -663
- package/src/superlocalmemory/ui/js/od-agents.js +0 -588
- package/src/superlocalmemory/ui/js/od-auth-gate.js +0 -257
- package/src/superlocalmemory/ui/js/od-backup.js +0 -878
- package/src/superlocalmemory/ui/js/od-boundedloops.js +0 -324
- package/src/superlocalmemory/ui/js/od-brain.js +0 -1095
- package/src/superlocalmemory/ui/js/od-compliance-ext.js +0 -301
- package/src/superlocalmemory/ui/js/od-components.js +0 -147
- package/src/superlocalmemory/ui/js/od-entities.js +0 -622
- package/src/superlocalmemory/ui/js/od-graph.js +0 -776
- package/src/superlocalmemory/ui/js/od-health.js +0 -579
- package/src/superlocalmemory/ui/js/od-mcp.js +0 -508
- package/src/superlocalmemory/ui/js/od-memories.js +0 -1499
- package/src/superlocalmemory/ui/js/od-mesh.js +0 -645
- package/src/superlocalmemory/ui/js/od-operations.js +0 -1268
- package/src/superlocalmemory/ui/js/od-ops-health.js +0 -417
- package/src/superlocalmemory/ui/js/od-optimize.js +0 -828
- package/src/superlocalmemory/ui/js/od-settings.js +0 -1275
- package/src/superlocalmemory/ui/js/od-shell.js +0 -819
- package/src/superlocalmemory/ui/js/od-skills.js +0 -600
- package/src/superlocalmemory/ui/js/od-team.js +0 -265
- package/src/superlocalmemory/ui/js/optimize.js +0 -191
- package/src/superlocalmemory/ui/js/profiles.js +0 -362
- package/src/superlocalmemory/ui/js/quick-actions.js +0 -334
- package/src/superlocalmemory/ui/js/recall-lab.js +0 -373
- package/src/superlocalmemory/ui/js/search.js +0 -86
- package/src/superlocalmemory/ui/js/settings.js +0 -556
- package/src/superlocalmemory/ui/js/timeline.js +0 -62
- package/src/superlocalmemory/ui/js/trust-dashboard.js +0 -225
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/bootstrap-icons.css +0 -2018
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/bootstrap.bundle.min.js +0 -7
- package/src/superlocalmemory/ui/vendor/bootstrap.min.css +0 -6
- package/src/superlocalmemory/ui/vendor/d3.v7.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/graphology-library.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/graphology.umd.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/inter-ui/inter-variable.min.css +0 -8
- package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable-Italic.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/sigma.min.js +0 -1
- package/src/superlocalmemory/vector/__init__.py +0 -9
- package/src/superlocalmemory/vector/lancedb_backend.py +0 -366
|
@@ -1,928 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
-
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
-
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
-
|
|
5
|
-
"""Skill Evolver — orchestrates the full evolution pipeline.
|
|
6
|
-
|
|
7
|
-
Pipeline: Trigger → Screen → LLM Confirm → Mutate → Blind Verify → Persist
|
|
8
|
-
|
|
9
|
-
Performance: NEVER runs on recall/remember hot path. Only during
|
|
10
|
-
consolidation (every 6h) or explicit trigger. Zero impact on latency.
|
|
11
|
-
|
|
12
|
-
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import dataclasses
|
|
18
|
-
import difflib
|
|
19
|
-
import hashlib
|
|
20
|
-
import json
|
|
21
|
-
import logging
|
|
22
|
-
import os
|
|
23
|
-
import re
|
|
24
|
-
from datetime import datetime, timezone
|
|
25
|
-
from pathlib import Path
|
|
26
|
-
from typing import Optional
|
|
27
|
-
|
|
28
|
-
from superlocalmemory.infra.data_root import canonical_data_root
|
|
29
|
-
from superlocalmemory.evolution.types import (
|
|
30
|
-
EvolutionCandidate,
|
|
31
|
-
EvolutionRecord,
|
|
32
|
-
EvolutionStatus,
|
|
33
|
-
EvolutionType,
|
|
34
|
-
TriggerType,
|
|
35
|
-
)
|
|
36
|
-
from superlocalmemory.evolution.evolution_store import EvolutionStore
|
|
37
|
-
from superlocalmemory.evolution.triggers import (
|
|
38
|
-
DegradationTrigger,
|
|
39
|
-
HealthCheckTrigger,
|
|
40
|
-
PostSessionTrigger,
|
|
41
|
-
)
|
|
42
|
-
from superlocalmemory.evolution import mutation_generator as mutgen
|
|
43
|
-
from superlocalmemory.evolution import blind_verifier as verifier
|
|
44
|
-
from superlocalmemory.evolution.budget import (
|
|
45
|
-
BudgetExhausted,
|
|
46
|
-
EvolutionBudget,
|
|
47
|
-
)
|
|
48
|
-
from superlocalmemory.evolution.llm_dispatch import _dispatch_llm
|
|
49
|
-
from superlocalmemory.evolution.model_selection import (
|
|
50
|
-
_MODEL_ALIASES,
|
|
51
|
-
_resolve_model_alias,
|
|
52
|
-
resolve_evolution_models,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
logger = logging.getLogger(__name__)
|
|
56
|
-
|
|
57
|
-
# Resolve the quarantine location under the selected runtime data root so a
|
|
58
|
-
# custom SLM_DATA_DIR is honored rather than always writing under the home dir.
|
|
59
|
-
EVOLVED_SKILLS_DIR = canonical_data_root() / "quarantine" / "skills"
|
|
60
|
-
|
|
61
|
-
# Model aliasing + per-step selection live in ``model_selection`` — a single
|
|
62
|
-
# source of truth shared with the config layer. ``_MODEL_ALIASES`` and
|
|
63
|
-
# ``_resolve_model_alias`` are imported above and re-exported here for
|
|
64
|
-
# callers/tests that still reference them via ``skill_evolver``.
|
|
65
|
-
__all__ = ["SkillEvolver", "detect_backend", "_resolve_model_alias"]
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def _ollama_running() -> bool:
|
|
69
|
-
"""Return True if a local Ollama daemon answers on the default port."""
|
|
70
|
-
try:
|
|
71
|
-
import urllib.request
|
|
72
|
-
req = urllib.request.Request(
|
|
73
|
-
"http://127.0.0.1:11434/api/tags", method="GET",
|
|
74
|
-
)
|
|
75
|
-
with urllib.request.urlopen(req, timeout=2): # noqa: S310
|
|
76
|
-
return True
|
|
77
|
-
except Exception:
|
|
78
|
-
return False
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def detect_backend() -> str:
|
|
82
|
-
"""Auto-detect best available LLM backend.
|
|
83
|
-
|
|
84
|
-
Priority: claude CLI → Ollama → API key → none
|
|
85
|
-
"""
|
|
86
|
-
import shutil
|
|
87
|
-
|
|
88
|
-
# 1. Claude CLI available?
|
|
89
|
-
if shutil.which("claude"):
|
|
90
|
-
return "claude"
|
|
91
|
-
|
|
92
|
-
# 2. Ollama running?
|
|
93
|
-
if _ollama_running():
|
|
94
|
-
return "ollama"
|
|
95
|
-
|
|
96
|
-
# 3. API key set?
|
|
97
|
-
if os.environ.get("ANTHROPIC_API_KEY"):
|
|
98
|
-
return "anthropic"
|
|
99
|
-
if os.environ.get("OPENAI_API_KEY"):
|
|
100
|
-
return "openai"
|
|
101
|
-
|
|
102
|
-
return "none"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
class SkillEvolver:
|
|
106
|
-
"""Main orchestrator for skill evolution.
|
|
107
|
-
|
|
108
|
-
Call `run_consolidation_cycle()` from the consolidation pipeline.
|
|
109
|
-
Call `run_post_session()` from the Stop hook.
|
|
110
|
-
|
|
111
|
-
Respects EvolutionConfig.enabled — does nothing if disabled.
|
|
112
|
-
Auto-detects LLM backend: claude CLI → Ollama → API → none.
|
|
113
|
-
"""
|
|
114
|
-
|
|
115
|
-
def __init__(
|
|
116
|
-
self,
|
|
117
|
-
db_path: str | Path,
|
|
118
|
-
config: object | None = None,
|
|
119
|
-
*,
|
|
120
|
-
profile_id: str = "default",
|
|
121
|
-
budget: EvolutionBudget | None = None,
|
|
122
|
-
audit_chain=None,
|
|
123
|
-
activator=None,
|
|
124
|
-
):
|
|
125
|
-
self._db_path = str(db_path)
|
|
126
|
-
self._store = EvolutionStore(db_path)
|
|
127
|
-
self._degradation = DegradationTrigger(db_path)
|
|
128
|
-
self._health = HealthCheckTrigger(db_path, profile_id=profile_id)
|
|
129
|
-
self._config = config
|
|
130
|
-
self._backend: str | None = None
|
|
131
|
-
# ResolvedModels, resolved lazily & cached via _get_models().
|
|
132
|
-
self._models = None
|
|
133
|
-
self._profile_id = profile_id
|
|
134
|
-
self._current_cycle_id: str | None = None
|
|
135
|
-
# Injectable activator for testing (CRIT-2).
|
|
136
|
-
self._activator = activator
|
|
137
|
-
|
|
138
|
-
# SB-3: SkillEvolver always holds a budget. Default one is rooted
|
|
139
|
-
# at ~/.superlocalmemory so production callers pick it up
|
|
140
|
-
# automatically. Tests inject their own.
|
|
141
|
-
if budget is None:
|
|
142
|
-
slm_home = canonical_data_root()
|
|
143
|
-
# ``learning_db`` must be learning.db: migration M010 creates
|
|
144
|
-
# evolution_llm_cost_log there. Every production caller
|
|
145
|
-
# (consolidation_engine, routes/evolution, MCP, CLI) passes
|
|
146
|
-
# memory.db as db_path, so handing db_path straight to the budget
|
|
147
|
-
# pointed it at a database where that table does not exist. Every
|
|
148
|
-
# evolution cycle then died with "no such table:
|
|
149
|
-
# evolution_llm_cost_log" — swallowed at debug level in
|
|
150
|
-
# consolidation step 11, so skill evolution silently never ran.
|
|
151
|
-
#
|
|
152
|
-
# Redirect only for the exact production filename. Tests pass
|
|
153
|
-
# ":memory:" or their own tmp file and must keep the old behaviour,
|
|
154
|
-
# rather than having a learning.db invented beside them.
|
|
155
|
-
learning_db = Path(self._db_path)
|
|
156
|
-
if learning_db.name == "memory.db":
|
|
157
|
-
learning_db = learning_db.parent / "learning.db"
|
|
158
|
-
budget = EvolutionBudget(
|
|
159
|
-
profile_id=profile_id,
|
|
160
|
-
learning_db=learning_db,
|
|
161
|
-
lock_dir=slm_home,
|
|
162
|
-
)
|
|
163
|
-
self._budget = budget
|
|
164
|
-
|
|
165
|
-
# Phase 2 (Module 8): tamper-evident audit chain for evolution events.
|
|
166
|
-
# Never use ":memory:" in production — use file path next to db_path.
|
|
167
|
-
if audit_chain is None:
|
|
168
|
-
from superlocalmemory.compliance.audit import AuditChain
|
|
169
|
-
if self._db_path == ":memory:":
|
|
170
|
-
# In-memory DB is test-only; use in-memory audit too.
|
|
171
|
-
audit_chain = AuditChain(":memory:")
|
|
172
|
-
else:
|
|
173
|
-
audit_db = Path(self._db_path).parent / "audit.db"
|
|
174
|
-
audit_chain = AuditChain(audit_db)
|
|
175
|
-
self._audit = audit_chain
|
|
176
|
-
|
|
177
|
-
def _is_enabled(self) -> bool:
|
|
178
|
-
"""Check if evolution is enabled in config."""
|
|
179
|
-
if self._config and hasattr(self._config, "evolution"):
|
|
180
|
-
return self._config.evolution.enabled
|
|
181
|
-
return False
|
|
182
|
-
|
|
183
|
-
def _is_auto_approve(self) -> bool:
|
|
184
|
-
"""Check evolution.auto_approve config key (LLD Decision A2, default False).
|
|
185
|
-
|
|
186
|
-
Config key wins. Falls back to SLM_EVO_AUTO_APPROVE env var for CI.
|
|
187
|
-
"""
|
|
188
|
-
evo_cfg = getattr(self._config, "evolution", None)
|
|
189
|
-
if evo_cfg is not None and hasattr(evo_cfg, "auto_approve"):
|
|
190
|
-
return bool(evo_cfg.auto_approve)
|
|
191
|
-
return os.environ.get("SLM_EVO_AUTO_APPROVE", "").strip() in (
|
|
192
|
-
"1", "true", "True",
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
def _get_activator(self):
|
|
196
|
-
"""Return the SkillActivator instance (injectable for tests)."""
|
|
197
|
-
if self._activator is not None:
|
|
198
|
-
return self._activator
|
|
199
|
-
from superlocalmemory.evolution.skill_activator import SkillActivator
|
|
200
|
-
return SkillActivator()
|
|
201
|
-
|
|
202
|
-
def _get_backend(self) -> str:
|
|
203
|
-
"""Get or detect the LLM backend."""
|
|
204
|
-
if self._backend:
|
|
205
|
-
return self._backend
|
|
206
|
-
|
|
207
|
-
configured = "auto"
|
|
208
|
-
if self._config and hasattr(self._config, "evolution"):
|
|
209
|
-
configured = self._config.evolution.backend
|
|
210
|
-
|
|
211
|
-
if configured == "auto":
|
|
212
|
-
self._backend = detect_backend()
|
|
213
|
-
else:
|
|
214
|
-
self._backend = configured
|
|
215
|
-
|
|
216
|
-
logger.info("Evolution backend: %s", self._backend)
|
|
217
|
-
return self._backend
|
|
218
|
-
|
|
219
|
-
def _get_models(self):
|
|
220
|
-
"""Resolve per-step evolution models (cached).
|
|
221
|
-
|
|
222
|
-
Defaults to the lowest-cost model for the active backend and keeps
|
|
223
|
-
the blind verifier independent of the generator where possible
|
|
224
|
-
(see ``model_selection.resolve_evolution_models``). Falls back to
|
|
225
|
-
stock ``EvolutionConfig`` defaults when no config is attached.
|
|
226
|
-
"""
|
|
227
|
-
if self._models is not None:
|
|
228
|
-
return self._models
|
|
229
|
-
backend = self._get_backend()
|
|
230
|
-
evo_cfg = getattr(self._config, "evolution", None)
|
|
231
|
-
if evo_cfg is None:
|
|
232
|
-
from superlocalmemory.core.config import EvolutionConfig
|
|
233
|
-
evo_cfg = EvolutionConfig()
|
|
234
|
-
self._models = resolve_evolution_models(
|
|
235
|
-
evo_cfg, backend, ollama_available=_ollama_running(),
|
|
236
|
-
)
|
|
237
|
-
return self._models
|
|
238
|
-
|
|
239
|
-
def run_consolidation_cycle(self, profile_id: str = "default") -> dict:
|
|
240
|
-
"""Run during consolidation. Checks triggers 2 and 3.
|
|
241
|
-
|
|
242
|
-
Wrapped in ``self._budget.cycle()`` — honours the
|
|
243
|
-
30min/10-LLM-calls/3-cycles-per-day caps (SB-3). If the budget
|
|
244
|
-
is exhausted, returns ``{"aborted": True}`` rather than raising
|
|
245
|
-
so the consolidation worker can continue cleanly.
|
|
246
|
-
"""
|
|
247
|
-
if not self._is_enabled():
|
|
248
|
-
return {"enabled": False, "message": "Evolution disabled. Enable via: slm config set evolution.enabled true"}
|
|
249
|
-
|
|
250
|
-
backend = self._get_backend()
|
|
251
|
-
if backend == "none":
|
|
252
|
-
return {"enabled": True, "backend": "none",
|
|
253
|
-
"message": "No LLM backend available. Install Claude Code, Ollama, or set an API key."}
|
|
254
|
-
|
|
255
|
-
try:
|
|
256
|
-
with self._budget.cycle() as _b:
|
|
257
|
-
self._current_cycle_id = None # budget already recorded one
|
|
258
|
-
return self._run_consolidation_body(profile_id, backend)
|
|
259
|
-
except BudgetExhausted as exc:
|
|
260
|
-
logger.warning(
|
|
261
|
-
"evolution cycle aborted: budget exhausted [%s]",
|
|
262
|
-
getattr(exc, "dimension", "?"),
|
|
263
|
-
)
|
|
264
|
-
return {
|
|
265
|
-
"enabled": True,
|
|
266
|
-
"backend": backend,
|
|
267
|
-
"aborted": True,
|
|
268
|
-
"budget_exhausted": True,
|
|
269
|
-
"dimension": getattr(exc, "dimension", None),
|
|
270
|
-
"candidates": 0, "evolved": 0, "rejected": 0, "skipped": 0,
|
|
271
|
-
}
|
|
272
|
-
finally:
|
|
273
|
-
self._current_cycle_id = None
|
|
274
|
-
|
|
275
|
-
def _run_consolidation_body(
|
|
276
|
-
self, profile_id: str, backend: str,
|
|
277
|
-
) -> dict:
|
|
278
|
-
"""Inner consolidation loop — runs under an open budget cycle."""
|
|
279
|
-
self._store.reset_cycle(profile_id)
|
|
280
|
-
results = {
|
|
281
|
-
"candidates": 0, "evolved": 0, "rejected": 0,
|
|
282
|
-
"skipped": 0, "quarantined": 0, "backend": backend,
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
# Prune recovered skills from anti-loop tracking
|
|
286
|
-
active_degraded = self._degradation.get_active_degraded(profile_id)
|
|
287
|
-
self._store.prune_recovered(active_degraded)
|
|
288
|
-
|
|
289
|
-
# Trigger 2: Degradation
|
|
290
|
-
candidates = self._degradation.scan(profile_id)
|
|
291
|
-
# Trigger 3: Health check (runs every Nth cycle)
|
|
292
|
-
candidates.extend(self._health.scan(profile_id))
|
|
293
|
-
|
|
294
|
-
results["candidates"] = len(candidates)
|
|
295
|
-
|
|
296
|
-
for candidate in candidates:
|
|
297
|
-
if not self._store.can_evolve(profile_id):
|
|
298
|
-
results["skipped"] += len(candidates) - results["evolved"] - results["rejected"]
|
|
299
|
-
break
|
|
300
|
-
|
|
301
|
-
outcome = self._process_candidate(candidate, profile_id)
|
|
302
|
-
if outcome == "evolved":
|
|
303
|
-
results["evolved"] += 1
|
|
304
|
-
elif outcome == "rejected":
|
|
305
|
-
results["rejected"] += 1
|
|
306
|
-
elif outcome == "quarantined":
|
|
307
|
-
results["quarantined"] += 1
|
|
308
|
-
else:
|
|
309
|
-
results["skipped"] += 1
|
|
310
|
-
|
|
311
|
-
logger.info(
|
|
312
|
-
"Evolution cycle: %d candidates, %d evolved, %d rejected, %d skipped",
|
|
313
|
-
results["candidates"], results["evolved"],
|
|
314
|
-
results["rejected"], results["skipped"],
|
|
315
|
-
)
|
|
316
|
-
return results
|
|
317
|
-
|
|
318
|
-
def run_post_session(
|
|
319
|
-
self, session_id: str, profile_id: str = "default",
|
|
320
|
-
) -> dict:
|
|
321
|
-
"""Run after a session ends. Checks trigger 1.
|
|
322
|
-
|
|
323
|
-
Wrapped in a budget cycle (SB-3) so post-session evolution is
|
|
324
|
-
subject to the same 10-LLM-call / 30-min wall-time cap.
|
|
325
|
-
"""
|
|
326
|
-
if not self._is_enabled():
|
|
327
|
-
return {"enabled": False, "candidates": 0, "evolved": 0, "rejected": 0}
|
|
328
|
-
|
|
329
|
-
try:
|
|
330
|
-
with self._budget.cycle() as _b:
|
|
331
|
-
return self._run_post_session_body(session_id, profile_id)
|
|
332
|
-
except BudgetExhausted as exc:
|
|
333
|
-
logger.warning(
|
|
334
|
-
"post-session evolution aborted: budget exhausted [%s]",
|
|
335
|
-
getattr(exc, "dimension", "?"),
|
|
336
|
-
)
|
|
337
|
-
return {
|
|
338
|
-
"enabled": True, "aborted": True, "budget_exhausted": True,
|
|
339
|
-
"dimension": getattr(exc, "dimension", None),
|
|
340
|
-
"candidates": 0, "evolved": 0, "rejected": 0,
|
|
341
|
-
}
|
|
342
|
-
finally:
|
|
343
|
-
self._current_cycle_id = None
|
|
344
|
-
|
|
345
|
-
def _run_post_session_body(
|
|
346
|
-
self, session_id: str, profile_id: str,
|
|
347
|
-
) -> dict:
|
|
348
|
-
results = {"candidates": 0, "evolved": 0, "rejected": 0}
|
|
349
|
-
|
|
350
|
-
trigger = PostSessionTrigger(self._db_path)
|
|
351
|
-
candidates = trigger.scan(session_id, profile_id)
|
|
352
|
-
results["candidates"] = len(candidates)
|
|
353
|
-
|
|
354
|
-
for candidate in candidates:
|
|
355
|
-
if not self._store.can_evolve(profile_id):
|
|
356
|
-
break
|
|
357
|
-
outcome = self._process_candidate(candidate, profile_id)
|
|
358
|
-
if outcome == "evolved":
|
|
359
|
-
results["evolved"] += 1
|
|
360
|
-
elif outcome == "rejected":
|
|
361
|
-
results["rejected"] += 1
|
|
362
|
-
|
|
363
|
-
return results
|
|
364
|
-
|
|
365
|
-
def evolve_candidate(
|
|
366
|
-
self, candidate: EvolutionCandidate, profile_id: str = "default",
|
|
367
|
-
) -> str:
|
|
368
|
-
"""Evolve a single candidate under a budget cycle (audit-10 fix).
|
|
369
|
-
|
|
370
|
-
Public entry point for the ``evolve_skill`` MCP tool. Opening the
|
|
371
|
-
budget cycle HERE is what makes the per-cycle LLM-call, wall-time and
|
|
372
|
-
per-day caps apply to manual/MCP-triggered evolution, exactly as they
|
|
373
|
-
do to consolidation. The MCP tool previously called
|
|
374
|
-
``_process_candidate`` directly, so budget charges happened outside a
|
|
375
|
-
cycle, the guard raised RuntimeError, ``_llm_call`` swallowed it, and
|
|
376
|
-
every cap was silently bypassed.
|
|
377
|
-
"""
|
|
378
|
-
try:
|
|
379
|
-
with self._budget.cycle():
|
|
380
|
-
self._current_cycle_id = None
|
|
381
|
-
return self._process_candidate(candidate, profile_id)
|
|
382
|
-
except BudgetExhausted as exc:
|
|
383
|
-
logger.info(
|
|
384
|
-
"evolve_candidate skipped: budget exhausted [%s]",
|
|
385
|
-
getattr(exc, "dimension", "?"),
|
|
386
|
-
)
|
|
387
|
-
return "skipped"
|
|
388
|
-
|
|
389
|
-
def _process_candidate(
|
|
390
|
-
self, candidate: EvolutionCandidate, profile_id: str,
|
|
391
|
-
) -> str:
|
|
392
|
-
"""Process a single evolution candidate through the governed state machine.
|
|
393
|
-
|
|
394
|
-
State machine (Phase 2):
|
|
395
|
-
CANDIDATE (insert_record once)
|
|
396
|
-
→ LLM confirm gate
|
|
397
|
-
fail → append_transition(CANDIDATE, REJECTED) → return "rejected"
|
|
398
|
-
→ Generate mutation
|
|
399
|
-
fail → append_transition(CANDIDATE, FAILED) → return "rejected"
|
|
400
|
-
→ Blind verify
|
|
401
|
-
fail → append_transition(CANDIDATE, REJECTED) → return "rejected"
|
|
402
|
-
pass → append_transition(CANDIDATE, VERIFIED_QUARANTINED)
|
|
403
|
-
→ emit audit "skill_promotion"
|
|
404
|
-
VERIFIED_QUARANTINED
|
|
405
|
-
→ no auto_approve → return "quarantined"
|
|
406
|
-
→ auto_approve → append_transition(VQ, APPROVED)
|
|
407
|
-
APPROVED
|
|
408
|
-
→ activate()
|
|
409
|
-
success → append_transition(APPROVED, ACTIVE) → emit "skill_activation"
|
|
410
|
-
→ return "evolved"
|
|
411
|
-
fail → append_transition(APPROVED, ROLLED_BACK)
|
|
412
|
-
→ emit "skill_activation_failed" → return "rejected"
|
|
413
|
-
|
|
414
|
-
Returns one of: "evolved", "rejected", "skipped", "quarantined"
|
|
415
|
-
"""
|
|
416
|
-
from superlocalmemory.evolution.skill_activator import SkillActivationError
|
|
417
|
-
|
|
418
|
-
now = datetime.now(timezone.utc).isoformat()
|
|
419
|
-
record_id = hashlib.sha256(
|
|
420
|
-
f"{candidate.skill_name}:{candidate.trigger.value}:{now}".encode(),
|
|
421
|
-
).hexdigest()[:16]
|
|
422
|
-
|
|
423
|
-
# Anti-loop: check if already addressed
|
|
424
|
-
context_hash = hashlib.sha256(
|
|
425
|
-
json.dumps(list(candidate.evidence)).encode(),
|
|
426
|
-
).hexdigest()[:12]
|
|
427
|
-
|
|
428
|
-
if self._store.is_addressed(candidate.skill_name, context_hash):
|
|
429
|
-
return "skipped"
|
|
430
|
-
|
|
431
|
-
if self._store.has_exceeded_attempts(candidate.skill_name, profile_id):
|
|
432
|
-
logger.info(
|
|
433
|
-
"Skill %s exceeded max attempts, flagging for review",
|
|
434
|
-
candidate.skill_name,
|
|
435
|
-
)
|
|
436
|
-
return "skipped"
|
|
437
|
-
|
|
438
|
-
# Mark as addressed (even on reject — prevents repeated scans)
|
|
439
|
-
self._store.mark_addressed(candidate.skill_name, context_hash)
|
|
440
|
-
|
|
441
|
-
# Step 1: Read original skill content
|
|
442
|
-
original_content = self._read_skill_content(candidate.skill_name)
|
|
443
|
-
|
|
444
|
-
# CRIT-1: insert_record is called EXACTLY ONCE per candidate.
|
|
445
|
-
# All subsequent state changes use append_transition with this record_id.
|
|
446
|
-
record = EvolutionRecord(
|
|
447
|
-
id=record_id,
|
|
448
|
-
skill_name=candidate.skill_name,
|
|
449
|
-
parent_skill_id=candidate.skill_name,
|
|
450
|
-
evolution_type=candidate.evolution_type,
|
|
451
|
-
trigger=candidate.trigger,
|
|
452
|
-
status=EvolutionStatus.CANDIDATE,
|
|
453
|
-
evidence=candidate.evidence,
|
|
454
|
-
original_content=original_content[:2000],
|
|
455
|
-
created_at=now,
|
|
456
|
-
)
|
|
457
|
-
self._store.insert_record(record, profile_id)
|
|
458
|
-
|
|
459
|
-
# Step 2: LLM confirmation gate (strict JSON parse)
|
|
460
|
-
confirmed = self._llm_confirm(candidate, original_content)
|
|
461
|
-
if not confirmed:
|
|
462
|
-
self._store.append_transition(
|
|
463
|
-
record_id, profile_id,
|
|
464
|
-
EvolutionStatus.CANDIDATE, EvolutionStatus.REJECTED,
|
|
465
|
-
reason="llm_confirmation_rejected",
|
|
466
|
-
)
|
|
467
|
-
return "rejected"
|
|
468
|
-
|
|
469
|
-
# Step 3: Generate mutation
|
|
470
|
-
prompt = mutgen.build_mutation_prompt(candidate, original_content)
|
|
471
|
-
evolved_content = self._generate_mutation(prompt)
|
|
472
|
-
if not evolved_content:
|
|
473
|
-
self._store.append_transition(
|
|
474
|
-
record_id, profile_id,
|
|
475
|
-
EvolutionStatus.CANDIDATE, EvolutionStatus.FAILED,
|
|
476
|
-
reason="mutation_generation_failed",
|
|
477
|
-
)
|
|
478
|
-
return "rejected"
|
|
479
|
-
|
|
480
|
-
# Step 4: Blind verification (independent model from generator)
|
|
481
|
-
description = self._extract_description(evolved_content)
|
|
482
|
-
v_prompt = verifier.build_verification_prompt(
|
|
483
|
-
candidate.skill_name, description, evolved_content,
|
|
484
|
-
)
|
|
485
|
-
v_result = self._blind_verify(v_prompt)
|
|
486
|
-
if not v_result.passed:
|
|
487
|
-
self._store.append_transition(
|
|
488
|
-
record_id, profile_id,
|
|
489
|
-
EvolutionStatus.CANDIDATE, EvolutionStatus.REJECTED,
|
|
490
|
-
reason=f"blind_verification: {v_result.reasoning}",
|
|
491
|
-
)
|
|
492
|
-
return "rejected"
|
|
493
|
-
|
|
494
|
-
# Step 5: Write to quarantine (returns path + dir_name — CRIT-2)
|
|
495
|
-
diff = self._compute_diff(original_content, evolved_content)
|
|
496
|
-
skill_path, dir_name = self._write_evolved_skill(
|
|
497
|
-
candidate, evolved_content, record_id,
|
|
498
|
-
)
|
|
499
|
-
|
|
500
|
-
# Compute generation from parent history
|
|
501
|
-
parent_history = self._store.get_skill_history(
|
|
502
|
-
candidate.skill_name, profile_id, limit=1,
|
|
503
|
-
)
|
|
504
|
-
parent_gen = (
|
|
505
|
-
parent_history[0].generation
|
|
506
|
-
if parent_history and parent_history[0].status == EvolutionStatus.PROMOTED
|
|
507
|
-
else 0
|
|
508
|
-
)
|
|
509
|
-
|
|
510
|
-
# CANDIDATE → VERIFIED_QUARANTINED
|
|
511
|
-
self._store.append_transition(
|
|
512
|
-
record_id, profile_id,
|
|
513
|
-
EvolutionStatus.CANDIDATE, EvolutionStatus.VERIFIED_QUARANTINED,
|
|
514
|
-
reason="blind_verified",
|
|
515
|
-
metadata={
|
|
516
|
-
"quarantine_dir_name": dir_name,
|
|
517
|
-
"diff_summary": self._summarize_diff(diff),
|
|
518
|
-
"generation": parent_gen + 1,
|
|
519
|
-
},
|
|
520
|
-
)
|
|
521
|
-
self._store.record_evolution_attempt(profile_id)
|
|
522
|
-
|
|
523
|
-
# Phase 2 (Module 8): emit "skill_promotion" audit event
|
|
524
|
-
evolved_hash = hashlib.sha256(evolved_content.encode()).hexdigest()
|
|
525
|
-
self._audit.log(
|
|
526
|
-
operation="skill_promotion",
|
|
527
|
-
agent_id=self._profile_id,
|
|
528
|
-
profile_id=self._profile_id,
|
|
529
|
-
content_hash=evolved_hash,
|
|
530
|
-
metadata={
|
|
531
|
-
"record_id": record_id,
|
|
532
|
-
"skill_name": candidate.skill_name,
|
|
533
|
-
"evolution_type": candidate.evolution_type.value,
|
|
534
|
-
"trigger": candidate.trigger.value,
|
|
535
|
-
"quarantine_dir": dir_name,
|
|
536
|
-
},
|
|
537
|
-
)
|
|
538
|
-
|
|
539
|
-
logger.info(
|
|
540
|
-
"Evolved skill: %s (%s via %s) → quarantine %s",
|
|
541
|
-
candidate.skill_name, candidate.evolution_type.value,
|
|
542
|
-
candidate.trigger.value, skill_path,
|
|
543
|
-
)
|
|
544
|
-
|
|
545
|
-
# Check auto-approve — if off, stop here for human review
|
|
546
|
-
if not self._is_auto_approve():
|
|
547
|
-
return "quarantined"
|
|
548
|
-
|
|
549
|
-
# VERIFIED_QUARANTINED → APPROVED (auto)
|
|
550
|
-
actor_id = "auto"
|
|
551
|
-
self._store.append_transition(
|
|
552
|
-
record_id, profile_id,
|
|
553
|
-
EvolutionStatus.VERIFIED_QUARANTINED, EvolutionStatus.APPROVED,
|
|
554
|
-
actor_id=actor_id, reason="auto_approved",
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
# APPROVED → ACTIVE or ROLLED_BACK
|
|
558
|
-
activator = self._get_activator()
|
|
559
|
-
try:
|
|
560
|
-
activation_result = activator.activate(
|
|
561
|
-
candidate.skill_name, dir_name, actor_id=actor_id,
|
|
562
|
-
)
|
|
563
|
-
self._store.append_transition(
|
|
564
|
-
record_id, profile_id,
|
|
565
|
-
EvolutionStatus.APPROVED, EvolutionStatus.ACTIVE,
|
|
566
|
-
actor_id=actor_id,
|
|
567
|
-
metadata={"live_path": activation_result.get("live_path", "")},
|
|
568
|
-
)
|
|
569
|
-
self._audit.log(
|
|
570
|
-
operation="skill_activation",
|
|
571
|
-
agent_id=actor_id,
|
|
572
|
-
profile_id=self._profile_id,
|
|
573
|
-
content_hash=activation_result["content_hash"],
|
|
574
|
-
metadata={
|
|
575
|
-
"record_id": record_id,
|
|
576
|
-
"skill_name": candidate.skill_name,
|
|
577
|
-
"live_path": activation_result["live_path"],
|
|
578
|
-
"backup_path": activation_result.get("backup_path"),
|
|
579
|
-
},
|
|
580
|
-
)
|
|
581
|
-
return "evolved"
|
|
582
|
-
except (SkillActivationError, FileNotFoundError, ValueError, OSError) as exc:
|
|
583
|
-
# Audit P1-1: broaden the catch. A missing quarantine artifact raises
|
|
584
|
-
# FileNotFoundError and a path-traversal guard raises ValueError; both
|
|
585
|
-
# previously escaped after APPROVED was recorded, leaving the machine
|
|
586
|
-
# stuck at APPROVED with no terminal transition.
|
|
587
|
-
# Audit P1-2: actually PERFORM the rollback instead of only logging
|
|
588
|
-
# one. The terminal status is honest — ROLLED_BACK only if a restore
|
|
589
|
-
# happened, otherwise FAILED (nothing was written to live).
|
|
590
|
-
try:
|
|
591
|
-
rb = activator.rollback(candidate.skill_name)
|
|
592
|
-
rolled_back = bool(rb.get("rolled_back"))
|
|
593
|
-
except Exception as rb_exc: # rollback must never mask the original error
|
|
594
|
-
logger.error(
|
|
595
|
-
"skill rollback errored for %s: %s",
|
|
596
|
-
candidate.skill_name, rb_exc,
|
|
597
|
-
)
|
|
598
|
-
rolled_back = False
|
|
599
|
-
end_status = (
|
|
600
|
-
EvolutionStatus.ROLLED_BACK if rolled_back
|
|
601
|
-
else EvolutionStatus.FAILED
|
|
602
|
-
)
|
|
603
|
-
self._store.append_transition(
|
|
604
|
-
record_id, profile_id,
|
|
605
|
-
EvolutionStatus.APPROVED, end_status,
|
|
606
|
-
actor_id=actor_id, reason=str(exc),
|
|
607
|
-
)
|
|
608
|
-
self._audit.log(
|
|
609
|
-
operation="skill_activation_failed",
|
|
610
|
-
agent_id=actor_id,
|
|
611
|
-
profile_id=self._profile_id,
|
|
612
|
-
content_hash="",
|
|
613
|
-
metadata={
|
|
614
|
-
"record_id": record_id,
|
|
615
|
-
"skill_name": candidate.skill_name,
|
|
616
|
-
"error": str(exc),
|
|
617
|
-
"rolled_back": rolled_back,
|
|
618
|
-
},
|
|
619
|
-
)
|
|
620
|
-
logger.error(
|
|
621
|
-
"skill activation failed for %s: %s (%s)",
|
|
622
|
-
candidate.skill_name, exc,
|
|
623
|
-
"rolled back" if rolled_back else "failed, nothing to roll back",
|
|
624
|
-
)
|
|
625
|
-
return "rejected"
|
|
626
|
-
|
|
627
|
-
# ------------------------------------------------------------------
|
|
628
|
-
# LLM calls — single-line funnel through evolution.llm_dispatch
|
|
629
|
-
# ------------------------------------------------------------------
|
|
630
|
-
#
|
|
631
|
-
# SB-2: every LLM call goes through ``_dispatch_llm``. That function
|
|
632
|
-
# owns model validation, redact_secrets, backend registry, and the
|
|
633
|
-
# cost-log write. No other code in this module touches a backend.
|
|
634
|
-
# SB-4: ``_dispatch_llm`` routes the claude-CLI path through
|
|
635
|
-
# ``run_subprocess_safe`` — no bare ``subprocess.run`` here.
|
|
636
|
-
|
|
637
|
-
def _llm_call(
|
|
638
|
-
self, prompt: str, max_tokens: int = 500, model: str = "haiku",
|
|
639
|
-
) -> str:
|
|
640
|
-
"""Funnel a prompt through :func:`_dispatch_llm`.
|
|
641
|
-
|
|
642
|
-
The ``model`` arg accepts short aliases (``haiku``/``sonnet``)
|
|
643
|
-
for backward compatibility; they are resolved to the allow-listed
|
|
644
|
-
model id before dispatch. Budget charge happens BEFORE dispatch;
|
|
645
|
-
on ``BudgetExhausted`` we return an empty string so the caller
|
|
646
|
-
treats this as "no evolution" (the usual fail-closed semantics).
|
|
647
|
-
"""
|
|
648
|
-
backend = self._get_backend()
|
|
649
|
-
if backend == "none":
|
|
650
|
-
return ""
|
|
651
|
-
|
|
652
|
-
# Charge the budget BEFORE dispatching so the cap is protective.
|
|
653
|
-
# If budget is exhausted we degrade gracefully (empty string).
|
|
654
|
-
try:
|
|
655
|
-
self._budget.charge_llm_call()
|
|
656
|
-
self._budget.check_time()
|
|
657
|
-
except BudgetExhausted as exc:
|
|
658
|
-
logger.info("evolution _llm_call skipped: %s", exc)
|
|
659
|
-
return ""
|
|
660
|
-
except RuntimeError:
|
|
661
|
-
# charge_llm_call/check_time outside cycle() — shouldn't
|
|
662
|
-
# happen in production wiring, but if a caller invokes
|
|
663
|
-
# _llm_call without opening a cycle (e.g. tests), let it
|
|
664
|
-
# pass so legacy behaviour is preserved.
|
|
665
|
-
pass
|
|
666
|
-
|
|
667
|
-
resolved_model = _resolve_model_alias(model)
|
|
668
|
-
try:
|
|
669
|
-
return _dispatch_llm(
|
|
670
|
-
prompt,
|
|
671
|
-
model=resolved_model,
|
|
672
|
-
learning_db=Path(self._db_path),
|
|
673
|
-
profile_id=self._profile_id,
|
|
674
|
-
max_tokens=max_tokens,
|
|
675
|
-
cycle_id=self._current_cycle_id,
|
|
676
|
-
)
|
|
677
|
-
except ValueError as exc:
|
|
678
|
-
# A ValueError from _dispatch_llm is a CONTRACT breach, not a
|
|
679
|
-
# runtime/transport failure: forbidden or unlisted model,
|
|
680
|
-
# non-positive max_tokens, max_tokens over MAX_TOKENS_CAP, or
|
|
681
|
-
# an empty profile_id. Every one is a misconfiguration in our
|
|
682
|
-
# own wiring — e.g. a caller requesting more tokens than the
|
|
683
|
-
# ceiling, which is exactly how mutation generation died
|
|
684
|
-
# silently for releases. Log at ERROR so it surfaces in logs
|
|
685
|
-
# and CI instead of masquerading as a normal "no LLM"
|
|
686
|
-
# fail-closed. Behaviour is unchanged: we still return "" so a
|
|
687
|
-
# consolidation cycle degrades gracefully rather than crashing.
|
|
688
|
-
logger.error(
|
|
689
|
-
"evolution dispatch misconfigured — no evolution this "
|
|
690
|
-
"call (returning empty string): %s", exc,
|
|
691
|
-
)
|
|
692
|
-
return ""
|
|
693
|
-
except Exception as exc: # noqa: BLE001 — fail-closed
|
|
694
|
-
logger.debug("evolution dispatch failed: %s", exc)
|
|
695
|
-
return ""
|
|
696
|
-
|
|
697
|
-
def _parse_approval_decision(self, response: str) -> bool:
|
|
698
|
-
"""Parse LLM approval decision from structured JSON.
|
|
699
|
-
|
|
700
|
-
Accepts ONLY {"decision": "approve"} (case-insensitive value).
|
|
701
|
-
Rejects: "yes", "maybe", "approve" as free-text, partial JSON, or any
|
|
702
|
-
other value including {"decision": "yes"} or {"decision": "approved"}.
|
|
703
|
-
|
|
704
|
-
LLMs sometimes emit leading text before the JSON object — the parser
|
|
705
|
-
finds the first '{' and last '}' to extract the JSON fragment.
|
|
706
|
-
|
|
707
|
-
Returns True only if decision is exactly "approve" (case-insensitive).
|
|
708
|
-
Returns False for all other inputs including empty string, malformed
|
|
709
|
-
JSON, or any decision value other than "approve".
|
|
710
|
-
"""
|
|
711
|
-
if not response:
|
|
712
|
-
return False
|
|
713
|
-
stripped = response.strip()
|
|
714
|
-
# Find first JSON object — LLM may emit leading explanation text.
|
|
715
|
-
start = stripped.find("{")
|
|
716
|
-
end = stripped.rfind("}")
|
|
717
|
-
if start == -1 or end == -1 or end <= start:
|
|
718
|
-
return False
|
|
719
|
-
try:
|
|
720
|
-
obj = json.loads(stripped[start:end + 1])
|
|
721
|
-
except (json.JSONDecodeError, ValueError):
|
|
722
|
-
return False
|
|
723
|
-
if not isinstance(obj, dict):
|
|
724
|
-
return False
|
|
725
|
-
decision = obj.get("decision", "")
|
|
726
|
-
return isinstance(decision, str) and decision.lower() == "approve"
|
|
727
|
-
|
|
728
|
-
def _build_confirm_prompt(
|
|
729
|
-
self, candidate: EvolutionCandidate, original: str,
|
|
730
|
-
) -> str:
|
|
731
|
-
"""Build the strict-JSON LLM confirmation prompt."""
|
|
732
|
-
return (
|
|
733
|
-
f"A skill '{candidate.skill_name}' has effective score "
|
|
734
|
-
f"{candidate.effective_score:.0%} over "
|
|
735
|
-
f"{candidate.invocation_count} invocations.\n"
|
|
736
|
-
f"Evidence: {'; '.join(candidate.evidence)}\n\n"
|
|
737
|
-
f"Should this skill be evolved ({candidate.evolution_type.value})?\n"
|
|
738
|
-
f"Respond ONLY with a JSON object. No other text.\n"
|
|
739
|
-
f'Approve: {{"decision": "approve"}}\n'
|
|
740
|
-
f'Reject: {{"decision": "reject", "reason": "one sentence"}}'
|
|
741
|
-
)
|
|
742
|
-
|
|
743
|
-
def _llm_confirm(self, candidate: EvolutionCandidate, original: str) -> bool:
|
|
744
|
-
"""LLM confirmation gate — strict structured decision parse (Module 3).
|
|
745
|
-
|
|
746
|
-
Replaces the legacy 'yes' in response.lower() check with strict JSON
|
|
747
|
-
parsing. Only {"decision": "approve"} returns True.
|
|
748
|
-
"""
|
|
749
|
-
prompt = self._build_confirm_prompt(candidate, original)
|
|
750
|
-
response = self._llm_call(
|
|
751
|
-
prompt, max_tokens=100, model=self._get_models().confirm,
|
|
752
|
-
)
|
|
753
|
-
if not response:
|
|
754
|
-
logger.warning(
|
|
755
|
-
"LLM confirmation gate: empty response, "
|
|
756
|
-
"skipping evolution for %s",
|
|
757
|
-
candidate.skill_name,
|
|
758
|
-
)
|
|
759
|
-
return False # Fail-closed: no LLM = no evolution
|
|
760
|
-
approved = self._parse_approval_decision(response)
|
|
761
|
-
if not approved:
|
|
762
|
-
logger.info(
|
|
763
|
-
"LLM confirmation gate rejected %s (response: %.80r)",
|
|
764
|
-
candidate.skill_name, response,
|
|
765
|
-
)
|
|
766
|
-
return approved
|
|
767
|
-
|
|
768
|
-
def _generate_mutation(self, prompt: str) -> Optional[str]:
|
|
769
|
-
"""Generate evolved SKILL.md via the configured mutation model.
|
|
770
|
-
|
|
771
|
-
Defaults to the lowest-cost model for the backend (v3.7.9);
|
|
772
|
-
users can opt up (e.g. to sonnet) via ``evolution.mutation_model``.
|
|
773
|
-
"""
|
|
774
|
-
mutation_model = self._get_models().mutation
|
|
775
|
-
for attempt in range(mutgen.MAX_APPLY_RETRIES):
|
|
776
|
-
response = self._llm_call(
|
|
777
|
-
prompt, max_tokens=4000, model=mutation_model,
|
|
778
|
-
)
|
|
779
|
-
if not response:
|
|
780
|
-
return None
|
|
781
|
-
content = mutgen.parse_mutation_output(response)
|
|
782
|
-
if content:
|
|
783
|
-
error = mutgen.validate_skill_content(content)
|
|
784
|
-
if error is None:
|
|
785
|
-
return content
|
|
786
|
-
prompt = mutgen.build_retry_prompt(prompt, error, attempt + 1)
|
|
787
|
-
else:
|
|
788
|
-
prompt = mutgen.build_retry_prompt(
|
|
789
|
-
prompt, "No valid SKILL.md content found in output", attempt + 1,
|
|
790
|
-
)
|
|
791
|
-
return None
|
|
792
|
-
|
|
793
|
-
def _blind_verify(self, prompt: str) -> verifier.VerificationResult:
|
|
794
|
-
"""Blind verification — runs on a model independent of the generator.
|
|
795
|
-
|
|
796
|
-
See ``model_selection.resolve_evolution_models``: the verify model
|
|
797
|
-
is kept different from the mutation model where possible so the
|
|
798
|
-
generator can't grade its own homework.
|
|
799
|
-
"""
|
|
800
|
-
response = self._llm_call(
|
|
801
|
-
prompt, max_tokens=500, model=self._get_models().verify,
|
|
802
|
-
)
|
|
803
|
-
if not response:
|
|
804
|
-
return verifier.VerificationResult(
|
|
805
|
-
passed=False, confidence=0.0, reasoning="No LLM response",
|
|
806
|
-
)
|
|
807
|
-
return verifier.parse_verification_response(response)
|
|
808
|
-
|
|
809
|
-
# ------------------------------------------------------------------
|
|
810
|
-
# Skill I/O
|
|
811
|
-
# ------------------------------------------------------------------
|
|
812
|
-
|
|
813
|
-
def _read_skill_content(self, skill_name: str) -> str:
|
|
814
|
-
"""Read a skill's SKILL.md content. Searches known skill directories."""
|
|
815
|
-
search_dirs = [
|
|
816
|
-
Path.home() / ".claude" / "skills",
|
|
817
|
-
Path.home() / ".claude" / "plugins",
|
|
818
|
-
EVOLVED_SKILLS_DIR,
|
|
819
|
-
]
|
|
820
|
-
|
|
821
|
-
# Handle namespaced skills (e.g., "superpowers:brainstorming")
|
|
822
|
-
if ":" in skill_name:
|
|
823
|
-
parts = skill_name.split(":")
|
|
824
|
-
search_patterns = [
|
|
825
|
-
f"**/{parts[-1]}/SKILL.md",
|
|
826
|
-
f"**/{skill_name.replace(':', '/')}/SKILL.md",
|
|
827
|
-
f"**/skills/{parts[-1]}/SKILL.md",
|
|
828
|
-
]
|
|
829
|
-
else:
|
|
830
|
-
search_patterns = [f"**/{skill_name}/SKILL.md"]
|
|
831
|
-
|
|
832
|
-
for search_dir in search_dirs:
|
|
833
|
-
if not search_dir.exists():
|
|
834
|
-
continue
|
|
835
|
-
for pattern in search_patterns:
|
|
836
|
-
matches = list(search_dir.glob(pattern))
|
|
837
|
-
if matches:
|
|
838
|
-
try:
|
|
839
|
-
return matches[0].read_text(encoding="utf-8")
|
|
840
|
-
except OSError:
|
|
841
|
-
continue
|
|
842
|
-
|
|
843
|
-
return ""
|
|
844
|
-
|
|
845
|
-
def _write_evolved_skill(
|
|
846
|
-
self,
|
|
847
|
-
candidate: EvolutionCandidate,
|
|
848
|
-
content: str,
|
|
849
|
-
record_id: str,
|
|
850
|
-
) -> tuple[Path, str]:
|
|
851
|
-
"""Write evolved SKILL.md to the quarantine directory for review.
|
|
852
|
-
|
|
853
|
-
Returns (skill_path, dir_name) — both are needed by _process_candidate:
|
|
854
|
-
- skill_path: the Path to the written SKILL.md (for logging)
|
|
855
|
-
- dir_name: the quarantine subdirectory name (CRIT-2; passed to SkillActivator)
|
|
856
|
-
|
|
857
|
-
The dir_name is DISTINCT from candidate.skill_name. It is the sanitized,
|
|
858
|
-
versioned subdirectory inside the quarantine root, e.g. "brainstorming-vabc12".
|
|
859
|
-
"""
|
|
860
|
-
EVOLVED_SKILLS_DIR.mkdir(parents=True, exist_ok=True)
|
|
861
|
-
|
|
862
|
-
# Build directory name. skill_name derives from a behavioral-assertion
|
|
863
|
-
# trigger (any non-whitespace), so sanitize for ALL evolution types —
|
|
864
|
-
# "../../.claude/settings" must not escape EVOLVED_SKILLS_DIR.
|
|
865
|
-
base_name = re.sub(r"[^a-zA-Z0-9_-]", "-",
|
|
866
|
-
candidate.skill_name).lower()[:50] or "skill"
|
|
867
|
-
if candidate.evolution_type == EvolutionType.FIX:
|
|
868
|
-
dir_name = f"{base_name}-v{record_id[:6]}"
|
|
869
|
-
elif candidate.evolution_type == EvolutionType.DERIVED:
|
|
870
|
-
# Extract name from evolved content frontmatter
|
|
871
|
-
name_match = re.search(r"name:\s*(.+)", content)
|
|
872
|
-
dir_name = name_match.group(1).strip() if name_match else f"{base_name}-derived"
|
|
873
|
-
dir_name = re.sub(r"[^a-zA-Z0-9_-]", "-", dir_name).lower()[:50]
|
|
874
|
-
else:
|
|
875
|
-
dir_name = base_name
|
|
876
|
-
dir_name = dir_name or "skill"
|
|
877
|
-
|
|
878
|
-
skill_dir = EVOLVED_SKILLS_DIR / dir_name
|
|
879
|
-
# Defense in depth: never write outside the evolved-skills directory.
|
|
880
|
-
if not str(skill_dir.resolve()).startswith(str(EVOLVED_SKILLS_DIR.resolve())):
|
|
881
|
-
raise ValueError(f"evolved skill path escapes sandbox: {dir_name!r}")
|
|
882
|
-
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
883
|
-
|
|
884
|
-
skill_path = skill_dir / "SKILL.md"
|
|
885
|
-
skill_path.write_text(content, encoding="utf-8")
|
|
886
|
-
|
|
887
|
-
# Write metadata sidecar (CRIT-2: includes quarantine_dir_name for recovery)
|
|
888
|
-
meta = {
|
|
889
|
-
"skill_id": dir_name,
|
|
890
|
-
"parent_skill_id": candidate.skill_name,
|
|
891
|
-
"evolution_type": candidate.evolution_type.value,
|
|
892
|
-
"trigger": candidate.trigger.value,
|
|
893
|
-
"record_id": record_id,
|
|
894
|
-
"quarantine_dir_name": dir_name,
|
|
895
|
-
"evidence": list(candidate.evidence),
|
|
896
|
-
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
897
|
-
}
|
|
898
|
-
meta_path = skill_dir / ".skill_meta.json"
|
|
899
|
-
meta_path.write_text(json.dumps(meta, indent=2), encoding="utf-8")
|
|
900
|
-
|
|
901
|
-
return skill_path, dir_name
|
|
902
|
-
|
|
903
|
-
# ------------------------------------------------------------------
|
|
904
|
-
# Utilities
|
|
905
|
-
# ------------------------------------------------------------------
|
|
906
|
-
|
|
907
|
-
def _extract_description(self, content: str) -> str:
|
|
908
|
-
"""Extract description from SKILL.md frontmatter."""
|
|
909
|
-
match = re.search(r"description:\s*(.+?)(?:\n|---)", content)
|
|
910
|
-
return match.group(1).strip() if match else "Skill for AI agent tasks"
|
|
911
|
-
|
|
912
|
-
def _compute_diff(self, original: str, evolved: str) -> str:
|
|
913
|
-
if not original:
|
|
914
|
-
return "(new skill — no original to diff)"
|
|
915
|
-
diff = difflib.unified_diff(
|
|
916
|
-
original.splitlines(keepends=True),
|
|
917
|
-
evolved.splitlines(keepends=True),
|
|
918
|
-
fromfile="original",
|
|
919
|
-
tofile="evolved",
|
|
920
|
-
n=3,
|
|
921
|
-
)
|
|
922
|
-
return "".join(diff)
|
|
923
|
-
|
|
924
|
-
def _summarize_diff(self, diff: str) -> str:
|
|
925
|
-
"""Count additions and removals."""
|
|
926
|
-
additions = sum(1 for line in diff.splitlines() if line.startswith("+") and not line.startswith("+++"))
|
|
927
|
-
removals = sum(1 for line in diff.splitlines() if line.startswith("-") and not line.startswith("---"))
|
|
928
|
-
return f"+{additions}/-{removals} lines"
|