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.
Files changed (303) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +275 -72
  3. package/bin/slm-npm +43 -89
  4. package/docs/pi-dev-integration.md +43 -0
  5. package/ide/configs/antigravity-mcp.json +2 -2
  6. package/ide/configs/chatgpt-desktop-mcp.json +1 -1
  7. package/ide/configs/claude-desktop-mcp.json +2 -2
  8. package/ide/configs/windsurf-mcp.json +2 -2
  9. package/ide/hooks/context-hook.js +6 -2
  10. package/ide/hooks/post-recall-hook.js +7 -3
  11. package/ide/hooks/tool-event-hook.sh +2 -1
  12. package/package.json +19 -10
  13. package/plugin/.claude-plugin/plugin.json +1 -1
  14. package/plugin/_GENERATED.md +1 -1
  15. package/plugin/agents/slm-memory-advisor.md +1 -1
  16. package/plugin/requirements.txt +1 -1
  17. package/plugin/skills/slm-session/SKILL.md +1 -1
  18. package/plugin-src/rules/AGENTS.md +1 -1
  19. package/pyproject.toml +40 -8
  20. package/scripts/postinstall-interactive.js +17 -94
  21. package/scripts/postinstall.js +185 -258
  22. package/scripts/preuninstall.js +9 -50
  23. package/src/superlocalmemory/__init__.py +2 -2
  24. package/src/superlocalmemory/attribution/mathematical_dna.py +1 -1
  25. package/src/superlocalmemory/attribution/signer.py +34 -19
  26. package/src/superlocalmemory/attribution/watermark.py +1 -1
  27. package/src/superlocalmemory/cli/_lazy_init.py +3 -5
  28. package/src/superlocalmemory/cli/commands.py +490 -195
  29. package/src/superlocalmemory/cli/context_commands.py +5 -4
  30. package/src/superlocalmemory/cli/daemon.py +282 -187
  31. package/src/superlocalmemory/cli/db_migrate.py +3 -1
  32. package/src/superlocalmemory/cli/diagnostics_cmd.py +28 -0
  33. package/src/superlocalmemory/cli/evidence_cmd.py +103 -0
  34. package/src/superlocalmemory/cli/ingest_cmd.py +7 -3
  35. package/src/superlocalmemory/cli/main.py +128 -31
  36. package/src/superlocalmemory/cli/pending_store.py +54 -38
  37. package/src/superlocalmemory/cli/scale_engine_cmd.py +37 -0
  38. package/src/superlocalmemory/cli/service_installer.py +57 -52
  39. package/src/superlocalmemory/cli/setup_wizard.py +142 -88
  40. package/src/superlocalmemory/cli/version_banner.py +2 -1
  41. package/src/superlocalmemory/code_graph/config.py +3 -1
  42. package/src/superlocalmemory/core/backend_orchestrator.py +81 -21
  43. package/src/superlocalmemory/core/config.py +65 -20
  44. package/src/superlocalmemory/core/consolidation_engine.py +9 -7
  45. package/src/superlocalmemory/core/context_cache.py +56 -8
  46. package/src/superlocalmemory/core/derivation_lineage.py +246 -0
  47. package/src/superlocalmemory/core/embedding_worker.py +32 -20
  48. package/src/superlocalmemory/core/embeddings.py +54 -18
  49. package/src/superlocalmemory/core/engine.py +150 -104
  50. package/src/superlocalmemory/core/engine_ingestion.py +513 -0
  51. package/src/superlocalmemory/core/engine_wiring.py +2 -0
  52. package/src/superlocalmemory/core/evidence_bundle.py +526 -0
  53. package/src/superlocalmemory/core/fact_consolidator.py +5 -11
  54. package/src/superlocalmemory/core/graph_analyzer.py +2 -2
  55. package/src/superlocalmemory/core/health_monitor.py +4 -2
  56. package/src/superlocalmemory/core/ingestion_command.py +636 -0
  57. package/src/superlocalmemory/core/injection.py +69 -18
  58. package/src/superlocalmemory/core/lifecycle_state.py +153 -0
  59. package/src/superlocalmemory/core/maintenance.py +23 -22
  60. package/src/superlocalmemory/core/maintenance_scheduler.py +51 -35
  61. package/src/superlocalmemory/core/mutations.py +143 -0
  62. package/src/superlocalmemory/core/platform_utils.py +7 -4
  63. package/src/superlocalmemory/core/ram_lock.py +16 -5
  64. package/src/superlocalmemory/core/rate_limit.py +1 -1
  65. package/src/superlocalmemory/core/recall_pipeline.py +60 -101
  66. package/src/superlocalmemory/core/recall_worker.py +76 -59
  67. package/src/superlocalmemory/core/registry.py +1 -1
  68. package/src/superlocalmemory/core/scale_engine.py +293 -0
  69. package/src/superlocalmemory/core/score_contract.py +62 -0
  70. package/src/superlocalmemory/core/security_primitives.py +3 -1
  71. package/src/superlocalmemory/core/slm_disabled.py +3 -5
  72. package/src/superlocalmemory/core/store_pipeline.py +172 -40
  73. package/src/superlocalmemory/core/tier_manager.py +32 -20
  74. package/src/superlocalmemory/core/worker_pool.py +13 -4
  75. package/src/superlocalmemory/dynamics/activation_guided_quantization.py +1 -1
  76. package/src/superlocalmemory/dynamics/eap_scheduler.py +10 -3
  77. package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +1 -1
  78. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +1 -1
  79. package/src/superlocalmemory/encoding/auto_linker.py +1 -1
  80. package/src/superlocalmemory/encoding/cognitive_consolidator.py +7 -16
  81. package/src/superlocalmemory/encoding/consolidator.py +22 -5
  82. package/src/superlocalmemory/encoding/fact_extractor.py +1 -1
  83. package/src/superlocalmemory/encoding/foresight.py +2 -0
  84. package/src/superlocalmemory/encoding/graph_builder.py +1 -1
  85. package/src/superlocalmemory/encoding/temporal_parser.py +2 -0
  86. package/src/superlocalmemory/evaluation/__init__.py +13 -0
  87. package/src/superlocalmemory/evaluation/calibration.py +308 -0
  88. package/src/superlocalmemory/evolution/skill_evolver.py +2 -1
  89. package/src/superlocalmemory/graph/cozo_backend.py +256 -23
  90. package/src/superlocalmemory/hooks/_outcome_common.py +21 -11
  91. package/src/superlocalmemory/hooks/antigravity_adapter.py +10 -31
  92. package/src/superlocalmemory/hooks/auto_invoker.py +25 -27
  93. package/src/superlocalmemory/hooks/auto_recall.py +31 -6
  94. package/src/superlocalmemory/hooks/auto_recall_hook.py +13 -33
  95. package/src/superlocalmemory/hooks/before_web_hook.py +9 -7
  96. package/src/superlocalmemory/hooks/claude_code_hooks.py +126 -39
  97. package/src/superlocalmemory/hooks/codex_assets.py +59 -0
  98. package/src/superlocalmemory/hooks/codex_hooks.py +186 -0
  99. package/src/superlocalmemory/hooks/context_payload.py +1 -1
  100. package/src/superlocalmemory/hooks/copilot_adapter.py +9 -24
  101. package/src/superlocalmemory/hooks/cursor_adapter.py +10 -32
  102. package/src/superlocalmemory/hooks/hook_daemon.py +4 -2
  103. package/src/superlocalmemory/hooks/hook_handlers.py +241 -55
  104. package/src/superlocalmemory/hooks/memory_protocol.py +5 -3
  105. package/src/superlocalmemory/hooks/post_tool_async_hook.py +4 -2
  106. package/src/superlocalmemory/hooks/session_registry.py +15 -8
  107. package/src/superlocalmemory/hooks/stop_outcome_hook.py +10 -6
  108. package/src/superlocalmemory/hooks/topic_shift_hook.py +42 -12
  109. package/src/superlocalmemory/hooks/user_prompt_hook.py +9 -14
  110. package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +19 -11
  111. package/src/superlocalmemory/infra/auth_middleware.py +38 -5
  112. package/src/superlocalmemory/infra/backup.py +7 -5
  113. package/src/superlocalmemory/infra/cloud_backup.py +18 -8
  114. package/src/superlocalmemory/infra/daemon_identity.py +248 -0
  115. package/src/superlocalmemory/infra/data_root.py +199 -0
  116. package/src/superlocalmemory/infra/event_bus.py +3 -1
  117. package/src/superlocalmemory/infra/local_diagnostics.py +327 -0
  118. package/src/superlocalmemory/infra/process_reaper.py +23 -0
  119. package/src/superlocalmemory/ingestion/adapter_manager.py +27 -9
  120. package/src/superlocalmemory/ingestion/base_adapter.py +25 -31
  121. package/src/superlocalmemory/ingestion/calendar_adapter.py +13 -4
  122. package/src/superlocalmemory/ingestion/credentials.py +14 -7
  123. package/src/superlocalmemory/ingestion/gmail_adapter.py +13 -4
  124. package/src/superlocalmemory/ingestion/transcript_adapter.py +7 -2
  125. package/src/superlocalmemory/learning/consolidation_quantization_worker.py +1 -1
  126. package/src/superlocalmemory/learning/ensemble.py +11 -0
  127. package/src/superlocalmemory/learning/entity_compiler.py +1 -1
  128. package/src/superlocalmemory/learning/feedback.py +1 -1
  129. package/src/superlocalmemory/learning/forgetting_scheduler.py +12 -7
  130. package/src/superlocalmemory/learning/quantization_scheduler.py +1 -1
  131. package/src/superlocalmemory/learning/ranker.py +4 -1
  132. package/src/superlocalmemory/learning/source_quality.py +1 -1
  133. package/src/superlocalmemory/learning/trigram_index.py +3 -2
  134. package/src/superlocalmemory/llm/backbone.py +13 -8
  135. package/src/superlocalmemory/math/ebbinghaus.py +1 -1
  136. package/src/superlocalmemory/math/fisher.py +1 -1
  137. package/src/superlocalmemory/math/fisher_quantized.py +1 -1
  138. package/src/superlocalmemory/math/hopfield.py +1 -1
  139. package/src/superlocalmemory/math/langevin.py +1 -1
  140. package/src/superlocalmemory/math/polar_quant.py +3 -4
  141. package/src/superlocalmemory/math/qjl.py +1 -1
  142. package/src/superlocalmemory/math/sheaf.py +1 -1
  143. package/src/superlocalmemory/math/turbo_quant.py +3 -2
  144. package/src/superlocalmemory/mcp/_daemon_proxy.py +12 -11
  145. package/src/superlocalmemory/mcp/_pool_adapter.py +27 -0
  146. package/src/superlocalmemory/mcp/http_transport.py +53 -0
  147. package/src/superlocalmemory/mcp/server.py +39 -13
  148. package/src/superlocalmemory/mcp/shared.py +69 -3
  149. package/src/superlocalmemory/mcp/tools_active.py +141 -31
  150. package/src/superlocalmemory/mcp/tools_core.py +128 -29
  151. package/src/superlocalmemory/mcp/tools_evolution.py +5 -7
  152. package/src/superlocalmemory/mcp/tools_learning.py +42 -2
  153. package/src/superlocalmemory/mcp/tools_mesh.py +7 -23
  154. package/src/superlocalmemory/mcp/tools_optimize.py +8 -1
  155. package/src/superlocalmemory/mcp/tools_v28.py +23 -2
  156. package/src/superlocalmemory/mcp/tools_v3.py +26 -1
  157. package/src/superlocalmemory/mcp/tools_v33.py +56 -17
  158. package/src/superlocalmemory/mesh/broker.py +2 -0
  159. package/src/superlocalmemory/mesh/remote_sync.py +50 -12
  160. package/src/superlocalmemory/optimize/cache/manager.py +77 -1
  161. package/src/superlocalmemory/optimize/cache/semantic.py +23 -3
  162. package/src/superlocalmemory/optimize/compress/ccr.py +4 -0
  163. package/src/superlocalmemory/optimize/compress/router.py +6 -1
  164. package/src/superlocalmemory/optimize/config/__init__.py +5 -0
  165. package/src/superlocalmemory/optimize/config/store.py +6 -4
  166. package/src/superlocalmemory/optimize/proxy/_helpers.py +15 -5
  167. package/src/superlocalmemory/optimize/proxy/capture.py +3 -2
  168. package/src/superlocalmemory/optimize/proxy/server.py +2 -2
  169. package/src/superlocalmemory/optimize/storage/db.py +14 -13
  170. package/src/superlocalmemory/retrieval/agentic.py +1 -1
  171. package/src/superlocalmemory/retrieval/ann_index.py +1 -1
  172. package/src/superlocalmemory/retrieval/bm25_channel.py +35 -11
  173. package/src/superlocalmemory/retrieval/bridge_discovery.py +73 -8
  174. package/src/superlocalmemory/retrieval/engine.py +169 -79
  175. package/src/superlocalmemory/retrieval/entity_channel.py +289 -67
  176. package/src/superlocalmemory/retrieval/forgetting_filter.py +1 -1
  177. package/src/superlocalmemory/retrieval/fusion.py +1 -1
  178. package/src/superlocalmemory/retrieval/hopfield_channel.py +118 -30
  179. package/src/superlocalmemory/retrieval/profile_channel.py +1 -1
  180. package/src/superlocalmemory/retrieval/quantization_aware_search.py +16 -10
  181. package/src/superlocalmemory/retrieval/reranker.py +56 -20
  182. package/src/superlocalmemory/retrieval/scope_policy.py +85 -0
  183. package/src/superlocalmemory/retrieval/semantic_channel.py +122 -14
  184. package/src/superlocalmemory/retrieval/spreading_activation.py +141 -25
  185. package/src/superlocalmemory/retrieval/strategy.py +1 -1
  186. package/src/superlocalmemory/retrieval/temporal_channel.py +30 -15
  187. package/src/superlocalmemory/retrieval/vector_store.py +1 -1
  188. package/src/superlocalmemory/server/api.py +10 -7
  189. package/src/superlocalmemory/server/bandit_loops.py +4 -2
  190. package/src/superlocalmemory/server/recall_serializer.py +24 -0
  191. package/src/superlocalmemory/server/route_mutations.py +84 -0
  192. package/src/superlocalmemory/server/routes/agents.py +8 -6
  193. package/src/superlocalmemory/server/routes/brain.py +14 -12
  194. package/src/superlocalmemory/server/routes/chat.py +29 -12
  195. package/src/superlocalmemory/server/routes/data_io.py +55 -24
  196. package/src/superlocalmemory/server/routes/helpers.py +29 -4
  197. package/src/superlocalmemory/server/routes/ingest.py +53 -36
  198. package/src/superlocalmemory/server/routes/memories.py +104 -43
  199. package/src/superlocalmemory/server/routes/mesh.py +31 -0
  200. package/src/superlocalmemory/server/routes/profiles.py +26 -4
  201. package/src/superlocalmemory/server/routes/tiers.py +43 -11
  202. package/src/superlocalmemory/server/routes/timeline.py +5 -1
  203. package/src/superlocalmemory/server/routes/v3_api.py +76 -21
  204. package/src/superlocalmemory/server/security_middleware.py +1 -1
  205. package/src/superlocalmemory/server/ui.py +6 -3
  206. package/src/superlocalmemory/server/unified_daemon.py +680 -293
  207. package/src/superlocalmemory/server/write_identity.py +147 -0
  208. package/src/superlocalmemory/storage/access_log.py +4 -3
  209. package/src/superlocalmemory/storage/database.py +118 -25
  210. package/src/superlocalmemory/storage/migration_runner.py +84 -1
  211. package/src/superlocalmemory/storage/migration_v33.py +1 -1
  212. package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +6 -60
  213. package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +120 -0
  214. package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +54 -0
  215. package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +52 -0
  216. package/src/superlocalmemory/storage/migrations/__init__.py +5 -0
  217. package/src/superlocalmemory/storage/models.py +16 -0
  218. package/src/superlocalmemory/storage/quantized_store.py +20 -3
  219. package/src/superlocalmemory/storage/v2_migrator.py +5 -3
  220. package/src/superlocalmemory/ui/favicon.svg +5 -0
  221. package/src/superlocalmemory/ui/index.html +1 -0
  222. package/src/superlocalmemory/ui/js/compliance.js +1 -1
  223. package/src/superlocalmemory/ui/js/core.js +49 -8
  224. package/src/superlocalmemory/ui/js/dashboard.js +23 -2
  225. package/src/superlocalmemory/ui/js/feedback.js +1 -1
  226. package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
  227. package/src/superlocalmemory/ui/js/graph-ui.js +1 -1
  228. package/src/superlocalmemory/ui/js/lifecycle.js +1 -1
  229. package/src/superlocalmemory/ui/js/ng-mesh.js +15 -49
  230. package/src/superlocalmemory/ui/js/settings.js +4 -2
  231. package/src/superlocalmemory/vector/lancedb_backend.py +57 -9
  232. package/bin/slm +0 -59
  233. package/bin/slm.bat +0 -77
  234. package/bin/slm.cmd +0 -5
  235. package/ide/integrations/langchain/README.md +0 -106
  236. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +0 -9
  237. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +0 -201
  238. package/ide/integrations/langchain/pyproject.toml +0 -38
  239. package/ide/integrations/langchain/tests/__init__.py +0 -3
  240. package/ide/integrations/langchain/tests/test_chat_message_history.py +0 -215
  241. package/ide/integrations/langchain/tests/test_security.py +0 -117
  242. package/ide/integrations/llamaindex/README.md +0 -81
  243. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +0 -9
  244. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +0 -316
  245. package/ide/integrations/llamaindex/pyproject.toml +0 -43
  246. package/ide/integrations/llamaindex/tests/__init__.py +0 -3
  247. package/ide/integrations/llamaindex/tests/test_chat_store.py +0 -294
  248. package/ide/integrations/llamaindex/tests/test_security.py +0 -241
  249. package/plugin-src/.mcp.json +0 -12
  250. package/plugin-src/agents/slm-memory-advisor.md +0 -44
  251. package/plugin-src/agents/slm-optimize-advisor.md +0 -38
  252. package/plugin-src/hooks/.gitkeep +0 -0
  253. package/plugin-src/hooks/hooks.json +0 -23
  254. package/plugin-src/manifest.json +0 -25
  255. package/plugin-src/requirements.txt +0 -1
  256. package/plugin-src/rules/CLAUDE.md.fragment +0 -44
  257. package/plugin-src/scripts/ensure-venv.bat +0 -122
  258. package/plugin-src/scripts/ensure-venv.sh +0 -105
  259. package/plugin-src/scripts/slm-launch +0 -15
  260. package/plugin-src/scripts/slm-launch.bat +0 -17
  261. package/plugin-src/settings.json +0 -16
  262. package/plugin-src/skills/slm-cache/SKILL.md +0 -140
  263. package/plugin-src/skills/slm-compress/SKILL.md +0 -143
  264. package/plugin-src/skills/slm-graph/SKILL.md +0 -300
  265. package/plugin-src/skills/slm-recall/SKILL.md +0 -204
  266. package/plugin-src/skills/slm-remember/SKILL.md +0 -194
  267. package/plugin-src/skills/slm-session/SKILL.md +0 -207
  268. package/plugin-src/skills/slm-status/SKILL.md +0 -149
  269. package/scripts/__tests__/build-plugin.test.mjs +0 -613
  270. package/scripts/_savings_math.py +0 -270
  271. package/scripts/build-dmg.sh +0 -417
  272. package/scripts/build-plugin.js +0 -742
  273. package/scripts/build-slm-hook.ps1 +0 -40
  274. package/scripts/build-slm-hook.sh +0 -45
  275. package/scripts/build_entry.py +0 -452
  276. package/scripts/ci/stage5b_gate.sh +0 -50
  277. package/scripts/dogfood_savings.py +0 -490
  278. package/scripts/generate-thumbnails.py +0 -218
  279. package/scripts/install-skills.ps1 +0 -4
  280. package/scripts/install-skills.sh +0 -5
  281. package/scripts/install.ps1 +0 -701
  282. package/scripts/install.sh +0 -1015
  283. package/scripts/postinstall_binary.js +0 -287
  284. package/scripts/prepack.js +0 -33
  285. package/scripts/release_manifest.py +0 -273
  286. package/scripts/slm-hook.spec +0 -56
  287. package/scripts/start-dashboard.ps1 +0 -52
  288. package/scripts/start-dashboard.sh +0 -41
  289. package/scripts/sync-wiki.ps1 +0 -127
  290. package/scripts/sync-wiki.sh +0 -82
  291. package/scripts/test-dmg.sh +0 -161
  292. package/scripts/test-npm-package.ps1 +0 -252
  293. package/scripts/test-npm-package.sh +0 -207
  294. package/scripts/verify-install.ps1 +0 -294
  295. package/scripts/verify-install.sh +0 -266
  296. package/scripts/verify-v27.ps1 +0 -301
  297. package/scripts/verify-v27.sh +0 -233
  298. package/src/superlocalmemory.egg-info/PKG-INFO +0 -513
  299. package/src/superlocalmemory.egg-info/SOURCES.txt +0 -529
  300. package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
  301. package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
  302. package/src/superlocalmemory.egg-info/requires.txt +0 -71
  303. package/src/superlocalmemory.egg-info/top_level.txt +0 -1
