superlocalmemory 3.6.23 → 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.
Files changed (301) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +271 -71
  3. package/bin/slm-npm +43 -89
  4. package/ide/configs/antigravity-mcp.json +2 -2
  5. package/ide/configs/chatgpt-desktop-mcp.json +1 -1
  6. package/ide/configs/claude-desktop-mcp.json +2 -2
  7. package/ide/configs/windsurf-mcp.json +2 -2
  8. package/ide/hooks/context-hook.js +6 -2
  9. package/ide/hooks/post-recall-hook.js +7 -3
  10. package/ide/hooks/tool-event-hook.sh +2 -1
  11. package/package.json +18 -10
  12. package/plugin/.claude-plugin/plugin.json +1 -1
  13. package/plugin/_GENERATED.md +1 -1
  14. package/plugin/agents/slm-memory-advisor.md +1 -1
  15. package/plugin/requirements.txt +1 -1
  16. package/plugin/skills/slm-session/SKILL.md +1 -1
  17. package/plugin-src/rules/AGENTS.md +1 -1
  18. package/pyproject.toml +40 -8
  19. package/scripts/postinstall-interactive.js +17 -94
  20. package/scripts/postinstall.js +185 -258
  21. package/scripts/preuninstall.js +9 -50
  22. package/src/superlocalmemory/__init__.py +2 -2
  23. package/src/superlocalmemory/attribution/mathematical_dna.py +1 -1
  24. package/src/superlocalmemory/attribution/signer.py +34 -19
  25. package/src/superlocalmemory/attribution/watermark.py +1 -1
  26. package/src/superlocalmemory/cli/_lazy_init.py +3 -5
  27. package/src/superlocalmemory/cli/commands.py +453 -191
  28. package/src/superlocalmemory/cli/context_commands.py +5 -4
  29. package/src/superlocalmemory/cli/daemon.py +282 -187
  30. package/src/superlocalmemory/cli/db_migrate.py +3 -1
  31. package/src/superlocalmemory/cli/diagnostics_cmd.py +28 -0
  32. package/src/superlocalmemory/cli/evidence_cmd.py +103 -0
  33. package/src/superlocalmemory/cli/ingest_cmd.py +7 -3
  34. package/src/superlocalmemory/cli/main.py +128 -31
  35. package/src/superlocalmemory/cli/pending_store.py +54 -38
  36. package/src/superlocalmemory/cli/scale_engine_cmd.py +37 -0
  37. package/src/superlocalmemory/cli/service_installer.py +57 -52
  38. package/src/superlocalmemory/cli/setup_wizard.py +142 -88
  39. package/src/superlocalmemory/cli/version_banner.py +2 -1
  40. package/src/superlocalmemory/code_graph/config.py +3 -1
  41. package/src/superlocalmemory/core/backend_orchestrator.py +81 -21
  42. package/src/superlocalmemory/core/config.py +65 -20
  43. package/src/superlocalmemory/core/consolidation_engine.py +9 -7
  44. package/src/superlocalmemory/core/context_cache.py +56 -8
  45. package/src/superlocalmemory/core/derivation_lineage.py +246 -0
  46. package/src/superlocalmemory/core/embedding_worker.py +32 -20
  47. package/src/superlocalmemory/core/embeddings.py +54 -18
  48. package/src/superlocalmemory/core/engine.py +150 -104
  49. package/src/superlocalmemory/core/engine_ingestion.py +513 -0
  50. package/src/superlocalmemory/core/engine_wiring.py +2 -0
  51. package/src/superlocalmemory/core/evidence_bundle.py +526 -0
  52. package/src/superlocalmemory/core/fact_consolidator.py +5 -11
  53. package/src/superlocalmemory/core/graph_analyzer.py +2 -2
  54. package/src/superlocalmemory/core/health_monitor.py +4 -2
  55. package/src/superlocalmemory/core/ingestion_command.py +636 -0
  56. package/src/superlocalmemory/core/injection.py +69 -18
  57. package/src/superlocalmemory/core/lifecycle_state.py +153 -0
  58. package/src/superlocalmemory/core/maintenance.py +1 -1
  59. package/src/superlocalmemory/core/maintenance_scheduler.py +51 -35
  60. package/src/superlocalmemory/core/mutations.py +143 -0
  61. package/src/superlocalmemory/core/platform_utils.py +7 -4
  62. package/src/superlocalmemory/core/ram_lock.py +16 -5
  63. package/src/superlocalmemory/core/rate_limit.py +1 -1
  64. package/src/superlocalmemory/core/recall_pipeline.py +60 -101
  65. package/src/superlocalmemory/core/recall_worker.py +76 -59
  66. package/src/superlocalmemory/core/registry.py +1 -1
  67. package/src/superlocalmemory/core/scale_engine.py +293 -0
  68. package/src/superlocalmemory/core/score_contract.py +62 -0
  69. package/src/superlocalmemory/core/security_primitives.py +3 -1
  70. package/src/superlocalmemory/core/slm_disabled.py +3 -5
  71. package/src/superlocalmemory/core/store_pipeline.py +172 -40
  72. package/src/superlocalmemory/core/tier_manager.py +32 -20
  73. package/src/superlocalmemory/core/worker_pool.py +13 -4
  74. package/src/superlocalmemory/dynamics/activation_guided_quantization.py +1 -1
  75. package/src/superlocalmemory/dynamics/eap_scheduler.py +10 -3
  76. package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +1 -1
  77. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +1 -1
  78. package/src/superlocalmemory/encoding/auto_linker.py +1 -1
  79. package/src/superlocalmemory/encoding/cognitive_consolidator.py +7 -16
  80. package/src/superlocalmemory/encoding/consolidator.py +22 -5
  81. package/src/superlocalmemory/encoding/fact_extractor.py +1 -1
  82. package/src/superlocalmemory/encoding/foresight.py +2 -0
  83. package/src/superlocalmemory/encoding/graph_builder.py +1 -1
  84. package/src/superlocalmemory/encoding/temporal_parser.py +2 -0
  85. package/src/superlocalmemory/evaluation/__init__.py +13 -0
  86. package/src/superlocalmemory/evaluation/calibration.py +308 -0
  87. package/src/superlocalmemory/evolution/skill_evolver.py +2 -1
  88. package/src/superlocalmemory/graph/cozo_backend.py +256 -23
  89. package/src/superlocalmemory/hooks/_outcome_common.py +21 -11
  90. package/src/superlocalmemory/hooks/antigravity_adapter.py +10 -31
  91. package/src/superlocalmemory/hooks/auto_invoker.py +25 -27
  92. package/src/superlocalmemory/hooks/auto_recall.py +31 -6
  93. package/src/superlocalmemory/hooks/auto_recall_hook.py +13 -33
  94. package/src/superlocalmemory/hooks/before_web_hook.py +9 -7
  95. package/src/superlocalmemory/hooks/claude_code_hooks.py +123 -35
  96. package/src/superlocalmemory/hooks/codex_assets.py +59 -0
  97. package/src/superlocalmemory/hooks/codex_hooks.py +186 -0
  98. package/src/superlocalmemory/hooks/context_payload.py +1 -1
  99. package/src/superlocalmemory/hooks/copilot_adapter.py +9 -24
  100. package/src/superlocalmemory/hooks/cursor_adapter.py +10 -32
  101. package/src/superlocalmemory/hooks/hook_daemon.py +4 -2
  102. package/src/superlocalmemory/hooks/hook_handlers.py +219 -32
  103. package/src/superlocalmemory/hooks/memory_protocol.py +5 -3
  104. package/src/superlocalmemory/hooks/post_tool_async_hook.py +4 -2
  105. package/src/superlocalmemory/hooks/session_registry.py +15 -8
  106. package/src/superlocalmemory/hooks/stop_outcome_hook.py +10 -6
  107. package/src/superlocalmemory/hooks/topic_shift_hook.py +42 -12
  108. package/src/superlocalmemory/hooks/user_prompt_hook.py +9 -14
  109. package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +19 -11
  110. package/src/superlocalmemory/infra/auth_middleware.py +38 -5
  111. package/src/superlocalmemory/infra/backup.py +7 -5
  112. package/src/superlocalmemory/infra/cloud_backup.py +18 -8
  113. package/src/superlocalmemory/infra/daemon_identity.py +248 -0
  114. package/src/superlocalmemory/infra/data_root.py +199 -0
  115. package/src/superlocalmemory/infra/event_bus.py +3 -1
  116. package/src/superlocalmemory/infra/local_diagnostics.py +327 -0
  117. package/src/superlocalmemory/infra/process_reaper.py +23 -0
  118. package/src/superlocalmemory/ingestion/adapter_manager.py +27 -9
  119. package/src/superlocalmemory/ingestion/base_adapter.py +25 -31
  120. package/src/superlocalmemory/ingestion/calendar_adapter.py +13 -4
  121. package/src/superlocalmemory/ingestion/credentials.py +14 -7
  122. package/src/superlocalmemory/ingestion/gmail_adapter.py +13 -4
  123. package/src/superlocalmemory/ingestion/transcript_adapter.py +7 -2
  124. package/src/superlocalmemory/learning/consolidation_quantization_worker.py +1 -1
  125. package/src/superlocalmemory/learning/ensemble.py +11 -0
  126. package/src/superlocalmemory/learning/entity_compiler.py +1 -1
  127. package/src/superlocalmemory/learning/feedback.py +1 -1
  128. package/src/superlocalmemory/learning/forgetting_scheduler.py +12 -7
  129. package/src/superlocalmemory/learning/quantization_scheduler.py +1 -1
  130. package/src/superlocalmemory/learning/ranker.py +4 -1
  131. package/src/superlocalmemory/learning/source_quality.py +1 -1
  132. package/src/superlocalmemory/learning/trigram_index.py +3 -2
  133. package/src/superlocalmemory/llm/backbone.py +1 -1
  134. package/src/superlocalmemory/math/ebbinghaus.py +1 -1
  135. package/src/superlocalmemory/math/fisher.py +1 -1
  136. package/src/superlocalmemory/math/fisher_quantized.py +1 -1
  137. package/src/superlocalmemory/math/hopfield.py +1 -1
  138. package/src/superlocalmemory/math/langevin.py +1 -1
  139. package/src/superlocalmemory/math/polar_quant.py +3 -4
  140. package/src/superlocalmemory/math/qjl.py +1 -1
  141. package/src/superlocalmemory/math/sheaf.py +1 -1
  142. package/src/superlocalmemory/math/turbo_quant.py +3 -2
  143. package/src/superlocalmemory/mcp/_daemon_proxy.py +12 -11
  144. package/src/superlocalmemory/mcp/_pool_adapter.py +27 -0
  145. package/src/superlocalmemory/mcp/http_transport.py +53 -0
  146. package/src/superlocalmemory/mcp/server.py +39 -13
  147. package/src/superlocalmemory/mcp/shared.py +69 -3
  148. package/src/superlocalmemory/mcp/tools_active.py +141 -31
  149. package/src/superlocalmemory/mcp/tools_core.py +128 -29
  150. package/src/superlocalmemory/mcp/tools_evolution.py +5 -7
  151. package/src/superlocalmemory/mcp/tools_learning.py +42 -2
  152. package/src/superlocalmemory/mcp/tools_mesh.py +7 -23
  153. package/src/superlocalmemory/mcp/tools_optimize.py +8 -1
  154. package/src/superlocalmemory/mcp/tools_v28.py +23 -2
  155. package/src/superlocalmemory/mcp/tools_v3.py +26 -1
  156. package/src/superlocalmemory/mcp/tools_v33.py +56 -17
  157. package/src/superlocalmemory/mesh/broker.py +2 -0
  158. package/src/superlocalmemory/mesh/remote_sync.py +50 -12
  159. package/src/superlocalmemory/optimize/cache/manager.py +77 -1
  160. package/src/superlocalmemory/optimize/cache/semantic.py +23 -3
  161. package/src/superlocalmemory/optimize/compress/ccr.py +4 -0
  162. package/src/superlocalmemory/optimize/compress/router.py +6 -1
  163. package/src/superlocalmemory/optimize/config/__init__.py +5 -0
  164. package/src/superlocalmemory/optimize/config/store.py +6 -4
  165. package/src/superlocalmemory/optimize/proxy/_helpers.py +15 -5
  166. package/src/superlocalmemory/optimize/proxy/capture.py +3 -2
  167. package/src/superlocalmemory/optimize/proxy/server.py +2 -2
  168. package/src/superlocalmemory/optimize/storage/db.py +12 -12
  169. package/src/superlocalmemory/retrieval/agentic.py +1 -1
  170. package/src/superlocalmemory/retrieval/ann_index.py +1 -1
  171. package/src/superlocalmemory/retrieval/bm25_channel.py +35 -11
  172. package/src/superlocalmemory/retrieval/bridge_discovery.py +73 -8
  173. package/src/superlocalmemory/retrieval/engine.py +169 -79
  174. package/src/superlocalmemory/retrieval/entity_channel.py +289 -67
  175. package/src/superlocalmemory/retrieval/forgetting_filter.py +1 -1
  176. package/src/superlocalmemory/retrieval/fusion.py +1 -1
  177. package/src/superlocalmemory/retrieval/hopfield_channel.py +118 -30
  178. package/src/superlocalmemory/retrieval/profile_channel.py +1 -1
  179. package/src/superlocalmemory/retrieval/quantization_aware_search.py +16 -10
  180. package/src/superlocalmemory/retrieval/reranker.py +56 -20
  181. package/src/superlocalmemory/retrieval/scope_policy.py +85 -0
  182. package/src/superlocalmemory/retrieval/semantic_channel.py +122 -14
  183. package/src/superlocalmemory/retrieval/spreading_activation.py +141 -25
  184. package/src/superlocalmemory/retrieval/strategy.py +1 -1
  185. package/src/superlocalmemory/retrieval/temporal_channel.py +30 -15
  186. package/src/superlocalmemory/retrieval/vector_store.py +1 -1
  187. package/src/superlocalmemory/server/api.py +10 -7
  188. package/src/superlocalmemory/server/bandit_loops.py +4 -2
  189. package/src/superlocalmemory/server/recall_serializer.py +24 -0
  190. package/src/superlocalmemory/server/route_mutations.py +84 -0
  191. package/src/superlocalmemory/server/routes/agents.py +8 -6
  192. package/src/superlocalmemory/server/routes/brain.py +14 -12
  193. package/src/superlocalmemory/server/routes/chat.py +29 -12
  194. package/src/superlocalmemory/server/routes/data_io.py +55 -24
  195. package/src/superlocalmemory/server/routes/helpers.py +8 -63
  196. package/src/superlocalmemory/server/routes/ingest.py +53 -36
  197. package/src/superlocalmemory/server/routes/memories.py +104 -43
  198. package/src/superlocalmemory/server/routes/mesh.py +31 -0
  199. package/src/superlocalmemory/server/routes/profiles.py +26 -4
  200. package/src/superlocalmemory/server/routes/tiers.py +43 -11
  201. package/src/superlocalmemory/server/routes/timeline.py +5 -1
  202. package/src/superlocalmemory/server/routes/v3_api.py +76 -21
  203. package/src/superlocalmemory/server/security_middleware.py +1 -1
  204. package/src/superlocalmemory/server/unified_daemon.py +680 -293
  205. package/src/superlocalmemory/server/write_identity.py +147 -0
  206. package/src/superlocalmemory/storage/access_log.py +4 -3
  207. package/src/superlocalmemory/storage/database.py +118 -25
  208. package/src/superlocalmemory/storage/migration_runner.py +84 -1
  209. package/src/superlocalmemory/storage/migration_v33.py +1 -1
  210. package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +6 -60
  211. package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +120 -0
  212. package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +54 -0
  213. package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +52 -0
  214. package/src/superlocalmemory/storage/migrations/__init__.py +5 -0
  215. package/src/superlocalmemory/storage/models.py +16 -0
  216. package/src/superlocalmemory/storage/quantized_store.py +20 -3
  217. package/src/superlocalmemory/storage/v2_migrator.py +5 -3
  218. package/src/superlocalmemory/ui/favicon.svg +5 -0
  219. package/src/superlocalmemory/ui/index.html +1 -0
  220. package/src/superlocalmemory/ui/js/compliance.js +1 -1
  221. package/src/superlocalmemory/ui/js/core.js +49 -8
  222. package/src/superlocalmemory/ui/js/dashboard.js +23 -2
  223. package/src/superlocalmemory/ui/js/feedback.js +1 -1
  224. package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
  225. package/src/superlocalmemory/ui/js/graph-ui.js +1 -1
  226. package/src/superlocalmemory/ui/js/lifecycle.js +1 -1
  227. package/src/superlocalmemory/ui/js/ng-mesh.js +15 -49
  228. package/src/superlocalmemory/ui/js/settings.js +4 -2
  229. package/src/superlocalmemory/vector/lancedb_backend.py +57 -9
  230. package/bin/slm +0 -59
  231. package/bin/slm.bat +0 -77
  232. package/bin/slm.cmd +0 -5
  233. package/ide/integrations/langchain/README.md +0 -106
  234. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +0 -9
  235. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +0 -201
  236. package/ide/integrations/langchain/pyproject.toml +0 -38
  237. package/ide/integrations/langchain/tests/__init__.py +0 -3
  238. package/ide/integrations/langchain/tests/test_chat_message_history.py +0 -215
  239. package/ide/integrations/langchain/tests/test_security.py +0 -117
  240. package/ide/integrations/llamaindex/README.md +0 -81
  241. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +0 -9
  242. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +0 -316
  243. package/ide/integrations/llamaindex/pyproject.toml +0 -43
  244. package/ide/integrations/llamaindex/tests/__init__.py +0 -3
  245. package/ide/integrations/llamaindex/tests/test_chat_store.py +0 -294
  246. package/ide/integrations/llamaindex/tests/test_security.py +0 -241
  247. package/plugin-src/.mcp.json +0 -12
  248. package/plugin-src/agents/slm-memory-advisor.md +0 -44
  249. package/plugin-src/agents/slm-optimize-advisor.md +0 -38
  250. package/plugin-src/hooks/.gitkeep +0 -0
  251. package/plugin-src/hooks/hooks.json +0 -23
  252. package/plugin-src/manifest.json +0 -25
  253. package/plugin-src/requirements.txt +0 -1
  254. package/plugin-src/rules/CLAUDE.md.fragment +0 -44
  255. package/plugin-src/scripts/ensure-venv.bat +0 -122
  256. package/plugin-src/scripts/ensure-venv.sh +0 -105
  257. package/plugin-src/scripts/slm-launch +0 -15
  258. package/plugin-src/scripts/slm-launch.bat +0 -17
  259. package/plugin-src/settings.json +0 -16
  260. package/plugin-src/skills/slm-cache/SKILL.md +0 -140
  261. package/plugin-src/skills/slm-compress/SKILL.md +0 -143
  262. package/plugin-src/skills/slm-graph/SKILL.md +0 -300
  263. package/plugin-src/skills/slm-recall/SKILL.md +0 -204
  264. package/plugin-src/skills/slm-remember/SKILL.md +0 -194
  265. package/plugin-src/skills/slm-session/SKILL.md +0 -207
  266. package/plugin-src/skills/slm-status/SKILL.md +0 -149
  267. package/scripts/__tests__/build-plugin.test.mjs +0 -613
  268. package/scripts/_savings_math.py +0 -270
  269. package/scripts/build-dmg.sh +0 -417
  270. package/scripts/build-plugin.js +0 -742
  271. package/scripts/build-slm-hook.ps1 +0 -40
  272. package/scripts/build-slm-hook.sh +0 -45
  273. package/scripts/build_entry.py +0 -452
  274. package/scripts/ci/stage5b_gate.sh +0 -50
  275. package/scripts/dogfood_savings.py +0 -490
  276. package/scripts/generate-thumbnails.py +0 -218
  277. package/scripts/install-skills.ps1 +0 -4
  278. package/scripts/install-skills.sh +0 -5
  279. package/scripts/install.ps1 +0 -701
  280. package/scripts/install.sh +0 -1015
  281. package/scripts/postinstall_binary.js +0 -287
  282. package/scripts/prepack.js +0 -33
  283. package/scripts/release_manifest.py +0 -273
  284. package/scripts/slm-hook.spec +0 -56
  285. package/scripts/start-dashboard.ps1 +0 -52
  286. package/scripts/start-dashboard.sh +0 -41
  287. package/scripts/sync-wiki.ps1 +0 -127
  288. package/scripts/sync-wiki.sh +0 -82
  289. package/scripts/test-dmg.sh +0 -161
  290. package/scripts/test-npm-package.ps1 +0 -252
  291. package/scripts/test-npm-package.sh +0 -207
  292. package/scripts/verify-install.ps1 +0 -294
  293. package/scripts/verify-install.sh +0 -266
  294. package/scripts/verify-v27.ps1 +0 -301
  295. package/scripts/verify-v27.sh +0 -233
  296. package/src/superlocalmemory.egg-info/PKG-INFO +0 -516
  297. package/src/superlocalmemory.egg-info/SOURCES.txt +0 -529
  298. package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
  299. package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
  300. package/src/superlocalmemory.egg-info/requires.txt +0 -71
  301. package/src/superlocalmemory.egg-info/top_level.txt +0 -1
