superlocalmemory 3.8.0 → 3.8.2

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 (134) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +32 -120
  3. package/package.json +9 -2
  4. package/plugin/.claude-plugin/plugin.json +1 -2
  5. package/plugin/CLAUDE.md +3 -3
  6. package/plugin/agents/slm-governance-advisor.md +1 -1
  7. package/plugin/agents/slm-loop-runner.md +1 -1
  8. package/plugin/agents/slm-memory-advisor.md +2 -2
  9. package/plugin/agents/slm-optimize-advisor.md +1 -1
  10. package/plugin/requirements.txt +1 -1
  11. package/plugin/skills/slm-cache/SKILL.md +1 -1
  12. package/plugin/skills/slm-compress/SKILL.md +1 -1
  13. package/plugin/skills/slm-governance/SKILL.md +1 -1
  14. package/plugin/skills/slm-graph/SKILL.md +1 -1
  15. package/plugin/skills/slm-loop/SKILL.md +1 -1
  16. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  17. package/plugin/skills/slm-profile/SKILL.md +1 -1
  18. package/plugin/skills/slm-recall/SKILL.md +3 -5
  19. package/plugin/skills/slm-remember/SKILL.md +1 -1
  20. package/plugin/skills/slm-scope/SKILL.md +1 -1
  21. package/plugin/skills/slm-session/SKILL.md +1 -1
  22. package/plugin/skills/slm-status/SKILL.md +1 -1
  23. package/plugin-src/rules/AGENTS.md +2 -1
  24. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  25. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  26. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  27. package/plugin-src/skills/slm-recall/SKILL.md +3 -5
  28. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  29. package/plugin-src/skills/slm-session/SKILL.md +1 -1
  30. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  31. package/pyproject.toml +2 -1
  32. package/scripts/postinstall.js +7 -1
  33. package/src/superlocalmemory/__init__.py +1 -1
  34. package/src/superlocalmemory/cli/commands.py +494 -9
  35. package/src/superlocalmemory/cli/daemon.py +7 -0
  36. package/src/superlocalmemory/cli/loop_cmd.py +2 -7
  37. package/src/superlocalmemory/cli/main.py +72 -7
  38. package/src/superlocalmemory/cli/setup_wizard.py +142 -16
  39. package/src/superlocalmemory/cli/version_banner.py +17 -3
  40. package/src/superlocalmemory/core/backend_orchestrator.py +18 -16
  41. package/src/superlocalmemory/core/component_healer.py +144 -0
  42. package/src/superlocalmemory/core/component_registry.py +487 -0
  43. package/src/superlocalmemory/core/config.py +21 -0
  44. package/src/superlocalmemory/core/embedding_worker.py +4 -5
  45. package/src/superlocalmemory/core/embeddings.py +132 -45
  46. package/src/superlocalmemory/core/engine.py +29 -22
  47. package/src/superlocalmemory/core/engine_ingestion.py +332 -45
  48. package/src/superlocalmemory/core/ingestion_command.py +154 -25
  49. package/src/superlocalmemory/core/injection.py +12 -7
  50. package/src/superlocalmemory/core/maintenance.py +43 -0
  51. package/src/superlocalmemory/core/maintenance_scheduler.py +44 -6
  52. package/src/superlocalmemory/core/recall_pipeline.py +42 -4
  53. package/src/superlocalmemory/core/store_pipeline.py +195 -20
  54. package/src/superlocalmemory/hooks/hook_handlers.py +6 -1
  55. package/src/superlocalmemory/hooks/portable_kit.py +34 -2
  56. package/src/superlocalmemory/learning/model_rollback.py +3 -0
  57. package/src/superlocalmemory/learning/ranker_retrain_online.py +2 -0
  58. package/src/superlocalmemory/learning/reward.py +50 -0
  59. package/src/superlocalmemory/learning/source_quality.py +523 -1
  60. package/src/superlocalmemory/loops/ledger.py +25 -5
  61. package/src/superlocalmemory/mcp/_daemon_proxy.py +6 -2
  62. package/src/superlocalmemory/mcp/_pool_adapter.py +4 -1
  63. package/src/superlocalmemory/mcp/server.py +11 -30
  64. package/src/superlocalmemory/mcp/tools_active.py +1 -1
  65. package/src/superlocalmemory/mcp/tools_core.py +21 -5
  66. package/src/superlocalmemory/mcp/tools_learning.py +2 -2
  67. package/src/superlocalmemory/retrieval/bridge_discovery.py +14 -0
  68. package/src/superlocalmemory/retrieval/engine.py +53 -21
  69. package/src/superlocalmemory/retrieval/reranker.py +3 -4
  70. package/src/superlocalmemory/retrieval/spreading_activation.py +68 -38
  71. package/src/superlocalmemory/server/config_file.py +90 -0
  72. package/src/superlocalmemory/server/origin.py +50 -0
  73. package/src/superlocalmemory/server/routes/backup.py +293 -70
  74. package/src/superlocalmemory/server/routes/behavioral.py +342 -61
  75. package/src/superlocalmemory/server/routes/brain.py +57 -16
  76. package/src/superlocalmemory/server/routes/config_api.py +84 -82
  77. package/src/superlocalmemory/server/routes/entity.py +100 -23
  78. package/src/superlocalmemory/server/routes/evolution.py +103 -100
  79. package/src/superlocalmemory/server/routes/learning.py +286 -105
  80. package/src/superlocalmemory/server/routes/learning_telemetry.py +153 -0
  81. package/src/superlocalmemory/server/routes/memories.py +8 -3
  82. package/src/superlocalmemory/server/routes/mesh.py +121 -32
  83. package/src/superlocalmemory/server/routes/ratelimit.py +33 -25
  84. package/src/superlocalmemory/server/routes/stats.py +93 -155
  85. package/src/superlocalmemory/server/routes/token.py +3 -13
  86. package/src/superlocalmemory/server/routes/v3_api.py +184 -20
  87. package/src/superlocalmemory/server/unified_daemon.py +732 -41
  88. package/src/superlocalmemory/storage/embedding_migrator.py +235 -0
  89. package/src/superlocalmemory/storage/migration_runner.py +79 -1
  90. package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +5 -0
  91. package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +270 -0
  92. package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +137 -0
  93. package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +93 -0
  94. package/src/superlocalmemory/storage/migrations/__init__.py +4 -0
  95. package/src/superlocalmemory/storage/schema.py +49 -1
  96. package/src/superlocalmemory/storage/schema_v32.py +2 -0
  97. package/src/superlocalmemory/storage/schema_v347.py +4 -0
  98. package/src/superlocalmemory/ui/index.html +6 -8
  99. package/src/superlocalmemory/ui/js/core.js +52 -9
  100. package/src/superlocalmemory/ui/js/dashboard.js +169 -82
  101. package/src/superlocalmemory/ui/js/od-backup.js +156 -65
  102. package/src/superlocalmemory/ui/js/od-brain.js +88 -51
  103. package/src/superlocalmemory/ui/js/od-components.js +147 -0
  104. package/src/superlocalmemory/ui/js/od-entities.js +65 -22
  105. package/src/superlocalmemory/ui/js/od-graph.js +46 -4
  106. package/src/superlocalmemory/ui/js/od-health.js +18 -0
  107. package/src/superlocalmemory/ui/js/od-memories.js +84 -5
  108. package/src/superlocalmemory/ui/js/od-mesh.js +23 -9
  109. package/src/superlocalmemory/ui/js/od-operations.js +36 -0
  110. package/src/superlocalmemory/ui/js/od-settings.js +186 -63
  111. package/src/superlocalmemory/ui/js/od-shell.js +249 -33
  112. package/src/superlocalmemory/ui/js/od-skills.js +44 -17
  113. package/src/superlocalmemory/ui/js/settings.js +15 -1
  114. package/plugin-src/.mcp.json +0 -12
  115. package/plugin-src/agents/slm-governance-advisor.md +0 -80
  116. package/plugin-src/agents/slm-loop-runner.md +0 -71
  117. package/plugin-src/agents/slm-memory-advisor.md +0 -49
  118. package/plugin-src/agents/slm-optimize-advisor.md +0 -44
  119. package/plugin-src/commands/slm-loop.md +0 -31
  120. package/plugin-src/hooks/.gitkeep +0 -0
  121. package/plugin-src/hooks/hooks.json +0 -102
  122. package/plugin-src/manifest.json +0 -30
  123. package/plugin-src/requirements.txt +0 -1
  124. package/plugin-src/rules/CLAUDE.md.fragment +0 -44
  125. package/plugin-src/scripts/ensure-venv.bat +0 -122
  126. package/plugin-src/scripts/ensure-venv.sh +0 -105
  127. package/plugin-src/scripts/slm-launch +0 -62
  128. package/plugin-src/scripts/slm-launch.bat +0 -23
  129. package/plugin-src/settings.json +0 -25
  130. package/plugin-src/skills/slm-governance/SKILL.md +0 -248
  131. package/plugin-src/skills/slm-loop/SKILL.md +0 -99
  132. package/plugin-src/skills/slm-mesh/SKILL.md +0 -282
  133. package/plugin-src/skills/slm-profile/SKILL.md +0 -148
  134. package/plugin-src/skills/slm-scope/SKILL.md +0 -176
