superlocalmemory 3.6.22 → 3.7.0
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/CHANGELOG.md +52 -0
- package/README.md +275 -72
- package/bin/slm-npm +43 -89
- package/docs/pi-dev-integration.md +43 -0
- package/ide/configs/antigravity-mcp.json +2 -2
- package/ide/configs/chatgpt-desktop-mcp.json +1 -1
- package/ide/configs/claude-desktop-mcp.json +2 -2
- package/ide/configs/windsurf-mcp.json +2 -2
- package/ide/hooks/context-hook.js +6 -2
- package/ide/hooks/post-recall-hook.js +7 -3
- package/ide/hooks/tool-event-hook.sh +2 -1
- package/package.json +19 -10
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/_GENERATED.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/pyproject.toml +40 -8
- package/scripts/postinstall-interactive.js +17 -94
- package/scripts/postinstall.js +185 -258
- package/scripts/preuninstall.js +9 -50
- package/src/superlocalmemory/__init__.py +2 -2
- package/src/superlocalmemory/attribution/mathematical_dna.py +1 -1
- package/src/superlocalmemory/attribution/signer.py +34 -19
- package/src/superlocalmemory/attribution/watermark.py +1 -1
- package/src/superlocalmemory/cli/_lazy_init.py +3 -5
- package/src/superlocalmemory/cli/commands.py +490 -195
- package/src/superlocalmemory/cli/context_commands.py +5 -4
- package/src/superlocalmemory/cli/daemon.py +282 -187
- package/src/superlocalmemory/cli/db_migrate.py +3 -1
- package/src/superlocalmemory/cli/diagnostics_cmd.py +28 -0
- package/src/superlocalmemory/cli/evidence_cmd.py +103 -0
- package/src/superlocalmemory/cli/ingest_cmd.py +7 -3
- package/src/superlocalmemory/cli/main.py +128 -31
- package/src/superlocalmemory/cli/pending_store.py +54 -38
- package/src/superlocalmemory/cli/scale_engine_cmd.py +37 -0
- package/src/superlocalmemory/cli/service_installer.py +57 -52
- package/src/superlocalmemory/cli/setup_wizard.py +142 -88
- package/src/superlocalmemory/cli/version_banner.py +2 -1
- package/src/superlocalmemory/code_graph/config.py +3 -1
- package/src/superlocalmemory/core/backend_orchestrator.py +81 -21
- package/src/superlocalmemory/core/config.py +65 -20
- package/src/superlocalmemory/core/consolidation_engine.py +9 -7
- package/src/superlocalmemory/core/context_cache.py +56 -8
- package/src/superlocalmemory/core/derivation_lineage.py +246 -0
- package/src/superlocalmemory/core/embedding_worker.py +32 -20
- package/src/superlocalmemory/core/embeddings.py +54 -18
- package/src/superlocalmemory/core/engine.py +150 -104
- package/src/superlocalmemory/core/engine_ingestion.py +513 -0
- package/src/superlocalmemory/core/engine_wiring.py +2 -0
- package/src/superlocalmemory/core/evidence_bundle.py +526 -0
- package/src/superlocalmemory/core/fact_consolidator.py +5 -11
- package/src/superlocalmemory/core/graph_analyzer.py +2 -2
- package/src/superlocalmemory/core/health_monitor.py +4 -2
- package/src/superlocalmemory/core/ingestion_command.py +636 -0
- package/src/superlocalmemory/core/injection.py +69 -18
- package/src/superlocalmemory/core/lifecycle_state.py +153 -0
- package/src/superlocalmemory/core/maintenance.py +23 -22
- package/src/superlocalmemory/core/maintenance_scheduler.py +51 -35
- package/src/superlocalmemory/core/mutations.py +143 -0
- package/src/superlocalmemory/core/platform_utils.py +7 -4
- package/src/superlocalmemory/core/ram_lock.py +16 -5
- package/src/superlocalmemory/core/rate_limit.py +1 -1
- package/src/superlocalmemory/core/recall_pipeline.py +60 -101
- package/src/superlocalmemory/core/recall_worker.py +76 -59
- package/src/superlocalmemory/core/registry.py +1 -1
- package/src/superlocalmemory/core/scale_engine.py +293 -0
- package/src/superlocalmemory/core/score_contract.py +62 -0
- package/src/superlocalmemory/core/security_primitives.py +3 -1
- package/src/superlocalmemory/core/slm_disabled.py +3 -5
- package/src/superlocalmemory/core/store_pipeline.py +172 -40
- package/src/superlocalmemory/core/tier_manager.py +32 -20
- package/src/superlocalmemory/core/worker_pool.py +13 -4
- package/src/superlocalmemory/dynamics/activation_guided_quantization.py +1 -1
- package/src/superlocalmemory/dynamics/eap_scheduler.py +10 -3
- package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +1 -1
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +1 -1
- package/src/superlocalmemory/encoding/auto_linker.py +1 -1
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +7 -16
- package/src/superlocalmemory/encoding/consolidator.py +22 -5
- package/src/superlocalmemory/encoding/fact_extractor.py +1 -1
- package/src/superlocalmemory/encoding/foresight.py +2 -0
- package/src/superlocalmemory/encoding/graph_builder.py +1 -1
- package/src/superlocalmemory/encoding/temporal_parser.py +2 -0
- package/src/superlocalmemory/evaluation/__init__.py +13 -0
- package/src/superlocalmemory/evaluation/calibration.py +308 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +2 -1
- package/src/superlocalmemory/graph/cozo_backend.py +256 -23
- package/src/superlocalmemory/hooks/_outcome_common.py +21 -11
- package/src/superlocalmemory/hooks/antigravity_adapter.py +10 -31
- package/src/superlocalmemory/hooks/auto_invoker.py +25 -27
- package/src/superlocalmemory/hooks/auto_recall.py +31 -6
- package/src/superlocalmemory/hooks/auto_recall_hook.py +13 -33
- package/src/superlocalmemory/hooks/before_web_hook.py +9 -7
- package/src/superlocalmemory/hooks/claude_code_hooks.py +126 -39
- package/src/superlocalmemory/hooks/codex_assets.py +59 -0
- package/src/superlocalmemory/hooks/codex_hooks.py +186 -0
- package/src/superlocalmemory/hooks/context_payload.py +1 -1
- package/src/superlocalmemory/hooks/copilot_adapter.py +9 -24
- package/src/superlocalmemory/hooks/cursor_adapter.py +10 -32
- package/src/superlocalmemory/hooks/hook_daemon.py +4 -2
- package/src/superlocalmemory/hooks/hook_handlers.py +241 -55
- package/src/superlocalmemory/hooks/memory_protocol.py +5 -3
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +4 -2
- package/src/superlocalmemory/hooks/session_registry.py +15 -8
- package/src/superlocalmemory/hooks/stop_outcome_hook.py +10 -6
- package/src/superlocalmemory/hooks/topic_shift_hook.py +42 -12
- package/src/superlocalmemory/hooks/user_prompt_hook.py +9 -14
- package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +19 -11
- package/src/superlocalmemory/infra/auth_middleware.py +38 -5
- package/src/superlocalmemory/infra/backup.py +7 -5
- package/src/superlocalmemory/infra/cloud_backup.py +18 -8
- package/src/superlocalmemory/infra/daemon_identity.py +248 -0
- package/src/superlocalmemory/infra/data_root.py +199 -0
- package/src/superlocalmemory/infra/event_bus.py +3 -1
- package/src/superlocalmemory/infra/local_diagnostics.py +327 -0
- package/src/superlocalmemory/infra/process_reaper.py +23 -0
- package/src/superlocalmemory/ingestion/adapter_manager.py +27 -9
- package/src/superlocalmemory/ingestion/base_adapter.py +25 -31
- package/src/superlocalmemory/ingestion/calendar_adapter.py +13 -4
- package/src/superlocalmemory/ingestion/credentials.py +14 -7
- package/src/superlocalmemory/ingestion/gmail_adapter.py +13 -4
- package/src/superlocalmemory/ingestion/transcript_adapter.py +7 -2
- package/src/superlocalmemory/learning/consolidation_quantization_worker.py +1 -1
- package/src/superlocalmemory/learning/ensemble.py +11 -0
- package/src/superlocalmemory/learning/entity_compiler.py +1 -1
- package/src/superlocalmemory/learning/feedback.py +1 -1
- package/src/superlocalmemory/learning/forgetting_scheduler.py +12 -7
- package/src/superlocalmemory/learning/quantization_scheduler.py +1 -1
- package/src/superlocalmemory/learning/ranker.py +4 -1
- package/src/superlocalmemory/learning/source_quality.py +1 -1
- package/src/superlocalmemory/learning/trigram_index.py +3 -2
- package/src/superlocalmemory/llm/backbone.py +13 -8
- package/src/superlocalmemory/math/ebbinghaus.py +1 -1
- package/src/superlocalmemory/math/fisher.py +1 -1
- package/src/superlocalmemory/math/fisher_quantized.py +1 -1
- package/src/superlocalmemory/math/hopfield.py +1 -1
- package/src/superlocalmemory/math/langevin.py +1 -1
- package/src/superlocalmemory/math/polar_quant.py +3 -4
- package/src/superlocalmemory/math/qjl.py +1 -1
- package/src/superlocalmemory/math/sheaf.py +1 -1
- package/src/superlocalmemory/math/turbo_quant.py +3 -2
- package/src/superlocalmemory/mcp/_daemon_proxy.py +12 -11
- package/src/superlocalmemory/mcp/_pool_adapter.py +27 -0
- package/src/superlocalmemory/mcp/http_transport.py +53 -0
- package/src/superlocalmemory/mcp/server.py +39 -13
- package/src/superlocalmemory/mcp/shared.py +69 -3
- package/src/superlocalmemory/mcp/tools_active.py +141 -31
- package/src/superlocalmemory/mcp/tools_core.py +128 -29
- package/src/superlocalmemory/mcp/tools_evolution.py +5 -7
- package/src/superlocalmemory/mcp/tools_learning.py +42 -2
- package/src/superlocalmemory/mcp/tools_mesh.py +7 -23
- package/src/superlocalmemory/mcp/tools_optimize.py +8 -1
- package/src/superlocalmemory/mcp/tools_v28.py +23 -2
- package/src/superlocalmemory/mcp/tools_v3.py +26 -1
- package/src/superlocalmemory/mcp/tools_v33.py +56 -17
- package/src/superlocalmemory/mesh/broker.py +2 -0
- package/src/superlocalmemory/mesh/remote_sync.py +50 -12
- package/src/superlocalmemory/optimize/cache/manager.py +77 -1
- package/src/superlocalmemory/optimize/cache/semantic.py +23 -3
- package/src/superlocalmemory/optimize/compress/ccr.py +4 -0
- package/src/superlocalmemory/optimize/compress/router.py +6 -1
- package/src/superlocalmemory/optimize/config/__init__.py +5 -0
- package/src/superlocalmemory/optimize/config/store.py +6 -4
- package/src/superlocalmemory/optimize/proxy/_helpers.py +15 -5
- package/src/superlocalmemory/optimize/proxy/capture.py +3 -2
- package/src/superlocalmemory/optimize/proxy/server.py +2 -2
- package/src/superlocalmemory/optimize/storage/db.py +14 -13
- package/src/superlocalmemory/retrieval/agentic.py +1 -1
- package/src/superlocalmemory/retrieval/ann_index.py +1 -1
- package/src/superlocalmemory/retrieval/bm25_channel.py +35 -11
- package/src/superlocalmemory/retrieval/bridge_discovery.py +73 -8
- package/src/superlocalmemory/retrieval/engine.py +169 -79
- package/src/superlocalmemory/retrieval/entity_channel.py +289 -67
- package/src/superlocalmemory/retrieval/forgetting_filter.py +1 -1
- package/src/superlocalmemory/retrieval/fusion.py +1 -1
- package/src/superlocalmemory/retrieval/hopfield_channel.py +118 -30
- package/src/superlocalmemory/retrieval/profile_channel.py +1 -1
- package/src/superlocalmemory/retrieval/quantization_aware_search.py +16 -10
- package/src/superlocalmemory/retrieval/reranker.py +56 -20
- package/src/superlocalmemory/retrieval/scope_policy.py +85 -0
- package/src/superlocalmemory/retrieval/semantic_channel.py +122 -14
- package/src/superlocalmemory/retrieval/spreading_activation.py +141 -25
- package/src/superlocalmemory/retrieval/strategy.py +1 -1
- package/src/superlocalmemory/retrieval/temporal_channel.py +30 -15
- package/src/superlocalmemory/retrieval/vector_store.py +1 -1
- package/src/superlocalmemory/server/api.py +10 -7
- package/src/superlocalmemory/server/bandit_loops.py +4 -2
- package/src/superlocalmemory/server/recall_serializer.py +24 -0
- package/src/superlocalmemory/server/route_mutations.py +84 -0
- package/src/superlocalmemory/server/routes/agents.py +8 -6
- package/src/superlocalmemory/server/routes/brain.py +14 -12
- package/src/superlocalmemory/server/routes/chat.py +29 -12
- package/src/superlocalmemory/server/routes/data_io.py +55 -24
- package/src/superlocalmemory/server/routes/helpers.py +29 -4
- package/src/superlocalmemory/server/routes/ingest.py +53 -36
- package/src/superlocalmemory/server/routes/memories.py +104 -43
- package/src/superlocalmemory/server/routes/mesh.py +31 -0
- package/src/superlocalmemory/server/routes/profiles.py +26 -4
- package/src/superlocalmemory/server/routes/tiers.py +43 -11
- package/src/superlocalmemory/server/routes/timeline.py +5 -1
- package/src/superlocalmemory/server/routes/v3_api.py +76 -21
- package/src/superlocalmemory/server/security_middleware.py +1 -1
- package/src/superlocalmemory/server/ui.py +6 -3
- package/src/superlocalmemory/server/unified_daemon.py +680 -293
- package/src/superlocalmemory/server/write_identity.py +147 -0
- package/src/superlocalmemory/storage/access_log.py +4 -3
- package/src/superlocalmemory/storage/database.py +118 -25
- package/src/superlocalmemory/storage/migration_runner.py +84 -1
- package/src/superlocalmemory/storage/migration_v33.py +1 -1
- package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +6 -60
- package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +120 -0
- package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +54 -0
- package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +52 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +5 -0
- package/src/superlocalmemory/storage/models.py +16 -0
- package/src/superlocalmemory/storage/quantized_store.py +20 -3
- package/src/superlocalmemory/storage/v2_migrator.py +5 -3
- package/src/superlocalmemory/ui/favicon.svg +5 -0
- package/src/superlocalmemory/ui/index.html +1 -0
- package/src/superlocalmemory/ui/js/compliance.js +1 -1
- package/src/superlocalmemory/ui/js/core.js +49 -8
- package/src/superlocalmemory/ui/js/dashboard.js +23 -2
- package/src/superlocalmemory/ui/js/feedback.js +1 -1
- package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
- package/src/superlocalmemory/ui/js/graph-ui.js +1 -1
- package/src/superlocalmemory/ui/js/lifecycle.js +1 -1
- package/src/superlocalmemory/ui/js/ng-mesh.js +15 -49
- package/src/superlocalmemory/ui/js/settings.js +4 -2
- package/src/superlocalmemory/vector/lancedb_backend.py +57 -9
- package/bin/slm +0 -59
- package/bin/slm.bat +0 -77
- package/bin/slm.cmd +0 -5
- package/ide/integrations/langchain/README.md +0 -106
- package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +0 -9
- package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +0 -201
- package/ide/integrations/langchain/pyproject.toml +0 -38
- package/ide/integrations/langchain/tests/__init__.py +0 -3
- package/ide/integrations/langchain/tests/test_chat_message_history.py +0 -215
- package/ide/integrations/langchain/tests/test_security.py +0 -117
- package/ide/integrations/llamaindex/README.md +0 -81
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +0 -9
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +0 -316
- package/ide/integrations/llamaindex/pyproject.toml +0 -43
- package/ide/integrations/llamaindex/tests/__init__.py +0 -3
- package/ide/integrations/llamaindex/tests/test_chat_store.py +0 -294
- package/ide/integrations/llamaindex/tests/test_security.py +0 -241
- package/plugin-src/.mcp.json +0 -12
- package/plugin-src/agents/slm-memory-advisor.md +0 -44
- package/plugin-src/agents/slm-optimize-advisor.md +0 -38
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +0 -23
- package/plugin-src/manifest.json +0 -25
- package/plugin-src/requirements.txt +0 -1
- package/plugin-src/rules/CLAUDE.md.fragment +0 -44
- package/plugin-src/scripts/ensure-venv.bat +0 -122
- package/plugin-src/scripts/ensure-venv.sh +0 -105
- package/plugin-src/scripts/slm-launch +0 -15
- package/plugin-src/scripts/slm-launch.bat +0 -17
- package/plugin-src/settings.json +0 -16
- package/plugin-src/skills/slm-cache/SKILL.md +0 -140
- package/plugin-src/skills/slm-compress/SKILL.md +0 -143
- package/plugin-src/skills/slm-graph/SKILL.md +0 -300
- package/plugin-src/skills/slm-recall/SKILL.md +0 -204
- package/plugin-src/skills/slm-remember/SKILL.md +0 -194
- package/plugin-src/skills/slm-session/SKILL.md +0 -207
- package/plugin-src/skills/slm-status/SKILL.md +0 -149
- package/scripts/__tests__/build-plugin.test.mjs +0 -613
- package/scripts/_savings_math.py +0 -270
- package/scripts/build-dmg.sh +0 -417
- package/scripts/build-plugin.js +0 -742
- package/scripts/build-slm-hook.ps1 +0 -40
- package/scripts/build-slm-hook.sh +0 -45
- package/scripts/build_entry.py +0 -452
- package/scripts/ci/stage5b_gate.sh +0 -50
- package/scripts/dogfood_savings.py +0 -490
- package/scripts/generate-thumbnails.py +0 -218
- package/scripts/install-skills.ps1 +0 -4
- package/scripts/install-skills.sh +0 -5
- package/scripts/install.ps1 +0 -701
- package/scripts/install.sh +0 -1015
- package/scripts/postinstall_binary.js +0 -287
- package/scripts/prepack.js +0 -33
- package/scripts/release_manifest.py +0 -273
- package/scripts/slm-hook.spec +0 -56
- package/scripts/start-dashboard.ps1 +0 -52
- package/scripts/start-dashboard.sh +0 -41
- package/scripts/sync-wiki.ps1 +0 -127
- package/scripts/sync-wiki.sh +0 -82
- package/scripts/test-dmg.sh +0 -161
- package/scripts/test-npm-package.ps1 +0 -252
- package/scripts/test-npm-package.sh +0 -207
- package/scripts/verify-install.ps1 +0 -294
- package/scripts/verify-install.sh +0 -266
- package/scripts/verify-v27.ps1 +0 -301
- package/scripts/verify-v27.sh +0 -233
- package/src/superlocalmemory.egg-info/PKG-INFO +0 -513
- package/src/superlocalmemory.egg-info/SOURCES.txt +0 -529
- package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
- package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
- package/src/superlocalmemory.egg-info/requires.txt +0 -71
- package/src/superlocalmemory.egg-info/top_level.txt +0 -1
|
@@ -11,7 +11,7 @@ to the daemon's /ingest endpoint.
|
|
|
11
11
|
OPT-IN only. Enabled via: slm adapters enable transcript
|
|
12
12
|
|
|
13
13
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
14
|
-
License:
|
|
14
|
+
License: AGPL-3.0-or-later
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
@@ -23,12 +23,17 @@ import time
|
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
|
|
25
25
|
from superlocalmemory.ingestion.base_adapter import BaseAdapter, AdapterConfig, IngestItem
|
|
26
|
+
from superlocalmemory.infra.data_root import state_path
|
|
26
27
|
|
|
27
28
|
logger = logging.getLogger("superlocalmemory.ingestion.transcript")
|
|
28
29
|
|
|
29
30
|
_WATCH_EXTENSIONS = {".srt", ".vtt", ".txt"}
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
def _adapters_config_path() -> Path:
|
|
34
|
+
return state_path("adapters.json")
|
|
35
|
+
|
|
36
|
+
|
|
32
37
|
class TranscriptAdapter(BaseAdapter):
|
|
33
38
|
"""Watches a directory for transcript files and ingests them."""
|
|
34
39
|
|
|
@@ -142,7 +147,7 @@ if __name__ == "__main__":
|
|
|
142
147
|
|
|
143
148
|
# Load config
|
|
144
149
|
watch_dir = ""
|
|
145
|
-
adapters_path =
|
|
150
|
+
adapters_path = _adapters_config_path()
|
|
146
151
|
if adapters_path.exists():
|
|
147
152
|
cfg = json.loads(adapters_path.read_text())
|
|
148
153
|
watch_dir = cfg.get("transcript", {}).get("watch_dir", "")
|
|
@@ -12,7 +12,7 @@ Wraps CognitiveConsolidator with scheduling logic:
|
|
|
12
12
|
Integration point: ConsolidationEngine._step7_ccq() calls this worker.
|
|
13
13
|
|
|
14
14
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
15
|
-
License:
|
|
15
|
+
License: AGPL-3.0-or-later
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
@@ -269,6 +269,17 @@ def ensemble_rerank(
|
|
|
269
269
|
for b, l in zip(n_bandit, n_lgbm)
|
|
270
270
|
]
|
|
271
271
|
|
|
272
|
+
# Retain the model/policy utility for auditability without overwriting the
|
|
273
|
+
# public query-relative relevance score.
|
|
274
|
+
for candidate, utility in zip(candidates, blended):
|
|
275
|
+
if isinstance(candidate, dict):
|
|
276
|
+
candidate["ranking_score"] = float(utility)
|
|
277
|
+
else:
|
|
278
|
+
try:
|
|
279
|
+
candidate.ranking_score = float(utility)
|
|
280
|
+
except (AttributeError, TypeError):
|
|
281
|
+
pass
|
|
282
|
+
|
|
272
283
|
# Stable-sort descending so equal scores preserve original order.
|
|
273
284
|
indexed = list(enumerate(candidates))
|
|
274
285
|
indexed.sort(key=lambda pair: -blended[pair[0]])
|
|
@@ -11,7 +11,7 @@ Builds knowledge summaries using PageRank centrality + Louvain community detecti
|
|
|
11
11
|
Runs after consolidation (every 6 hours or on-demand).
|
|
12
12
|
|
|
13
13
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
14
|
-
License:
|
|
14
|
+
License: AGPL-3.0-or-later
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
@@ -18,7 +18,7 @@ HR-08: Runs synchronously in main thread.
|
|
|
18
18
|
HR-09: Access events are idempotent.
|
|
19
19
|
|
|
20
20
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
21
|
-
License:
|
|
21
|
+
License: AGPL-3.0-or-later
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
from __future__ import annotations
|
|
@@ -60,7 +60,7 @@ class ForgettingScheduler:
|
|
|
60
60
|
self._last_run_times: dict[str, float] = {}
|
|
61
61
|
|
|
62
62
|
def run_decay_cycle(
|
|
63
|
-
self, profile_id: str, *, force: bool = False,
|
|
63
|
+
self, profile_id: str, *, force: bool = False, dry_run: bool = False,
|
|
64
64
|
) -> dict:
|
|
65
65
|
"""Run a full decay cycle for all facts in a profile.
|
|
66
66
|
|
|
@@ -70,6 +70,7 @@ class ForgettingScheduler:
|
|
|
70
70
|
Args:
|
|
71
71
|
profile_id: Profile to process.
|
|
72
72
|
force: If True, bypass interval check.
|
|
73
|
+
dry_run: Compute transitions without persisting retention or lifecycle.
|
|
73
74
|
|
|
74
75
|
Returns:
|
|
75
76
|
Stats dict: {total, active, warm, cold, archive, forgotten,
|
|
@@ -87,7 +88,8 @@ class ForgettingScheduler:
|
|
|
87
88
|
facts_data = self._fetch_facts_with_metadata(profile_id)
|
|
88
89
|
|
|
89
90
|
if not facts_data:
|
|
90
|
-
|
|
91
|
+
if not dry_run:
|
|
92
|
+
self._last_run_times[profile_id] = now
|
|
91
93
|
return {
|
|
92
94
|
"total": 0, "active": 0, "warm": 0, "cold": 0,
|
|
93
95
|
"archive": 0, "forgotten": 0, "transitions": 0,
|
|
@@ -100,7 +102,8 @@ class ForgettingScheduler:
|
|
|
100
102
|
retention_results = self._ebbinghaus.batch_compute_retention(facts_data)
|
|
101
103
|
|
|
102
104
|
# Step 4: Batch UPSERT into fact_retention
|
|
103
|
-
|
|
105
|
+
if not dry_run:
|
|
106
|
+
self._db.batch_upsert_retention(retention_results, profile_id)
|
|
104
107
|
|
|
105
108
|
# Step 5: Count zones and transitions
|
|
106
109
|
zone_counts = {"active": 0, "warm": 0, "cold": 0, "archive": 0, "forgotten": 0}
|
|
@@ -121,11 +124,13 @@ class ForgettingScheduler:
|
|
|
121
124
|
forgotten_fact_ids.append(result["fact_id"])
|
|
122
125
|
|
|
123
126
|
# Step 6: Soft-delete forgotten facts (HR-04)
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
if not dry_run:
|
|
128
|
+
for fact_id in forgotten_fact_ids:
|
|
129
|
+
self._soft_delete_with_audit(fact_id, profile_id)
|
|
126
130
|
|
|
127
131
|
# Update last run time
|
|
128
|
-
|
|
132
|
+
if not dry_run:
|
|
133
|
+
self._last_run_times[profile_id] = now
|
|
129
134
|
|
|
130
135
|
return {
|
|
131
136
|
"total": len(retention_results),
|
|
@@ -208,7 +208,10 @@ class AdaptiveRanker:
|
|
|
208
208
|
return self._rerank_heuristic(results, query_context)
|
|
209
209
|
|
|
210
210
|
order = np.argsort(-scores, kind="stable")
|
|
211
|
-
return [
|
|
211
|
+
return [
|
|
212
|
+
{**results[int(i)], "_adaptive_score": float(scores[int(i)])}
|
|
213
|
+
for i in order
|
|
214
|
+
]
|
|
212
215
|
|
|
213
216
|
# --- legacy load path (back-compat) --------------------------------
|
|
214
217
|
|
|
@@ -44,6 +44,7 @@ from typing import Optional
|
|
|
44
44
|
# Import the RAM semaphore at module scope so tests can monkeypatch
|
|
45
45
|
# ``trigram_index.ram_reservation`` to a no-op on CI boxes with tight RAM.
|
|
46
46
|
from superlocalmemory.core.ram_lock import ram_reservation
|
|
47
|
+
from superlocalmemory.infra.data_root import DynamicStatePath, state_path
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
# --------------------------------------------------------------------------
|
|
@@ -205,7 +206,7 @@ class TrigramIndex:
|
|
|
205
206
|
``(entity_id, hits)`` candidates.
|
|
206
207
|
"""
|
|
207
208
|
|
|
208
|
-
CACHE_DB_PATH: Path =
|
|
209
|
+
CACHE_DB_PATH: Path = DynamicStatePath("active_brain_cache.db")
|
|
209
210
|
MAX_TRIGRAMS: int = 1_000_000
|
|
210
211
|
LOOKUP_LIMIT: int = 10
|
|
211
212
|
LOOKUP_MIN_HITS: int = 2
|
|
@@ -537,7 +538,7 @@ def get_or_none() -> Optional[TrigramIndex]:
|
|
|
537
538
|
return _SINGLETON
|
|
538
539
|
if not TrigramIndex.CACHE_DB_PATH.exists():
|
|
539
540
|
return None
|
|
540
|
-
default_source =
|
|
541
|
+
default_source = state_path("memory.db")
|
|
541
542
|
if not default_source.exists():
|
|
542
543
|
return None
|
|
543
544
|
_SINGLETON = TrigramIndex(source_db_path=default_source)
|
|
@@ -14,7 +14,7 @@ Providers:
|
|
|
14
14
|
- ``"azure"``: Azure OpenAI (via AI Foundry deployment).
|
|
15
15
|
|
|
16
16
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
17
|
-
License:
|
|
17
|
+
License: AGPL-3.0-or-later
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
from __future__ import annotations
|
|
@@ -103,7 +103,9 @@ class LLMBackbone:
|
|
|
103
103
|
self._default_temperature = config.temperature
|
|
104
104
|
self._default_max_tokens = config.max_tokens
|
|
105
105
|
|
|
106
|
-
# Resolve API key: config > environment variable.
|
|
106
|
+
# Resolve API key: config > environment variable. A custom api_base
|
|
107
|
+
# with an empty api_key is treated as intentionally keyless so local
|
|
108
|
+
# OpenAI-compatible endpoints do not inherit ambient cloud keys.
|
|
107
109
|
if self._provider == "ollama":
|
|
108
110
|
self._api_key = ""
|
|
109
111
|
host = config.api_base or os.environ.get(
|
|
@@ -111,19 +113,22 @@ class LLMBackbone:
|
|
|
111
113
|
)
|
|
112
114
|
self._base_url = f"{host.rstrip('/')}/api/chat"
|
|
113
115
|
elif self._provider == "openrouter":
|
|
114
|
-
self._api_key =
|
|
115
|
-
_ENV_KEYS.get(self._provider, ""), "",
|
|
116
|
-
)
|
|
116
|
+
self._api_key = self._resolve_api_key(config)
|
|
117
117
|
self._base_url = config.api_base or _OPENROUTER_URL
|
|
118
118
|
elif self._provider:
|
|
119
|
-
self._api_key =
|
|
120
|
-
_ENV_KEYS.get(self._provider, ""), "",
|
|
121
|
-
)
|
|
119
|
+
self._api_key = self._resolve_api_key(config)
|
|
122
120
|
self._base_url = config.api_base
|
|
123
121
|
else:
|
|
124
122
|
self._api_key = ""
|
|
125
123
|
self._base_url = ""
|
|
126
124
|
|
|
125
|
+
def _resolve_api_key(self, config: LLMConfig) -> str:
|
|
126
|
+
if config.api_key:
|
|
127
|
+
return config.api_key
|
|
128
|
+
if config.api_base:
|
|
129
|
+
return ""
|
|
130
|
+
return os.environ.get(_ENV_KEYS.get(self._provider, ""), "")
|
|
131
|
+
|
|
127
132
|
# -- Properties ---------------------------------------------------------
|
|
128
133
|
|
|
129
134
|
def is_available(self) -> bool:
|
|
@@ -26,7 +26,7 @@ References:
|
|
|
26
26
|
the classical compact groups.
|
|
27
27
|
|
|
28
28
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
29
|
-
License:
|
|
29
|
+
License: AGPL-3.0-or-later
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
32
|
from __future__ import annotations
|
|
@@ -41,6 +41,7 @@ import numpy as np
|
|
|
41
41
|
from numpy.typing import NDArray
|
|
42
42
|
|
|
43
43
|
from superlocalmemory.core.config import PolarQuantConfig
|
|
44
|
+
from superlocalmemory.infra.data_root import state_path
|
|
44
45
|
|
|
45
46
|
logger = logging.getLogger(__name__)
|
|
46
47
|
|
|
@@ -113,9 +114,7 @@ class PolarQuantEncoder:
|
|
|
113
114
|
"""
|
|
114
115
|
path_str = self._config.rotation_matrix_path
|
|
115
116
|
if not path_str:
|
|
116
|
-
path_str = str(
|
|
117
|
-
Path.home() / ".superlocalmemory" / f"polar_rotation_{self._d}.npy",
|
|
118
|
-
)
|
|
117
|
+
path_str = str(state_path(f"polar_rotation_{self._d}.npy"))
|
|
119
118
|
|
|
120
119
|
path = Path(path_str)
|
|
121
120
|
|
|
@@ -14,7 +14,7 @@ Severity = ||delta_0 f(e)|| / (||R_a emb_a|| + ||R_b emb_b||).
|
|
|
14
14
|
Refs: Curry 2014 (arXiv:1303.3255), Hansen & Ghrist 2019, Robinson 2020.
|
|
15
15
|
|
|
16
16
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
17
|
-
License:
|
|
17
|
+
License: AGPL-3.0-or-later
|
|
18
18
|
"""
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
@@ -9,7 +9,7 @@ D_mse <= sqrt(3*pi/2) / 4^b. No scipy (HR-SCIPY-01). 2-byte "TQ" prefix on
|
|
|
9
9
|
all BLOBs (HR-MIG-02). Bit-widths: 2, 4, 8 only (HR-3BIT-01).
|
|
10
10
|
|
|
11
11
|
References: TurboQuant (arXiv 2504.19874), PolarQuant (arXiv 2502.02617).
|
|
12
|
-
Part of Qualixar | Author: Varun Pratap Bhardwaj | License:
|
|
12
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj | License: AGPL-3.0-or-later
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
@@ -24,6 +24,7 @@ import numpy as np
|
|
|
24
24
|
from numpy.typing import NDArray
|
|
25
25
|
|
|
26
26
|
from superlocalmemory.core.config import PolarQuantConfig
|
|
27
|
+
from superlocalmemory.infra.data_root import canonical_data_root
|
|
27
28
|
|
|
28
29
|
logger = logging.getLogger(__name__)
|
|
29
30
|
|
|
@@ -173,7 +174,7 @@ class TurboQuantEncoder:
|
|
|
173
174
|
def _load_or_create_rotation_matrix(self) -> NDArray:
|
|
174
175
|
"""Load/create rotation matrix with copy-on-detect (AUDIT C4-MED-01)."""
|
|
175
176
|
d = self._d
|
|
176
|
-
slm_dir =
|
|
177
|
+
slm_dir = canonical_data_root()
|
|
177
178
|
|
|
178
179
|
turbo_path_str = self._config.rotation_matrix_path
|
|
179
180
|
if not turbo_path_str:
|
|
@@ -79,25 +79,26 @@ class DaemonPoolProxy:
|
|
|
79
79
|
def store(
|
|
80
80
|
self, content: str, metadata: dict | None = None,
|
|
81
81
|
) -> dict[str, Any]:
|
|
82
|
-
body =
|
|
82
|
+
body = {
|
|
83
83
|
"content": content,
|
|
84
84
|
"tags": (metadata or {}).get("tags", ""),
|
|
85
85
|
"metadata": metadata or {},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
data=body,
|
|
90
|
-
headers={"Content-Type": "application/json"},
|
|
91
|
-
method="POST",
|
|
92
|
-
)
|
|
86
|
+
"session_id": (metadata or {}).get("session_id", ""),
|
|
87
|
+
"idempotency_key": (metadata or {}).get("idempotency_key") or None,
|
|
88
|
+
}
|
|
93
89
|
try:
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
# One identity-aware daemon client owns descriptor validation,
|
|
91
|
+
# capability delivery, and exact-instance targeting. A raw urllib
|
|
92
|
+
# POST here previously became unauthenticated when /remember was
|
|
93
|
+
# hardened and could also attach to a stale/foreign port.
|
|
94
|
+
from superlocalmemory.cli.daemon import daemon_request
|
|
95
|
+
|
|
96
|
+
data = daemon_request("POST", "/remember", body)
|
|
96
97
|
except Exception as exc:
|
|
97
98
|
logger.warning("daemon /remember failed: %s", exc)
|
|
98
99
|
return {"ok": False, "error": str(exc)}
|
|
99
100
|
if not isinstance(data, dict):
|
|
100
|
-
return {"ok": False, "error": "
|
|
101
|
+
return {"ok": False, "error": "owned daemon unavailable"}
|
|
101
102
|
data.setdefault("ok", True)
|
|
102
103
|
return data
|
|
103
104
|
|
|
@@ -34,6 +34,10 @@ class PoolRecallItem:
|
|
|
34
34
|
fact: PoolFact = field(default_factory=PoolFact)
|
|
35
35
|
score: float = 0.0
|
|
36
36
|
confidence: float = 0.0
|
|
37
|
+
relevance_score: float = 0.0
|
|
38
|
+
ranking_score: float | None = None
|
|
39
|
+
memory_confidence: float = 0.0
|
|
40
|
+
rank_position: int = 0
|
|
37
41
|
trust_score: float = 0.0
|
|
38
42
|
channel_scores: dict[str, float] = field(default_factory=dict)
|
|
39
43
|
evidence_chain: list[Any] = field(default_factory=list)
|
|
@@ -46,6 +50,12 @@ class PoolRecallResponse:
|
|
|
46
50
|
retrieval_time_ms: float = 0.0
|
|
47
51
|
channel_weights: dict[str, float] = field(default_factory=dict)
|
|
48
52
|
total_candidates: int = 0
|
|
53
|
+
score_contract_version: str = "2"
|
|
54
|
+
calibration_status: str = "uncalibrated"
|
|
55
|
+
calibration_id: str | None = None
|
|
56
|
+
answer_confidence: float | None = None
|
|
57
|
+
abstained: bool = False
|
|
58
|
+
abstention_reason: str | None = None
|
|
49
59
|
|
|
50
60
|
|
|
51
61
|
class PoolError(RuntimeError):
|
|
@@ -107,6 +117,15 @@ def pool_recall(query: str, limit: int = 10, **kwargs: Any) -> PoolRecallRespons
|
|
|
107
117
|
),
|
|
108
118
|
score=float(item.get("score", 0.0)),
|
|
109
119
|
confidence=float(item.get("confidence", 0.0)),
|
|
120
|
+
relevance_score=float(item.get("relevance_score", item.get("score", 0.0))),
|
|
121
|
+
ranking_score=(
|
|
122
|
+
float(item["ranking_score"])
|
|
123
|
+
if item.get("ranking_score") is not None else None
|
|
124
|
+
),
|
|
125
|
+
memory_confidence=float(
|
|
126
|
+
item.get("memory_confidence", item.get("confidence", 0.0))
|
|
127
|
+
),
|
|
128
|
+
rank_position=int(item.get("rank_position", 0)),
|
|
110
129
|
trust_score=float(item.get("trust_score", 0.0)),
|
|
111
130
|
channel_scores=item.get("channel_scores", {}) or {},
|
|
112
131
|
evidence_chain=list(item.get("evidence_chain", []) or []),
|
|
@@ -122,6 +141,14 @@ def pool_recall(query: str, limit: int = 10, **kwargs: Any) -> PoolRecallRespons
|
|
|
122
141
|
if isinstance(raw, dict) else {},
|
|
123
142
|
total_candidates=int(raw.get("total_candidates", 0))
|
|
124
143
|
if isinstance(raw, dict) else 0,
|
|
144
|
+
score_contract_version=str(raw.get("score_contract_version", "2"))
|
|
145
|
+
if isinstance(raw, dict) else "2",
|
|
146
|
+
calibration_status=str(raw.get("calibration_status", "uncalibrated"))
|
|
147
|
+
if isinstance(raw, dict) else "uncalibrated",
|
|
148
|
+
calibration_id=raw.get("calibration_id") if isinstance(raw, dict) else None,
|
|
149
|
+
answer_confidence=raw.get("answer_confidence") if isinstance(raw, dict) else None,
|
|
150
|
+
abstained=bool(raw.get("abstained", False)) if isinstance(raw, dict) else False,
|
|
151
|
+
abstention_reason=raw.get("abstention_reason") if isinstance(raw, dict) else None,
|
|
125
152
|
)
|
|
126
153
|
|
|
127
154
|
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
"""Resource-safe FastMCP Streamable-HTTP integration.
|
|
6
|
+
|
|
7
|
+
MCP SDK 1.27.1 passes an AnyIO ``MemoryObjectReceiveStream`` directly to
|
|
8
|
+
``EventSourceResponse`` for every JSON-RPC POST. The response consumes that
|
|
9
|
+
stream but does not close it after normal iteration, leaving one receive
|
|
10
|
+
endpoint per request for the garbage collector. The response is the owner of
|
|
11
|
+
that per-request iterator, so SLM closes it at the response boundary.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from mcp.server.fastmcp import FastMCP
|
|
17
|
+
from sse_starlette.sse import EventSourceResponse
|
|
18
|
+
from starlette.types import Receive, Scope, Send
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ClosingEventSourceResponse(EventSourceResponse):
|
|
22
|
+
"""EventSourceResponse that closes the async iterator it consumes."""
|
|
23
|
+
|
|
24
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
25
|
+
try:
|
|
26
|
+
await super().__call__(scope, receive, send)
|
|
27
|
+
finally:
|
|
28
|
+
close = getattr(self.body_iterator, "aclose", None)
|
|
29
|
+
if close is not None:
|
|
30
|
+
await close()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def install_streamable_http_resource_guard() -> None:
|
|
34
|
+
"""Install the response owner used by MCP's Streamable-HTTP transport."""
|
|
35
|
+
from mcp.server import streamable_http
|
|
36
|
+
|
|
37
|
+
streamable_http.EventSourceResponse = ClosingEventSourceResponse
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SLMFastMCP(FastMCP):
|
|
41
|
+
"""FastMCP with SLM release identity and deterministic SSE cleanup."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, *args, product_version: str = "3.7.0", **kwargs) -> None:
|
|
44
|
+
super().__init__(*args, **kwargs)
|
|
45
|
+
# FastMCP delegates the initialize response to the low-level MCP
|
|
46
|
+
# server. Without an explicit value it reports the installed ``mcp``
|
|
47
|
+
# library version, which makes an SLM 3.7 server identify itself as
|
|
48
|
+
# (for example) 1.27.1 to every IDE client.
|
|
49
|
+
self._mcp_server.version = product_version
|
|
50
|
+
|
|
51
|
+
def streamable_http_app(self):
|
|
52
|
+
install_streamable_http_resource_guard()
|
|
53
|
+
return super().streamable_http_app()
|
|
@@ -27,11 +27,11 @@ _os.environ.setdefault('SLM_SKIP_DEP_CHECK', '1')
|
|
|
27
27
|
import logging
|
|
28
28
|
import sys
|
|
29
29
|
|
|
30
|
-
from mcp.
|
|
30
|
+
from superlocalmemory.mcp.http_transport import SLMFastMCP
|
|
31
31
|
|
|
32
32
|
logger = logging.getLogger(__name__)
|
|
33
33
|
|
|
34
|
-
server =
|
|
34
|
+
server = SLMFastMCP("SuperLocalMemory V3")
|
|
35
35
|
|
|
36
36
|
# Lazy engine singleton -------------------------------------------------------
|
|
37
37
|
|
|
@@ -175,6 +175,18 @@ _PROFILE_DEFINITIONS: dict[str, frozenset[str]] = {
|
|
|
175
175
|
"mesh": _PROFILE_MESH,
|
|
176
176
|
} # "whole" intentionally absent — maps to raw server (D-2 LOCKED)
|
|
177
177
|
|
|
178
|
+
# Compatibility aliases published by the v3.6 README. Keep these explicit so
|
|
179
|
+
# a stale client configuration has one deterministic meaning and emits a
|
|
180
|
+
# migration warning. Any other value is a configuration error (fail closed).
|
|
181
|
+
_PROFILE_ALIASES: dict[str, str] = {
|
|
182
|
+
"core14": "core",
|
|
183
|
+
"code20": "code",
|
|
184
|
+
"full38": "full",
|
|
185
|
+
"power50": "power",
|
|
186
|
+
"mesh8": "mesh",
|
|
187
|
+
"whole81": "whole",
|
|
188
|
+
}
|
|
189
|
+
|
|
178
190
|
_profile = _os_reg.environ.get("SLM_MCP_PROFILE", "").strip().lower()
|
|
179
191
|
|
|
180
192
|
|
|
@@ -188,19 +200,26 @@ def _resolve_profile_allowed(
|
|
|
188
200
|
Returns:
|
|
189
201
|
None — for "" (no selection) or "whole" (raw server, all tools).
|
|
190
202
|
frozenset — the profile's tool set for known profiles.
|
|
191
|
-
|
|
203
|
+
|
|
204
|
+
Raises:
|
|
205
|
+
ValueError: if ``profile`` is neither canonical nor an explicitly
|
|
206
|
+
supported compatibility alias.
|
|
192
207
|
"""
|
|
193
|
-
|
|
208
|
+
canonical = _PROFILE_ALIASES.get(profile, profile)
|
|
209
|
+
if canonical != profile:
|
|
210
|
+
logger.warning(
|
|
211
|
+
"SLM_MCP_PROFILE=%r is deprecated; use %r instead.",
|
|
212
|
+
profile,
|
|
213
|
+
canonical,
|
|
214
|
+
)
|
|
215
|
+
if not canonical or canonical == "whole":
|
|
194
216
|
return None
|
|
195
|
-
if
|
|
196
|
-
return definitions[
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
"
|
|
200
|
-
profile,
|
|
201
|
-
", ".join(sorted(definitions)),
|
|
217
|
+
if canonical in definitions:
|
|
218
|
+
return definitions[canonical]
|
|
219
|
+
valid = ", ".join((*sorted(definitions), "whole"))
|
|
220
|
+
raise ValueError(
|
|
221
|
+
f"SLM_MCP_PROFILE={profile!r} is not recognised; valid profiles: {valid}"
|
|
202
222
|
)
|
|
203
|
-
return essential
|
|
204
223
|
|
|
205
224
|
|
|
206
225
|
class _FilteredServer:
|
|
@@ -237,7 +256,14 @@ elif _user_allowlist_str:
|
|
|
237
256
|
elif _profile == "whole":
|
|
238
257
|
_target = server # NEW raw-server
|
|
239
258
|
elif _profile:
|
|
240
|
-
|
|
259
|
+
_profile_allowed = _resolve_profile_allowed(
|
|
260
|
+
_profile, _PROFILE_DEFINITIONS, _ESSENTIAL_TOOLS
|
|
261
|
+
)
|
|
262
|
+
_target = (
|
|
263
|
+
server
|
|
264
|
+
if _profile_allowed is None
|
|
265
|
+
else _FilteredServer(server, _profile_allowed)
|
|
266
|
+
)
|
|
241
267
|
else:
|
|
242
268
|
_target = _FilteredServer(server, _ESSENTIAL_TOOLS) # default (unchanged)
|
|
243
269
|
|