@@ -13,25 +13,24 @@ Part of Qualixar | Author: Varun Pratap Bhardwaj
13
13
 
14
14
  from __future__ import annotations
15
15
 
16
+ import hashlib
16
17
  import logging
17
- from pathlib import Path
18
18
  from typing import Callable
19
19
 
20
20
  from mcp.types import ToolAnnotations
21
21
 
22
22
  from superlocalmemory.core.config import CANONICAL_RECALL_LIMIT
23
+ from superlocalmemory.infra.data_root import canonical_data_root, state_path
24
+ from superlocalmemory.mcp.shared import authorize_mcp_mutation
23
25
 
24
26
  logger = logging.getLogger(__name__)
25
27
 
26
- _DB_PATH = str(Path.home() / ".superlocalmemory" / "memory.db")
27
-
28
-
29
28
  def _emit_event(event_type: str, payload: dict | None = None,
30
29
  source_agent: str = "mcp_client") -> None:
31
30
  """Emit an event to the EventBus (best-effort, never raises)."""
32
31
  try:
33
32
  from superlocalmemory.infra.event_bus import EventBus
34
- bus = EventBus.get_instance(_DB_PATH)
33
+ bus = EventBus.get_instance(str(state_path("memory.db")))
35
34
  bus.emit(event_type, payload=payload, source_agent=source_agent,
36
35
  source_protocol="mcp")
