@miller-tech/uap 1.109.0 → 1.110.0

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.
@@ -685,6 +685,16 @@
685
685
  renderLiveEvents();
686
686
  } catch {}
687
687
  };
688
+ // Full live snapshot (cross-process) — used when the WebSocket is down so
689
+ // SSE alone keeps the dashboard live rather than falling to slow polling. (C)
690
+ eventSource.addEventListener('snapshot', (e) => {
691
+ if (ws && ws.readyState === WebSocket.OPEN) return;
692
+ try {
693
+ render(JSON.parse(e.data));
694
+ document.getElementById('ws-status').className = 'status-dot connected';
695
+ document.getElementById('refresh-info').textContent = 'Live (SSE) - ' + new Date().toLocaleTimeString();
696
+ } catch {}
697
+ });
688
698
  eventSource.onerror = () => { eventSource.close(); setTimeout(connectSSE, 5000); };
689
699
  } catch {}
690
700
  }
@@ -1164,10 +1174,18 @@
1164
1174
  `<strong style="color:#58a6ff;font-size:18px">${fmt(sv.totalTokensSaved)}</strong> tokens · ` +
1165
1175
  `<strong style="color:#3fb950;font-size:18px">$${(sv.totalCostSavedUsd||0).toFixed(2)}</strong> saved across all UAP influences`);
1166
1176
  if (!sv.influences.length) { setHTML('savings-table', '<div class="empty">No savings data</div>'); return; }
1167
- const rows = sv.influences.map(i =>
1168
- `<tr><td>${esc(i.influence)}</td><td style="text-align:right">${fmt(i.tokensSaved)}</td>` +
1169
- `<td style="text-align:right;color:#3fb950">$${(i.costSavedUsd||0).toFixed(4)}</td>` +
1170
- `<td>${badge(i.quality)}</td><td style="color:var(--fg3);font-size:11px">${esc(i.detail||'')}</td></tr>`).join('');
1177
+ // Unmeasured influences (e.g. routing with no UAP-driven calls yet) render an
1178
+ // explicit dimmed "—" rather than a real-looking $0, so idle != broken.
1179
+ const idleCell = '<span style="color:var(--fg3)" title="not active / no data recorded yet">\u2014</span>';
1180
+ const rows = sv.influences.map(i => {
1181
+ const idle = i.quality === 'unmeasured';
1182
+ const tok = idle ? idleCell : fmt(i.tokensSaved);
1183
+ const cost = idle ? idleCell : `<span style="color:#3fb950">$${(i.costSavedUsd||0).toFixed(4)}</span>`;
1184
+ return `<tr${idle ? ' style="opacity:.55"' : ''}><td>${esc(i.influence)}</td>` +
1185
+ `<td style="text-align:right">${tok}</td>` +
1186
+ `<td style="text-align:right">${cost}</td>` +
1187
+ `<td>${badge(i.quality)}</td><td style="color:var(--fg3);font-size:11px">${esc(i.detail||'')}</td></tr>`;
1188
+ }).join('');
1171
1189
  setHTML('savings-table',
1172
1190
  `<table><thead><tr><th>Influence</th><th style="text-align:right">Tokens saved</th>` +
1173
1191
  `<th style="text-align:right">Cost saved</th><th>Quality</th><th>Detail</th></tr></thead><tbody>${rows}</tbody></table>`);