superlocalmemory 2.7.2 → 2.7.4

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.
@@ -12,10 +12,23 @@ Attribution must be preserved in all copies or derivatives.
12
12
  """
13
13
 
14
14
  """
15
- TrustScorer — Silent trust signal collection for AI agents.
15
+ TrustScorer — Bayesian Beta-Binomial trust scoring for AI agents.
16
+
17
+ Scoring Model:
18
+ Each agent's trust is modeled as a Beta(alpha, beta) distribution.
19
+ - alpha accumulates evidence of trustworthy behavior
20
+ - beta accumulates evidence of untrustworthy behavior
21
+ - Trust score = alpha / (alpha + beta) (posterior mean)
22
+
23
+ Prior: Beta(2.0, 1.0) → initial trust = 0.667
24
+ This gives new agents a positive-but-not-maximal starting trust,
25
+ well above the 0.3 enforcement threshold but with room to grow.
26
+
27
+ This follows the MACLA Beta-Binomial approach (arXiv:2512.18950)
28
+ already used in pattern_learner.py for confidence scoring.
16
29
 
17
30
  v2.5 BEHAVIOR (this version):
18
- - All agents start at trust 1.0
31
+ - All agents start at Beta(2.0, 1.0) → trust 0.667
19
32
  - Signals are collected silently (no enforcement, no ranking, no blocking)
20
33
  - Trust scores are updated in agent_registry.trust_score
21
34
  - Dashboard shows scores but they don't affect recall ordering yet
@@ -30,31 +43,26 @@ v3.0 BEHAVIOR (future):
30
43
  - Admin approval workflow for untrusted agents
31
44
 
32
45
  Trust Signals (all silently collected):
33
- POSITIVE (increase trust):
46
+ POSITIVE (increase alpha — build trust):
34
47
  - Memory recalled by other agents (cross-agent validation)
35
48
  - Memory updated (shows ongoing relevance)
36
49
  - High importance memories (agent writes valuable content)
37
50
  - Consistent write patterns (not spam-like)
38
51
 
39
- NEGATIVE (decrease trust):
52
+ NEGATIVE (increase beta — erode trust):
40
53
  - Memory deleted shortly after creation (low quality)
41
54
  - Very high write volume in short time (potential spam/poisoning)
42
55
  - Content flagged or overwritten by user
43
56
 
44
57
  NEUTRAL:
45
- - Normal read/write patterns
58
+ - Normal read/write patterns (tiny alpha nudge to reward activity)
46
59
  - Agent disconnects/reconnects
47
60
 
48
- Scoring Algorithm:
49
- Bayesian-inspired moving average. Each signal adjusts the score
50
- by a small delta. Score is clamped to [0.0, 1.0].
51
-
52
- new_score = old_score + (delta * decay_factor)
53
- decay_factor = 1 / (1 + signal_count * 0.01) # Stabilizes over time
54
-
55
- This means early signals have more impact, and the score converges
56
- as more data is collected. Similar to MACLA Beta-Binomial approach
57
- (arXiv:2512.18950) but simplified for local computation.
61
+ Decay:
62
+ Every DECAY_INTERVAL signals per agent, both alpha and beta are
63
+ multiplied by DECAY_FACTOR (0.995). This slowly forgets very old
64
+ signals so recent behavior matters more. Floors prevent total
65
+ information loss: alpha >= 1.0, beta >= 0.5.
58
66
 
59
67
  Security (OWASP for Agentic AI):