@@ -30,23 +30,67 @@
30
30
  var _tokenCache = null;
31
31
 
32
32
  /* ── Auth helper ──────────────────────────────────────────── */
33
- function getToken() {
33
+ function tokenError(message) {
34
+ var error = new Error(message);
35
+ error.code = 'install_token_missing';
36
+ return error;
37
+ }
38
+ function getToken(refresh) {
39
+ if (refresh) _tokenCache = null;
34
40
  if (_tokenCache) return Promise.resolve(_tokenCache);
35
41
  return fetch('/internal/token')
36
- .then(function (r) { return r.json(); })
37
- .then(function (d) { _tokenCache = d.token || ''; return _tokenCache; })
38
- .catch(function () { return ''; });
39
- }
40
- /** PUT with X-Install-Token. Use for all new config endpoints. */
41
- function authPut(url, body) {
42
- return getToken().then(function (tok) {
43
- return fetch(url, {
44
- method: 'PUT',
45
- headers: { 'Content-Type': 'application/json', 'X-Install-Token': tok },
46
- body: JSON.stringify(body)
42
+ .then(function (response) {
43
+ if (!response.ok) throw new Error('Could not refresh dashboard credential');
44
+ return response.json();
45
+ })
46
+ .then(function (data) {
47
+ var token = data.token || '';
48
+ if (!token) throw tokenError('Dashboard credential is empty');
49
+ _tokenCache = token;
50
+ return token;
51
+ });
52
+ }
53
+ function sendAuthenticated(url, method, body, token) {
54
+ return fetch(url, {
55
+ method: method,
56
+ headers: { 'Content-Type': 'application/json', 'X-Install-Token': token },
57
+ body: JSON.stringify(body)
58
+ });
59
+ }
60
+ function requireSuccess(response) {
61
+ if (response.ok) return Promise.resolve(response);
62
+ var details = typeof response.json === 'function'
63
+ ? response.json().catch(function () { return {}; })
64
+ : Promise.resolve({});
65
+ return details.then(function (data) {
66
+ var message = data.error || data.detail || data.message ||
67
+ ('Request failed with status ' + response.status);
68
+ var error = new Error(message);
69
+ error.status = response.status;
70
+ throw error;
71
+ });
72
+ }
73
+ /** Authenticated dashboard mutation; callers must re-read daemon truth on success. */
74
+ function authRequest(url, method, body) {
75
+ function attempt(refresh) {
76
+ return getToken(refresh).then(function (token) {
77
+ return sendAuthenticated(url, method, body, token);
47
78
  });
79
+ }
80
+ return attempt(false).then(function (response) {
81
+ if (response.status === 401 || response.status === 403) {
82
+ return attempt(true).then(requireSuccess);
83
+ }
84
+ return requireSuccess(response);
85
+ }, function (error) {
86
+ if (error && error.code === 'install_token_missing') {
87
+ return attempt(true).then(requireSuccess);
88
+ }
89
+ throw error;
48
90
  });
49
91
  }
92
+ function authPut(url, body) { return authRequest(url, 'PUT', body); }
93
+ function authPost(url, body) { return authRequest(url, 'POST', body); }
50
94
 
51
95
  /* ── Helpers ──────────────────────────────────────────────── */
52
96
  function esc(s) {
@@ -270,9 +314,7 @@
270
314
  var ep = q('endpoint') ? q('endpoint').value : '';
271
315
  if (k) { body.api_key = k; }
272
316
  if (ep) { body.base_url = ep; body.endpoint = ep; }
273
- fetch('/api/v3/mode/set', {
274
- method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body)
275
- }).then(function (r) { return r.json(); })
317
+ authPost('/api/v3/mode/set', body).then(function (r) { return r.json(); })
276
318
  .then(function () { setSt('mode', 'Saved', true); toast('Mode saved'); loadMode(); })
277
319
  .catch(function () { setSt('mode', 'Error', false); toast('Mode save failed', true); });
278
320
  }
@@ -348,10 +390,8 @@
348
390
  embedding_dimension: parseInt(q('emb-dim') ? q('emb-dim').value : '768') || 768,
349
391
  embedding_key: q('emb-key') ? q('emb-key').value : '' }
350
392
  : { embedding_provider:'default' };
351
- fetch('/api/v3/mode/set', {
352
- method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body)
353
- }).then(function (r) { return r.json(); })
354
- .then(function () { setSt('emb', 'Saved', true); toast('Embedding config saved'); })
393
+ authPost('/api/v3/mode/set', body).then(function (r) { return r.json(); })
394
+ .then(function () { setSt('emb', 'Saved', true); toast('Embedding config saved'); loadEmb(); })
355
395
  .catch(function () { setSt('emb', 'Error', false); toast('Embedding save failed', true); });
356
396
  }
357
397
  function testEmb() {
@@ -418,6 +458,7 @@
418
458
  setSt('stor', 'Saved', true);
419
459
  toast('Storage backends saved');
420
460
  showRestartNote('stor', !!d.restart_required);
461
+ loadStorage();
421
462
  }).catch(function () { setSt('stor', 'Error', false); toast('Storage save failed', true); });