37
36
  except Exception:
@@ -58,7 +57,6 @@ def _record_recall_hits(
58
57
  signal quality is never load-bearing on recall correctness.
59
58
  """
60
59
  try:
61
- from pathlib import Path
62
60
  from superlocalmemory.learning.signals import (
63
61
  LearningSignals,
64
62
  enqueue_shown_flip,
@@ -66,7 +64,7 @@ def _record_recall_hits(
66
64
 
67
65
  engine = get_engine()
68
66
  pid = engine.profile_id
69
- slm_dir = Path.home() / ".superlocalmemory"
67
+ slm_dir = canonical_data_root()
70
68
 
71
69
  shown_ids = [r.get("fact_id", "") for r in results[:10]
72
70
  if r.get("fact_id")]
@@ -108,6 +106,7 @@ def register_core_tools(server, get_engine: Callable) -> None:
108
106
  agent_id: str = "mcp_client",
109
107
  scope: str | None = None,
110
108
  shared_with: str = "",
109
+ idempotency_key: str = "",
111
110
  ) -> dict:
112
111
  """Store content to memory with intelligent indexing.
113
112
 
@@ -128,13 +127,22 @@ def register_core_tools(server, get_engine: Callable) -> None:
128
127
  "agent_id": agent_id,
129
128
  "session_id": session_id,
130
129
  }