60
68
  - Memory poisoning (#1 threat): Trust scoring is the first defense layer
@@ -64,7 +72,6 @@ Security (OWASP for Agentic AI):
64
72
 
65
73
  import json
66
74
  import logging
67
- import math
68
75
  import threading
69
76
  from datetime import datetime, timedelta
70
77
  from pathlib import Path
@@ -72,24 +79,59 @@ from typing import Optional, Dict, List
72
79
 
73
80
  logger = logging.getLogger("superlocalmemory.trust")
74
81
 
75
- # Signal deltas (how much each signal moves the trust score)
76
- SIGNAL_DELTAS = {
77
- # Positive signals
78
- "memory_recalled_by_others": +0.02,
79
- "memory_updated": +0.01,
80
- "high_importance_write": +0.015, # importance >= 7
81
- "consistent_pattern": +0.01,
82
-
83
- # Negative signals
84
- "quick_delete": -0.03, # deleted within 1 hour of creation
85
- "high_volume_burst": -0.02, # >20 writes in 5 minutes
86
- "content_overwritten_by_user": -0.01,
87
-
88
- # Neutral (logged but no score change)
89
- "normal_write": 0.0,
90
- "normal_recall": 0.0,
82
+ # ---------------------------------------------------------------------------
83
+ # Beta-Binomial signal weights
84
+ # ---------------------------------------------------------------------------
85
+ # Positive signals increment alpha (building trust).
86
+ # Negative signals increment beta (eroding trust).
87
+ # Neutral signals give a tiny alpha nudge to reward normal activity.
88
+ #
89
+ # Asymmetry: negative weights are larger than positive weights.
90
+ # This means it's harder to build trust than to lose it — the system
91
+ # is intentionally skeptical. One poisoning event takes many good
92
+ # actions to recover from.
93
+ # ---------------------------------------------------------------------------
94
+
95
+ SIGNAL_WEIGHTS = {
96
+ # Positive signals → alpha += weight
97
+ "memory_recalled_by_others": ("positive", 0.30), # cross-agent validation
98
+ "memory_updated": ("positive", 0.15), # ongoing relevance
99
+ "high_importance_write": ("positive", 0.20), # valuable content (importance >= 7)
100
+ "consistent_pattern": ("positive", 0.15), # stable write behavior
101
+
102
+ # Negative signals → beta += weight
103
+ "quick_delete": ("negative", 0.50), # deleted within 1 hour
104
+ "high_volume_burst": ("negative", 0.40), # >20 writes in 5 minutes
105
+ "content_overwritten_by_user": ("negative", 0.25), # user had to fix output
106
+
107
+ # Neutral signals → tiny alpha nudge
108
+ "normal_write": ("neutral", 0.01),
109
+ "normal_recall": ("neutral", 0.01),
91
110
  }
92
111
 
112
+ # Backward-compatible: expose SIGNAL_DELTAS as a derived dict so that
113
+ # bm6_trust.py (which imports SIGNAL_DELTAS) and any other consumer
114
+ # continues to work. The values represent the *direction* and *magnitude*
115
+ # of each signal: positive for alpha, negative for beta, zero for neutral.
116
+ SIGNAL_DELTAS = {}
117
+ for _sig, (_direction, _weight) in SIGNAL_WEIGHTS.items():
118
+ if _direction == "positive":
119
+ SIGNAL_DELTAS[_sig] = +_weight
120
+ elif _direction == "negative":
121
+ SIGNAL_DELTAS[_sig] = -_weight
122
+ else:
123
+ SIGNAL_DELTAS[_sig] = 0.0
124
+
125
+ # ---------------------------------------------------------------------------
126
+ # Beta prior and decay parameters
127
+ # ---------------------------------------------------------------------------
128
+ INITIAL_ALPHA = 2.0 # Slight positive prior
129
+ INITIAL_BETA = 1.0 # → initial trust = 2/(2+1) = 0.667
130
+ DECAY_FACTOR = 0.995 # Multiply alpha & beta every DECAY_INTERVAL signals
131
+ DECAY_INTERVAL = 50 # Apply decay every N signals per agent
132
+ ALPHA_FLOOR = 1.0 # Never decay alpha below this
133
+ BETA_FLOOR = 0.5 # Never decay beta below this
134
+
93
135
  # Thresholds
94
136
  QUICK_DELETE_HOURS = 1 # Delete within 1 hour = negative signal
95
137
  BURST_THRESHOLD = 20 # >20 writes in burst window = negative
@@ -98,9 +140,12 @@ BURST_WINDOW_MINUTES = 5 # Burst detection window
98
140
 
99
141
  class TrustScorer:
100
142
  """
101
- Silent trust signal collector for AI agents.
143
+ Bayesian Beta-Binomial trust scorer for AI agents.
144
+
145
+ Each agent is modeled as Beta(alpha, beta). Positive signals
146
+ increment alpha, negative signals increment beta. The trust
147
+ score is the posterior mean: alpha / (alpha + beta).
102
148
 
103
- v2.5: Collection only, no enforcement. All agents start at 1.0.
104
149
  Thread-safe singleton per database path.
105
150
  """
106
151
 
@@ -136,19 +181,26 @@ class TrustScorer:
136
181
  self._write_timestamps: Dict[str, list] = {}
137
182
  self._timestamps_lock = threading.Lock()
138
183
 
139
- # Signal count per agent (for decay factor calculation)
184
+ # Signal count per agent (for decay interval tracking)
140
185
  self._signal_counts: Dict[str, int] = {}
141
186
 
187
+ # In-memory cache of Beta parameters per agent
188
+ # Key: agent_id, Value: (alpha, beta)
189
+ self._beta_params: Dict[str, tuple] = {}
190
+ self._beta_lock = threading.Lock()
191
+
142
192
  self._init_schema()
143
- logger.info("TrustScorer initialized (v2.5silent collection, no enforcement)")
193
+ logger.info("TrustScorer initialized (Beta-Binomialalpha=%.1f, beta=%.1f prior)",
194
+ INITIAL_ALPHA, INITIAL_BETA)
144
195
 
145
196
  def _init_schema(self):
146
- """Create trust_signals table for audit trail."""
197
+ """Create trust_signals table and add alpha/beta columns to agent_registry."""
147
198
  try:
148
199
  from db_connection_manager import DbConnectionManager
149
200
  mgr = DbConnectionManager.get_instance(self.db_path)
150
201
 
151
202
  def _create(conn):
203
+ # Trust signals audit trail
152
204
  conn.execute('''
153
205
  CREATE TABLE IF NOT EXISTS trust_signals (
154
206
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -169,6 +221,18 @@ class TrustScorer:
169
221
  CREATE INDEX IF NOT EXISTS idx_trust_created
170
222
  ON trust_signals(created_at)
171
223
  ''')
224
+
225
+ # Add trust_alpha and trust_beta columns to agent_registry
226
+ # (backward compatible — old databases get these columns added)
227
+ for col_name, col_default in [("trust_alpha", INITIAL_ALPHA),
228
+ ("trust_beta", INITIAL_BETA)]:
229
+ try:
230
+ conn.execute(
231
+ f'ALTER TABLE agent_registry ADD COLUMN {col_name} REAL DEFAULT {col_default}'
232
+ )
233
+ except Exception:
234
+ pass # Column already exists
235
+
172
236
  conn.commit()
173
237
 
174
238
  mgr.execute_write(_create)
@@ -189,11 +253,108 @@ class TrustScorer:
189
253
  ''')
190
254
  conn.execute('CREATE INDEX IF NOT EXISTS idx_trust_agent ON trust_signals(agent_id)')
191
255
  conn.execute('CREATE INDEX IF NOT EXISTS idx_trust_created ON trust_signals(created_at)')
256
+
257
+ # Add trust_alpha and trust_beta columns (backward compatible)
258
+ for col_name, col_default in [("trust_alpha", INITIAL_ALPHA),
259
+ ("trust_beta", INITIAL_BETA)]:
260
+ try:
261
+ conn.execute(
262
+ f'ALTER TABLE agent_registry ADD COLUMN {col_name} REAL DEFAULT {col_default}'
263
+ )
264
+ except sqlite3.OperationalError:
265
+ pass # Column already exists
266
+
192
267
  conn.commit()
193
268
  conn.close()
194
269
 
195
270
  # =========================================================================
196
- # Signal Recording
271
+ # Beta Parameter Management
272
+ # =========================================================================
273
+
274
+ def _get_beta_params(self, agent_id: str) -> tuple:
275
+ """
276
+ Get (alpha, beta) for an agent. Checks in-memory cache first,
277
+ then database, then falls back to prior defaults.
278
+
279
+ Returns:
280
+ (alpha, beta) tuple
281
+ """
282
+ with self._beta_lock:
283
+ if agent_id in self._beta_params:
284
+ return self._beta_params[agent_id]
285
+
286
+ # Not in cache — read from database
287
+ alpha, beta = None, None
288
+ try:
289
+ from db_connection_manager import DbConnectionManager
290
+ mgr = DbConnectionManager.get_instance(self.db_path)
291
+
292
+ with mgr.read_connection() as conn:
293
+ cursor = conn.cursor()
294
+ cursor.execute(
295
+ "SELECT trust_alpha, trust_beta FROM agent_registry WHERE agent_id = ?",
296
+ (agent_id,)
297
+ )
298
+ row = cursor.fetchone()
299
+ if row:
300
+ alpha = row[0]
301
+ beta = row[1]
302
+ except Exception:
303
+ pass
304
+
305
+ # Fall back to defaults if NULL or missing
306
+ if alpha is None or beta is None:
307
+ alpha = INITIAL_ALPHA
308
+ beta = INITIAL_BETA
309
+
310
+ with self._beta_lock:
311
+ self._beta_params[agent_id] = (alpha, beta)
312
+
313
+ return (alpha, beta)
314
+
315
+ def _set_beta_params(self, agent_id: str, alpha: float, beta: float):
316
+ """
317
+ Update (alpha, beta) in cache and persist to agent_registry.
318
+ Also computes and stores the derived trust_score = alpha/(alpha+beta).
319
+ """
320
+ trust_score = alpha / (alpha + beta) if (alpha + beta) > 0 else 0.0
321
+
322
+ with self._beta_lock:
323
+ self._beta_params[agent_id] = (alpha, beta)
324
+
325
+ try:
326
+ from db_connection_manager import DbConnectionManager
327
+ mgr = DbConnectionManager.get_instance(self.db_path)
328
+
329
+ def _update(conn):
330
+ conn.execute(
331
+ """UPDATE agent_registry
332
+ SET trust_score = ?, trust_alpha = ?, trust_beta = ?
333
+ WHERE agent_id = ?""",
334
+ (round(trust_score, 4), round(alpha, 4), round(beta, 4), agent_id)
335
+ )
336
+ conn.commit()
337
+
338
+ mgr.execute_write(_update)
339
+ except Exception as e:
340
+ logger.error("Failed to persist Beta params for %s: %s", agent_id, e)
341
+
342
+ def _apply_decay(self, agent_id: str, alpha: float, beta: float) -> tuple:
343
+ """
344
+ Apply periodic decay to alpha and beta to forget very old signals.
345
+
346
+ Called every DECAY_INTERVAL signals per agent.
347
+ Multiplies both by DECAY_FACTOR with floor constraints.
348
+
349
+ Returns:
350
+ (decayed_alpha, decayed_beta)
351
+ """
352
+ new_alpha = max(ALPHA_FLOOR, alpha * DECAY_FACTOR)
353
+ new_beta = max(BETA_FLOOR, beta * DECAY_FACTOR)
354
+ return (new_alpha, new_beta)
355
+
356
+ # =========================================================================
357
+ # Signal Recording (Beta-Binomial Update)
197
358
  # =========================================================================
198
359
 
199
360
  def record_signal(
@@ -203,50 +364,68 @@ class TrustScorer:
203
364
  context: Optional[dict] = None,
204
365
  ) -> bool:
205
366
  """
206
- Record a trust signal for an agent.
367
+ Record a trust signal for an agent using Beta-Binomial update.
207
368
 
208
- Silently adjusts the agent's trust score based on the signal type.
209
- The signal and score change are logged to trust_signals table.
369
+ Positive signals increment alpha (trust evidence).
370
+ Negative signals increment beta (distrust evidence).
371
+ Neutral signals give a tiny alpha nudge.
372
+
373
+ Trust score = alpha / (alpha + beta) — the posterior mean.
210
374
 
211
375
  Args:
212
376
  agent_id: Agent that generated the signal
213
- signal_type: One of SIGNAL_DELTAS keys
377
+ signal_type: One of SIGNAL_WEIGHTS keys
214
378
  context: Additional context (memory_id, etc.)
379
+
380
+ Returns:
381
+ True if signal was recorded successfully
215
382
  """
216
- if signal_type not in SIGNAL_DELTAS:
383
+ if signal_type not in SIGNAL_WEIGHTS:
217
384
  logger.warning("Unknown trust signal: %s", signal_type)
218
- return
385
+ return False
386
+
387
+ direction, weight = SIGNAL_WEIGHTS[signal_type]
219
388
 
220
- delta = SIGNAL_DELTAS[signal_type]
389
+ # Get current Beta parameters
390
+ alpha, beta = self._get_beta_params(agent_id)
391
+ old_score = alpha / (alpha + beta) if (alpha + beta) > 0 else 0.0
221
392
 
222
- # Get current trust score from agent registry
223
- old_score = self._get_agent_trust(agent_id)
224
- if old_score is None:
225
- old_score = 1.0 # Default for unknown agents
393
+ # Apply Beta-Binomial update
394
+ if direction == "positive":
395
+ alpha += weight
396
+ elif direction == "negative":
397
+ beta += weight
398
+ else: # neutral — tiny alpha nudge
399
+ alpha += weight
226
400
 
227
- # Apply decay factor (score stabilizes over time)
228
- count = self._signal_counts.get(agent_id, 0)
229
- decay = 1.0 / (1.0 + count * 0.01)
230
- adjusted_delta = delta * decay
401
+ # Apply periodic decay
402
+ count = self._signal_counts.get(agent_id, 0) + 1
403
+ self._signal_counts[agent_id] = count
231
404
 
232
- # Calculate new score (clamped to [0.0, 1.0])
233
- new_score = max(0.0, min(1.0, old_score + adjusted_delta))
405
+ if count % DECAY_INTERVAL == 0:
406
+ alpha, beta = self._apply_decay(agent_id, alpha, beta)
234
407
 
235
- # Update signal count
236
- self._signal_counts[agent_id] = count + 1
408
+ # Compute new trust score (posterior mean)
409
+ new_score = alpha / (alpha + beta) if (alpha + beta) > 0 else 0.0
410
+
411
+ # Compute delta for audit trail (backward compatible with trust_signals table)
412
+ delta = new_score - old_score
237
413
 
238
414
  # Persist signal to audit trail
239
- self._persist_signal(agent_id, signal_type, adjusted_delta, old_score, new_score, context)
415
+ self._persist_signal(agent_id, signal_type, delta, old_score, new_score, context)
240
416
 
241
- # Update agent trust score (if score actually changed)
242
- if abs(new_score - old_score) > 0.0001:
243
- self._update_agent_trust(agent_id, new_score)
417
+ # Persist updated Beta parameters and derived trust_score
418
+ self._set_beta_params(agent_id, alpha, beta)
244
419
 
245
420
  logger.debug(
246
- "Trust signal: agent=%s, type=%s, delta=%.4f, score=%.4f→%.4f",
247
- agent_id, signal_type, adjusted_delta, old_score, new_score
421
+ "Trust signal: agent=%s, type=%s (%s, w=%.2f), "
422
+ "alpha=%.2f, beta=%.2f, score=%.4f->%.4f",
423
+ agent_id, signal_type, direction, weight,
424
+ alpha, beta, old_score, new_score
248
425
  )
249
426
 
427
+ return True
428
+
250
429
  def _persist_signal(self, agent_id, signal_type, delta, old_score, new_score, context):
251
430
  """Save signal to trust_signals table."""
252
431
  try:
@@ -265,7 +444,12 @@ class TrustScorer:
265
444
  logger.error("Failed to persist trust signal: %s", e)
266
445
 
267
446
  def _get_agent_trust(self, agent_id: str) -> Optional[float]:
268
- """Get current trust score from agent_registry."""
447
+ """
448
+ Get current trust score from agent_registry.
449
+
450
+ This reads the derived trust_score column (which is always kept
451
+ in sync with alpha/(alpha+beta) by _set_beta_params).
452
+ """
269
453
  try:
270
454
  from db_connection_manager import DbConnectionManager
271
455
  mgr = DbConnectionManager.get_instance(self.db_path)
@@ -282,7 +466,13 @@ class TrustScorer:
282
466
  return None
283
467
 
284
468
  def _update_agent_trust(self, agent_id: str, new_score: float):
285
- """Update trust score in agent_registry."""
469
+ """
470
+ Update trust score in agent_registry (legacy compatibility method).
471
+
472
+ In Beta-Binomial mode, this is a no-op because _set_beta_params
473
+ already updates trust_score alongside alpha and beta. Kept for
474
+ backward compatibility if any external code calls it directly.
475
+ """
286
476
  try:
287
477
  from db_connection_manager import DbConnectionManager
288
478
  mgr = DbConnectionManager.get_instance(self.db_path)
@@ -373,16 +563,40 @@ class TrustScorer:
373
563
  # =========================================================================
374
564
 
375
565
  def get_trust_score(self, agent_id: str) -> float:
376
- """Get current trust score for an agent. Returns 1.0 if unknown."""
377
- score = self._get_agent_trust(agent_id)
378
- return score if score is not None else 1.0
566
+ """
567
+ Get current trust score for an agent.
568
+
569
+ Computes alpha/(alpha+beta) from cached or stored Beta params.
570
+ Returns INITIAL_ALPHA/(INITIAL_ALPHA+INITIAL_BETA) = 0.667 for
571
+ unknown agents.
572
+ """
573
+ alpha, beta = self._get_beta_params(agent_id)
574
+ if (alpha + beta) > 0:
575
+ return alpha / (alpha + beta)
576
+ return INITIAL_ALPHA / (INITIAL_ALPHA + INITIAL_BETA)
577
+
578
+ def get_beta_params(self, agent_id: str) -> Dict[str, float]:
579
+ """
580
+ Get the Beta distribution parameters for an agent.
581
+
582
+ Returns:
583
+ {"alpha": float, "beta": float, "trust_score": float}
584
+ """
585
+ alpha, beta = self._get_beta_params(agent_id)
586
+ score = alpha / (alpha + beta) if (alpha + beta) > 0 else 0.0
587
+ return {
588
+ "alpha": round(alpha, 4),
589
+ "beta": round(beta, 4),
590
+ "trust_score": round(score, 4),
591
+ }
379
592
 
380
593
  def check_trust(self, agent_id: str, operation: str = "write") -> bool:
381
594
  """
382
595
  Check if agent is trusted enough for the given operation.
383
596
 
384
597
  v2.6 enforcement: blocks write/delete for agents with trust < 0.3.
385
- New agents start at 1.0 — only repeated bad behavior triggers blocking.
598
+ New agents start at Beta(2,1) → trust 0.667 — only repeated bad
599
+ behavior triggers blocking.
386
600
 
387
601
  Args:
388
602
  agent_id: The agent identifier
@@ -394,14 +608,12 @@ class TrustScorer:
394
608
  if operation == "read":
395
609
  return True # Reads are always allowed
396
610
 
397
- score = self._get_agent_trust(agent_id)
398
- if score is None:
399
- return True # Unknown agent = first-time = allowed (starts at 1.0)
611
+ score = self.get_trust_score(agent_id)
400
612
 
401
613
  threshold = 0.3 # Block write/delete below this
402
614
  if score < threshold:
403
615
  logger.warning(
404
- "Trust enforcement: agent '%s' blocked from '%s' (trust=%.2f < %.2f)",
616
+ "Trust enforcement: agent '%s' blocked from '%s' (trust=%.4f < %.2f)",
405
617
  agent_id, operation, score, threshold
406
618
  )
407
619
  return False
@@ -479,7 +691,9 @@ class TrustScorer:
479
691
  "total_signals": total_signals,
480
692
  "by_signal_type": by_type,
481
693
  "by_agent": by_agent,
482
- "avg_trust_score": round(avg, 4) if avg else 1.0,
694
+ "avg_trust_score": round(avg, 4) if avg else INITIAL_ALPHA / (INITIAL_ALPHA + INITIAL_BETA),
695
+ "scoring_model": "Beta-Binomial",
696
+ "prior": f"Beta({INITIAL_ALPHA}, {INITIAL_BETA})",
483
697
  "enforcement": "enabled (v2.6 — write/delete blocked below 0.3 trust)",
484
698
  }
485
699
 
package/ui/app.js CHANGED
@@ -1445,9 +1445,9 @@ async function loadAgents() {
1445
1445
 
1446
1446
  // Trust score
1447
1447
  var tdTrust = document.createElement('td');
1448
- var trustScore = agent.trust_score != null ? agent.trust_score : 1.0;
1449
- tdTrust.className = trustScore < 0.7 ? 'text-danger fw-bold'
1450
- : trustScore < 0.9 ? 'text-warning fw-bold' : 'text-success fw-bold';
1448
+ var trustScore = agent.trust_score != null ? agent.trust_score : 0.667;
1449
+ tdTrust.className = trustScore < 0.3 ? 'text-danger fw-bold'
1450
+ : trustScore < 0.5 ? 'text-warning fw-bold' : 'text-success fw-bold';
1451
1451
  tdTrust.textContent = trustScore.toFixed(2);
1452
1452
  tr.appendChild(tdTrust);
1453
1453
 
@@ -1524,7 +1524,7 @@ async function loadTrustOverview() {
1524
1524
  card2.className = 'border rounded p-3 text-center';
1525
1525
  var val2 = document.createElement('div');
1526
1526
  val2.className = 'fs-4 fw-bold';
1527
- val2.textContent = (stats.avg_trust_score || 1.0).toFixed(3);
1527
+ val2.textContent = (stats.avg_trust_score || 0.667).toFixed(3);
1528
1528
  card2.appendChild(val2);
1529
1529
  var lbl2 = document.createElement('small');
1530
1530
  lbl2.className = 'text-muted';
package/ui/index.html CHANGED
@@ -742,6 +742,10 @@
742
742
  </li>
743
743
  </ul>
744
744
 
745
+ <!-- Privacy Notice & Feedback Progress (v2.7.4) -->
746
+ <div id="privacy-notice"></div>
747
+ <div id="feedback-progress" class="mb-3"></div>
748
+
745
749
  <div class="tab-content">
746
750
  <!-- Graph Visualization -->
747
751
  <div class="tab-pane fade show active" id="graph-pane">
@@ -956,6 +960,20 @@
956
960
  </div>
957
961
  </div>
958
962
 
963
+ <!-- What We Learned (v2.7.4 — Summary Card) -->
964
+ <div class="card p-3 mb-3" id="what-we-learned-card">
965
+ <div class="d-flex justify-content-between align-items-center mb-3">
966
+ <h6 class="mb-0"><i class="bi bi-lightbulb"></i> What SuperLocalMemory Learned About You</h6>
967
+ <span class="badge bg-success" id="learned-profile-badge">default</span>
968
+ </div>
969
+ <div id="what-we-learned-content">
970
+ <div class="text-center text-muted py-3">
971
+ <i class="bi bi-hourglass-split" style="font-size: 1.5rem;"></i>
972
+ <p class="mt-2 mb-0 small">Loading learned insights...</p>
973
+ </div>
974
+ </div>
975
+ </div>
976
+
959
977
  <div class="row g-3">
960
978
  <!-- Tech Preferences (Layer 1) -->
961
979
  <div class="col-md-6">
@@ -1239,6 +1257,25 @@
1239
1257
  </div>
1240
1258
  </div>
1241
1259
  </div>
1260
+ <!-- Learning Data Management (v2.7.4) -->
1261
+ <div class="card p-3 mb-3">
1262
+ <h5 class="mb-3"><i class="bi bi-brain"></i> Learning Data</h5>
1263
+ <p class="text-muted small mb-3">
1264
+ SuperLocalMemory learns from your usage patterns to improve recall results.
1265
+ All learning data is stored locally in <code>~/.claude-memory/learning.db</code>.
1266
+ </p>
1267
+ <div id="learning-data-stats" class="mb-3"></div>
1268
+ <div class="d-flex gap-2">
1269
+ <button class="btn btn-outline-danger" onclick="resetLearningData()">
1270
+ <i class="bi bi-arrow-counterclockwise"></i> Reset Learning Data
1271
+ </button>
1272
+ <button class="btn btn-outline-info" onclick="backupLearningDb()">
1273
+ <i class="bi bi-download"></i> Backup Learning DB
1274
+ </button>
1275
+ </div>
1276
+ <small class="text-muted mt-2 d-block">Reset clears all learned preferences, feedback signals, and patterns. Your memories are preserved.</small>
1277
+ </div>
1278
+
1242
1279
  <!-- Backup History -->
1243
1280
  <div class="card p-3">
1244
1281
  <h5 class="mb-3"><i class="bi bi-clock-history"></i> Backup History</h5>
@@ -1319,6 +1356,7 @@
1319
1356
  <script src="static/js/events.js"></script>
1320
1357
  <script src="static/js/agents.js"></script>
1321
1358
  <script src="static/js/learning.js"></script>
1359
+ <script src="static/js/feedback.js"></script>
1322
1360
  <script src="static/js/init.js"></script>
1323
1361
 
1324
1362
  <footer>
package/ui/js/agents.js CHANGED
@@ -78,9 +78,9 @@ async function loadAgents() {
78
78
  tr.appendChild(tdProto);
79
79
 
80
80
  var tdTrust = document.createElement('td');
81
- var trustScore = agent.trust_score != null ? agent.trust_score : 1.0;
82
- tdTrust.className = trustScore < 0.7 ? 'text-danger fw-bold'
83
- : trustScore < 0.9 ? 'text-warning fw-bold' : 'text-success fw-bold';
81
+ var trustScore = agent.trust_score != null ? agent.trust_score : 0.667;
82
+ tdTrust.className = trustScore < 0.3 ? 'text-danger fw-bold'
83
+ : trustScore < 0.5 ? 'text-warning fw-bold' : 'text-success fw-bold';
84
84
  tdTrust.textContent = trustScore.toFixed(2);
85
85
  tr.appendChild(tdTrust);
86
86
 
@@ -133,7 +133,7 @@ async function loadTrustOverview() {
133
133
 
134
134
  var cardData = [
135
135
  { value: (stats.total_signals || 0).toLocaleString(), label: 'Total Signals Collected', cls: '' },
136
- { value: (stats.avg_trust_score || 1.0).toFixed(3), label: 'Average Trust Score', cls: '' },
136
+ { value: (stats.avg_trust_score || 0.667).toFixed(3), label: 'Average Trust Score', cls: '' },
137
137
  { value: stats.enforcement || 'disabled', label: 'Enforcement Status', cls: 'text-info' }
138
138
  ];
139
139