422
463
  }
423
464
 
@@ -442,6 +483,7 @@
442
483
  }).then(function (r) { return r.json(); })
443
484
  .then(function (d) {
444
485
  setSt('scope', d.success !== false ? 'Applied' : esc(d.error || 'Error'), d.success !== false);
486
+ if (d.success !== false) loadScope();
445
487
  })
446
488
  .catch(function () { setSt('scope', 'Error', false); toast('Scope save failed', true); });
447
489
  }
@@ -467,6 +509,75 @@
467
509
  }).catch(function () {});
468
510
  }
469
511
 
512
+ /* ═══════════════════════════════════════════════════════════
513
+ GROUP — Recall & Memory Behaviour (v3.8.2, live-apply, no restart)
514
+ PUT /api/v3/runtime/config {retrieval:{top_k,use_cross_encoder},
515
+ injection:{enabled,core_block_enabled,
516
+ core_block_max_facts}}
517
+ ═══════════════════════════════════════════════════════════ */
518
+ function buildRuntime() {
519
+ var topkWrap = makeRange('rt-topk', 1, 200, 1, 20);
520
+ var topkInp = topkWrap.querySelector('input');
521
+ var xencSw = makeSwitch('rt-xenc', true);
522
+ var injSw = makeSwitch('rt-inj', true);
523
+ var cbSw = makeSwitch('rt-cb', true);
524
+ var cbWrap = makeRange('rt-cbmax', 0, 50, 1, 5);
525
+ var cbInp = cbWrap.querySelector('input');
526
+ var st = makeSt('rt');
527
+ function save() {
528
+ authPut('/api/v3/runtime/config', {
529
+ retrieval: {
530
+ top_k: Number(topkInp.value),
531
+ use_cross_encoder: xencSw.classList.contains('on')
532
+ },
533
+ injection: {
534
+ enabled: injSw.classList.contains('on'),
535
+ core_block_enabled: cbSw.classList.contains('on'),
536
+ core_block_max_facts: Number(cbInp.value)
537
+ }
538
+ }).then(function (r) { return r.json(); })
539
+ .then(function (d) {
540
+ setSt('rt', d.success !== false ? 'Applied' : esc(d.error || 'Error'), d.success !== false);
541
+ if (d.success !== false) loadRuntime();
542
+ })
543
+ .catch(function () { setSt('rt', 'Error', false); toast('Save failed', true); });
544
+ }
545
+ // Ranges save on release ('change'), not each drag tick ('input').
546
+ topkInp.addEventListener('change', save);
547
+ cbInp.addEventListener('change', save);
548
+ xencSw.addEventListener('od-toggle', save);
549
+ injSw.addEventListener('od-toggle', save);
550
+ cbSw.addEventListener('od-toggle', save);
551
+ return makeGrp('Recall & Memory Behaviour (auto-saves, no restart)', 'operations', [
552
+ makeRow('Recall depth', 'Memories each recall considers — higher is more thorough, a little slower', 'retrieval.top_k', topkWrap),
553
+ makeRow('Reranker', 'Re-scores results for quality (uses ~200MB RAM, adds ~200ms)', 'retrieval.use_cross_encoder', xencSw),
554
+ makeRow('Inject memories into prompts', 'Master switch for automatic memory injection', 'injection.enabled', injSw),
555
+ makeRow('Core-memory block', 'Prepend your most important facts to every prompt', 'injection.core_block_enabled', cbSw),
556
+ makeRow('Core-block size', 'Max facts in the core-memory block', 'injection.core_block_max_facts', cbWrap),
557
+ makeRow('Status', '', '', st)
558
+ ]);
559
+ }
560
+ function loadRuntime() {
561
+ fetch('/api/v3/runtime/config').then(function (r) { return r.ok ? r.json() : null; })
562
+ .then(function (d) {
563
+ if (!d || !d.config) return;
564
+ var rt = d.config.retrieval || {}, inj = d.config.injection || {};
565
+ function setRange(id, val) {
566
+ var i = q(id); if (i == null) return;
567
+ i.value = val; var disp = q(id + '-disp'); if (disp) disp.textContent = val;
568
+ }
569
+ function setSw(id, on) {
570
+ var s = q(id); if (!s) return;
571
+ s.classList.toggle('on', !!on); s.setAttribute('aria-checked', String(!!on));
572
+ }
573
+ if (rt.top_k != null) setRange('rt-topk', rt.top_k);
574
+ setSw('rt-xenc', rt.use_cross_encoder);
575
+ setSw('rt-inj', inj.enabled);
576
+ setSw('rt-cb', inj.core_block_enabled);
577
+ if (inj.core_block_max_facts != null) setRange('rt-cbmax', inj.core_block_max_facts);
578
+ }).catch(function () {});
579
+ }
580
+
470
581
  /* ═══════════════════════════════════════════════════════════
471
582
  GROUP 5 — Auto-Capture
472
583
  ═══════════════════════════════════════════════════════════ */
