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.
- package/CHANGELOG.md +112 -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 +2 -2
- 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 +3 -5
- 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 +2 -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 +3 -5
- 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/scripts/postinstall.js +7 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/commands.py +494 -9
- package/src/superlocalmemory/cli/daemon.py +7 -0
- package/src/superlocalmemory/cli/loop_cmd.py +2 -7
- package/src/superlocalmemory/cli/main.py +72 -7
- package/src/superlocalmemory/cli/setup_wizard.py +142 -16
- package/src/superlocalmemory/cli/version_banner.py +17 -3
- package/src/superlocalmemory/core/backend_orchestrator.py +18 -16
- package/src/superlocalmemory/core/component_healer.py +144 -0
- package/src/superlocalmemory/core/component_registry.py +487 -0
- package/src/superlocalmemory/core/config.py +21 -0
- package/src/superlocalmemory/core/embedding_worker.py +4 -5
- package/src/superlocalmemory/core/embeddings.py +132 -45
- package/src/superlocalmemory/core/engine.py +29 -22
- package/src/superlocalmemory/core/engine_ingestion.py +332 -45
- package/src/superlocalmemory/core/ingestion_command.py +154 -25
- package/src/superlocalmemory/core/injection.py +12 -7
- package/src/superlocalmemory/core/maintenance.py +43 -0
- package/src/superlocalmemory/core/maintenance_scheduler.py +44 -6
- package/src/superlocalmemory/core/recall_pipeline.py +42 -4
- package/src/superlocalmemory/core/store_pipeline.py +195 -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/_daemon_proxy.py +6 -2
- package/src/superlocalmemory/mcp/_pool_adapter.py +4 -1
- package/src/superlocalmemory/mcp/server.py +11 -30
- package/src/superlocalmemory/mcp/tools_active.py +1 -1
- package/src/superlocalmemory/mcp/tools_core.py +21 -5
- package/src/superlocalmemory/mcp/tools_learning.py +2 -2
- package/src/superlocalmemory/retrieval/bridge_discovery.py +14 -0
- package/src/superlocalmemory/retrieval/engine.py +53 -21
- package/src/superlocalmemory/retrieval/reranker.py +3 -4
- package/src/superlocalmemory/retrieval/spreading_activation.py +68 -38
- 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 +342 -61
- 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 +286 -105
- package/src/superlocalmemory/server/routes/learning_telemetry.py +153 -0
- package/src/superlocalmemory/server/routes/memories.py +8 -3
- 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 +184 -20
- package/src/superlocalmemory/server/unified_daemon.py +732 -41
- package/src/superlocalmemory/storage/embedding_migrator.py +235 -0
- 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 +6 -8
- 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-components.js +147 -0
- package/src/superlocalmemory/ui/js/od-entities.js +65 -22
- package/src/superlocalmemory/ui/js/od-graph.js +46 -4
- package/src/superlocalmemory/ui/js/od-health.js +18 -0
- package/src/superlocalmemory/ui/js/od-memories.js +84 -5
- package/src/superlocalmemory/ui/js/od-mesh.js +23 -9
- package/src/superlocalmemory/ui/js/od-operations.js +36 -0
- package/src/superlocalmemory/ui/js/od-settings.js +186 -63
- 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
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
var GRAPH_URL = '/api/graph';
|
|
17
17
|
var RECALL_URL = '/api/search'; /* POST {query, limit} -> {results:[...]} */
|
|
18
18
|
var MAX_NODES = 120; /* default budget; slider range 20-2000 */
|
|
19
|
+
/* Keep force-layout work bounded even when a user asks to render "All". */
|
|
20
|
+
var PHYSICS_MAX_NODES = 160;
|
|
21
|
+
var PRE_SETTLE_TICKS = 24;
|
|
19
22
|
|
|
20
23
|
/* ---- 16-colour categorical palette (Tableau-inspired, accessible) ---- */
|
|
21
24
|
var CAT_PALETTE = [
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
var raf = null, running = false, fitFrames = 0;
|
|
42
45
|
/* settle-freeze: stop the rAF loop once kinetic energy drops below SETTLE_KE */
|
|
43
46
|
var frames = 0, _ke = 0;
|
|
44
|
-
var SETTLE_MIN =
|
|
47
|
+
var SETTLE_MIN = 30, SETTLE_KE = 0.2, SETTLE_MAX_FRAMES = 180;
|
|
45
48
|
var NODES = [], LINKS = [], idx = {};
|
|
46
49
|
var tierVisible = { 1: true, 2: true, 3: true }; /* independent tier toggles */
|
|
47
50
|
var scale = 1, ox = 0, oy = 0, dpr = Math.max(1, window.devicePixelRatio || 1);
|
|
@@ -90,6 +93,31 @@
|
|
|
90
93
|
'<div class="inspector-empty"><div style="font-size:34px;margin-bottom:8px">◇</div>' +
|
|
91
94
|
'Loading your knowledge graph…</div>' +
|
|
92
95
|
'</div>' +
|
|
96
|
+
// Quick Insight Actions — restored: 5 data-insight-action buttons + #insight-results.
|
|
97
|
+
// quick-actions.js (global, non-IIFE) defines fetchInsight(action) which calls
|
|
98
|
+
// GET /api/v3/insights/{action} and writes into #insight-results.
|
|
99
|
+
// Delegation wired in wireControls() so buttons survive re-renders.
|
|
100
|
+
'<div id="odg-insights-panel" style="border-top:1px solid var(--border);' +
|
|
101
|
+
'padding:10px 12px 8px">' +
|
|
102
|
+
'<div style="font-size:10.5px;font-weight:600;color:var(--fg-3);' +
|
|
103
|
+
'text-transform:uppercase;letter-spacing:0.06em;margin-bottom:7px">' +
|
|
104
|
+
'Quick Insights</div>' +
|
|
105
|
+
'<div style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:7px">' +
|
|
106
|
+
'<button class="chip" style="font-size:11px;padding:3px 9px;cursor:pointer" ' +
|
|
107
|
+
'data-insight-action="changed_this_week">Changed This Week</button>' +
|
|
108
|
+
'<button class="chip" style="font-size:11px;padding:3px 9px;cursor:pointer" ' +
|
|
109
|
+
'data-insight-action="opinions">Opinions</button>' +
|
|
110
|
+
'<button class="chip" style="font-size:11px;padding:3px 9px;cursor:pointer" ' +
|
|
111
|
+
'data-insight-action="contradictions">Contradictions</button>' +
|
|
112
|
+
'<button class="chip" style="font-size:11px;padding:3px 9px;cursor:pointer" ' +
|
|
113
|
+
'data-insight-action="health">Memory Health</button>' +
|
|
114
|
+
'<button class="chip" style="font-size:11px;padding:3px 9px;cursor:pointer" ' +
|
|
115
|
+
'data-insight-action="cross_project">Cross-Project</button>' +
|
|
116
|
+
'</div>' +
|
|
117
|
+
// #insight-results — quick-actions.js writes here via document.getElementById
|
|
118
|
+
'<div id="insight-results" ' +
|
|
119
|
+
'style="max-height:200px;overflow-y:auto;font-size:12px"></div>' +
|
|
120
|
+
'</div>' +
|
|
93
121
|
'<div class="ask">' +
|
|
94
122
|
'<div class="ask-head"><span data-ic="brain"></span> Ask your memory</div>' +
|
|
95
123
|
'<div class="ask-log" id="odg-log">' +
|
|
@@ -223,7 +251,11 @@
|
|
|
223
251
|
|
|
224
252
|
/* ---------------- physics (ported from design) ---------------- */
|
|
225
253
|
function tick() {
|
|
226
|
-
|
|
254
|
+
/* Force repulsion is O(n^2). Render every requested node, but only
|
|
255
|
+
simulate a stable bounded subset so the dashboard remains interactive
|
|
256
|
+
for existing installs with very large graphs. */
|
|
257
|
+
var vis = NODES.filter(visible).slice(0, PHYSICS_MAX_NODES), simulated = {}, i, j;
|
|
258
|
+
vis.forEach(function (n) { simulated[n.id] = true; });
|
|
227
259
|
for (i = 0; i < vis.length; i++) {
|
|
228
260
|
var a = vis[i];
|
|
229
261
|
for (j = i + 1; j < vis.length; j++) {
|
|
@@ -234,7 +266,7 @@
|
|
|
234
266
|
}
|
|
235
267
|
}
|
|
236
268
|
LINKS.forEach(function (l) {
|
|
237
|
-
if (!visLink(l)) return;
|
|
269
|
+
if (!visLink(l) || !simulated[l.s] || !simulated[l.t]) return;
|
|
238
270
|
var a = idx[l.s], b = idx[l.t];
|
|
239
271
|
var dx = b.x - a.x, dy = b.y - a.y, d = Math.sqrt(dx * dx + dy * dy) + 0.01;
|
|
240
272
|
var rest = (a.type === 'community' || b.type === 'community') ? 150 : 100;
|
|
@@ -495,6 +527,16 @@
|
|
|
495
527
|
q('#odg-send').onclick = sendAsk;
|
|
496
528
|
q('#odg-ask').addEventListener('keydown', function (e) { if (e.key === 'Enter') sendAsk(); });
|
|
497
529
|
|
|
530
|
+
// Quick Insight Actions — delegated on mount so buttons survive od-graph.js re-renders.
|
|
531
|
+
// quick-actions.js:fetchInsight() is global (non-IIFE); it writes into #insight-results
|
|
532
|
+
// which is mounted inside odg-insights-panel above.
|
|
533
|
+
mount.addEventListener('click', function (e) {
|
|
534
|
+
var btn = e.target.closest('[data-insight-action]');
|
|
535
|
+
if (btn && typeof fetchInsight === 'function') {
|
|
536
|
+
fetchInsight(btn.getAttribute('data-insight-action'));
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
498
540
|
cv.addEventListener('mousedown', function (e) {
|
|
499
541
|
var r = cv.getBoundingClientRect(), n = nodeAt(e.clientX - r.left, e.clientY - r.top);
|
|
500
542
|
if (n) { dragNode = n; selected = n; renderInsp(n); wake(); } else { panning = true; }
|
|
@@ -557,7 +599,7 @@
|
|
|
557
599
|
running = false; draw(); updateCount(); updateLegend(); return;
|
|
558
600
|
}
|
|
559
601
|
readPalette(); resize(); updateCount(); updateLegend(); selected = null; setInspectorEmpty();
|
|
560
|
-
for (var i = 0; i <
|
|
602
|
+
for (var i = 0; i < PRE_SETTLE_TICKS; i++) tick(); /* bounded pre-settle keeps navigation responsive */
|
|
561
603
|
fitFrames = 0; frames = 0;
|
|
562
604
|
if (!running) { running = true; loop(); } else { wake(); }
|
|
563
605
|
}).catch(function (err) {
|
|
@@ -183,6 +183,20 @@
|
|
|
183
183
|
'</tbody>' +
|
|
184
184
|
'</table>' +
|
|
185
185
|
'</div>' +
|
|
186
|
+
'</div>' +
|
|
187
|
+
|
|
188
|
+
// System Health — Components (v3.8.2 UX-6): what's installed vs missing,
|
|
189
|
+
// with copy-paste fix commands + a Retry-now button. The daemon
|
|
190
|
+
// auto-heals fixable items on start; this is transparency + fallback.
|
|
191
|
+
// Rendered by od-components.js → window.odRenderComponents.
|
|
192
|
+
'<div class="card" style="margin-top:16px">' +
|
|
193
|
+
'<div class="card-head">' +
|
|
194
|
+
'<h3>System Health — Components</h3>' +
|
|
195
|
+
'<span class="sub">models & dependencies · self-healing</span>' +
|
|
196
|
+
'</div>' +
|
|
197
|
+
'<div class="card-pad" id="od-h-components">' +
|
|
198
|
+
'<div class="dim" style="text-align:center;padding:20px">Loading…</div>' +
|
|
199
|
+
'</div>' +
|
|
186
200
|
'</div>'
|
|
187
201
|
);
|
|
188
202
|
}
|
|
@@ -515,6 +529,10 @@
|
|
|
515
529
|
populateTrustSignals(trustStats);
|
|
516
530
|
populateAgentSummary(agentsData, eventsStats);
|
|
517
531
|
populateAgentTable(agentsData);
|
|
532
|
+
// v3.8.2 UX-6: component "what's missing" report (od-components.js).
|
|
533
|
+
if (typeof window.odRenderComponents === 'function') {
|
|
534
|
+
window.odRenderComponents(document.getElementById('od-h-components'));
|
|
535
|
+
}
|
|
518
536
|
|
|
519
537
|
}).catch(function (err) {
|
|
520
538
|
container.innerHTML =
|
|
@@ -106,6 +106,8 @@
|
|
|
106
106
|
catCounts: {}, // pre-fetched per-category totals
|
|
107
107
|
tlRange: '30d',
|
|
108
108
|
cluLoaded: false, // reset in render() to avoid stale state on re-entry
|
|
109
|
+
searchQ: null,
|
|
110
|
+
requestSeq: 0,
|
|
109
111
|
};
|
|
110
112
|
|
|
111
113
|
// Known categories from the daemon (pre-fetched at render time)
|
|
@@ -119,7 +121,7 @@
|
|
|
119
121
|
var id = 'od-mem-' + Math.random().toString(36).slice(2, 8);
|
|
120
122
|
_st = Object.assign({}, _st, {
|
|
121
123
|
rootId: id, page: 0, category: null, sort: 'created',
|
|
122
|
-
memories: [], catCounts: {}, cluLoaded: false,
|
|
124
|
+
memories: [], catCounts: {}, cluLoaded: false, searchQ: null, requestSeq: 0,
|
|
123
125
|
});
|
|
124
126
|
// Clear per-id timeline cache for this render instance
|
|
125
127
|
_tlLoaded = {};
|
|
@@ -145,6 +147,9 @@
|
|
|
145
147
|
'<button class="tab" data-od-act="tab" data-tab="timeline">Creation timeline</button>' +
|
|
146
148
|
'<button class="tab" data-od-act="tab" data-tab="clusters">' +
|
|
147
149
|
'Knowledge clusters <span class="cnt" id="' + id + '-cnt-clusters">…</span></button>' +
|
|
150
|
+
// Recall Lab tab — restored: recall-lab.js is already loaded in index.html and uses
|
|
151
|
+
// document-level click/keydown delegation keyed on IDs below, so it survives re-renders.
|
|
152
|
+
'<button class="tab" data-od-act="tab" data-tab="recall">Recall Lab</button>' +
|
|
148
153
|
'</div>' +
|
|
149
154
|
'<div class="tabpane active" id="' + id + '-pane-all">' + _allScaffold(id) + '</div>' +
|
|
150
155
|
'<div class="tabpane" id="' + id + '-pane-timeline">' + _tlScaffold(id) + '</div>' +
|
|
@@ -153,6 +158,40 @@
|
|
|
153
158
|
_loading('Loading clusters…') +
|
|
154
159
|
'</div>' +
|
|
155
160
|
'</div>' +
|
|
161
|
+
// Recall Lab pane — exact IDs required by recall-lab.js:
|
|
162
|
+
// #recall-lab-query (input), #recall-lab-search (button — click check),
|
|
163
|
+
// #recall-lab-per-page (select, optional), #recall-lab-meta, #recall-lab-results.
|
|
164
|
+
// Backend: POST /api/v3/recall/trace
|
|
165
|
+
'<div class="tabpane" id="' + id + '-pane-recall" style="padding-top:12px">' +
|
|
166
|
+
'<div style="margin-bottom:14px">' +
|
|
167
|
+
'<p style="font-size:13px;color:var(--fg-2);margin-bottom:10px">' +
|
|
168
|
+
'Trace how a recall query is resolved — matching algorithm, scoring, and final result set.' +
|
|
169
|
+
'</p>' +
|
|
170
|
+
'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">' +
|
|
171
|
+
'<input id="recall-lab-query" placeholder="Enter recall query…" autocomplete="off" ' +
|
|
172
|
+
'style="flex:1;min-width:200px;padding:8px 12px;' +
|
|
173
|
+
'border:1px solid var(--border);border-radius:6px;font-size:13px;' +
|
|
174
|
+
'background:var(--bg-2);color:var(--fg)">' +
|
|
175
|
+
'<select id="recall-lab-per-page" ' +
|
|
176
|
+
'style="padding:7px 10px;border:1px solid var(--border);border-radius:6px;' +
|
|
177
|
+
'font-size:13px;background:var(--bg-2);color:var(--fg)">' +
|
|
178
|
+
'<option value="5">5</option>' +
|
|
179
|
+
'<option value="10" selected>10</option>' +
|
|
180
|
+
'<option value="20">20</option>' +
|
|
181
|
+
'<option value="50">50</option>' +
|
|
182
|
+
'</select>' +
|
|
183
|
+
'<button id="recall-lab-search" ' +
|
|
184
|
+
'style="padding:8px 18px;background:var(--accent);color:#fff;' +
|
|
185
|
+
'border:none;border-radius:6px;font-size:13px;cursor:pointer;' +
|
|
186
|
+
'white-space:nowrap">Run Trace</button>' +
|
|
187
|
+
'</div>' +
|
|
188
|
+
'</div>' +
|
|
189
|
+
// #recall-lab-meta — written by recall-lab.js before results (timing, count, etc.)
|
|
190
|
+
'<div id="recall-lab-meta" ' +
|
|
191
|
+
'style="font-size:12px;color:var(--fg-2);margin-bottom:8px"></div>' +
|
|
192
|
+
// #recall-lab-results — written by recall-lab.js (result cards, pagination)
|
|
193
|
+
'<div id="recall-lab-results"></div>' +
|
|
194
|
+
'</div>' +
|
|
156
195
|
'</div>'
|
|
157
196
|
);
|
|
158
197
|
}
|
|
@@ -306,6 +345,8 @@
|
|
|
306
345
|
if (_st.scopeView && _st.scopeView !== 'mine') {
|
|
307
346
|
url += '&scope=' + encodeURIComponent(_st.scopeView);
|
|
308
347
|
}
|
|
348
|
+
var requestSeq = _st.requestSeq + 1;
|
|
349
|
+
_st = Object.assign({}, _st, { requestSeq: requestSeq });
|
|
309
350
|
var wrap = document.getElementById(id + '-tbl-wrap');
|
|
310
351
|
if (wrap) wrap.innerHTML = _loading('Loading memories…');
|
|
311
352
|
|
|
@@ -315,10 +356,12 @@
|
|
|
315
356
|
return r.json();
|
|
316
357
|
})
|
|
317
358
|
.then(function (d) {
|
|
359
|
+
if (_st.rootId !== id || _st.requestSeq !== requestSeq) return;
|
|
318
360
|
_st = Object.assign({}, _st, { memories: d.memories || [], total: d.total || 0 });
|
|
319
361
|
_renderTable(id, d);
|
|
320
362
|
})
|
|
321
363
|
.catch(function (err) {
|
|
364
|
+
if (_st.rootId !== id || _st.requestSeq !== requestSeq) return;
|
|
322
365
|
var w = document.getElementById(id + '-tbl-wrap');
|
|
323
366
|
if (w) w.innerHTML = '<div class="card-pad" style="color:var(--danger);text-align:center">' +
|
|
324
367
|
'Failed to load memories: ' + _esc(err.message) + '</div>';
|
|
@@ -442,22 +485,38 @@
|
|
|
442
485
|
var _searchTimer = null;
|
|
443
486
|
function _doSearch(id, q) {
|
|
444
487
|
clearTimeout(_searchTimer);
|
|
488
|
+
var requestSeq = _st.requestSeq + 1;
|
|
489
|
+
// /api/search is a full-corpus semantic query. It cannot truthfully retain
|
|
490
|
+
// the list endpoint's category/scope selection, so clear those controls
|
|
491
|
+
// before the request rather than leaving the table and chips disagreeing.
|
|
492
|
+
_st = Object.assign({}, _st, {
|
|
493
|
+
searchQ: q || null,
|
|
494
|
+
category: q ? null : _st.category,
|
|
495
|
+
scopeView: q ? 'mine' : _st.scopeView,
|
|
496
|
+
page: 0,
|
|
497
|
+
requestSeq: requestSeq,
|
|
498
|
+
});
|
|
499
|
+
if (q) _rebuildCatBar(id);
|
|
445
500
|
_searchTimer = setTimeout(function () {
|
|
446
|
-
|
|
501
|
+
if (_st.rootId !== id || _st.requestSeq !== requestSeq) return;
|
|
447
502
|
if (!q) { _loadMem(id); return; }
|
|
448
503
|
var wrap = document.getElementById(id + '-tbl-wrap');
|
|
449
504
|
if (wrap) wrap.innerHTML = _loading('Searching…');
|
|
450
505
|
fetch('/api/search', { method: 'POST', credentials: 'same-origin',
|
|
451
506
|
headers: { 'Content-Type': 'application/json' },
|
|
507
|
+
slmInvalidatesCache: false,
|
|
508
|
+
slmRequiresWriteAuth: false,
|
|
452
509
|
body: JSON.stringify({ query: q, limit: 50, window: _st.window || '' })
|
|
453
510
|
}).then(function (r) {
|
|
454
511
|
if (!r.ok) throw new Error('HTTP ' + r.status);
|
|
455
512
|
return r.json();
|
|
456
513
|
}).then(function (d) {
|
|
514
|
+
if (_st.rootId !== id || _st.requestSeq !== requestSeq) return;
|
|
457
515
|
var mems = (d.results || []).map(_normSearch);
|
|
458
516
|
_st = Object.assign({}, _st, { memories: mems, total: mems.length });
|
|
459
517
|
_renderTable(id, { memories: mems, total: mems.length, limit: mems.length, offset: 0 });
|
|
460
518
|
}).catch(function (err) {
|
|
519
|
+
if (_st.rootId !== id || _st.requestSeq !== requestSeq) return;
|
|
461
520
|
var w = document.getElementById(id + '-tbl-wrap');
|
|
462
521
|
if (w) w.innerHTML = '<div class="card-pad" style="color:var(--danger);text-align:center;' +
|
|
463
522
|
'padding:24px">Search failed: ' + _esc(err.message) + '</div>';
|
|
@@ -760,7 +819,13 @@
|
|
|
760
819
|
if (bar) bar.querySelectorAll('[data-od-act="cat"]').forEach(function (c) {
|
|
761
820
|
c.classList.toggle('on', c.dataset.cat === el.dataset.cat);
|
|
762
821
|
});
|
|
763
|
-
|
|
822
|
+
clearTimeout(_searchTimer);
|
|
823
|
+
_st = Object.assign({}, _st, {
|
|
824
|
+
category: el.dataset.cat || null, page: 0, searchQ: null,
|
|
825
|
+
requestSeq: _st.requestSeq + 1,
|
|
826
|
+
});
|
|
827
|
+
var searchInput = document.querySelector('#' + id + ' [data-od-act="search"]');
|
|
828
|
+
if (searchInput) searchInput.value = '';
|
|
764
829
|
_loadMem(id);
|
|
765
830
|
return;
|
|
766
831
|
}
|
|
@@ -778,7 +843,13 @@
|
|
|
778
843
|
if (sroot) sroot.querySelectorAll('[data-od-act="scope-view"]').forEach(function (b) {
|
|
779
844
|
b.classList.toggle('active', b.dataset.scope === el.dataset.scope);
|
|
780
845
|
});
|
|
781
|
-
|
|
846
|
+
clearTimeout(_searchTimer);
|
|
847
|
+
_st = Object.assign({}, _st, {
|
|
848
|
+
scopeView: el.dataset.scope, page: 0, searchQ: null,
|
|
849
|
+
requestSeq: _st.requestSeq + 1,
|
|
850
|
+
});
|
|
851
|
+
var scopedSearchInput = document.querySelector('#' + id + ' [data-od-act="search"]');
|
|
852
|
+
if (scopedSearchInput) scopedSearchInput.value = '';
|
|
782
853
|
_loadMem(id);
|
|
783
854
|
return;
|
|
784
855
|
}
|
|
@@ -791,7 +862,15 @@
|
|
|
791
862
|
if (act === 'close-drawer') { _closeDrawer(); return; }
|
|
792
863
|
if (act === 'pg') {
|
|
793
864
|
var pg = parseInt(el.dataset.page, 10);
|
|
794
|
-
if (!isNaN(pg)) {
|
|
865
|
+
if (!isNaN(pg)) {
|
|
866
|
+
clearTimeout(_searchTimer);
|
|
867
|
+
_st = Object.assign({}, _st, {
|
|
868
|
+
page: pg, searchQ: null, requestSeq: _st.requestSeq + 1,
|
|
869
|
+
});
|
|
870
|
+
var pagedSearchInput = document.querySelector('#' + id + ' [data-od-act="search"]');
|
|
871
|
+
if (pagedSearchInput) pagedSearchInput.value = '';
|
|
872
|
+
_loadMem(id);
|
|
873
|
+
}
|
|
795
874
|
return;
|
|
796
875
|
}
|
|
797
876
|
if (act === 'expand-cluster') { _expandCluster(id, el.dataset.cid); return; }
|
|
@@ -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
|
|
@@ -96,6 +96,9 @@
|
|
|
96
96
|
'<button class="tab" data-od-tab="trust">Trust</button>' +
|
|
97
97
|
'<button class="tab" data-od-tab="compliance">Compliance</button>' +
|
|
98
98
|
'<button class="tab" data-od-tab="loops">Bounded Loops</button>' +
|
|
99
|
+
// Ingestion tab — restored: re-mounts #ingestion-overview + #ingestion-adapters
|
|
100
|
+
// so ng-ingestion.js (loaded in index.html) can populate them on call.
|
|
101
|
+
'<button class="tab" data-od-tab="ingestion">Ingestion</button>' +
|
|
99
102
|
'</div>' +
|
|
100
103
|
|
|
101
104
|
// ══════ LIFECYCLE TAB ══════════════════════════════════════════════
|
|
@@ -427,6 +430,31 @@
|
|
|
427
430
|
'</div>' +
|
|
428
431
|
'</div>' +
|
|
429
432
|
|
|
433
|
+
'</section>' +
|
|
434
|
+
|
|
435
|
+
// ══════ INGESTION TAB ═════════════════════════════════════════════
|
|
436
|
+
// Restoration: mounts #ingestion-overview and #ingestion-adapters so that
|
|
437
|
+
// window.loadIngestionStatus() (ng-ingestion.js) can populate them.
|
|
438
|
+
// ng-ingestion.js is loaded in index.html and defines loadIngestionStatus().
|
|
439
|
+
// Adapter action buttons use data-act-click="adapter-action" which is handled
|
|
440
|
+
// by event-delegation.js → adapterAction() defined in ng-ingestion.js.
|
|
441
|
+
// Backend: GET /api/adapters · POST /api/adapters/{enable|disable|start|stop}
|
|
442
|
+
'<section class="tabpane" data-od-pane="ingestion">' +
|
|
443
|
+
|
|
444
|
+
'<div class="page-head" style="margin-bottom:16px">' +
|
|
445
|
+
'<h2 style="font-size:20px;margin-bottom:6px">Connectors & Ingestion</h2>' +
|
|
446
|
+
'<p style="font-size:13.5px">' +
|
|
447
|
+
'Gmail, Calendar, and Transcript adapters — enable to start building memory ' +
|
|
448
|
+
'from external sources. All processing is local.' +
|
|
449
|
+
'</p>' +
|
|
450
|
+
'</div>' +
|
|
451
|
+
|
|
452
|
+
// #ingestion-overview — ng-ingestion.js: 4 stat cards (Available/Enabled/Running/Privacy)
|
|
453
|
+
'<div id="ingestion-overview"></div>' +
|
|
454
|
+
|
|
455
|
+
// #ingestion-adapters — ng-ingestion.js: adapter cards (Gmail/Calendar/Transcript)
|
|
456
|
+
'<div id="ingestion-adapters" style="margin-top:16px"></div>' +
|
|
457
|
+
|
|
430
458
|
'</section>'
|
|
431
459
|
);
|
|
432
460
|
}
|
|
@@ -1227,6 +1255,14 @@
|
|
|
1227
1255
|
window.odRenderTeam(document.getElementById('od-team-mount'));
|
|
1228
1256
|
}
|
|
1229
1257
|
|
|
1258
|
+
// Ingestion (Gmail / Calendar / Transcript) — re-linked to ng-ingestion.js.
|
|
1259
|
+
// #ingestion-overview and #ingestion-adapters are now in the skeleton DOM
|
|
1260
|
+
// (inside the Ingestion tab section); calling loadIngestionStatus() renders them.
|
|
1261
|
+
// The function is null-guarded: no-op if ng-ingestion.js failed to load.
|
|
1262
|
+
if (typeof window.loadIngestionStatus === 'function') {
|
|
1263
|
+
window.loadIngestionStatus();
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1230
1266
|
}).catch(function (err) {
|
|
1231
1267
|
container.innerHTML =
|
|
1232
1268
|
'<div style="color:var(--danger);text-align:center;padding:48px 0">' +
|