@@ -0,0 +1,120 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
+ # Part of SuperLocalMemory V3
4
+
5
+ """M018 — durable canonical-ingestion operation records.
6
+
7
+ This is the EXPAND phase of the V3.7 ingestion migration. It is additive:
8
+ legacy ``pending_memories`` and ``ingestion_log`` remain untouched until the
9
+ backfill and dual-write comparison prove the new contract.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import sqlite3
15
+
16
+ NAME = "M018_ingestion_operations"
17
+ DB_TARGET = "memory"
18
+
19
+ DDL = """
20
+ CREATE TABLE IF NOT EXISTS ingestion_operations (
21
+ operation_id TEXT PRIMARY KEY,
22
+ profile_id TEXT NOT NULL,
23
+ source_type TEXT NOT NULL,
24
+ idempotency_key TEXT NOT NULL,
25
+ source_hash TEXT NOT NULL,
26
+ raw_content TEXT NOT NULL,
27
+ raw_metadata_json TEXT NOT NULL DEFAULT '{}',
28
+ scope TEXT NOT NULL DEFAULT 'personal'
29
+ CHECK (scope IN ('personal', 'project', 'shared', 'global')),
30
+ shared_with_json TEXT NOT NULL DEFAULT '[]',
31
+ trusted_actor_id TEXT NOT NULL DEFAULT '',
32
+ session_id TEXT NOT NULL DEFAULT '',
33
+ session_date TEXT NOT NULL DEFAULT '',
34
+ speaker TEXT NOT NULL DEFAULT '',
35
+ role TEXT NOT NULL DEFAULT 'user',
36
+ state TEXT NOT NULL DEFAULT 'raw'
37
+ CHECK (state IN ('raw', 'queryable', 'enriching', 'complete', 'failed')),
38
+ queryable_fact_ids_json TEXT NOT NULL DEFAULT '[]',
39
+ final_fact_ids_json TEXT NOT NULL DEFAULT '[]',
40
+ derivation_version TEXT NOT NULL DEFAULT '',
41
+ derivation_state_json TEXT NOT NULL DEFAULT '{}',
42
+ lease_owner TEXT NOT NULL DEFAULT '',
43
+ lease_expires_at REAL NOT NULL DEFAULT 0,
44
+ next_retry_at REAL NOT NULL DEFAULT 0,
45
+ attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
46
+ last_error TEXT NOT NULL DEFAULT '',
47
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
48
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
49
+ UNIQUE(profile_id, source_type, idempotency_key)
50
+ );
51
+ CREATE INDEX IF NOT EXISTS idx_ingestion_operations_state
52
+ ON ingestion_operations(state, updated_at);
53
+ CREATE INDEX IF NOT EXISTS idx_ingestion_operations_source_hash
54
+ ON ingestion_operations(profile_id, source_hash);
55
+ """
56
+
57
+
58
+ def apply(conn: sqlite3.Connection) -> None:
59
+ """Create the additive operation table and indexes idempotently."""
60
+ conn.executescript(DDL)
61
+ columns = {
62
+ row[1]
63
+ for row in conn.execute("PRAGMA table_info(ingestion_operations)").fetchall()
64
+ }
65
+ additive_columns = {
66
+ "derivation_state_json": "TEXT NOT NULL DEFAULT '{}'",
67
+ "session_date": "TEXT NOT NULL DEFAULT ''",
68
+ "speaker": "TEXT NOT NULL DEFAULT ''",
69
+ "role": "TEXT NOT NULL DEFAULT 'user'",
70
+ "lease_owner": "TEXT NOT NULL DEFAULT ''",
71
+ "lease_expires_at": "REAL NOT NULL DEFAULT 0",
72
+ "next_retry_at": "REAL NOT NULL DEFAULT 0",
73
+ }
74
+ for name, declaration in additive_columns.items():
75
+ if name not in columns:
76
+ conn.execute(
77
+ f"ALTER TABLE ingestion_operations ADD COLUMN {name} {declaration}"
78
+ )
79
+
80
+
81
+ def verify(conn: sqlite3.Connection) -> bool:
82
+ """Return true only when the complete M018 contract is present."""
83
+ table = conn.execute(
84
+ "SELECT 1 FROM sqlite_master WHERE type='table' "
85
+ "AND name='ingestion_operations'"
86
+ ).fetchone()
87
+ if table is None:
88
+ return False
89
+ columns = {
90
+ row[1]
91
+ for row in conn.execute("PRAGMA table_info(ingestion_operations)").fetchall()
92
+ }
93
+ required = {
94
+ "operation_id",
95
+ "profile_id",
96
+ "source_type",
97
+ "idempotency_key",
98
+ "source_hash",
99
+ "raw_content",
100
+ "state",
101
+ "queryable_fact_ids_json",
102
+ "final_fact_ids_json",
103
+ "derivation_version",
104
+ "derivation_state_json",
105
+ "session_date",
106
+ "speaker",
107
+ "role",
108
+ "lease_owner",
109
+ "lease_expires_at",
110
+ "next_retry_at",
111
+ "attempt_count",
112
+ "last_error",
113
+ }
114
+ if not required <= columns:
115
+ return False
116
+ indexes = {
117
+ row[1]
118
+ for row in conn.execute("PRAGMA index_list(ingestion_operations)").fetchall()
119
+ }
120
+ return "idx_ingestion_operations_state" in indexes
@@ -0,0 +1,54 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
+
4
+ """M019 — durable, non-fabricated derivation lineage."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import sqlite3
9
+
10
+ NAME = "M019_derivation_lineage"
11
+ DB_TARGET = "memory"
12
+
13
+ DDL = """
14
+ CREATE TABLE IF NOT EXISTS derivation_lineage (
15
+ lineage_id TEXT PRIMARY KEY,
16
+ profile_id TEXT NOT NULL,
17
+ object_type TEXT NOT NULL,
18
+ object_id TEXT NOT NULL,
19
+ operation_id TEXT NOT NULL DEFAULT '',
20
+ derivation_version TEXT NOT NULL DEFAULT '',
21
+ source_status TEXT NOT NULL
22
+ CHECK (source_status IN ('exact', 'derived_from_facts', 'unresolved', 'not_applicable')),
23
+ source_start INTEGER,
24
+ source_end INTEGER,
25
+ source_text_sha256 TEXT NOT NULL DEFAULT '',
26
+ source_fact_ids_json TEXT NOT NULL DEFAULT '[]',
27
+ unresolved_reason TEXT NOT NULL DEFAULT '',
28
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
29
+ UNIQUE(profile_id, object_type, object_id, operation_id)
30
+ );
31
+ CREATE INDEX IF NOT EXISTS idx_derivation_lineage_profile
32
+ ON derivation_lineage(profile_id, object_type, object_id);
33
+ CREATE INDEX IF NOT EXISTS idx_derivation_lineage_operation
34
+ ON derivation_lineage(operation_id);
35
+ """
36
+
37
+
38
+ def apply(conn: sqlite3.Connection) -> None:
39
+ conn.executescript(DDL)
40
+
41
+
42
+ def verify(conn: sqlite3.Connection) -> bool:
43
+ table = conn.execute(
44
+ "SELECT 1 FROM sqlite_master WHERE type='table' AND name='derivation_lineage'"
45
+ ).fetchone()
46
+ if table is None:
47
+ return False
48
+ columns = {row[1] for row in conn.execute("PRAGMA table_info(derivation_lineage)")}
49
+ return {
50
+ "lineage_id", "profile_id", "object_type", "object_id",
51
+ "operation_id", "derivation_version", "source_status",
52
+ "source_start", "source_end", "source_text_sha256",
53
+ "source_fact_ids_json", "unresolved_reason",
54
+ } <= columns
@@ -0,0 +1,52 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
+
4
+ """M020 — forward-only integrity repair for learned model state.
5
+
6
+ M002's DDL shipped before model-state SHA values were backfilled. Do not
7
+ modify M002: its stored hash is part of every existing user's upgrade record.
8
+ This migration deterministically fills only missing digests and never changes
9
+ the model payload or ranking metadata.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import hashlib
15
+ import sqlite3
16
+
17
+ NAME = "M020_model_state_integrity"
18
+ DB_TARGET = "learning"
19
+
20
+ # The runner records a stable fingerprint even though this migration uses a
21
+ # Python apply function for binary-safe SHA computation.
22
+ DDL = "-- M020: backfill missing learning_model_state.bytes_sha256\n"
23
+
24
+
25
+ def verify(conn: sqlite3.Connection) -> bool:
26
+ """Return True only when every persisted model has an integrity digest."""
27
+ try:
28
+ missing = conn.execute(
29
+ "SELECT 1 FROM learning_model_state "
30
+ "WHERE bytes_sha256 = '' OR bytes_sha256 IS NULL LIMIT 1"
31
+ ).fetchone()
32
+ except sqlite3.Error:
33
+ return False
34
+ return missing is None
35
+
36
+
37
+ def apply(conn: sqlite3.Connection) -> None:
38
+ """Backfill SHA-256 values without modifying model bytes or metadata."""
39
+ rows = conn.execute(
40
+ "SELECT id, state_bytes FROM learning_model_state "
41
+ "WHERE bytes_sha256 = '' OR bytes_sha256 IS NULL"
42
+ ).fetchall()
43
+ updates = [
44
+ (hashlib.sha256(state_bytes).hexdigest(), row_id)
45
+ for row_id, state_bytes in rows
46
+ if state_bytes is not None
47
+ ]
48
+ if updates:
49
+ conn.executemany(
50
+ "UPDATE learning_model_state SET bytes_sha256 = ? WHERE id = ?",
51
+ updates,
52
+ )
@@ -24,6 +24,8 @@ from . import (
24
24
  M004_cross_platform_sync_log,
25
25
  M005_bandit_tables,
26
26
  M015_add_pinned_column,
27
+ M019_derivation_lineage,
28
+ M020_model_state_integrity,
27
29
  )
28
30
 
29
31
  # ---------------------------------------------------------------------------
@@ -72,6 +74,9 @@ __all__ = (
72
74
  "M003_migration_log",
73
75
  "M004_cross_platform_sync_log",
74
76
  "M005_bandit_tables",
77
+ "M015_add_pinned_column",
78
+ "M019_derivation_lineage",
79
+ "M020_model_state_integrity",
75
80
  # Legacy re-exports (backward compat):
76
81
  "CURRENT_SCHEMA_VERSION",
77
82
  "get_schema_version",
@@ -402,6 +402,12 @@ class RetrievalResult:
402
402
  score: float = 0.0
403
403
  channel_scores: dict[str, float] = field(default_factory=dict)
404
404
  confidence: float = 0.0
405
+ # Score Contract v2. Legacy ``score`` and ``confidence`` remain one-release
406
+ # aliases of relevance_score and memory_confidence respectively.
407
+ relevance_score: float | None = None
408
+ ranking_score: float | None = None
409
+ memory_confidence: float | None = None
410
+ rank_position: int = 0
405
411
  evidence_chain: list[str] = field(default_factory=list)
406
412
  trust_score: float = 0.5
407
413
  # LLD-00 §3 + P0.4: HMAC marker emitted during recall so post-tool hooks
@@ -424,3 +430,13 @@ class RecallResponse:
424
430
  # v3.6.6: Evidence floor. True when floor gates out ALL results.
425
431
  # Additive field — backward compatible (defaults to False).
426
432
  no_confident_match: bool = False
433
+ score_contract_version: str = "2"
434
+ calibration_status: str = "uncalibrated"
435
+ calibration_id: str | None = None
436
+ answer_confidence: float | None = None
437
+ abstained: bool = False
438
+ abstention_reason: str | None = None
439
+ # Cross-encoder execution is observable; cold/busy/error fallback must not
440
+ # be mistaken for a reranked response.
441
+ reranker_applied: bool = False
442
+ reranker_status: str = "not_configured"
@@ -12,7 +12,7 @@ HR-06: BLOB columns use Python bytes, not base64.
12
12
  HR-07: QJL is optional -- system works without it.
13
13
 
14
14
  Part of Qualixar | Author: Varun Pratap Bhardwaj
15
- License: Elastic-2.0
15
+ License: AGPL-3.0-or-later
16
16
  """