@@ -481,9 +592,9 @@
481
592
  capture_decisions: swDec.classList.contains('on'),
482
593
  capture_bugs: swBug.classList.contains('on')
483
594
  });
484
- fetch('/api/v3/auto-capture/config', {
485
- method:'PUT', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body)
486
- }).catch(function () { toast('Auto-capture save failed', true); });
595
+ authPut('/api/v3/auto-capture/config', body)
596
+ .then(function (r) { if (!r.ok) throw new Error('Save failed'); loadCapture(); })
597
+ .catch(function () { toast('Auto-capture save failed', true); });
487
598
  }
488
599
  sw.addEventListener('od-toggle', save);
489
600
  swDec.addEventListener('od-toggle', save);
@@ -516,13 +627,11 @@
516
627
  var sw = makeSwitch('rec-en', true);
517
628
  var swSess = makeSwitch('rec-sess', true);
518
629
  function save() {
519
- fetch('/api/v3/auto-recall/config', {
520
- method:'PUT', headers:{'Content-Type':'application/json'},
521
- body:JSON.stringify({
630
+ authPut('/api/v3/auto-recall/config', {
522
631
  enabled: sw.classList.contains('on'),
523
632
  on_session_start: swSess.classList.contains('on')
524
- })
525
- }).catch(function () { toast('Auto-recall save failed', true); });
633
+ }).then(function (r) { if (!r.ok) throw new Error('Save failed'); loadRecall(); })
634
+ .catch(function () { toast('Auto-recall save failed', true); });
526
635
  }
