superlocalmemory 2.8.0 → 2.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.
@@ -0,0 +1,276 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
+ // Behavioral Learning tab — outcomes, patterns, cross-project transfers (v2.8)
4
+ // NOTE: All dynamic values use textContent or escapeHtml() from core.js before DOM insertion.
5
+
6
+ var _behavioralData = null;
7
+
8
+ async function loadBehavioral() {
9
+ try {
10
+ var response = await fetch('/api/behavioral/status');
11
+ var data = await response.json();
12
+ _behavioralData = data;
13
+
14
+ if (!data.available) {
15
+ showEmpty('behavioral-patterns-content', 'lightbulb', 'Behavioral learning not available. Upgrade to v2.8.');
16
+ return;
17
+ }
18
+
19
+ renderBehavioralStats(data);
20
+ renderBehavioralPatterns(data);
21
+ renderBehavioralTransfers(data);
22
+ renderBehavioralOutcomes(data);
23
+
24
+ var badge = document.getElementById('behavioral-profile-badge');
25
+ if (badge) badge.textContent = data.active_profile || 'default';
26
+ } catch (error) {
27
+ console.error('Error loading behavioral:', error);
28
+ }
29
+ }
30
+
31
+ function renderBehavioralStats(data) {
32
+ var stats = data.stats || {};
33
+ animateCounter('bh-success-count', stats.success_count || 0);
34
+ animateCounter('bh-failure-count', stats.failure_count || 0);
35
+ animateCounter('bh-partial-count', stats.partial_count || 0);
36
+ animateCounter('bh-patterns-count', stats.patterns_count || 0);
37
+ }
38
+
39
+ function renderBehavioralPatterns(data) {
40
+ var container = document.getElementById('behavioral-patterns-content');
41
+ if (!container) return;
42
+ var patterns = data.patterns || [];
43
+ container.textContent = '';
44
+
45
+ if (patterns.length === 0) {
46
+ var empty = document.createElement('div');
47
+ empty.className = 'text-center text-muted py-3';
48
+ empty.textContent = 'No patterns learned yet. Report outcomes to start learning.';
49
+ container.appendChild(empty);
50
+ return;
51
+ }
52
+
53
+ for (var i = 0; i < patterns.length; i++) {
54
+ var p = patterns[i];
55
+ var successRate = Math.round((p.success_rate || 0) * 100);
56
+ var confPct = Math.round((p.confidence || 0) * 100);
57
+ var barColor = successRate >= 70 ? 'bg-success' : (successRate >= 40 ? 'bg-warning' : 'bg-danger');
58
+
59
+ var row = document.createElement('div');
60
+ row.className = 'd-flex align-items-center mb-2';
61
+
62
+ // Pattern key label
63
+ var label = document.createElement('div');
64
+ label.style.minWidth = '140px';
65
+ var labelCode = document.createElement('code');
66
+ labelCode.className = 'small';
67
+ labelCode.textContent = p.pattern_key || '';
68
+ label.appendChild(labelCode);
69
+
70
+ // Success rate progress bar
71
+ var barWrap = document.createElement('div');
72
+ barWrap.className = 'flex-grow-1 mx-2';
73
+ var progress = document.createElement('div');
74
+ progress.className = 'progress';
75
+ progress.style.height = '20px';
76
+ progress.style.borderRadius = '10px';
77
+ var barEl = document.createElement('div');
78
+ barEl.className = 'progress-bar ' + barColor;
79
+ barEl.style.width = successRate + '%';
80
+ barEl.style.borderRadius = '10px';
81
+ barEl.style.fontSize = '0.7rem';
82
+ barEl.textContent = successRate + '% success';
83
+ progress.appendChild(barEl);
84
+ barWrap.appendChild(progress);
85
+
86
+ // Evidence count
87
+ var evidence = document.createElement('small');
88
+ evidence.className = 'text-muted';
89
+ evidence.style.minWidth = '50px';
90
+ evidence.style.textAlign = 'right';
91
+ evidence.textContent = (p.evidence_count || 0) + ' ev.';
92
+
93
+ // Confidence badge
94
+ var confBadge = document.createElement('span');
95
+ confBadge.className = 'badge ms-2 ' + (confPct >= 70 ? 'bg-success' : (confPct >= 40 ? 'bg-warning' : 'bg-secondary'));
96
+ confBadge.style.minWidth = '50px';
97
+ confBadge.textContent = confPct + '%';
98
+
99
+ row.appendChild(label);
100
+ row.appendChild(barWrap);
101
+ row.appendChild(evidence);
102
+ row.appendChild(confBadge);
103
+ container.appendChild(row);
104
+ }
105
+ }
106
+
107
+ function renderBehavioralTransfers(data) {
108
+ var container = document.getElementById('behavioral-transfers-content');
109
+ if (!container) return;
110
+ var transfers = data.transfers || [];
111
+ container.textContent = '';
112
+
113
+ if (transfers.length === 0) {
114
+ var empty = document.createElement('div');
115
+ empty.className = 'text-center text-muted py-3';
116
+ empty.textContent = 'No cross-project transfers yet. Patterns transfer automatically when confidence is high.';
117
+ container.appendChild(empty);
118
+ return;
119
+ }
120
+
121
+ var table = document.createElement('table');
122
+ table.className = 'table table-sm table-hover mb-0';
123
+ var thead = document.createElement('thead');
124
+ var headRow = document.createElement('tr');
125
+ ['Pattern', 'From Project', 'To Project', 'Confidence', 'Date'].forEach(function(h) {
126
+ var th = document.createElement('th');
127
+ th.textContent = h;
128
+ headRow.appendChild(th);
129
+ });
130
+ thead.appendChild(headRow);
131
+ table.appendChild(thead);
132
+
133
+ var tbody = document.createElement('tbody');
134
+ for (var i = 0; i < transfers.length; i++) {
135
+ var t = transfers[i];
136
+ var row = document.createElement('tr');
137
+
138
+ var patternCell = document.createElement('td');
139
+ var patternCode = document.createElement('code');
140
+ patternCode.className = 'small';
141
+ patternCode.textContent = t.pattern_key || '';
142
+ patternCell.appendChild(patternCode);
143
+ row.appendChild(patternCell);
144
+
145
+ var fromCell = document.createElement('td');
146
+ var fromBadge = document.createElement('span');
147
+ fromBadge.className = 'badge bg-secondary';
148
+ fromBadge.textContent = t.from_project || '';
149
+ fromCell.appendChild(fromBadge);
150
+ row.appendChild(fromCell);
151
+
152
+ var toCell = document.createElement('td');
153
+ var toBadge = document.createElement('span');
154
+ toBadge.className = 'badge bg-primary';
155
+ toBadge.textContent = t.to_project || '';
156
+ toCell.appendChild(toBadge);
157
+ row.appendChild(toCell);
158
+
159
+ var confCell = document.createElement('td');
160
+ confCell.textContent = Math.round((t.confidence || 0) * 100) + '%';
161
+ row.appendChild(confCell);
162
+
163
+ var dateCell = document.createElement('td');
164
+ dateCell.className = 'small text-muted';
165
+ dateCell.textContent = formatDate(t.transferred_at || '');
166
+ row.appendChild(dateCell);
167
+
168
+ tbody.appendChild(row);
169
+ }
170
+ table.appendChild(tbody);
171
+ container.appendChild(table);
172
+ }
173
+
174
+ function renderBehavioralOutcomes(data) {
175
+ var container = document.getElementById('behavioral-outcomes-content');
176
+ if (!container) return;
177
+ var outcomes = data.recent_outcomes || [];
178
+ container.textContent = '';
179
+
180
+ if (outcomes.length === 0) {
181
+ var empty = document.createElement('div');
182
+ empty.className = 'text-center text-muted py-3';
183
+ empty.textContent = 'No outcomes recorded yet. Use the form above or the report_outcome MCP tool.';
184
+ container.appendChild(empty);
185
+ return;
186
+ }
187
+
188
+ var table = document.createElement('table');
189
+ table.className = 'table table-sm table-hover mb-0';
190
+ var thead = document.createElement('thead');
191
+ var headRow = document.createElement('tr');
192
+ ['Memory IDs', 'Outcome', 'Action Type', 'Date'].forEach(function(h) {
193
+ var th = document.createElement('th');
194
+ th.textContent = h;
195
+ headRow.appendChild(th);
196
+ });
197
+ thead.appendChild(headRow);
198
+ table.appendChild(thead);
199
+
200
+ var outcomeBadgeColors = {
201
+ success: 'bg-success',
202
+ failure: 'bg-danger',
203
+ partial: 'bg-warning'
204
+ };
205
+
206
+ var tbody = document.createElement('tbody');
207
+ for (var i = 0; i < outcomes.length; i++) {
208
+ var o = outcomes[i];
209
+ var row = document.createElement('tr');
210
+
211
+ var idsCell = document.createElement('td');
212
+ var memIds = o.memory_ids || [];
213
+ idsCell.textContent = memIds.join(', ');
214
+ row.appendChild(idsCell);
215
+
216
+ var outcomeCell = document.createElement('td');
217
+ var outBadge = document.createElement('span');
218
+ outBadge.className = 'badge ' + (outcomeBadgeColors[o.outcome] || 'bg-secondary');
219
+ outBadge.textContent = o.outcome || '';
220
+ outcomeCell.appendChild(outBadge);
221
+ row.appendChild(outcomeCell);
222
+
223
+ var actionCell = document.createElement('td');
224
+ actionCell.textContent = o.action_type || '';
225
+ row.appendChild(actionCell);
226
+
227
+ var dateCell = document.createElement('td');
228
+ dateCell.className = 'small text-muted';
229
+ dateCell.textContent = formatDate(o.created_at || '');
230
+ row.appendChild(dateCell);
231
+
232
+ tbody.appendChild(row);
233
+ }
234
+ table.appendChild(tbody);
235
+ container.appendChild(table);
236
+ }
237
+
238
+ async function reportOutcome() {
239
+ var memIdsInput = document.getElementById('bh-memory-ids');
240
+ var outcomeSelect = document.getElementById('bh-outcome');
241
+ var actionSelect = document.getElementById('bh-action-type');
242
+ var contextInput = document.getElementById('bh-context');
243
+
244
+ var rawIds = (memIdsInput.value || '').trim();
245
+ if (!rawIds) {
246
+ showToast('Enter at least one memory ID.');
247
+ return;
248
+ }
249
+
250
+ var memoryIds = rawIds.split(',').map(function(id) { return id.trim(); }).filter(function(id) { return id.length > 0; });
251
+
252
+ try {
253
+ var response = await fetch('/api/behavioral/report-outcome', {
254
+ method: 'POST',
255
+ headers: { 'Content-Type': 'application/json' },
256
+ body: JSON.stringify({
257
+ memory_ids: memoryIds,
258
+ outcome: outcomeSelect.value,
259
+ action_type: actionSelect.value,
260
+ context: contextInput.value.trim() || undefined
261
+ })
262
+ });
263
+ var data = await response.json();
264
+ if (response.ok) {
265
+ showToast('Outcome reported successfully.');
266
+ memIdsInput.value = '';
267
+ contextInput.value = '';
268
+ loadBehavioral(); // Refresh
269
+ } else {
270
+ showToast(data.detail || 'Failed to report outcome.');
271
+ }
272
+ } catch (error) {
273
+ console.error('Error reporting outcome:', error);
274
+ showToast('Error reporting outcome.');
275
+ }
276
+ }
@@ -0,0 +1,252 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
+ // Compliance tab — audit trail, retention policies, ABAC (v2.8)
4
+ // NOTE: All dynamic values use textContent or escapeHtml() from core.js before DOM insertion.
5
+
6
+ var _complianceData = null;
7
+
8
+ async function loadCompliance() {
9
+ var filterEl = document.getElementById('cp-audit-filter');
10
+ var filterValue = filterEl ? filterEl.value : '';
11
+
12
+ try {
13
+ var url = '/api/compliance/status';
14
+ if (filterValue) url += '?event_type=' + encodeURIComponent(filterValue);
15
+ var response = await fetch(url);
16
+ var data = await response.json();
17
+ _complianceData = data;
18
+
19
+ if (!data.available) {
20
+ showEmpty('compliance-audit-content', 'shield-lock', 'Compliance engine not available. Upgrade to v2.8.');
21
+ return;
22
+ }
23
+
24
+ renderComplianceStats(data);
25
+ renderCompliancePolicies(data);
26
+ renderComplianceAudit(data);
27
+
28
+ var badge = document.getElementById('compliance-profile-badge');
29
+ if (badge) badge.textContent = data.active_profile || 'default';
30
+ } catch (error) {
31
+ console.error('Error loading compliance:', error);
32
+ }
33
+ }
34
+
35
+ function renderComplianceStats(data) {
36
+ var stats = data.stats || {};
37
+ animateCounter('cp-audit-count', stats.audit_count || 0);
38
+ animateCounter('cp-retention-count', stats.retention_count || 0);
39
+ animateCounter('cp-abac-count', stats.abac_count || 0);
40
+ }
41
+
42
+ function renderCompliancePolicies(data) {
43
+ var container = document.getElementById('compliance-policies-content');
44
+ if (!container) return;
45
+ var policies = data.retention_policies || [];
46
+ container.textContent = '';
47
+
48
+ if (policies.length === 0) {
49
+ var empty = document.createElement('div');
50
+ empty.className = 'text-center text-muted py-3';
51
+ empty.textContent = 'No retention policies configured. Create one above or use the set_retention_policy MCP tool.';
52
+ container.appendChild(empty);
53
+ return;
54
+ }
55
+
56
+ var table = document.createElement('table');
57
+ table.className = 'table table-sm table-hover mb-0';
58
+ var thead = document.createElement('thead');
59
+ var headRow = document.createElement('tr');
60
+ ['Policy Name', 'Retention (days)', 'Category', 'Action', 'Created'].forEach(function(h) {
61
+ var th = document.createElement('th');
62
+ th.textContent = h;
63
+ headRow.appendChild(th);
64
+ });
65
+ thead.appendChild(headRow);
66
+ table.appendChild(thead);
67
+
68
+ var tbody = document.createElement('tbody');
69
+ for (var i = 0; i < policies.length; i++) {
70
+ var pol = policies[i];
71
+ var row = document.createElement('tr');
72
+
73
+ var nameCell = document.createElement('td');
74
+ var nameIcon = document.createElement('i');
75
+ nameIcon.className = 'bi bi-shield-check text-success me-1';
76
+ nameCell.appendChild(nameIcon);
77
+ nameCell.appendChild(document.createTextNode(pol.name || ''));
78
+ row.appendChild(nameCell);
79
+
80
+ var daysCell = document.createElement('td');
81
+ daysCell.textContent = (pol.retention_days || 0) + ' days';
82
+ row.appendChild(daysCell);
83
+
84
+ var catCell = document.createElement('td');
85
+ if (pol.category) {
86
+ var catBadge = document.createElement('span');
87
+ catBadge.className = 'badge bg-info';
88
+ catBadge.textContent = pol.category;
89
+ catCell.appendChild(catBadge);
90
+ } else {
91
+ catCell.textContent = 'All';
92
+ }
93
+ row.appendChild(catCell);
94
+
95
+ var actionCell = document.createElement('td');
96
+ var actionColors = { archive: 'bg-secondary', tombstone: 'bg-danger', notify: 'bg-warning' };
97
+ var actionBadge = document.createElement('span');
98
+ actionBadge.className = 'badge ' + (actionColors[pol.action] || 'bg-secondary');
99
+ actionBadge.textContent = pol.action || '';
100
+ actionCell.appendChild(actionBadge);
101
+ row.appendChild(actionCell);
102
+
103
+ var dateCell = document.createElement('td');
104
+ dateCell.className = 'small text-muted';
105
+ dateCell.textContent = formatDate(pol.created_at || '');
106
+ row.appendChild(dateCell);
107
+
108
+ tbody.appendChild(row);
109
+ }
110
+ table.appendChild(tbody);
111
+ container.appendChild(table);
112
+ }
113
+
114
+ function renderComplianceAudit(data) {
115
+ var container = document.getElementById('compliance-audit-content');
116
+ if (!container) return;
117
+ var events = data.audit_events || [];
118
+ container.textContent = '';
119
+
120
+ if (events.length === 0) {
121
+ var empty = document.createElement('div');
122
+ empty.className = 'text-center text-muted py-3';
123
+ empty.textContent = 'No audit events recorded yet.';
124
+ container.appendChild(empty);
125
+ return;
126
+ }
127
+
128
+ var wrapper = document.createElement('div');
129
+ wrapper.style.maxHeight = '400px';
130
+ wrapper.style.overflowY = 'auto';
131
+
132
+ var table = document.createElement('table');
133
+ table.className = 'table table-sm table-hover mb-0';
134
+ var thead = document.createElement('thead');
135
+ thead.style.position = 'sticky';
136
+ thead.style.top = '0';
137
+ thead.style.backgroundColor = 'var(--bs-body-bg)';
138
+ var headRow = document.createElement('tr');
139
+ ['Timestamp', 'Event', 'Actor', 'Action', 'Target', 'Result'].forEach(function(h) {
140
+ var th = document.createElement('th');
141
+ th.textContent = h;
142
+ headRow.appendChild(th);
143
+ });
144
+ thead.appendChild(headRow);
145
+ table.appendChild(thead);
146
+
147
+ var eventBadgeColors = {
148
+ recall: 'bg-primary',
149
+ remember: 'bg-success',
150
+ delete: 'bg-danger',
151
+ lifecycle_transition: 'bg-warning',
152
+ access_denied: 'bg-danger',
153
+ retention_enforced: 'bg-warning'
154
+ };
155
+
156
+ var tbody = document.createElement('tbody');
157
+ for (var i = 0; i < events.length; i++) {
158
+ var ev = events[i];
159
+ var row = document.createElement('tr');
160
+
161
+ var tsCell = document.createElement('td');
162
+ tsCell.className = 'small text-muted';
163
+ tsCell.textContent = formatDateFull(ev.timestamp || '');
164
+ row.appendChild(tsCell);
165
+
166
+ var typeCell = document.createElement('td');
167
+ var typeBadge = document.createElement('span');
168
+ typeBadge.className = 'badge ' + (eventBadgeColors[ev.event_type] || 'bg-secondary');
169
+ typeBadge.textContent = ev.event_type || '';
170
+ typeCell.appendChild(typeBadge);
171
+ row.appendChild(typeCell);
172
+
173
+ var actorCell = document.createElement('td');
174
+ actorCell.textContent = ev.actor || '';
175
+ row.appendChild(actorCell);
176
+
177
+ var actionCell = document.createElement('td');
178
+ actionCell.textContent = ev.action || '';
179
+ row.appendChild(actionCell);
180
+
181
+ var targetCell = document.createElement('td');
182
+ targetCell.className = 'small';
183
+ targetCell.textContent = ev.target || '';
184
+ row.appendChild(targetCell);
185
+
186
+ var resultCell = document.createElement('td');
187
+ if (ev.result === 'success' || ev.result === 'allowed') {
188
+ var okBadge = document.createElement('span');
189
+ okBadge.className = 'badge bg-success';
190
+ okBadge.textContent = ev.result;
191
+ resultCell.appendChild(okBadge);
192
+ } else if (ev.result === 'denied' || ev.result === 'error') {
193
+ var failBadge = document.createElement('span');
194
+ failBadge.className = 'badge bg-danger';
195
+ failBadge.textContent = ev.result;
196
+ resultCell.appendChild(failBadge);
197
+ } else {
198
+ resultCell.textContent = ev.result || '';
199
+ }
200
+ row.appendChild(resultCell);
201
+
202
+ tbody.appendChild(row);
203
+ }
204
+ table.appendChild(tbody);
205
+ wrapper.appendChild(table);
206
+ container.appendChild(wrapper);
207
+ }
208
+
209
+ async function createRetentionPolicy() {
210
+ var nameInput = document.getElementById('cp-policy-name');
211
+ var daysInput = document.getElementById('cp-retention-days');
212
+ var categoryInput = document.getElementById('cp-category');
213
+ var actionSelect = document.getElementById('cp-action');
214
+
215
+ var policyName = (nameInput.value || '').trim();
216
+ if (!policyName) {
217
+ showToast('Enter a policy name.');
218
+ return;
219
+ }
220
+
221
+ var days = parseInt(daysInput.value, 10);
222
+ if (isNaN(days) || days <= 0) {
223
+ showToast('Enter a valid number of days.');
224
+ return;
225
+ }
226
+
227
+ try {
228
+ var response = await fetch('/api/compliance/retention-policy', {
229
+ method: 'POST',
230
+ headers: { 'Content-Type': 'application/json' },
231
+ body: JSON.stringify({
232
+ name: policyName,
233
+ retention_days: days,
234
+ category: categoryInput.value.trim() || undefined,
235
+ action: actionSelect.value
236
+ })
237
+ });
238
+ var data = await response.json();
239
+ if (response.ok) {
240
+ showToast('Retention policy created.');
241
+ nameInput.value = '';
242
+ daysInput.value = '90';
243
+ categoryInput.value = '';
244
+ loadCompliance(); // Refresh
245
+ } else {
246
+ showToast(data.detail || 'Failed to create policy.');
247
+ }
248
+ } catch (error) {
249
+ console.error('Error creating retention policy:', error);
250
+ showToast('Error creating retention policy.');
251
+ }
252
+ }
package/ui/js/init.js CHANGED
@@ -33,3 +33,13 @@ if (agentsTab) agentsTab.addEventListener('shown.bs.tab', loadAgents);
33
33
  // v2.7 learning tab (graceful)
34
34
  var learningTab = document.getElementById('learning-tab');
35
35
  if (learningTab) learningTab.addEventListener('shown.bs.tab', loadLearning);
36
+
37
+ // v2.8 tabs (graceful — elements may not exist on older installs)
38
+ var lifecycleTab = document.getElementById('lifecycle-tab');
39
+ if (lifecycleTab) lifecycleTab.addEventListener('shown.bs.tab', loadLifecycle);
40
+
41
+ var behavioralTab = document.getElementById('behavioral-tab');
42
+ if (behavioralTab) behavioralTab.addEventListener('shown.bs.tab', loadBehavioral);
43
+
44
+ var complianceTab = document.getElementById('compliance-tab');
45
+ if (complianceTab) complianceTab.addEventListener('shown.bs.tab', loadCompliance);