130
+ effective_idempotency_key = idempotency_key
131
+ if not effective_idempotency_key and session_id:
132
+ material = (
133
+ f"{agent_id}\0{session_id}\0{scope or ''}\0{shared_with}\0{content}"
134
+ )
135
+ effective_idempotency_key = "mcp:" + hashlib.sha256(
136
+ material.encode("utf-8")
137
+ ).hexdigest()
131
138
  # Parse shared_with from comma-separated string
132
139
  _shared_list = [s.strip() for s in shared_with.split(",") if s.strip()] if shared_with else None
133
140
  # v3.5.5 WRITE-THROUGH: route through the daemon's /remember, which does
134
141
  # a synchronous verbatim insert (memory is keyword/BM25-recallable the
135
142
  # instant this returns) and enqueues async enrichment. This closes the
136
143
  # recall window so a parallel/next agent finds memories saved seconds ago.
137
- # Falls back to pending.db only if the daemon is unreachable.
144
+ # Falls back to the capability-owned worker only if the daemon is
145
+ # unreachable. Raw pending.db writes are legacy replay input only.
138
146
  try:
139
147
  import asyncio as _asyncio
140
148
  from superlocalmemory.cli.daemon import daemon_request, is_daemon_running
@@ -145,36 +153,87 @@ def register_core_tools(server, get_engine: Callable) -> None:
145
153
  resp = await _asyncio.to_thread(daemon_request, "POST", "/remember", {
146
154
  "content": content, "tags": tags, "metadata": meta,
147
155
  "scope": scope, "shared_with": _shared_list,
156
+ "session_id": session_id,
157
+ "idempotency_key": effective_idempotency_key or None,
148
158
  })
149
159
  if resp and (resp.get("fact_ids") is not None or resp.get("ok")):
150
160
  fids = resp.get("fact_ids") or []