17
17
 
18
18
  from __future__ import annotations
@@ -150,13 +150,29 @@ class QuantizedEmbeddingStore:
150
150
  query_embedding: NDArray,
151
151
  profile_id: str,
152
152
  top_k: int = 50,
153
+ *,
154
+ bit_widths: tuple[int, ...] | None = None,
153
155
  ) -> list[tuple[str, float]]:
154
156
  """Search polar embeddings for a profile.
155
157
 
156
158
  Pre-filters by lifecycle_zone (excludes 'forgotten').
159
+ ``bit_widths`` selects a persisted precision tier; ``None`` preserves
160
+ the legacy all-quantized-row behavior.
157
161
  Returns [(fact_id, similarity)] sorted descending.
158
162
  """
163
+ if bit_widths is not None and not bit_widths:
164
+ return []
159
165
  try:
166
+ width_clause = ""
167
+ params: list[object] = [profile_id]
168
+ if bit_widths is not None:
169
+ widths = tuple(int(width) for width in bit_widths)
170
+ width_clause = (
171
+ " AND pe.bit_width IN ("
172
+ + ",".join("?" for _ in widths)
173
+ + ")"
174
+ )
175
+ params.extend(widths)
160
176
  rows = self._db.execute(
161
177
  "SELECT pe.fact_id, pe.radius, pe.angle_indices, "
162
178
  " pe.qjl_bits, pe.bit_width "
@@ -164,8 +180,9 @@ class QuantizedEmbeddingStore:
164
180
  "JOIN fact_retention fr "
165
181
  " ON pe.fact_id = fr.fact_id AND fr.profile_id = pe.profile_id "
166
182
  "WHERE pe.profile_id = ? "
167
- " AND fr.lifecycle_zone NOT IN ('forgotten')",
168
- (profile_id,),
183
+ " AND fr.lifecycle_zone NOT IN ('forgotten')"
184
+ + width_clause,
185
+ tuple(params),
169
186
  )
