superlocalmemory 4.0.0 → 4.0.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 +35 -0
- package/README.md +11 -11
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- 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-governance/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-loop/SKILL.md +1 -1
- package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
- package/plugin-src/skills/slm-profile/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-scope/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 +5 -4
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/commands.py +60 -1
- package/src/superlocalmemory/cli/main.py +24 -1
- package/src/superlocalmemory/compliance/gdpr.py +104 -73
- package/src/superlocalmemory/contracts/__init__.py +1 -0
- package/src/superlocalmemory/contracts/schemas/agent-experience-v1.schema.json +92 -0
- package/src/superlocalmemory/contracts/schemas/agent-integration-contract-v2.schema.json +46 -0
- package/src/superlocalmemory/contracts/schemas/cognitive-turn-receipt-v1.schema.json +59 -0
- package/src/superlocalmemory/contracts/v402.py +62 -0
- package/src/superlocalmemory/core/engine.py +10 -0
- package/src/superlocalmemory/core/recall_pipeline.py +6 -0
- package/src/superlocalmemory/core/recall_worker.py +12 -0
- package/src/superlocalmemory/core/worker_pool.py +12 -0
- package/src/superlocalmemory/hooks/hook_handlers.py +16 -0
- package/src/superlocalmemory/hooks/post_tool_outcome_hook.py +12 -6
- package/src/superlocalmemory/hooks/session_registry.py +136 -3
- package/src/superlocalmemory/hooks/user_prompt_hook.py +9 -2
- package/src/superlocalmemory/integrations/__init__.py +1 -0
- package/src/superlocalmemory/integrations/bounded_loops_v051.py +236 -0
- package/src/superlocalmemory/learning/database.py +21 -14
- package/src/superlocalmemory/mcp/_daemon_proxy.py +9 -0
- package/src/superlocalmemory/mcp/server.py +5 -0
- package/src/superlocalmemory/mcp/tools_brain.py +132 -0
- package/src/superlocalmemory/mcp/tools_core.py +25 -6
- package/src/superlocalmemory/mcp/tools_v3.py +16 -2
- package/src/superlocalmemory/retrieval/engine.py +43 -1
- package/src/superlocalmemory/retrieval/temporal_utils.py +16 -1
- package/src/superlocalmemory/retrieval/temporal_validity_filter.py +151 -0
- package/src/superlocalmemory/server/routes/brain.py +206 -1
- package/src/superlocalmemory/server/routes/helpers.py +53 -35
- package/src/superlocalmemory/server/routes/v3_api.py +118 -11
- package/src/superlocalmemory/server/ui.py +7 -2
- package/src/superlocalmemory/server/unified_daemon.py +37 -4
- package/src/superlocalmemory/storage/_migration_internals.py +4 -0
- package/src/superlocalmemory/storage/_schema_version.py +2 -2
- package/src/superlocalmemory/storage/agent_experience.py +490 -0
- package/src/superlocalmemory/storage/database.py +189 -34
- package/src/superlocalmemory/storage/migration_runner.py +8 -0
- package/src/superlocalmemory/storage/migrations/M015_add_pinned_column.py +18 -0
- package/src/superlocalmemory/storage/migrations/M040_agent_experience_receipts.py +254 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +2 -0
- package/src/superlocalmemory/storage/schema.py +4 -0
- package/src/superlocalmemory/ui/index.html +2 -2
- package/src/superlocalmemory/ui/js/auto-settings.js +18 -14
- package/src/superlocalmemory/ui/js/brain.js +57 -1
- package/src/superlocalmemory/ui/js/od-brain.js +114 -40
- package/src/superlocalmemory/ui/js/od-settings.js +8 -1
|
@@ -222,19 +222,20 @@ async function loadModeSettings() {
|
|
|
222
222
|
// Show provider panel and populate model dropdown
|
|
223
223
|
updateModeUI();
|
|
224
224
|
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
// user
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
225
|
+
// GET /mode deliberately exposes only a redacted endpoint host.
|
|
226
|
+
// updateProviderUI() sets a provider default as an editable value;
|
|
227
|
+
// clear it after a reload and expose the redacted host as a hint only.
|
|
228
|
+
// Otherwise a user who changes nothing can overwrite a full saved URL
|
|
229
|
+
// with the host-only display value on the next Save.
|
|
230
|
+
setTimeout(function() {
|
|
231
|
+
var epEl = document.getElementById('settings-endpoint');
|
|
232
|
+
if (epEl) {
|
|
233
|
+
epEl.value = '';
|
|
234
|
+
epEl.placeholder = data.endpoint || 'Custom endpoint (optional)';
|
|
235
|
+
}
|
|
236
|
+
var epRow = document.getElementById('settings-endpoint-row');
|
|
237
|
+
if (epRow && data.endpoint) epRow.style.display = 'block';
|
|
238
|
+
}, 0);
|
|
238
239
|
|
|
239
240
|
// After provider UI updates, set the saved model value
|
|
240
241
|
if (model) {
|
|
@@ -432,7 +433,10 @@ async function saveAllSettings() {
|
|
|
432
433
|
try {
|
|
433
434
|
// V3.4.24: Include embedding params in save payload
|
|
434
435
|
var embParams = getEmbeddingParams();
|
|
435
|
-
|
|
436
|
+
// Password-style fields reload blank. Omit blanks so the API can
|
|
437
|
+
// distinguish "unchanged" from an explicit clear request.
|
|
438
|
+
var payload = Object.assign({mode: mode, provider: provider, model: model}, embParams);
|
|
439
|
+
if (apiKey) payload.api_key = apiKey;
|
|
436
440
|
if (endpoint) { payload.base_url = endpoint; payload.endpoint = endpoint; }
|
|
437
441
|
var modeResp = await fetch('/api/v3/mode/set', {
|
|
438
442
|
method: 'POST',
|
|
@@ -825,7 +825,11 @@
|
|
|
825
825
|
function cardCrossPlatform(cp) {
|
|
826
826
|
const data = cp || {};
|
|
827
827
|
const wrap = EL('section', {className: 'brain-section'});
|
|
828
|
-
wrap.appendChild(EL('h4', {text: '
|
|
828
|
+
wrap.appendChild(EL('h4', {text: 'Configured integrations'}));
|
|
829
|
+
wrap.appendChild(EL('p', {
|
|
830
|
+
className: 'brain-help',
|
|
831
|
+
text: 'Installation and sync availability. Recent client activity is shown separately in Living Brain.',
|
|
832
|
+
}));
|
|
829
833
|
|
|
830
834
|
const grid = EL('div', {className: 'brain-adapter-grid'});
|
|
831
835
|
const order = [
|
|
@@ -1096,6 +1100,7 @@
|
|
|
1096
1100
|
function renderAll(brain, behavioral) {
|
|
1097
1101
|
const b = brain || {};
|
|
1098
1102
|
const nodes = [
|
|
1103
|
+
cardLivingBrain(b.living_brain),
|
|
1099
1104
|
cardLearning(b.learning),
|
|
1100
1105
|
];
|
|
1101
1106
|
// Only shows when there are legacy rows pending migration; hidden
|
|
@@ -1125,6 +1130,57 @@
|
|
|
1125
1130
|
return nodes;
|
|
1126
1131
|
}
|
|
1127
1132
|
|
|
1133
|
+
// --------------------------------------------------------------------
|
|
1134
|
+
// Card: Living Brain — portable evidence, never an automatic control
|
|
1135
|
+
// --------------------------------------------------------------------
|
|
1136
|
+
function cardLivingBrain(snapshot) {
|
|
1137
|
+
const data = snapshot || {};
|
|
1138
|
+
const feedback = data.feedback || {};
|
|
1139
|
+
const clients = (data.connected_clients || {}).clients || [];
|
|
1140
|
+
const quality = data.source_quality || {};
|
|
1141
|
+
const graph = data.graph || {};
|
|
1142
|
+
const wrap = EL('section', {className: 'brain-section'});
|
|
1143
|
+
wrap.appendChild(EL('h4', {text: 'Living Brain'}));
|
|
1144
|
+
wrap.appendChild(EL('p', {
|
|
1145
|
+
className: 'brain-help',
|
|
1146
|
+
text: 'A local evidence view of what SLM has observed. It does not automatically change your model, tools, or memories.',
|
|
1147
|
+
}));
|
|
1148
|
+
|
|
1149
|
+
const clientText = clients.length
|
|
1150
|
+
? clients.map((client) => {
|
|
1151
|
+
const seconds = Number(client.last_seen_seconds_ago || 0);
|
|
1152
|
+
return String(client.kind || 'other') + ' active ' + seconds + 's ago';
|
|
1153
|
+
}).join(' · ')
|
|
1154
|
+
: 'No client activity in the last 5 minutes';
|
|
1155
|
+
const grid = EL('div', {className: 'brain-stat-grid'});
|
|
1156
|
+
grid.appendChild(statRow('Recent clients', clientText));
|
|
1157
|
+
grid.appendChild(statRow('Explicit feedback', feedback.explicit_signals || 0));
|
|
1158
|
+
grid.appendChild(statRow('Implicit signals', feedback.implicit_signals || 0));
|
|
1159
|
+
grid.appendChild(statRow('Settled outcomes', feedback.settled_outcomes || 0));
|
|
1160
|
+
grid.appendChild(statRow(
|
|
1161
|
+
'Observed source quality',
|
|
1162
|
+
quality.mean_quality == null ? 'No evidence yet' : Number(quality.mean_quality).toFixed(3),
|
|
1163
|
+
));
|
|
1164
|
+
grid.appendChild(statRow('Sources with evidence', quality.observed_sources || 0));
|
|
1165
|
+
grid.appendChild(statRow('Memory graph nodes', graph.fact_nodes || 0));
|
|
1166
|
+
grid.appendChild(statRow('Memory graph edges', graph.association_edges || 0));
|
|
1167
|
+
wrap.appendChild(grid);
|
|
1168
|
+
|
|
1169
|
+
const byType = feedback.signals_by_type || {};
|
|
1170
|
+
const signalKinds = Object.keys(byType).sort();
|
|
1171
|
+
wrap.appendChild(EL('p', {
|
|
1172
|
+
className: 'brain-help',
|
|
1173
|
+
text: signalKinds.length
|
|
1174
|
+
? 'Signals: ' + signalKinds.map((kind) => kind + ' (' + Number(byType[kind] || 0) + ')').join(', ')
|
|
1175
|
+
: 'No feedback signals recorded yet. Report outcomes or use memory feedback to start the loop.',
|
|
1176
|
+
}));
|
|
1177
|
+
wrap.appendChild(badge(
|
|
1178
|
+
data.is_real ? 'real' : 'stub',
|
|
1179
|
+
data.source || 'local evidence',
|
|
1180
|
+
));
|
|
1181
|
+
return wrap;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1128
1184
|
// --------------------------------------------------------------------
|
|
1129
1185
|
// S9-DASH-05: live closed-loop tiles
|
|
1130
1186
|
// --------------------------------------------------------------------
|
|
@@ -32,12 +32,31 @@
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function fetchLivingBrain() {
|
|
36
|
+
// The canonical Living Brain read model is install-token gated. The
|
|
37
|
+
// browser obtains that token only from the local same-origin endpoint;
|
|
38
|
+
// it is never rendered, stored, or sent to another origin.
|
|
39
|
+
return apiFetch('/internal/token').then(function (tokenBody) {
|
|
40
|
+
var token = tokenBody && typeof tokenBody.token === 'string'
|
|
41
|
+
? tokenBody.token.trim() : '';
|
|
42
|
+
if (!token) throw new Error('/internal/token → missing token');
|
|
43
|
+
return fetch('/api/v3/brain', {
|
|
44
|
+
credentials: 'same-origin',
|
|
45
|
+
headers: { 'X-Install-Token': token },
|
|
46
|
+
}).then(function (r) {
|
|
47
|
+
if (!r.ok) throw new Error('/api/v3/brain → ' + r.status);
|
|
48
|
+
return r.json();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
35
53
|
function fetchAll() {
|
|
36
54
|
return Promise.all([
|
|
37
55
|
apiFetch('/api/learning/status'),
|
|
38
56
|
apiFetch('/api/behavioral/status'),
|
|
39
57
|
apiFetch('/api/behavioral/assertions?category=skill_performance&limit=50'),
|
|
40
58
|
apiFetch('/api/behavioral/tool-events?limit=500'),
|
|
59
|
+
fetchLivingBrain(),
|
|
41
60
|
]);
|
|
42
61
|
}
|
|
43
62
|
|
|
@@ -184,7 +203,7 @@
|
|
|
184
203
|
// ======================================================================
|
|
185
204
|
// Tab: OVERVIEW
|
|
186
205
|
// ======================================================================
|
|
187
|
-
function buildOverview(learning, behavioral, dateMap) {
|
|
206
|
+
function buildOverview(learning, behavioral, dateMap, living) {
|
|
188
207
|
var sec = EL('section', { className: 'tabpane active', 'data-p': 'overview' });
|
|
189
208
|
var stats = (learning && learning.stats) || {};
|
|
190
209
|
var eng = (learning && learning.engagement) || {};
|
|
@@ -208,6 +227,9 @@
|
|
|
208
227
|
var healthColor = healthStatus === 'HEALTHY' ? 'var(--ok)'
|
|
209
228
|
: healthStatus === 'ACTIVE' ? 'var(--cyan)' : undefined;
|
|
210
229
|
var pCount = ((beh.patterns) || []).length;
|
|
230
|
+
var feedback = (living && living.feedback) || {};
|
|
231
|
+
var graph = (living && living.graph) || {};
|
|
232
|
+
var experience = (living && living.agent_experience) || {};
|
|
211
233
|
|
|
212
234
|
// KPI strip
|
|
213
235
|
var strip = EL('div', { className: 'kpi-strip', style: 'margin-bottom:16px' });
|
|
@@ -291,6 +313,13 @@
|
|
|
291
313
|
['Models trained', String(stats.models_trained || 0)],
|
|
292
314
|
['Verified active models', String(stats.models_active_verified || 0)],
|
|
293
315
|
['Sources tracked', String(stats.tracked_sources || 0)],
|
|
316
|
+
['Explicit feedback', String(feedback.explicit_signals || 0)],
|
|
317
|
+
['Settled outcomes', String(feedback.settled_outcomes || 0)],
|
|
318
|
+
['Claimed evidence authority', String(experience.claimed_evidence_experiences || 0)],
|
|
319
|
+
['Cognitive turns', String(experience.turns_total || 0) +
|
|
320
|
+
' · ' + String((experience.turns_by_state || {}).finalized || 0) + ' finalized'],
|
|
321
|
+
['Graph evidence', String(graph.fact_nodes || 0) + ' nodes · ' +
|
|
322
|
+
String(graph.association_edges || 0) + ' edges'],
|
|
294
323
|
].forEach(function (row) {
|
|
295
324
|
var r = EL('div', { className: 'list-row' });
|
|
296
325
|
r.appendChild(EL('span', { className: 'muted', style: 'flex:1', text: row[0] }));
|
|
@@ -308,6 +337,35 @@
|
|
|
308
337
|
grid.appendChild(priv);
|
|
309
338
|
sec.appendChild(grid);
|
|
310
339
|
|
|
340
|
+
// Keep evidence distinct from ranking or reward so a non-technical user
|
|
341
|
+
// can see what SLM observed without inferring an automatic behavior change.
|
|
342
|
+
var evc = EL('div', { className: 'card', style: 'margin-top:16px' });
|
|
343
|
+
var evh = EL('div', { className: 'card-head' });
|
|
344
|
+
evh.appendChild(EL('h3', { text: 'Agent evidence' }));
|
|
345
|
+
evh.appendChild(EL('span', { className: 'sub', text: 'local receipts · observation only' }));
|
|
346
|
+
evc.appendChild(evh);
|
|
347
|
+
var evb = EL('div', { className: 'card-pad' });
|
|
348
|
+
evb.appendChild(EL('p', {
|
|
349
|
+
className: 'muted',
|
|
350
|
+
style: 'margin:0 0 14px;font-size:12px;line-height:1.55',
|
|
351
|
+
text: 'SLM records completed work when an integration supplies evidence. These records do not change recall, ranking, or model routing by themselves.',
|
|
352
|
+
}));
|
|
353
|
+
var evGrid = EL('div', { className: 'kpi-strip', style: 'margin:0' });
|
|
354
|
+
evGrid.appendChild(kpiCard('fact_check', 'Recorded experiences',
|
|
355
|
+
fmtNum(experience.experiences_total || 0), 'profile-scoped durable receipts',
|
|
356
|
+
Number(experience.experiences_total || 0) > 0, undefined, true));
|
|
357
|
+
evGrid.appendChild(kpiCard('verified', 'Claimed evidence authority',
|
|
358
|
+
fmtNum(experience.claimed_evidence_experiences || 0), 'declared by the producing host',
|
|
359
|
+
Number(experience.claimed_evidence_experiences || 0) > 0, undefined, true));
|
|
360
|
+
evGrid.appendChild(kpiCard('account_tree', 'Cognitive turns',
|
|
361
|
+
fmtNum(experience.turns_total || 0),
|
|
362
|
+
fmtNum((experience.turns_by_state || {}).open || 0) + ' open · ' +
|
|
363
|
+
fmtNum((experience.turns_by_state || {}).finalized || 0) + ' finalized',
|
|
364
|
+
Number(experience.turns_total || 0) > 0, undefined, true));
|
|
365
|
+
evb.appendChild(evGrid);
|
|
366
|
+
evc.appendChild(evb);
|
|
367
|
+
sec.appendChild(evc);
|
|
368
|
+
|
|
311
369
|
// Activity heatmap (tool events are activity, never reward labels)
|
|
312
370
|
var hmc = EL('div', { className: 'card', style: 'margin-top:16px' });
|
|
313
371
|
var hmh = EL('div', { className: 'card-head' });
|
|
@@ -589,54 +647,54 @@
|
|
|
589
647
|
// ======================================================================
|
|
590
648
|
// Tab: CONNECTED CLIENTS
|
|
591
649
|
// ======================================================================
|
|
592
|
-
function buildClients(
|
|
650
|
+
function buildClients(living, configured) {
|
|
593
651
|
var sec = EL('section', { className: 'tabpane', 'data-p': 'clients' });
|
|
652
|
+
var clients = ((living && living.connected_clients) || {}).clients || [];
|
|
653
|
+
var configuredData = configured || {};
|
|
594
654
|
|
|
595
|
-
//
|
|
655
|
+
// Activity is presence reported by host lifecycle hooks, not the old
|
|
656
|
+
// tool-event proxy. A configured adapter and a recent client are two
|
|
657
|
+
// different truths, rendered as separate cards.
|
|
596
658
|
var evc = EL('div', { className: 'card', style: 'margin-bottom:16px' });
|
|
597
659
|
var evh = EL('div', { className: 'card-head' });
|
|
598
|
-
evh.appendChild(EL('h3', { text: '
|
|
599
|
-
evh.appendChild(EL('span', { className: 'sub', text: '
|
|
660
|
+
evh.appendChild(EL('h3', { text: 'Recent client activity' }));
|
|
661
|
+
evh.appendChild(EL('span', { className: 'sub', text: 'host lifecycle presence · last 5 minutes' }));
|
|
600
662
|
evc.appendChild(evh);
|
|
601
663
|
var evb = EL('div', { className: 'card-pad' });
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
cVals.push((dateMap && dateMap[cd.toISOString().slice(0, 10)]) || 0);
|
|
607
|
-
}
|
|
608
|
-
var evSp = EL('div', { id: 'od-brain-sp-clients' });
|
|
609
|
-
var hasData = cVals.some(function (v) { return v > 0; });
|
|
610
|
-
if (hasData && typeof window.slmSpark === 'function') {
|
|
611
|
-
evSp.innerHTML = window.slmSpark(cVals, { w: 600, h: 150, color: 'var(--cyan)' });
|
|
612
|
-
var sv2 = evSp.querySelector('svg'); if (sv2) sv2.style.height = '150px';
|
|
664
|
+
if (clients.length === 0) {
|
|
665
|
+
evb.appendChild(EL('p', { className: 'muted', style: 'padding:16px;text-align:center',
|
|
666
|
+
text: 'No host activity in the last 5 minutes. This does not mean an integration is uninstalled.',
|
|
667
|
+
}));
|
|
613
668
|
} else {
|
|
614
|
-
|
|
615
|
-
|
|
669
|
+
clients.forEach(function (client) {
|
|
670
|
+
var row = EL('div', { className: 'list-row' });
|
|
671
|
+
row.appendChild(EL('b', { style: 'flex:1', text: String(client.kind || 'other') }));
|
|
672
|
+
row.appendChild(EL('span', { className: 'muted',
|
|
673
|
+
text: 'active ' + Number(client.last_seen_seconds_ago || 0) + 's ago',
|
|
674
|
+
}));
|
|
675
|
+
evb.appendChild(row);
|
|
676
|
+
});
|
|
616
677
|
}
|
|
617
|
-
evb.appendChild(evSp);
|
|
618
|
-
// TODO: GET /api/clients — no live connected-client session data available via these endpoints.
|
|
619
|
-
// tool-events captures tool invocations but not distinct client identities or session counts.
|
|
620
|
-
evb.appendChild(EL('p', {
|
|
621
|
-
className: 'muted',
|
|
622
|
-
style: 'margin-top:12px;font-size:13px',
|
|
623
|
-
text: 'Bars above represent all tool invocations logged to this daemon. ' +
|
|
624
|
-
'A dedicated client-session endpoint is not yet exposed via the public API.',
|
|
625
|
-
}));
|
|
626
678
|
evc.appendChild(evb);
|
|
627
679
|
sec.appendChild(evc);
|
|
628
680
|
|
|
629
|
-
//
|
|
681
|
+
// Configured integrations are installation/sync state, not client activity.
|
|
630
682
|
var tc = EL('div', { className: 'card' });
|
|
631
683
|
var tch = EL('div', { className: 'card-head' });
|
|
632
|
-
tch.appendChild(EL('h3', { text: '
|
|
684
|
+
tch.appendChild(EL('h3', { text: 'Configured integrations' }));
|
|
685
|
+
tch.appendChild(EL('span', { className: 'sub', text: 'installation and sync availability' }));
|
|
633
686
|
tc.appendChild(tch);
|
|
634
687
|
var tcb = EL('div', { className: 'card-pad' });
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
688
|
+
Object.keys(configuredData).sort().forEach(function (kind) {
|
|
689
|
+
var state = configuredData[kind] || {};
|
|
690
|
+
var row = EL('div', { className: 'list-row' });
|
|
691
|
+
row.appendChild(EL('span', { style: 'flex:1', text: kind.replace(/_/g, ' ') }));
|
|
692
|
+
row.appendChild(EL('span', {
|
|
693
|
+
className: 'badge ' + (state.active ? 'ok' : 'warn'),
|
|
694
|
+
text: state.active ? 'available' : (state.reason || 'not available'),
|
|
695
|
+
}));
|
|
696
|
+
tcb.appendChild(row);
|
|
697
|
+
});
|
|
640
698
|
tc.appendChild(tcb);
|
|
641
699
|
sec.appendChild(tc);
|
|
642
700
|
return sec;
|
|
@@ -645,7 +703,7 @@
|
|
|
645
703
|
// ======================================================================
|
|
646
704
|
// Tab: SOURCE QUALITY
|
|
647
705
|
// ======================================================================
|
|
648
|
-
function buildSourceQuality(learning) {
|
|
706
|
+
function buildSourceQuality(learning, living) {
|
|
649
707
|
var sec = EL('section', { className: 'tabpane', 'data-p': 'sources' });
|
|
650
708
|
var scores = (learning && learning.source_scores) || {};
|
|
651
709
|
var entries = Object.keys(scores).sort(function (a, b) {
|
|
@@ -657,13 +715,27 @@
|
|
|
657
715
|
ch.appendChild(EL('span', { className: 'sub', text: 'persisted source-outcome posterior · 0.0–1.0' }));
|
|
658
716
|
card.appendChild(ch);
|
|
659
717
|
var cb = EL('div', { className: 'card-pad' });
|
|
660
|
-
|
|
718
|
+
var aggregate = (living && living.source_quality) || {};
|
|
719
|
+
var observedSources = Number(aggregate.observed_sources || 0);
|
|
720
|
+
if (observedSources === 0) {
|
|
721
|
+
cb.appendChild(EL('p', {
|
|
722
|
+
className: 'muted',
|
|
723
|
+
style: 'padding:16px;text-align:center;font-size:13px',
|
|
724
|
+
text: 'No source-quality evidence has settled yet. Recall hits alone do not establish source quality.',
|
|
725
|
+
}));
|
|
726
|
+
} else if (entries.length === 0) {
|
|
661
727
|
cb.appendChild(EL('p', {
|
|
662
728
|
className: 'muted',
|
|
663
729
|
style: 'padding:16px;text-align:center;font-size:13px',
|
|
664
|
-
text: '
|
|
730
|
+
text: String(observedSources) + ' sources have evidence, but no per-source posterior is available yet.',
|
|
665
731
|
}));
|
|
666
732
|
} else {
|
|
733
|
+
cb.appendChild(EL('p', {
|
|
734
|
+
className: 'muted',
|
|
735
|
+
style: 'font-size:13px;margin-bottom:16px',
|
|
736
|
+
text: String(observedSources) + ' sources with observed mean quality ' +
|
|
737
|
+
Number(aggregate.mean_quality).toFixed(3) + '.',
|
|
738
|
+
}));
|
|
667
739
|
entries.forEach(function (k) {
|
|
668
740
|
var v = Number(scores[k]);
|
|
669
741
|
var row = EL('div', { style: 'margin-bottom:14px' });
|
|
@@ -765,6 +837,8 @@
|
|
|
765
837
|
var learning = results[0] || {};
|
|
766
838
|
var behavioral = results[1] || {};
|
|
767
839
|
var events = ((results[3] && results[3].events) || []);
|
|
840
|
+
var brain = results[4] || {};
|
|
841
|
+
var living = brain.living_brain || {};
|
|
768
842
|
var dateMap = buildDateMap(events);
|
|
769
843
|
var pCount = ((behavioral.patterns) || []).length;
|
|
770
844
|
|
|
@@ -778,11 +852,11 @@
|
|
|
778
852
|
container.replaceChildren(
|
|
779
853
|
head,
|
|
780
854
|
buildTabRow(pCount),
|
|
781
|
-
buildOverview(learning, behavioral, dateMap),
|
|
855
|
+
buildOverview(learning, behavioral, dateMap, living),
|
|
782
856
|
buildReward(behavioral),
|
|
783
857
|
buildBehaviour(learning, behavioral),
|
|
784
|
-
buildClients(
|
|
785
|
-
buildSourceQuality(learning)
|
|
858
|
+
buildClients(living, brain.cross_platform),
|
|
859
|
+
buildSourceQuality(learning, living)
|
|
786
860
|
);
|
|
787
861
|
wireTabs(container);
|
|
788
862
|
}).catch(function (err) { showError(container, err); });
|
|
@@ -302,7 +302,14 @@
|
|
|
302
302
|
var mi = q('model'); if (mi) mi.value = d.model || '';
|
|
303
303
|
var k = q('apikey');
|
|
304
304
|
if (k) k.placeholder = d.has_key ? '(key saved — enter new to replace)' : 'sk-...';
|
|
305
|
-
|
|
305
|
+
// GET /mode deliberately returns only a redacted host. Showing that
|
|
306
|
+
// host as the editable value would turn a passive reload + save into a
|
|
307
|
+
// destructive replacement of the real URL, so it is display-only.
|
|
308
|
+
var ep = q('endpoint');
|
|
309
|
+
if (ep) {
|
|
310
|
+
ep.value = '';
|
|
311
|
+
ep.placeholder = d.endpoint || 'http://localhost:11434';
|
|
312
|
+
}
|
|
306
313
|
}).catch(function () {});
|
|
307
314
|
}
|
|
308
315
|
function saveMode() {
|