161
+ materialization_state = resp.get("materialization_state")
162
+ if materialization_state is None:
163
+ materialization_state = (
164
+ "complete" if resp.get("status") == "stored" else "queryable"
165
+ )
166
+ pending = materialization_state != "complete"
151
167
  return {
152
168
  "success": True,
153
- "fact_ids": fids or [f"pending:{resp.get('pending_id','')}"],
154
- "count": len(fids) if fids else 1,
155
- "pending": not fids,
156
- "message": "Stored (recallable now; enriching async).",
169
+ "fact_ids": fids,
170
+ "count": int(resp.get("count", len(fids))),
171
+ "pending": pending,
172
+ "pending_id": resp.get("pending_id") if pending else None,
173
+ "operation_id": resp.get("operation_id"),
174
+ "materialization_state": materialization_state,
175
+ "message": (
176
+ "Stored through canonical daemon ingestion."
177
+ if not pending
178
+ else "Queryable now; canonical enrichment is still running."
179
+ ),
157
180
  }
158
181
  except Exception as dexc:
159
182
  logger.debug("MCP remember via daemon failed, pending fallback: %s", dexc)
160
183
 
161
184
  try:
162
- from superlocalmemory.cli.pending_store import store_pending
163
- # v3.6.15: preserve a non-personal scope through the offline path so
164
- # the materializer replays the right visibility (else --scope global
165
- # would silently downgrade to personal when the daemon is offline).
166
- if scope and scope != "personal":
167
- meta = {**meta, "scope": scope}
168
- if _shared_list:
169
- meta["shared_with"] = _shared_list
170
- pending_id = store_pending(content, tags=tags, metadata=meta)
185
+ import asyncio as _asyncio
186
+ from superlocalmemory.mcp._daemon_proxy import choose_pool
187
+
188
+ worker_meta = {
189
+ **meta,
190
+ "tags": tags,
191
+ "scope": scope or "personal",
192
+ "shared_with": _shared_list or [],
193
+ "idempotency_key": (
194
+ effective_idempotency_key
195
+ or "mcp:" + hashlib.sha256(content.encode("utf-8")).hexdigest()
196
+ ),
197
+ }
198
+ stored = await _asyncio.to_thread(
199
+ choose_pool().store,
200
+ content,
201
+ worker_meta,
202
+ )
203
+ if not isinstance(stored, dict) or not stored.get("ok"):
204
+ raise RuntimeError(
205
+ (stored or {}).get("error", "canonical worker store failed")
206
+ if isinstance(stored, dict)
207
+ else "canonical worker returned an invalid response"
208
+ )
209
+ fact_ids = list(stored.get("fact_ids") or [])
210
+ materialization_state = str(
211
+ stored.get("materialization_state") or "complete"
212
+ )
213
+ allowed_states = {"queryable", "enriching", "complete"}
214
+ if materialization_state not in allowed_states:
215
+ raise RuntimeError(
216
+ "canonical worker returned invalid materialization state: "
217
+ f"{materialization_state}"
218
+ )
219
+ pending = materialization_state != "complete"
220
+ operation_id = stored.get("operation_id")
221
+ pending_id = stored.get("pending_id")
222
+ if pending and pending_id is None:
223
+ pending_id = operation_id
171
224
  return {
172
225
  "success": True,
173
- "fact_ids": [f"pending:{pending_id}"],
174
- "count": 1,
175
- "pending": True,
176
- "pending_id": pending_id,
177
- "message": "Stored — facts will appear shortly (daemon offline).",
226
+ "fact_ids": fact_ids,
227
+ "count": int(stored.get("count", len(fact_ids))),
228
+ "pending": pending,
229
+ "pending_id": pending_id if pending else None,
230
+ "operation_id": operation_id,
231
+ "materialization_state": materialization_state,
232
+ "message": (
233
+ "Stored through canonical local ingestion."
234
+ if not pending
235
+ else "Queryable now; canonical enrichment is still running."
236
+ ),
178
237
  }
179
238
  except Exception as exc:
180
239
  logger.exception("remember failed")
@@ -277,6 +336,12 @@ def register_core_tools(server, get_engine: Callable) -> None:
277
336
  "retrieval_time_ms": result.get("retrieval_time_ms", 0),
278
337
  # v3.6.6: surface evidence-floor signal to MCP clients.
279
338
  "no_confident_match": result.get("no_confident_match", False),
339
+ "score_contract_version": result.get("score_contract_version", "2"),
340
+ "calibration_status": result.get("calibration_status", "uncalibrated"),
341
+ "calibration_id": result.get("calibration_id"),
342
+ "answer_confidence": result.get("answer_confidence"),
343
+ "abstained": result.get("abstained", False),
344
+ "abstention_reason": result.get("abstention_reason"),
280
345
  }
281
346
  return {"success": False, "error": result.get("error", "Recall failed")}
282
347
  except Exception as exc:
@@ -403,12 +468,19 @@ def register_core_tools(server, get_engine: Callable) -> None:
403
468
  try:
404
469
  engine = get_engine()
405
470
  pid = profile_id or engine.profile_id
471
+ authorization = authorize_mcp_mutation(
472
+ engine,
473
+ "update",
474
+ mutation_source="mcp-build-memory-graph",
475
+ profile_id=pid,
476
+ )
406
477
  facts = engine._db.get_all_facts(pid)
407
478
  edge_count = 0
408
479
  for fact in facts:
409
480
  if engine._graph_builder:
410
481
  engine._graph_builder.build_edges(fact, pid)
411
482
  edge_count += 1