170
187
  except Exception as exc:
171
188
  logger.error("search query failed: %s", exc)
@@ -17,10 +17,12 @@ import sys
17
17
  from datetime import datetime, UTC
18
18
  from pathlib import Path
19
19
 
20
+ from superlocalmemory.infra.data_root import DynamicStatePath, canonical_data_root
21
+
20
22
  logger = logging.getLogger(__name__)
21
23
 
22
24
  V2_BASE = Path.home() / ".claude-memory"
23
- V3_BASE = Path.home() / ".superlocalmemory"
25
+ V3_BASE = DynamicStatePath()
24
26
  V2_DB_NAME = "memory.db"
25
27
  BACKUP_NAME = "memory-v2-backup.db"
26
28
 
@@ -143,10 +145,10 @@ V3_INDEXES_SQL = [
143
145
  class V2Migrator:
144
146
  """Migrate V2 database to V3 schema."""
145
147
 
146
- def __init__(self, home: Path | None = None):
148
+ def __init__(self, home: Path | None = None, v3_base: Path | None = None):
147
149
  self._home = home or Path.home()
148
150
  self._v2_base = self._home / ".claude-memory"
149
- self._v3_base = self._home / ".superlocalmemory"
151
+ self._v3_base = Path(v3_base) if v3_base is not None else canonical_data_root()
150
152
  self._v2_db = self._v2_base / V2_DB_NAME
151
153
  self._v3_db = self._v3_base / V2_DB_NAME
152
154
  self._backup_db = self._v3_base / BACKUP_NAME
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <rect width="64" height="64" rx="14" fill="#0a0e1a"/>
3
+ <path d="M16 32h12l8-14 12 14" fill="none" stroke="#45e0a8" stroke-linecap="round" stroke-linejoin="round" stroke-width="5"/>
4
+ <circle cx="16" cy="32" r="5" fill="#45e0a8"/><circle cx="36" cy="18" r="5" fill="#45e0a8"/><circle cx="48" cy="32" r="5" fill="#45e0a8"/>
5
+ </svg>
@@ -8,6 +8,7 @@
8
8
  on mismatch, so the browser cannot show stale UI after an upgrade. -->
9
9
  <meta name="slm-version" content="__SLM_VERSION__">
10
10
  <title>SuperLocalMemory V3 — Dashboard</title>
11
+ <link rel="icon" type="image/svg+xml" href="static/favicon.svg">
11
12
 
12
13
  <!-- Bootstrap CSS (vendored locally v3.4.21 — no CDN calls, works offline) -->
13
14
  <link href="static/vendor/bootstrap.min.css" rel="stylesheet">
@@ -1,4 +1,4 @@
1
- // SPDX-License-Identifier: Elastic-2.0
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  // Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
3
  // Compliance tab — audit trail, retention policies, ABAC (v2.8)
4
4
  // NOTE: All dynamic values use textContent or escapeHtml() from core.js before DOM insertion.
@@ -14,6 +14,23 @@
14
14
  // ============================================================================
15
15
 
16
16
  window.SLM_FETCH_TIMEOUT_MS = 15000;
17
+ window.SLM_INSTALL_TOKEN_KEY = 'slm_install_token';
18
+
19
+ window.slmInstallToken = async function (forceRefresh) {
20
+ if (!forceRefresh) {
21
+ var cached = sessionStorage.getItem(window.SLM_INSTALL_TOKEN_KEY);
22
+ if (cached) return cached;
23
+ }
24
+ var response = await window.__slmOriginalFetch(
25
+ '/internal/token',
26
+ {credentials: 'same-origin'}
27
+ );
28
+ if (!response.ok) return '';
29
+ var payload = await response.json();
30
+ var token = payload && payload.token ? payload.token : '';
31
+ if (token) sessionStorage.setItem(window.SLM_INSTALL_TOKEN_KEY, token);
32
+ return token;
33
+ };
17
34
 
18
35
  // Global fetch patch: apply the abort timeout to every relative-URL request
19
36
  // automatically. 17 UI modules call bare fetch() — patching here avoids
@@ -26,18 +43,42 @@ window.SLM_FETCH_TIMEOUT_MS = 15000;
26
43
  if (window.__slmFetchPatched) return;
27
44
  window.__slmFetchPatched = true;
28
45
  var _origFetch = window.fetch.bind(window);
46
+ window.__slmOriginalFetch = _origFetch;
29
47
  window.fetch = function (input, init) {
30
- init = init || {};
48
+ init = Object.assign({}, init || {});
31
49
  var urlStr = typeof input === 'string' ? input : (input && input.url) || '';
32
50
  var isRelative = !(/^https?:\/\//i.test(urlStr));
33
- if (!isRelative || init.signal) {
34
- return _origFetch(input, init);
51
+ var method = String(
52
+ init.method || (input && input.method) || 'GET'
53
+ ).toUpperCase();
54
+ var mutating = ['POST', 'PUT', 'PATCH', 'DELETE'].indexOf(method) !== -1;
55
+
56
+ function send() {
57
+ if (!isRelative || init.signal) {
58
+ return _origFetch(input, init);
59
+ }
60
+ var controller = new AbortController();
61
+ var timeoutMs = init.timeoutMs || window.SLM_FETCH_TIMEOUT_MS;
62
+ var timer = setTimeout(function () { controller.abort(); }, timeoutMs);
63
+ init.signal = controller.signal;
64
+ return _origFetch(input, init).finally(function () { clearTimeout(timer); });
65
+ }
66
+
67
+ if (isRelative && mutating && urlStr !== '/internal/token') {
68
+ return window.slmInstallToken(false).then(function (token) {
69
+ if (!token) throw new Error('local write credential unavailable');
70
+ var headers = new Headers(
71
+ init.headers || (input && input.headers) || {}
72
+ );
73
+ if (!headers.has('X-Install-Token')) {
74
+ headers.set('X-Install-Token', token);
75
+ }
76
+ init.headers = headers;
77
+ init.credentials = init.credentials || 'same-origin';
78
+ return send();
79
+ });
35
80
  }
36
- var controller = new AbortController();
37
- var timeoutMs = init.timeoutMs || window.SLM_FETCH_TIMEOUT_MS;
38
- var timer = setTimeout(function () { controller.abort(); }, timeoutMs);
39
- init.signal = controller.signal;
40
- return _origFetch(input, init).finally(function () { clearTimeout(timer); });
81
+ return send();
41
82
  };
42
83
  })();
43
84
 
@@ -72,14 +72,35 @@ document.addEventListener('click', function(e) {
72
72
  }
73
73
  });
