superlocalmemory 3.8.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/README.md +32 -120
- package/package.json +9 -2
- package/plugin/.claude-plugin/plugin.json +1 -2
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +1 -1
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/pyproject.toml +2 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/commands.py +134 -7
- package/src/superlocalmemory/cli/daemon.py +7 -0
- package/src/superlocalmemory/cli/loop_cmd.py +2 -7
- package/src/superlocalmemory/cli/main.py +10 -4
- package/src/superlocalmemory/cli/version_banner.py +17 -3
- package/src/superlocalmemory/core/backend_orchestrator.py +18 -16
- package/src/superlocalmemory/core/embedding_worker.py +4 -5
- package/src/superlocalmemory/core/embeddings.py +119 -45
- package/src/superlocalmemory/core/engine.py +24 -21
- package/src/superlocalmemory/core/engine_ingestion.py +332 -45
- package/src/superlocalmemory/core/ingestion_command.py +134 -25
- package/src/superlocalmemory/core/injection.py +12 -7
- package/src/superlocalmemory/core/maintenance_scheduler.py +17 -7
- package/src/superlocalmemory/core/recall_pipeline.py +5 -3
- package/src/superlocalmemory/core/store_pipeline.py +153 -20
- package/src/superlocalmemory/hooks/hook_handlers.py +6 -1
- package/src/superlocalmemory/hooks/portable_kit.py +34 -2
- package/src/superlocalmemory/learning/model_rollback.py +3 -0
- package/src/superlocalmemory/learning/ranker_retrain_online.py +2 -0
- package/src/superlocalmemory/learning/reward.py +50 -0
- package/src/superlocalmemory/learning/source_quality.py +523 -1
- package/src/superlocalmemory/loops/ledger.py +25 -5
- package/src/superlocalmemory/mcp/server.py +11 -30
- package/src/superlocalmemory/mcp/tools_core.py +4 -3
- package/src/superlocalmemory/mcp/tools_learning.py +2 -2
- package/src/superlocalmemory/retrieval/engine.py +53 -21
- package/src/superlocalmemory/retrieval/reranker.py +3 -4
- package/src/superlocalmemory/server/config_file.py +90 -0
- package/src/superlocalmemory/server/origin.py +50 -0
- package/src/superlocalmemory/server/routes/backup.py +293 -70
- package/src/superlocalmemory/server/routes/behavioral.py +336 -59
- package/src/superlocalmemory/server/routes/brain.py +57 -16
- package/src/superlocalmemory/server/routes/config_api.py +84 -82
- package/src/superlocalmemory/server/routes/entity.py +100 -23
- package/src/superlocalmemory/server/routes/evolution.py +103 -100
- package/src/superlocalmemory/server/routes/learning.py +276 -105
- package/src/superlocalmemory/server/routes/learning_telemetry.py +153 -0
- package/src/superlocalmemory/server/routes/mesh.py +121 -32
- package/src/superlocalmemory/server/routes/ratelimit.py +33 -25
- package/src/superlocalmemory/server/routes/stats.py +93 -155
- package/src/superlocalmemory/server/routes/token.py +3 -13
- package/src/superlocalmemory/server/routes/v3_api.py +64 -20
- package/src/superlocalmemory/server/unified_daemon.py +467 -40
- package/src/superlocalmemory/storage/migration_runner.py +79 -1
- package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +5 -0
- package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +270 -0
- package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +137 -0
- package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +93 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +4 -0
- package/src/superlocalmemory/storage/schema.py +49 -1
- package/src/superlocalmemory/storage/schema_v32.py +2 -0
- package/src/superlocalmemory/storage/schema_v347.py +4 -0
- package/src/superlocalmemory/ui/index.html +3 -6
- package/src/superlocalmemory/ui/js/core.js +52 -9
- package/src/superlocalmemory/ui/js/dashboard.js +169 -82
- package/src/superlocalmemory/ui/js/od-backup.js +156 -65
- package/src/superlocalmemory/ui/js/od-brain.js +88 -51
- package/src/superlocalmemory/ui/js/od-entities.js +22 -22
- package/src/superlocalmemory/ui/js/od-graph.js +11 -4
- package/src/superlocalmemory/ui/js/od-memories.js +47 -5
- package/src/superlocalmemory/ui/js/od-mesh.js +23 -9
- package/src/superlocalmemory/ui/js/od-settings.js +113 -59
- package/src/superlocalmemory/ui/js/od-shell.js +249 -33
- package/src/superlocalmemory/ui/js/od-skills.js +44 -17
- package/src/superlocalmemory/ui/js/settings.js +15 -1
- package/plugin-src/.mcp.json +0 -12
- package/plugin-src/agents/slm-governance-advisor.md +0 -80
- package/plugin-src/agents/slm-loop-runner.md +0 -71
- package/plugin-src/agents/slm-memory-advisor.md +0 -49
- package/plugin-src/agents/slm-optimize-advisor.md +0 -44
- package/plugin-src/commands/slm-loop.md +0 -31
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +0 -102
- package/plugin-src/manifest.json +0 -30
- package/plugin-src/requirements.txt +0 -1
- package/plugin-src/rules/CLAUDE.md.fragment +0 -44
- package/plugin-src/scripts/ensure-venv.bat +0 -122
- package/plugin-src/scripts/ensure-venv.sh +0 -105
- package/plugin-src/scripts/slm-launch +0 -62
- package/plugin-src/scripts/slm-launch.bat +0 -23
- package/plugin-src/settings.json +0 -25
- package/plugin-src/skills/slm-governance/SKILL.md +0 -248
- package/plugin-src/skills/slm-loop/SKILL.md +0 -99
- package/plugin-src/skills/slm-mesh/SKILL.md +0 -282
- package/plugin-src/skills/slm-profile/SKILL.md +0 -148
- package/plugin-src/skills/slm-scope/SKILL.md +0 -176
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// Design-system CSS classes used throughout — no invented inline colours.
|
|
8
8
|
//
|
|
9
9
|
// Endpoints (all via meshFetch → X-Install-Token):
|
|
10
|
-
// GET /mesh/status → {broker_up,
|
|
11
|
-
// GET /mesh/peers → {peers:[{peer_id, session_id, status, last_heartbeat,
|
|
10
|
+
// GET /mesh/status → {broker_up, remote_peer_count, local_session_count, uptime_s}
|
|
11
|
+
// GET /mesh/peers?view=all → {peers:[{peer_id, session_id, status, last_heartbeat,
|
|
12
12
|
// summary, host, port, agent_type, project_path}]}
|
|
13
13
|
// POST /mesh/send → body:{from_peer, to_peer, content, type} → {ok:true}
|
|
14
14
|
//
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
var _container = null;
|
|
77
77
|
var _timer = null;
|
|
78
78
|
var _observer = null; // MutationObserver — re-starts refresh on tab activate
|
|
79
|
+
var _requestSeq = 0;
|
|
79
80
|
|
|
80
81
|
// ── HTML helpers ──────────────────────────────────────────────────────────
|
|
81
82
|
// CRIT C3: every API-derived string MUST pass through this.
|
|
@@ -198,12 +199,15 @@
|
|
|
198
199
|
return;
|
|
199
200
|
}
|
|
200
201
|
var up = data.broker_up === true ? 'Broker up' : 'Broker offline';
|
|
201
|
-
var
|
|
202
|
-
|
|
202
|
+
var remoteCount = data.remote_peer_count != null ? data.remote_peer_count : data.peer_count;
|
|
203
|
+
var cnt = remoteCount != null
|
|
204
|
+
? remoteCount + ' remote peer' + (remoteCount === 1 ? '' : 's')
|
|
203
205
|
: '';
|
|
206
|
+
var localCount = data.local_session_count || 0;
|
|
204
207
|
var upt = data.uptime_s != null ? 'uptime ' + fmtUptime(data.uptime_s) : '';
|
|
205
208
|
var parts = [up];
|
|
206
209
|
if (cnt) parts.push(cnt);
|
|
210
|
+
if (localCount) parts.push(localCount + ' local session' + (localCount === 1 ? '' : 's'));
|
|
207
211
|
if (upt) parts.push(upt);
|
|
208
212
|
el.textContent = 'Agents sharing this memory mesh on your machine and LAN. ' +
|
|
209
213
|
parts.join(' · ') + '. Peers go stale after 5 min without a heartbeat, dead after 30.';
|
|
@@ -236,10 +240,10 @@
|
|
|
236
240
|
'justify-content:center;margin-bottom:16px;color:var(--fg-3)">' +
|
|
237
241
|
meshEmptyIcon() +
|
|
238
242
|
'</div>' +
|
|
239
|
-
'<h3 style="font-size:15px;margin-bottom:6px">No
|
|
243
|
+
'<h3 style="font-size:15px;margin-bottom:6px">No mesh sessions connected</h3>' +
|
|
240
244
|
'<p style="font-size:12.5px;color:var(--fg-2);' +
|
|
241
245
|
'max-width:30ch;line-height:1.5">' +
|
|
242
|
-
'Start another agent session.
|
|
246
|
+
'Start another agent session or connect a trusted LAN peer. Sessions register via the' +
|
|
243
247
|
' <code>mesh_summary</code> MCP tool.' +
|
|
244
248
|
'</p>' +
|
|
245
249
|
'</div>';
|
|
@@ -253,6 +257,9 @@
|
|
|
253
257
|
var pidFrag = escapeHtml((p.peer_id || '').slice(0, 12));
|
|
254
258
|
var summary = escapeHtml(p.summary || 'No summary');
|
|
255
259
|
var atype = escapeHtml(p.agent_type || 'unknown');
|
|
260
|
+
var isLocal = String(p.host || '').toLowerCase() === '127.0.0.1' ||
|
|
261
|
+
String(p.host || '').toLowerCase() === 'localhost' || String(p.host || '') === '::1';
|
|
262
|
+
var scopeLabel = isLocal ? 'local session' : 'remote peer';
|
|
256
263
|
var ago = escapeHtml(timeAgo(p.last_heartbeat));
|
|
257
264
|
var proj = escapeHtml(p.project_path || '~');
|
|
258
265
|
var port = p.port ? ' · :' + p.port : '';
|
|
@@ -274,7 +281,7 @@
|
|
|
274
281
|
'<div style="flex:1;min-width:0">' +
|
|
275
282
|
'<h3 style="font-size:15px;white-space:nowrap;overflow:hidden;' +
|
|
276
283
|
'text-overflow:ellipsis">' + name + '</h3>' +
|
|
277
|
-
'<span class="mono dim" style="font-size:11px">' + pidFrag + '</span>' +
|
|
284
|
+
'<span class="mono dim" style="font-size:11px">' + pidFrag + ' · ' + scopeLabel + '</span>' +
|
|
278
285
|
'</div>' +
|
|
279
286
|
'<span class="badge ' + badgeCls(st) + '">' +
|
|
280
287
|
'<span class="dot"></span>' + escapeHtml(st) +
|
|
@@ -424,15 +431,22 @@
|
|
|
424
431
|
|
|
425
432
|
// ── Data loaders ──────────────────────────────────────────────────────────
|
|
426
433
|
function loadAll() {
|
|
434
|
+
var requestSeq = _requestSeq + 1;
|
|
435
|
+
_requestSeq = requestSeq;
|
|
427
436
|
meshFetch('/mesh/status')
|
|
428
437
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
429
438
|
.catch(function () { return null; })
|
|
430
|
-
.then(
|
|
439
|
+
.then(function (data) {
|
|
440
|
+
if (_requestSeq === requestSeq) renderPageDesc(data);
|
|
441
|
+
});
|
|
431
442
|
|
|
432
|
-
|
|
443
|
+
// The broker distinguishes remote LAN peers from local agent sessions.
|
|
444
|
+
// Both are real mesh participants and must be visible to operators.
|
|
445
|
+
meshFetch('/mesh/peers?view=all')
|
|
433
446
|
.then(function (r) { return r.ok ? r.json() : { peers: [] }; })
|
|
434
447
|
.catch(function () { return { peers: [] }; })
|
|
435
448
|
.then(function (d) {
|
|
449
|
+
if (_requestSeq !== requestSeq) return;
|
|
436
450
|
_peers = Array.isArray(d && d.peers) ? d.peers : [];
|
|
437
451
|
renderPeers(_peers);
|
|
438
452
|
// If the currently selected peer has expired, deselect
|
|
@@ -30,23 +30,67 @@
|
|
|
30
30
|
var _tokenCache = null;
|
|
31
31
|
|
|
32
32
|
/* ── Auth helper ──────────────────────────────────────────── */
|
|
33
|
-
function
|
|
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 (
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
});
|
|
39
59
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
|
|
352
|
-
|
|
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
|
}
|
|
@@ -481,9 +523,9 @@
|
|
|
481
523
|
capture_decisions: swDec.classList.contains('on'),
|
|
482
524
|
capture_bugs: swBug.classList.contains('on')
|
|
483
525
|
});
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
526
|
+
authPut('/api/v3/auto-capture/config', body)
|
|
527
|
+
.then(function (r) { if (!r.ok) throw new Error('Save failed'); loadCapture(); })
|
|
528
|
+
.catch(function () { toast('Auto-capture save failed', true); });
|
|
487
529
|
}
|
|
488
530
|
sw.addEventListener('od-toggle', save);
|
|
489
531
|
swDec.addEventListener('od-toggle', save);
|
|
@@ -516,13 +558,11 @@
|
|
|
516
558
|
var sw = makeSwitch('rec-en', true);
|
|
517
559
|
var swSess = makeSwitch('rec-sess', true);
|
|
518
560
|
function save() {
|
|
519
|
-
|
|
520
|
-
method:'PUT', headers:{'Content-Type':'application/json'},
|
|
521
|
-
body:JSON.stringify({
|
|
561
|
+
authPut('/api/v3/auto-recall/config', {
|
|
522
562
|
enabled: sw.classList.contains('on'),
|
|
523
563
|
on_session_start: swSess.classList.contains('on')
|
|
524
|
-
})
|
|
525
|
-
|
|
564
|
+
}).then(function (r) { if (!r.ok) throw new Error('Save failed'); loadRecall(); })
|
|
565
|
+
.catch(function () { toast('Auto-recall save failed', true); });
|
|
526
566
|
}
|
|
527
567
|
sw.addEventListener('od-toggle', save);
|
|
528
568
|
swSess.addEventListener('od-toggle', save);
|
|
@@ -552,15 +592,12 @@
|
|
|
552
592
|
var arSw = makeSwitch('ai-actr', false);
|
|
553
593
|
var st = makeSt('ai');
|
|
554
594
|
function save() {
|
|
555
|
-
|
|
556
|
-
method:'PUT', headers:{'Content-Type':'application/json'},
|
|
557
|
-
body:JSON.stringify({
|
|
595
|
+
authPut('/api/v3/auto-invoke/config', {
|
|
558
596
|
enabled: sw.classList.contains('on'),
|
|
559
597
|
min_score: parseFloat(q('ai-fok') ? q('ai-fok').value : '0.12'),
|
|
560
598
|
act_r_mode: arSw.classList.contains('on')
|
|
561
|
-
})
|
|
562
|
-
|
|
563
|
-
.then(function () { setSt('ai', 'Saved', true); })
|
|
599
|
+
}).then(function (r) { return r.json(); })
|
|
600
|
+
.then(function () { setSt('ai', 'Saved', true); loadInvoke(); })
|
|
564
601
|
.catch(function () { toast('Auto-invoke save failed', true); });
|
|
565
602
|
}
|
|
566
603
|
sw.addEventListener('od-toggle', save);
|
|
@@ -602,6 +639,7 @@
|
|
|
602
639
|
{ fontSize:'12px', color:'var(--fg-2)', fontFamily:'var(--font-mono)' });
|
|
603
640
|
var saveBtn = makeBtn('forg-save', 'Save Forgetting', true);
|
|
604
641
|
var st = makeSt('forg');
|
|
642
|
+
var restNote = makeRestartNote('forg');
|
|
605
643
|
saveBtn.addEventListener('click', saveForgetting);
|
|
606
644
|
onSw.addEventListener('od-toggle', saveForgetting);
|
|
607
645
|
immSw.addEventListener('od-toggle', saveForgetting);
|
|
@@ -615,7 +653,7 @@
|
|
|
615
653
|
makeRow('Forget threshold', 'Memories below this strength are permanently deleted', 'forgetting.forget_threshold', fgtR),
|
|
616
654
|
makeRow('Run every (minutes)', 'How often the forgetting engine runs in the background', 'forgetting.scheduler_interval_minutes', intInp),
|
|
617
655
|
makeRow('Memory zone stats', '', 'forgetting.stats', statsEl),
|
|
618
|
-
makeRow('Actions', '', '', bRow(saveBtn, st))
|
|
656
|
+
makeRow('Actions', '', '', bRow(saveBtn, st, restNote))
|
|
619
657
|
]);
|
|
620
658
|
}
|
|
621
659
|
function loadForgetting() {
|
|
@@ -647,6 +685,7 @@
|
|
|
647
685
|
}
|
|
648
686
|
function saveForgetting() {
|
|
649
687
|
setSt('forg', 'Saving…', null);
|
|
688
|
+
showRestartNote('forg', false);
|
|
650
689
|
authPut('/api/v3/forgetting/config', {
|
|
651
690
|
enabled: q('forg-en') ? q('forg-en').classList.contains('on') : false,
|
|
652
691
|
core_memory_immune: q('forg-imm') ? q('forg-imm').classList.contains('on') : true,
|
|
@@ -654,7 +693,12 @@
|
|
|
654
693
|
forget_threshold: parseFloat(q('forg-fgt') ? q('forg-fgt').value : '0.05'),
|
|
655
694
|
scheduler_interval_minutes: parseInt(q('forg-int') ? q('forg-int').value : '30') || 30
|
|
656
695
|
}).then(function (r) { return r.json(); })
|
|
657
|
-
.then(function () {
|
|
696
|
+
.then(function (d) {
|
|
697
|
+
setSt('forg', 'Saved', true);
|
|
698
|
+
toast('Forgetting settings saved');
|
|
699
|
+
showRestartNote('forg', !!d.restart_required);
|
|
700
|
+
loadForgetting();
|
|
701
|
+
})
|
|
658
702
|
.catch(function () { setSt('forg', 'Error', false); toast('Forgetting save failed', true); });
|
|
659
703
|
}
|
|
660
704
|
|
|
@@ -673,7 +717,7 @@
|
|
|
673
717
|
var st = makeSt('evo');
|
|
674
718
|
sw.addEventListener('od-toggle', function () {
|
|
675
719
|
var on = sw.classList.contains('on');
|
|
676
|
-
|
|
720
|
+
authPost(on ? '/api/evolution/enable' : '/api/evolution/disable', {})
|
|
677
721
|
.then(function (r) { return r.json(); })
|
|
678
722
|
.then(function (d) {
|
|
679
723
|
if (d.ok === false) {
|
|
@@ -692,11 +736,9 @@
|
|
|
692
736
|
});
|
|
693
737
|
saveBtn.addEventListener('click', function () {
|
|
694
738
|
setSt('evo', 'Saving…', null);
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}).then(function (r) { return r.json(); })
|
|
699
|
-
.then(function () { setSt('evo', 'Saved', true); })
|
|
739
|
+
authPost('/api/evolution/config', { backend: q('evo-bk') ? q('evo-bk').value : 'auto' })
|
|
740
|
+
.then(function (r) { return r.json(); })
|
|
741
|
+
.then(function (d) { if (d.ok === false) throw new Error(d.error || 'Save failed'); setSt('evo', 'Saved', true); loadEvolution(); })
|
|
700
742
|
.catch(function () { setSt('evo', 'Error', false); toast('Evolution save failed', true); });
|
|
701
743
|
});
|
|
702
744
|
return makeGrp('Skill Evolution', 'skill', [
|
|
@@ -731,7 +773,7 @@
|
|
|
731
773
|
saveBtn.addEventListener('click', saveBackupConfig);
|
|
732
774
|
nowBtn.addEventListener('click', function () {
|
|
733
775
|
setSt('bk', 'Creating…', null);
|
|
734
|
-
|
|
776
|
+
authPost('/api/backup/create', {}).then(function (r) { return r.json(); })
|
|
735
777
|
.then(function (d) {
|
|
736
778
|
setSt('bk', d.success ? 'Done: ' + esc(d.filename || '') : 'Failed', d.success);
|
|
737
779
|
toast(d.success ? 'Backup created' : 'Backup failed', !d.success);
|
|
@@ -768,15 +810,12 @@
|
|
|
768
810
|
}
|
|
769
811
|
function saveBackupConfig() {
|
|
770
812
|
setSt('bk', 'Saving…', null);
|
|
771
|
-
|
|
772
|
-
method:'POST', headers:{'Content-Type':'application/json'},
|
|
773
|
-
body:JSON.stringify({
|
|
813
|
+
authPost('/api/backup/configure', {
|
|
774
814
|
interval_hours: parseInt(q('bk-int') ? q('bk-int').value : '168'),
|
|
775
815
|
max_backups: parseInt(q('bk-max') ? q('bk-max').value : '10'),
|
|
776
816
|
enabled: q('bk-en') ? q('bk-en').classList.contains('on') : true
|
|
777
|
-
})
|
|
778
|
-
|
|
779
|
-
.then(function () { setSt('bk', 'Saved', true); toast('Backup settings saved'); })
|
|
817
|
+
}).then(function (r) { return r.json(); })
|
|
818
|
+
.then(function (d) { if (d.success === false) throw new Error(d.message || 'Save failed'); setSt('bk', 'Saved', true); toast('Backup settings saved'); loadBackup(); })
|
|
780
819
|
.catch(function () { setSt('bk', 'Error', false); toast('Backup save failed', true); });
|
|
781
820
|
}
|
|
782
821
|
|
|
@@ -787,15 +826,24 @@
|
|
|
787
826
|
function buildMesh() {
|
|
788
827
|
var sw = makeSwitch('mesh-en', false);
|
|
789
828
|
var st = makeSt('mesh');
|
|
829
|
+
var restNote = makeRestartNote('mesh');
|
|
790
830
|
sw.addEventListener('od-toggle', function () {
|
|
831
|
+
showRestartNote('mesh', false);
|
|
791
832
|
authPut('/api/v3/mesh/config', { enabled: sw.classList.contains('on') })
|
|
792
833
|
.then(function (r) { return r.json(); })
|
|
793
|
-
.then(function () {
|
|
794
|
-
|
|
834
|
+
.then(function (d) {
|
|
835
|
+
setSt('mesh', 'Saved', true);
|
|
836
|
+
showRestartNote('mesh', !!d.restart_required);
|
|
837
|
+
})
|
|
838
|
+
.catch(function () {
|
|
839
|
+
setSt('mesh', 'Error', false);
|
|
840
|
+
toast('Mesh save failed', true);
|
|
841
|
+
loadMesh();
|
|
842
|
+
});
|
|
795
843
|
});
|
|
796
844
|
return makeGrp('Mesh Network', 'mesh', [
|
|
797
845
|
makeRow('Enable mesh sync', 'Synchronise memories across devices on the same local network', 'mesh.enabled', sw),
|
|
798
|
-
makeRow('Status', '', '', st)
|
|
846
|
+
makeRow('Status', '', '', bRow(st, restNote))
|
|
799
847
|
]);
|
|
800
848
|
}
|
|
801
849
|
function loadMesh() {
|
|
@@ -817,6 +865,7 @@
|
|
|
817
865
|
var pmR = makeRange('trust-pm', '0', '1', '0.01', '0.5');
|
|
818
866
|
var saveBtn = makeBtn('trust-save', 'Save Trust', true);
|
|
819
867
|
var st = makeSt('trust');
|
|
868
|
+
var restNote = makeRestartNote('trust');
|
|
820
869
|
saveBtn.addEventListener('click', saveTrust);
|
|
821
870
|
twSw.addEventListener('od-toggle', saveTrust);
|
|
822
871
|
fpSw.addEventListener('od-toggle', saveTrust);
|
|
@@ -825,7 +874,7 @@
|
|
|
825
874
|
makeRow('Weight recall by trust', 'Higher-trust sources appear first in recall results', 'trust.use_trust_weighting', twSw),
|
|
826
875
|
makeRow('Prefer first-party agents', 'Your own Claude/Gemini sessions rank higher than third-party', 'trust.trust_first_party', fpSw),
|
|
827
876
|
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))
|
|
877
|
+
makeRow('Actions', '', '', bRow(saveBtn, st, restNote))
|
|
829
878
|
]);
|
|
830
879
|
}
|
|
831
880
|
function loadTrust() {
|
|
@@ -842,12 +891,17 @@
|
|
|
842
891
|
}
|
|
843
892
|
function saveTrust() {
|
|
844
893
|
setSt('trust', 'Saving…', null);
|
|
894
|
+
showRestartNote('trust', false);
|
|
845
895
|
authPut('/api/v3/trust/config', {
|
|
846
896
|
use_trust_weighting: q('trust-tw') ? q('trust-tw').classList.contains('on') : true,
|
|
847
897
|
trust_first_party: q('trust-fp') ? q('trust-fp').classList.contains('on') : false,
|
|
848
898
|
promotion_min_trust: parseFloat(q('trust-pm') ? q('trust-pm').value : '0.5')
|
|
849
899
|
}).then(function (r) { return r.json(); })
|
|
850
|
-
.then(function () {
|
|
900
|
+
.then(function (d) {
|
|
901
|
+
setSt('trust', 'Saved', true);
|
|
902
|
+
showRestartNote('trust', !!d.restart_required);
|
|
903
|
+
loadTrust();
|
|
904
|
+
})
|
|
851
905
|
.catch(function () { setSt('trust', 'Error', false); toast('Trust save failed', true); });
|
|
852
906
|
}
|
|
853
907
|
|
|
@@ -899,7 +953,7 @@
|
|
|
899
953
|
read: parseInt(q('rl-read') ? q('rl-read').value : '300') || 300,
|
|
900
954
|
window: parseInt(q('rl-window') ? q('rl-window').value : '60') || 60
|
|
901
955
|
}).then(function (r) { return r.json(); })
|
|
902
|
-
.then(function (d) { setSt('rl', 'Saved', true); _rlSetLb(d); })
|
|
956
|
+
.then(function (d) { setSt('rl', 'Saved', true); _rlSetLb(d); loadRateLimit(); })
|
|
903
957
|
.catch(function () { setSt('rl', 'Error', false); toast('Rate limit save failed', true); });
|
|
904
958
|
}
|
|
905
959
|
|
|
@@ -952,6 +1006,7 @@
|
|
|
952
1006
|
setSt('dmn', 'Saved', true);
|
|
953
1007
|
toast('Daemon settings saved');
|
|
954
1008
|
showRestartNote('dmn', !!d.restart_required);
|
|
1009
|
+
loadDaemon();
|
|
955
1010
|
}).catch(function () { setSt('dmn', 'Error', false); toast('Daemon save failed', true); });
|
|
956
1011
|
}
|
|
957
1012
|
|
|
@@ -992,7 +1047,7 @@
|
|
|
992
1047
|
var headDiv = el('div', null, { marginBottom:'22px' });
|
|
993
1048
|
headDiv.appendChild(el('h2', { text:'Settings' },
|
|
994
1049
|
{ 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.
|
|
1050
|
+
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
1051
|
{ color:'var(--fg-2)', marginTop:'5px', maxWidth:'62ch' }));
|
|
997
1052
|
hub.appendChild(headDiv);
|
|
998
1053
|
|
|
@@ -1022,15 +1077,14 @@
|
|
|
1022
1077
|
var discardBtn = el('button', { type:'button' }); discardBtn.className = 'btn ghost';
|
|
1023
1078
|
discardBtn.textContent = 'Discard';
|
|
1024
1079
|
var syncBadge = el('span', null, { alignSelf:'center', marginLeft:'auto' });
|
|
1025
|
-
syncBadge.className = 'badge
|
|
1026
|
-
syncBadge.innerHTML = '<span class="dot"></span>
|
|
1080
|
+
syncBadge.className = 'badge';
|
|
1081
|
+
syncBadge.innerHTML = '<span class="dot"></span> Ready';
|
|
1027
1082
|
saveAllBtn.addEventListener('click', function () {
|
|
1028
1083
|
['mode-save','emb-save','stor-save','forg-save','trust-save','rl-save','evo-save','bk-save','dmn-save']
|
|
1029
1084
|
.forEach(function (i) { var b = q(i); if (b) b.click(); });
|
|
1030
1085
|
saveBackupConfig();
|
|
1031
|
-
syncBadge.className = 'badge
|
|
1032
|
-
syncBadge.innerHTML = '<span class="dot"></span>
|
|
1033
|
-
setTimeout(function () { syncBadge.innerHTML = '<span class="dot"></span> All synced'; }, 3000);
|
|
1086
|
+
syncBadge.className = 'badge';
|
|
1087
|
+
syncBadge.innerHTML = '<span class="dot"></span> Check each section';
|
|
1034
1088
|
});
|
|
1035
1089
|
discardBtn.addEventListener('click', function () { loadAll(); });
|
|
1036
1090
|
foot.appendChild(saveAllBtn); foot.appendChild(discardBtn); foot.appendChild(syncBadge);
|