483
+ authorization.complete()
412
484
  return {
413
485
  "success": True,
414
486
  "facts_processed": len(facts),
@@ -424,6 +496,13 @@ def register_core_tools(server, get_engine: Callable) -> None:
424
496
  try:
425
497
  engine = get_engine()
426
498
  old = engine.profile_id
499
+ authorization = authorize_mcp_mutation(
500
+ engine,
501
+ "update",
502
+ mutation_source="mcp-switch-profile",
503
+ profile_id=profile_id,
504
+ content_preview=f"{old} -> {profile_id}",
505
+ )
427
506
  engine.profile_id = profile_id
428
507
 
429
508
  # Persist to both config stores so CLI and Dashboard stay in sync
@@ -446,6 +525,7 @@ def register_core_tools(server, get_engine: Callable) -> None:
446
525
  except Exception:
447
526
  logger.debug("worker-pool recycle on profile switch skipped")
448
527
 
528
+ authorization.complete()
449
529
  return {
450
530
  "success": True,
451
531
  "previous_profile": old,
@@ -513,6 +593,14 @@ def register_core_tools(server, get_engine: Callable) -> None:
513
593
  """Correct or annotate a learned behavioral pattern to improve retrieval."""
514
594
  try:
515
595
  engine = get_engine()
596
+ authorization = authorize_mcp_mutation(
597
+ engine,
598
+ "update",
599
+ mutation_source="mcp-correct-pattern",
600
+ profile_id=engine.profile_id,
601
+ fact_id=pattern_id,
602
+ content_preview=correction,
603
+ )
516
604
  from superlocalmemory.learning.behavioral import BehavioralPatternStore
517
605
  store = BehavioralPatternStore(engine._db.db_path)
518
606
  store.record(
@@ -521,6 +609,7 @@ def register_core_tools(server, get_engine: Callable) -> None:
521
609
  pattern_key=pattern_id,
522
610
  metadata={"correction": correction},
523
611
  )
612
+ authorization.complete()
524
613
  return {"success": True, "pattern_id": pattern_id}
525
614
  except Exception as exc:
526
615
  logger.exception("correct_pattern failed")
@@ -548,7 +637,9 @@ def register_core_tools(server, get_engine: Callable) -> None:
548
637
  result = pool._send({
549
638
  "cmd": "delete_memory",
550
639
  "fact_id": fact_id,
551
- "agent_id": agent_id,
640
+ # Informational IDE/client label only. The worker derives its
641
+ # authorization actor from the private local capability.
642
+ "source_agent_id": agent_id,
552
643
  })
553
644
  if result.get("ok"):
554
645
  logger.info("Memory deleted: %s by agent: %s", fact_id[:16], agent_id)
@@ -589,7 +680,7 @@ def register_core_tools(server, get_engine: Callable) -> None:
589
680
  "cmd": "update_memory",
590
681
  "fact_id": fact_id,
591
682
  "content": content.strip(),
592
- "agent_id": agent_id,
683
+ "source_agent_id": agent_id,
593
684
  })
594
685
  if result.get("ok"):
595
686
  logger.info("Memory updated: %s by agent: %s", fact_id[:16], agent_id)
@@ -607,7 +698,7 @@ def register_core_tools(server, get_engine: Callable) -> None:
607
698
  "product": "SuperLocalMemory V3",
608
699
  "author": "Varun Pratap Bhardwaj",
609
700
  "organization": "Qualixar",