527
636
  sw.addEventListener('od-toggle', save);
528
637
  swSess.addEventListener('od-toggle', save);
@@ -552,15 +661,12 @@
552
661
  var arSw = makeSwitch('ai-actr', false);
553
662
  var st = makeSt('ai');
554
663
  function save() {
555
- fetch('/api/v3/auto-invoke/config', {
556
- method:'PUT', headers:{'Content-Type':'application/json'},
557
- body:JSON.stringify({
664
+ authPut('/api/v3/auto-invoke/config', {
558
665
  enabled: sw.classList.contains('on'),
559
666
  min_score: parseFloat(q('ai-fok') ? q('ai-fok').value : '0.12'),
560
667
  act_r_mode: arSw.classList.contains('on')
561
- })
562
- }).then(function (r) { return r.json(); })
563
- .then(function () { setSt('ai', 'Saved', true); })
668
+ }).then(function (r) { return r.json(); })
669
+ .then(function () { setSt('ai', 'Saved', true); loadInvoke(); })
564
670
  .catch(function () { toast('Auto-invoke save failed', true); });
565
671
  }
566
672
  sw.addEventListener('od-toggle', save);
@@ -602,6 +708,7 @@
602
708
  { fontSize:'12px', color:'var(--fg-2)', fontFamily:'var(--font-mono)' });
603
709
  var saveBtn = makeBtn('forg-save', 'Save Forgetting', true);
604
710
  var st = makeSt('forg');
711
+ var restNote = makeRestartNote('forg');
605
712
  saveBtn.addEventListener('click', saveForgetting);
606
713
  onSw.addEventListener('od-toggle', saveForgetting);
607
714
  immSw.addEventListener('od-toggle', saveForgetting);