74
74
 
75
+ async function dashboardInstallToken() {
76
+ var key = 'slm_install_token';
77
+ var cached = sessionStorage.getItem(key);
78
+ if (cached) return cached;
79
+ var response = await fetch('/internal/token', {credentials: 'same-origin'});
80
+ if (!response.ok) return '';
81
+ var payload = await response.json();
82
+ var token = payload && payload.token ? payload.token : '';
83
+ if (token) sessionStorage.setItem(key, token);
84
+ return token;
85
+ }
86
+
75
87
  // Quick store
76
- document.getElementById('quick-store-btn')?.addEventListener('click', function() {
88
+ document.getElementById('quick-store-btn')?.addEventListener('click', async function() {
77
89
  var input = document.getElementById('quick-store-input');
78
90
  var content = input.value.trim();
79
91
  if (!content) return;
92
+ var token = await dashboardInstallToken();
93
+ if (!token) {
94
+ showToast('Store failed: local write credential unavailable', 'error');
95
+ return;
96
+ }
80
97
  fetch('/remember', {
81
98
  method: 'POST',
82
- headers: {'Content-Type': 'application/json'},
99
+ credentials: 'same-origin',
100
+ headers: {
101
+ 'Content-Type': 'application/json',
102
+ 'X-Install-Token': token
103
+ },
83
104
  body: JSON.stringify({content: content})
84
105
  }).then(function(r) {
85
106
  if (!r.ok) return r.json().catch(function() { return {}; }).then(function(d) {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * SuperLocalMemory V2 - Feedback Module (v2.7.4)
3
3
  * Copyright (c) 2026 Varun Pratap Bhardwaj
4
- * Licensed under Elastic License 2.0
4
+ * Licensed under GNU Affero General Public License v3.0 or later
5
5
  *
6
6
  * Collects implicit and explicit feedback signals from dashboard
7
7
  * interactions. All data stays 100% local.
@@ -1,5 +1,5 @@
1
1
  // SuperLocalMemory V2.6.5 - Interactive Knowledge Graph - Filtering Module
2
- // Copyright (c) 2026 Varun Pratap Bhardwaj — Elastic License 2.0
2
+ // Copyright (c) 2026 Varun Pratap Bhardwaj — GNU Affero General Public License v3.0 or later
3
3
  // Part of modular graph visualization system (split from monolithic graph-cytoscape.js)
4
4
 
5
5
  // ============================================================================
@@ -1,5 +1,5 @@
1
1
  // SuperLocalMemory V2.6.5 - Interactive Knowledge Graph - UI Elements Module
2
- // Copyright (c) 2026 Varun Pratap Bhardwaj — Elastic License 2.0
2
+ // Copyright (c) 2026 Varun Pratap Bhardwaj — GNU Affero General Public License v3.0 or later
3
3
  // Part of modular graph visualization system (split from monolithic graph-cytoscape.js)
4
4
 
5
5
  // ============================================================================
@@ -1,4 +1,4 @@
1
- // SPDX-License-Identifier: Elastic-2.0
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
2
  // Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
3
  // Lifecycle tab — state distribution, compaction, transitions (v2.8)
4
4
  // NOTE: All dynamic values pass through escapeHtml() or textContent for DOM insertion.
@@ -84,50 +84,26 @@
84
84
  }, REFRESH_INTERVAL);
85
85
  };
86
86
 
87
- // Try BOTH brokers: daemon (port 8765 /mesh/*) AND standalone slm-mesh (port 7899 /*)
88
- var STANDALONE_PORT = null;
89
-
90
- function fetchStandaloneBroker(path) {
91
- var ports = [7899];
92
- return fetch('http://127.0.0.1:' + ports[0] + path, { signal: AbortSignal.timeout(2000) })
93
- .then(function(r) {
94
- if (r.ok) { STANDALONE_PORT = ports[0]; return r.json(); }
95
- STANDALONE_PORT = null;
96
- return null;
97
- })
98
- .catch(function() { STANDALONE_PORT = null; return null; });
99
- }
100
-
101
87
  function fetchMeshStatus() {
102
- Promise.all([
103
- meshFetch('/mesh/status').then(function(r) {
104
- if (!r.ok) return r.status === 401 ? { _auth_error: true } : null;
105
- return r.json();
106
- }).catch(function() { return null; }),
107
- fetchStandaloneBroker('/health')
108
- ]).then(function(results) {
109
- var daemon = results[0];
88
+ meshFetch('/mesh/status').then(function(r) {
89
+ if (!r.ok) return r.status === 401 ? { _auth_error: true } : null;
90
+ return r.json();
91
+ }).catch(function() { return null; }).then(function(daemon) {
110
92
  if (daemon && daemon._auth_error) { renderMeshStatusAuthError(); return; }
111
- renderMeshStatus(daemon, results[1]);
93
+ renderMeshStatus(daemon, null);
112
94
  });
113
95
  }
114
96
 
115
97
  function fetchMeshPeers() {
116
- Promise.all([
117
- meshFetch('/mesh/peers').then(function(r) {
118
- if (!r.ok) return r.status === 401 ? { _auth_error: true } : { peers: [] };
119
- return r.json();
120
- }).catch(function() { return { peers: [] }; }),
121
- fetchStandaloneBroker('/peers')
122
- ]).then(function(results) {
123
- var daemonResult = results[0];
98
+ meshFetch('/mesh/peers').then(function(r) {
99
+ if (!r.ok) return r.status === 401 ? { _auth_error: true } : { peers: [] };
100
+ return r.json();
101
+ }).catch(function() { return { peers: [] }; }).then(function(daemonResult) {
124
102
  if (daemonResult && daemonResult._auth_error) { renderMeshPeersAuthError(); return; }
125
103
  var daemonPeers = (daemonResult && daemonResult.peers) || [];
126
- var standalonePeers = (results[1] && (results[1].peers || results[1])) || [];
127
- if (!Array.isArray(standalonePeers)) standalonePeers = [];
128
104
  var seen = {};
129
105
  var allPeers = [];
130
- daemonPeers.concat(standalonePeers).forEach(function(p) {
106
+ daemonPeers.forEach(function(p) {
131
107
  var id = p.peer_id || p.id || JSON.stringify(p);
132
108
  if (!seen[id]) { seen[id] = true; allPeers.push(p); }
133
109
  });
@@ -200,28 +176,18 @@
200
176
  // Standalone broker (TypeScript, slm-mesh npm)
201
177
  var standaloneUp = standaloneData && standaloneData.status === 'ok';
202
178
  var standaloneUptime = standaloneData ? (standaloneData.uptime || 0) : 0;
203
- var standaloneVersion = standaloneData ? (standaloneData.version || '') : '';
204
-
205
- var anyUp = daemonUp || standaloneUp;
206
- var statusText = anyUp ? 'Active' : 'Offline';
207
- var statusKey = anyUp ? 'active' : 'dead';
208
- var bestUptime = Math.max(daemonUptime, standaloneUptime);
209
-
210
- // Combined info
211
- var brokerInfo = [];
212
- if (daemonUp) brokerInfo.push('Daemon (Python)');
213
- if (standaloneUp) brokerInfo.push('slm-mesh ' + standaloneVersion);
179
+ var statusText = daemonUp ? 'Active' : 'Offline';
180
+ var statusKey = daemonUp ? 'active' : 'dead';
214
181
 
215
182
  el.innerHTML =
216
183
  '<div class="row g-3">' +
217
184
  statusCard('Broker', statusDot(statusKey) + ' ' + statusText, 'bi-wifi') +
218
185
  statusCard('Peers', daemonPeers, 'bi-people') +
219
- statusCard('Uptime', formatUptime(bestUptime), 'bi-clock') +
220
- statusCard('Brokers', brokerInfo.length, 'bi-hdd-stack') +
186
+ statusCard('Uptime', formatUptime(daemonUptime), 'bi-clock') +
187
+ statusCard('Broker', daemonUp ? 1 : 0, 'bi-hdd-stack') +
221
188
  '</div>' +
222
189
  '<div style="font-size:0.75rem;color:var(--ng-text-quaternary);margin-top:8px;text-align:center">' +
223
- (brokerInfo.length > 0 ? 'Running: ' + brokerInfo.join(' + ') : 'No brokers detected') +
224
- (standaloneUp ? ' (port 7899)' : '') +
190
+ (daemonUp ? 'Running: integrated SLM daemon' : 'No broker detected') +
225
191
  ' · Peers register via <code>mesh_summary</code> MCP tool and expire after 60s without heartbeat' +
226
192
  '</div>';
227
193
  }
@@ -272,12 +272,14 @@ function _updateSidebarWidget(destinations) {
272
272
 
273
273
  if (primary.destination_type === 'google_drive') {
274
274
  var email = config.email || 'Google Drive';
275
- avatar.innerHTML = '<img src="https://www.google.com/s2/favicons?domain=google.com&sz=32" style="width:28px;height:28px;border-radius:50%;" onerror="this.outerHTML=\'<i class=\\\'bi bi-google\\\' style=\\\'font-size:14px;color:#4285f4;\\\'></i>\'">';
275
+ // Keep the dashboard fully local. CSP deliberately forbids remote
276
+ // avatar requests, and a provider icon conveys the same state.
277
+ avatar.innerHTML = '<i class="bi bi-google" style="font-size:14px;color:#4285f4;"></i>';
276
278
  name.textContent = email.split('@')[0];
277
279
  name.title = email;
278
280
  } else if (primary.destination_type === 'github') {
279
281
  var username = config.username || 'GitHub';
280
- avatar.innerHTML = '<img src="https://github.com/' + username + '.png?size=56" style="width:28px;height:28px;border-radius:50%;" onerror="this.outerHTML=\'<i class=\\\'bi bi-github\\\' style=\\\'font-size:14px;\\\'></i>\'">';
282
+ avatar.innerHTML = '<i class="bi bi-github" style="font-size:14px;"></i>';
281
283
  name.textContent = username;
282
284
  }
283
285