610
- "license": "Elastic-2.0",
701
+ "license": "AGPL-3.0-or-later",
611
702
  "urls": {
612
703
  "product": "https://superlocalmemory.com",
613
704
  "author": "https://varunpratap.com",
@@ -627,6 +718,14 @@ def _format_results(results) -> list[dict]:
627
718
  "content": r.fact.content,
628
719
  "score": round(r.score, 3),
629
720
  "confidence": round(r.confidence, 3),
721
+ "relevance_score": round(
722
+ getattr(r, "relevance_score", r.score) or 0.0, 3
723
+ ),
724
+ "ranking_score": getattr(r, "ranking_score", None),
725
+ "memory_confidence": round(
726
+ getattr(r, "memory_confidence", r.confidence) or 0.0, 3
727
+ ),
728
+ "rank_position": int(getattr(r, "rank_position", 0) or 0),
630
729
  "trust_score": round(r.trust_score, 3),
631
730
  "fact_type": r.fact.fact_type.value,
632
731
  "channel_scores": {
@@ -22,12 +22,10 @@ from pathlib import Path
22
22
  from typing import Callable
23
23
 
24
24
  from mcp.types import ToolAnnotations
25
+ from superlocalmemory.infra.data_root import state_path
25
26
 
26
27
  logger = logging.getLogger(__name__)
27
28
 
28
- MEMORY_DB = Path.home() / ".superlocalmemory" / "memory.db"
29
-
30
-
31
29
  def register_evolution_tools(server, get_engine: Callable) -> None:
32
30
  """Register evolution MCP tools for skill evolution intelligence."""
33
31
 
@@ -49,7 +47,7 @@ def register_evolution_tools(server, get_engine: Callable) -> None:
49
47
  """
50
48
  try:
51
49
  # Check if evolution is enabled in config
52
- config_path = Path.home() / ".superlocalmemory" / "config.json"
50
+ config_path = state_path("config.json")
53
51
  evo_cfg = {}
54
52
  if config_path.exists():
55
53
  with open(config_path) as f:
@@ -82,7 +80,7 @@ def register_evolution_tools(server, get_engine: Callable) -> None:
82
80
  class _Cfg:
83
81
  evolution = _EvoCfg()
84
82
 
85
- db_path = str(MEMORY_DB)
83
+ db_path = str(state_path("memory.db"))
86
84
  evolver = SkillEvolver(db_path, _Cfg())
87
85
 
88
86
  # Build candidate from manual trigger
@@ -145,7 +143,7 @@ def register_evolution_tools(server, get_engine: Callable) -> None:
145
143
  try:
146
144
  engine = get_engine()
147
145
  profile_id = engine.profile_id if engine else "default"
148
- db_path = str(MEMORY_DB)
146
+ db_path = str(state_path("memory.db"))
149
147
 
150
148
  conn = sqlite3.connect(db_path, timeout=10)
151
149
  conn.row_factory = sqlite3.Row
@@ -273,7 +271,7 @@ def register_evolution_tools(server, get_engine: Callable) -> None:
273
271
  skill_name: Specific skill name (empty = all skills)
274
272
  """
275
273
  try:
276
- db_path = str(MEMORY_DB)
274
+ db_path = str(state_path("memory.db"))
277
275
  conn = sqlite3.connect(db_path, timeout=10)
278
276
  conn.row_factory = sqlite3.Row
279
277
 
@@ -24,6 +24,8 @@ from typing import Callable
24
24
 
25
25
  from mcp.types import ToolAnnotations
26
26
 
27
+ from superlocalmemory.mcp.shared import authorize_mcp_mutation
28
+
27
29
  logger = logging.getLogger(__name__)
28
30
 
29
31
  _MAX_SUMMARY_LEN = 500 # Truncate input/output summaries
@@ -69,6 +71,13 @@ def register_learning_tools(server, get_engine: Callable) -> None:
69
71
  output_clean = _scrub(output_summary[:_MAX_SUMMARY_LEN])
70
72
 
71
73
  try:
74
+ authorization = authorize_mcp_mutation(
75
+ engine,
76
+ "update",
77
+ mutation_source="mcp-log-tool-event",
78
+ profile_id=engine.profile_id,
79
+ content_preview=tool_name,
80
+ )
72
81
  engine._db.execute(
73
82
  "INSERT INTO tool_events "
74
83
  "(session_id, profile_id, project_path, tool_name, event_type, "
@@ -77,6 +86,7 @@ def register_learning_tools(server, get_engine: Callable) -> None:
77
86
  (session_id, engine.profile_id, project_path, tool_name,
78
87
  event_type, input_clean, output_clean, duration_ms, metadata, now),
79
88
  )
89
+ authorization.complete()
80
90
  return {"success": True, "tool": tool_name, "event": event_type}
81
91
  except Exception as exc:
82
92
  logger.debug("log_tool_event failed: %s", exc)
@@ -144,7 +154,22 @@ def register_learning_tools(server, get_engine: Callable) -> None:
144
154
  assertion_id: The assertion ID to reinforce
145
155
  """
146
156
  engine = get_engine()
147
- return _update_assertion_confidence(engine._db, assertion_id, reinforce=True)
157
+ try:
158
+ authorization = authorize_mcp_mutation(
159
+ engine,
160
+ "update",
161
+ mutation_source="mcp-reinforce-assertion",
162
+ profile_id=engine.profile_id,
163
+ fact_id=assertion_id,
164
+ )
165
+ result = _update_assertion_confidence(
166
+ engine._db, assertion_id, reinforce=True,
167
+ )
168
+ if result.get("success"):
169
+ authorization.complete()
170
+ return result
171
+ except Exception as exc:
172
+ return {"success": False, "error": str(exc)}
148
173
 
149
174
  @server.tool(annotations=ToolAnnotations(idempotentHint=True))
150
175
  async def contradict_assertion(assertion_id: str) -> dict:
@@ -157,7 +182,22 @@ def register_learning_tools(server, get_engine: Callable) -> None:
157
182
  assertion_id: The assertion ID to contradict
158
183
  """
159
184
  engine = get_engine()
160
- return _update_assertion_confidence(engine._db, assertion_id, reinforce=False)
185
+ try:
186
+ authorization = authorize_mcp_mutation(
187
+ engine,
188
+ "delete",
189
+ mutation_source="mcp-contradict-assertion",
190
+ profile_id=engine.profile_id,
191
+ fact_id=assertion_id,
192
+ )
193
+ result = _update_assertion_confidence(
194
+ engine._db, assertion_id, reinforce=False,
195
+ )
196
+ if result.get("success"):
197
+ authorization.complete()
198
+ return result
199
+ except Exception as exc:
200
+ return {"success": False, "error": str(exc)}
161
201
 
162
202
 
163
203
  def _update_assertion_confidence(db, assertion_id: str, reinforce: bool) -> dict:
@@ -17,7 +17,6 @@ Auto-heartbeat keeps the session alive as long as the MCP server is running.
17
17
  from __future__ import annotations
18
18
 
19
19
  import asyncio
20
- import json
21
20
  import logging
22
21
  import os
23
22
  import threading
@@ -38,18 +37,6 @@ _HEARTBEAT_THREAD: threading.Thread | None = None
38
37
  _REGISTERED = False
39
38
 
40
39
 
41
- def _daemon_url() -> str:
42
- """Get the daemon base URL."""
43
- port = 8765
44
- try:
45
- port_file = os.path.join(os.path.expanduser("~"), ".superlocalmemory", "daemon.port")
46
- if os.path.exists(port_file):
47
- port = int(open(port_file).read().strip())
48
- except Exception:
49
- pass
50
- return f"http://127.0.0.1:{port}"
51
-
52
-
53
40
  def _detect_project_path() -> str:
54
41
  """Detect current project path from env or cwd."""
55
42
  return (
@@ -60,15 +47,11 @@ def _detect_project_path() -> str:
60
47
 
61
48
 
62
49
  def _mesh_request(method: str, path: str, body: dict | None = None) -> dict | None:
63
- """Send request to daemon mesh broker."""
64
- import urllib.request
65
- url = f"{_daemon_url()}/mesh{path}"
50
+ """Send an exact-instance, capability-authenticated mesh request."""
66
51
  try:
67
- data = json.dumps(body).encode() if body else None
68
- headers = {"Content-Type": "application/json"} if data else {}
69
- req = urllib.request.Request(url, data=data, headers=headers, method=method)
70
- resp = urllib.request.urlopen(req, timeout=10)
71
- return json.loads(resp.read().decode())
52
+ from superlocalmemory.cli.daemon import daemon_request
53
+
54
+ return daemon_request(method, f"/mesh{path}", body)
72
55
  except Exception as exc:
73
56
  logger.debug("Mesh request failed: %s %s — %s", method, path, exc)
74
57
  return None
@@ -232,8 +215,9 @@ def register_mesh_tools(server, get_engine: Callable) -> None:
232
215
  async def mesh_state(key: str = "", value: str = "", action: str = "get") -> dict:
233
216
  """Get or set shared state across all sessions.
234
217
 
235
- Shared state is visible to all peers. Use for coordinating work:
236
- server IPs, API keys, feature flags, task assignments.
218
+ Shared state is visible to authenticated peers. Use it for non-secret
219
+ coordination metadata such as feature flags and task assignments.
220
+ Credentials, tokens, passwords, and API keys are rejected.
237
221
 
238
222
  Args:
239
223
  key: State key name
@@ -219,10 +219,17 @@ def register_optimize_tools(server) -> None:
219
219
  norm_tid = _normalize_tenant_id(tenant)
220
220
  ttl_exp = time.time() + ttl_seconds
221
221
 
222
- CacheDB.get_default().set(
222
+ db = CacheDB.get_default()
223
+ db.set(
223
224
  cache_key, norm_tid, value_bytes,
224
225
  model="mcp-kv", ttl_expires=ttl_exp, tags=["mcp-kv"],
225
226
  )
227
+ # ``tag_json`` is descriptive only; tag invalidation joins the
228
+ # normalized llmcache_tags index. CacheManager registers that
229
+ # index itself, whereas this direct MCP KV surface writes to
230
+ # CacheDB. Register it here so ``slm cache invalidate --tag
231
+ # mcp-kv`` actually removes entries created by slm_cache_set.
232
+ db.tag_register(cache_key, norm_tid, ["mcp-kv"])
226
233
  return {"ok": True, "stored": True, "note": None}
227
234
 
228
235
  except Exception as exc:
@@ -15,6 +15,8 @@ from __future__ import annotations
15
15
  import logging
16
16
  from typing import Callable
17
17
 
18
+ from superlocalmemory.mcp.shared import authorize_mcp_mutation
19
+
18
20
  logger = logging.getLogger(__name__)
19
21
 
20
22
 
@@ -42,6 +44,11 @@ def register_v28_tools(server, get_engine: Callable) -> None:
42
44
  """
43
45
  try:
44
46
  engine = get_engine()
47
+ authorization = authorize_mcp_mutation(
48
+ engine,
49
+ "update",
50
+ mutation_source="mcp-report-outcome",
51
+ )
45
52
  from superlocalmemory.learning.outcomes import OutcomeTracker
46
53
  tracker = OutcomeTracker(engine._db)
47
54
  ids = [mid.strip() for mid in memory_ids.split(",") if mid.strip()]
@@ -58,8 +65,8 @@ def register_v28_tools(server, get_engine: Callable) -> None:
58
65
  # Previously, outcomes were stored but never created learning signals.
59
66
  try:
60
67
  from superlocalmemory.learning.feedback import FeedbackCollector
61
- from pathlib import Path as _Path
62
- learning_db = _Path.home() / ".superlocalmemory" / "learning.db"
68
+ from superlocalmemory.infra.data_root import state_path
69
+ learning_db = state_path("learning.db")
63
70
  collector = FeedbackCollector(learning_db)
64
71
  signal_map = {"success": ("user_positive", 1.0),
65
72
  "failure": ("user_negative", 0.0),
@@ -75,6 +82,7 @@ def register_v28_tools(server, get_engine: Callable) -> None:
75
82
  except Exception as exc2:
76
83
  logger.debug("Outcome→signal bridge: %s", exc2)
77
84
 
85
+ authorization.complete()
78
86
  return {"success": True, "outcome_id": ao.outcome_id, "outcome": outcome}
79
87
  except Exception as exc:
80
88
  logger.exception("report_outcome failed")
@@ -134,8 +142,14 @@ def register_v28_tools(server, get_engine: Callable) -> None:
134
142
  """
135
143
  try:
136
144
  engine = get_engine()
145
+ authorization = authorize_mcp_mutation(
146
+ engine,
147
+ "update",
148
+ mutation_source="mcp-retention-policy",
149
+ )
137
150
  engine._db.set_config("retention_cold_days", str(cold_after_days))
138
151
  engine._db.set_config("retention_archive_days", str(archive_after_days))
152
+ authorization.complete()
139
153
  return {
140
154
  "success": True,
141
155
  "cold_after_days": cold_after_days,
@@ -174,10 +188,17 @@ def register_v28_tools(server, get_engine: Callable) -> None:
174
188
  "proposed": new_state.value,
175
189
  })
176
190
  if not dry_run:
191
+ authorization = authorize_mcp_mutation(
192
+ engine,
193
+ "update",
194
+ mutation_source="mcp-compact-memories",
195
+ profile_id=pid,
196
+ )
177
197
  for c in candidates:
178
198
  engine._db.update_fact(
179
199
  c["fact_id"], {"lifecycle": c["proposed"]},
180
200
  )
201
+ authorization.complete()
181
202
  return {
182
203
  "success": True,
183
204
  "dry_run": dry_run,
@@ -14,6 +14,8 @@ from __future__ import annotations
14
14
  import logging
15
15
  from typing import Callable
16
16
 
17
+ from superlocalmemory.mcp.shared import authorize_mcp_mutation
18
+
17
19
  logger = logging.getLogger(__name__)
18
20
 
19
21
 
@@ -64,6 +66,14 @@ def register_v3_tools(server, get_engine: Callable) -> None:
64
66
  "success": False,
65
67
  "error": f"Invalid mode '{mode}'. Use 'a', 'b', or 'c'.",
66
68
  }
69
+ engine = get_engine()
70
+ authorization = authorize_mcp_mutation(
71
+ engine,
72
+ "update",
73
+ mutation_source="mcp-set-mode",
74
+ profile_id=engine.profile_id,
75
+ content_preview=mode_lower,
76
+ )
67
77
  from superlocalmemory.core.config import SLMConfig
68
78
  from superlocalmemory.mcp.server import reset_engine
69
79
 
@@ -80,6 +90,7 @@ def register_v3_tools(server, get_engine: Callable) -> None:
80
90
  )
81
91
 
82
92
  reset_engine()
93
+ authorization.complete()
83
94
 
84
95
  return {
85
96
  "success": True,
@@ -290,8 +301,16 @@ def register_v3_tools(server, get_engine: Callable) -> None:
290
301
  results.append({
291
302
  "fact_id": item.get("fact_id", ""),
292
303
  "content": item.get("content", ""),
293
- "final_score": round(float(item.get("score", 0.0)), 4),
304
+ "score": round(float(item.get("score", 0.0)), 4),
305
+ "relevance_score": round(
306
+ float(item.get("relevance_score", item.get("score", 0.0))), 4
307
+ ),
308
+ "ranking_score": item.get("ranking_score"),
294
309
  "confidence": round(float(item.get("confidence", 0.0)), 3),
310
+ "memory_confidence": round(
311
+ float(item.get("memory_confidence", item.get("confidence", 0.0))), 3
312
+ ),
313
+ "rank_position": int(item.get("rank_position", 0)),
295
314
  "trust_score": round(float(item.get("trust_score", 0.0)), 3),
296
315
  "channel_scores": item.get("channel_scores", {}) or {},
297
316
  "evidence_chain": item.get("evidence_chain", []) or [],
@@ -307,6 +326,12 @@ def register_v3_tools(server, get_engine: Callable) -> None:
307
326
  "channel_weights": raw.get("channel_weights", {}) if isinstance(raw, dict) else {},
308
327
  "total_candidates": raw.get("total_candidates", 0) if isinstance(raw, dict) else 0,
309
328
  "retrieval_time_ms": round(float(raw.get("retrieval_time_ms", 0.0)) if isinstance(raw, dict) else 0.0, 1),
329
+ "score_contract_version": raw.get("score_contract_version", "2") if isinstance(raw, dict) else "2",
330
+ "calibration_status": raw.get("calibration_status", "uncalibrated") if isinstance(raw, dict) else "uncalibrated",
331
+ "calibration_id": raw.get("calibration_id") if isinstance(raw, dict) else None,
332
+ "answer_confidence": raw.get("answer_confidence") if isinstance(raw, dict) else None,
333
+ "abstained": bool(raw.get("abstained", False)) if isinstance(raw, dict) else False,
334
+ "abstention_reason": raw.get("abstention_reason") if isinstance(raw, dict) else None,
310
335
  }
311
336
  except Exception as exc:
312
337
  logger.exception("recall_trace failed")