@@ -615,7 +722,7 @@
615
722
  makeRow('Forget threshold', 'Memories below this strength are permanently deleted', 'forgetting.forget_threshold', fgtR),
616
723
  makeRow('Run every (minutes)', 'How often the forgetting engine runs in the background', 'forgetting.scheduler_interval_minutes', intInp),
617
724
  makeRow('Memory zone stats', '', 'forgetting.stats', statsEl),
618
- makeRow('Actions', '', '', bRow(saveBtn, st))
725
+ makeRow('Actions', '', '', bRow(saveBtn, st, restNote))
619
726
  ]);
620
727
  }
621
728
  function loadForgetting() {
@@ -647,6 +754,7 @@
647
754
  }
648
755
  function saveForgetting() {
649
756
  setSt('forg', 'Saving…', null);
757
+ showRestartNote('forg', false);
650
758
  authPut('/api/v3/forgetting/config', {
651
759
  enabled: q('forg-en') ? q('forg-en').classList.contains('on') : false,
652
760
  core_memory_immune: q('forg-imm') ? q('forg-imm').classList.contains('on') : true,
@@ -654,7 +762,12 @@
654
762
  forget_threshold: parseFloat(q('forg-fgt') ? q('forg-fgt').value : '0.05'),
655
763
  scheduler_interval_minutes: parseInt(q('forg-int') ? q('forg-int').value : '30') || 30
656
764
  }).then(function (r) { return r.json(); })
657
- .then(function () { setSt('forg', 'Saved', true); toast('Forgetting settings saved'); })
765
+ .then(function (d) {
766
+ setSt('forg', 'Saved', true);
767
+ toast('Forgetting settings saved');
768
+ showRestartNote('forg', !!d.restart_required);
769
+ loadForgetting();
770
+ })
658
771
  .catch(function () { setSt('forg', 'Error', false); toast('Forgetting save failed', true); });
659
772
  }
660
773
 
@@ -673,7 +786,7 @@
673
786
  var st = makeSt('evo');
674
787
  sw.addEventListener('od-toggle', function () {
675
788
  var on = sw.classList.contains('on');
676
- fetch(on ? '/api/evolution/enable' : '/api/evolution/disable', { method:'POST' })
789
+ authPost(on ? '/api/evolution/enable' : '/api/evolution/disable', {})
677
790
  .then(function (r) { return r.json(); })
678
791
  .then(function (d) {
679
792
  if (d.ok === false) {
@@ -692,11 +805,9 @@
692
805
  });
693
806
  saveBtn.addEventListener('click', function () {
694
807
  setSt('evo', 'Saving…', null);
695
- fetch('/api/evolution/config', {
696
- method:'POST', headers:{'Content-Type':'application/json'},
697
- body:JSON.stringify({ backend: q('evo-bk') ? q('evo-bk').value : 'auto' })
698
- }).then(function (r) { return r.json(); })
699
- .then(function () { setSt('evo', 'Saved', true); })
808
+ authPost('/api/evolution/config', { backend: q('evo-bk') ? q('evo-bk').value : 'auto' })
809
+ .then(function (r) { return r.json(); })
810
+ .then(function (d) { if (d.ok === false) throw new Error(d.error || 'Save failed'); setSt('evo', 'Saved', true); loadEvolution(); })
700
811
  .catch(function () { setSt('evo', 'Error', false); toast('Evolution save failed', true); });
701
812
  });
702
813
  return makeGrp('Skill Evolution', 'skill', [
@@ -731,7 +842,7 @@
731
842
  saveBtn.addEventListener('click', saveBackupConfig);
732
843
  nowBtn.addEventListener('click', function () {
733
844
  setSt('bk', 'Creating…', null);
734
- fetch('/api/backup/create', { method:'POST' }).then(function (r) { return r.json(); })
845
+ authPost('/api/backup/create', {}).then(function (r) { return r.json(); })
735
846
  .then(function (d) {
736
847
  setSt('bk', d.success ? 'Done: ' + esc(d.filename || '') : 'Failed', d.success);
737
848
  toast(d.success ? 'Backup created' : 'Backup failed', !d.success);
@@ -768,15 +879,12 @@
768
879
  }
769
880
  function saveBackupConfig() {
770
881
  setSt('bk', 'Saving…', null);
771
- fetch('/api/backup/configure', {
772
- method:'POST', headers:{'Content-Type':'application/json'},
773
- body:JSON.stringify({
882
+ authPost('/api/backup/configure', {
774
883
  interval_hours: parseInt(q('bk-int') ? q('bk-int').value : '168'),
775
884
  max_backups: parseInt(q('bk-max') ? q('bk-max').value : '10'),
776
885
  enabled: q('bk-en') ? q('bk-en').classList.contains('on') : true
777
- })
778
- }).then(function (r) { return r.json(); })
779
- .then(function () { setSt('bk', 'Saved', true); toast('Backup settings saved'); })
886
+ }).then(function (r) { return r.json(); })
887
+ .then(function (d) { if (d.success === false) throw new Error(d.message || 'Save failed'); setSt('bk', 'Saved', true); toast('Backup settings saved'); loadBackup(); })
780
888
  .catch(function () { setSt('bk', 'Error', false); toast('Backup save failed', true); });
781
889
  }
782
890
 
@@ -787,15 +895,24 @@
787
895
  function buildMesh() {
788
896
  var sw = makeSwitch('mesh-en', false);
789
897
  var st = makeSt('mesh');
898
+ var restNote = makeRestartNote('mesh');
790
899
  sw.addEventListener('od-toggle', function () {
900
+ showRestartNote('mesh', false);
791
901
  authPut('/api/v3/mesh/config', { enabled: sw.classList.contains('on') })
792
902
  .then(function (r) { return r.json(); })
793
- .then(function () { setSt('mesh', 'Saved', true); })
794
- .catch(function () { setSt('mesh', 'Error', false); toast('Mesh save failed', true); });
903
+ .then(function (d) {
904
+ setSt('mesh', 'Saved', true);
905
+ showRestartNote('mesh', !!d.restart_required);
906
+ })
907
+ .catch(function () {
908
+ setSt('mesh', 'Error', false);
909
+ toast('Mesh save failed', true);
910
+ loadMesh();
911
+ });
795
912
  });
796
913
  return makeGrp('Mesh Network', 'mesh', [
797
914
  makeRow('Enable mesh sync', 'Synchronise memories across devices on the same local network', 'mesh.enabled', sw),
798
- makeRow('Status', '', '', st)
915
+ makeRow('Status', '', '', bRow(st, restNote))
799
916
  ]);
800
917
  }
801
918
  function loadMesh() {
@@ -817,6 +934,7 @@
817
934
  var pmR = makeRange('trust-pm', '0', '1', '0.01', '0.5');
818
935
  var saveBtn = makeBtn('trust-save', 'Save Trust', true);
819
936
  var st = makeSt('trust');
937
+ var restNote = makeRestartNote('trust');
820
938
  saveBtn.addEventListener('click', saveTrust);
821
939
  twSw.addEventListener('od-toggle', saveTrust);
822
940
  fpSw.addEventListener('od-toggle', saveTrust);
@@ -825,7 +943,7 @@
825
943
  makeRow('Weight recall by trust', 'Higher-trust sources appear first in recall results', 'trust.use_trust_weighting', twSw),
826
944
  makeRow('Prefer first-party agents', 'Your own Claude/Gemini sessions rank higher than third-party', 'trust.trust_first_party', fpSw),
827
945
  makeRow('Promotion min trust', 'Min trust score required to promote a memory to long-term storage', 'trust.promotion_min_trust', pmR),
828
- makeRow('Actions', '', '', bRow(saveBtn, st))
946
+ makeRow('Actions', '', '', bRow(saveBtn, st, restNote))
829
947
  ]);
830
948
  }
831
949
  function loadTrust() {
@@ -842,12 +960,17 @@
842
960
  }
843
961
  function saveTrust() {
844
962
  setSt('trust', 'Saving…', null);
963
+ showRestartNote('trust', false);
845
964
  authPut('/api/v3/trust/config', {
846
965
  use_trust_weighting: q('trust-tw') ? q('trust-tw').classList.contains('on') : true,
847
966
  trust_first_party: q('trust-fp') ? q('trust-fp').classList.contains('on') : false,
848
967
  promotion_min_trust: parseFloat(q('trust-pm') ? q('trust-pm').value : '0.5')
849
968
  }).then(function (r) { return r.json(); })
850
- .then(function () { setSt('trust', 'Saved', true); })
969
+ .then(function (d) {
970
+ setSt('trust', 'Saved', true);
971
+ showRestartNote('trust', !!d.restart_required);
972
+ loadTrust();
973
+ })
851
974
  .catch(function () { setSt('trust', 'Error', false); toast('Trust save failed', true); });
852
975
  }
853
976
 
@@ -899,7 +1022,7 @@
899
1022
  read: parseInt(q('rl-read') ? q('rl-read').value : '300') || 300,
900
1023
  window: parseInt(q('rl-window') ? q('rl-window').value : '60') || 60
901
1024
  }).then(function (r) { return r.json(); })
902
- .then(function (d) { setSt('rl', 'Saved', true); _rlSetLb(d); })
1025
+ .then(function (d) { setSt('rl', 'Saved', true); _rlSetLb(d); loadRateLimit(); })
903
1026
  .catch(function () { setSt('rl', 'Error', false); toast('Rate limit save failed', true); });
904
1027
  }
905
1028
 
@@ -952,6 +1075,7 @@
952
1075
  setSt('dmn', 'Saved', true);
953
1076
  toast('Daemon settings saved');
954
1077
  showRestartNote('dmn', !!d.restart_required);
1078
+ loadDaemon();
955
1079
  }).catch(function () { setSt('dmn', 'Error', false); toast('Daemon save failed', true); });
956
1080
  }
957
1081
 
@@ -971,8 +1095,8 @@
971
1095
  });
972
1096
  }
973
1097
  function loadAll() {
974
- loadMode(); loadEmb(); loadStorage(); loadScope(); loadCapture();
975
- loadRecall(); loadInvoke(); loadForgetting(); loadEvolution();
1098
+ loadMode(); loadEmb(); loadStorage(); loadScope(); loadRuntime();
1099
+ loadCapture(); loadRecall(); loadInvoke(); loadForgetting(); loadEvolution();
976
1100
  loadBackup(); loadMesh(); loadTrust(); loadRateLimit(); loadDaemon();
977
1101
  }
978
1102
 
@@ -992,7 +1116,7 @@
992
1116
  var headDiv = el('div', null, { marginBottom:'22px' });
993
1117
  headDiv.appendChild(el('h2', { text:'Settings' },
994
1118
  { fontSize:'22px', fontWeight:'650', letterSpacing:'-0.02em' }));
995
- headDiv.appendChild(el('p', { text:'Every SuperLocalMemory configuration, grouped and searchable — no config files to edit by hand. Changes hot-reload the local daemon.' },
1119
+ headDiv.appendChild(el('p', { text:'Every SuperLocalMemory configuration, grouped and searchable — no config files to edit by hand. Settings show when a daemon restart is required.' },
996
1120
  { color:'var(--fg-2)', marginTop:'5px', maxWidth:'62ch' }));
997
1121
  hub.appendChild(headDiv);
998
1122
 
@@ -1007,7 +1131,7 @@
1007
1131
  hub.appendChild(sWrap);
1008
1132
 
1009
1133
  var frag = document.createDocumentFragment();
1010
- [buildMode(), buildEmb(), buildStorage(), buildScope(),
1134
+ [buildMode(), buildEmb(), buildStorage(), buildScope(), buildRuntime(),
1011
1135
  buildCapture(), buildRecall(), buildInvoke(), buildForgetting(),
1012
1136
  buildEvolution(), buildBackup(), buildMesh(), buildTrust(),
1013
1137
  buildRateLimit(), buildDaemon()
@@ -1022,15 +1146,14 @@
1022
1146
  var discardBtn = el('button', { type:'button' }); discardBtn.className = 'btn ghost';
1023
1147
  discardBtn.textContent = 'Discard';
1024
1148
  var syncBadge = el('span', null, { alignSelf:'center', marginLeft:'auto' });
1025
- syncBadge.className = 'badge ok';
1026
- syncBadge.innerHTML = '<span class="dot"></span> All synced';
1149
+ syncBadge.className = 'badge';
1150
+ syncBadge.innerHTML = '<span class="dot"></span> Ready';
1027
1151
  saveAllBtn.addEventListener('click', function () {
1028
1152
  ['mode-save','emb-save','stor-save','forg-save','trust-save','rl-save','evo-save','bk-save','dmn-save']
1029
1153
  .forEach(function (i) { var b = q(i); if (b) b.click(); });
1030
1154
  saveBackupConfig();
1031
- syncBadge.className = 'badge ok';
1032
- syncBadge.innerHTML = '<span class="dot"></span> Saved';
1033
- setTimeout(function () { syncBadge.innerHTML = '<span class="dot"></span> All synced'; }, 3000);
1155
+ syncBadge.className = 'badge';
1156
+ syncBadge.innerHTML = '<span class="dot"></span> Check each section';
1034
1157
  });
1035
1158
  discardBtn.addEventListener('click', function () { loadAll(); });
1036
1159
  foot.appendChild(saveAllBtn); foot.appendChild(discardBtn); foot.appendChild(syncBadge);