Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 50 additions & 11 deletions src/voice/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,55 @@ function isMomentWorthy(text: string): boolean {
}

let vitalsTokenCount = 0;

// ── Centralized vitals snapshot ────────────────────────────────────────
// All surfaces (API, UI, logs) share the same cached snapshot so values
// are always consistent regardless of who reads them or when.
interface VitalsSnapshot {
cpu: number;
mem: number;
heapUsed: number;
heapTotal: number;
uptime: number;
tokens: number;
ts: number; // unix-ms when this snapshot was taken
}

let _lastCpuUsage = process.cpuUsage();
let _lastCpuTime = process.hrtime.bigint();
let _vitalsCache: VitalsSnapshot | null = null;
const VITALS_CACHE_MS = 1000; // cache for 1s — all readers within 1s see identical values

function getVitalsSnapshot(): VitalsSnapshot {
const now = Date.now();
if (_vitalsCache && now - _vitalsCache.ts < VITALS_CACHE_MS) return _vitalsCache;

const mem = process.memoryUsage();
const currentCpu = process.cpuUsage();
const currentTime = process.hrtime.bigint();

// Delta-based CPU: measure CPU microseconds consumed since last sample
const userDelta = currentCpu.user - _lastCpuUsage.user;
const sysDelta = currentCpu.system - _lastCpuUsage.system;
const wallDeltaUs = Number(currentTime - _lastCpuTime) / 1000; // ns → µs
const cpuPercent = wallDeltaUs > 0
? Math.min(100, Math.round((userDelta + sysDelta) / wallDeltaUs * 100))
: 0;

_lastCpuUsage = currentCpu;
_lastCpuTime = currentTime;

_vitalsCache = {
cpu: cpuPercent,
mem: Math.round(mem.rss / 1024 / 1024),
heapUsed: Math.round(mem.heapUsed / 1024 / 1024),
heapTotal: Math.round(mem.heapTotal / 1024 / 1024),
uptime: Math.round(process.uptime()),
tokens: vitalsTokenCount,
ts: now,
};
return _vitalsCache;
}
const PHOTOS_DIR = "memory/photos";
const INDEX_FILE = "memory/photos/INDEX.md";
const LATEST_FRAME_FILE = "memory/.latest-frame.jpg";
Expand Down Expand Up @@ -1595,17 +1644,7 @@ ${runningContext}`;
jsonReply(res, 200, { status: "ok" });

} else if (url.pathname === "/api/vitals") {
const mem = process.memoryUsage();
const cpuUsage = process.cpuUsage();
const cpuPercent = Math.min(100, Math.round((cpuUsage.user + cpuUsage.system) / 1000 / (process.uptime() * 10000)));
jsonReply(res, 200, {
cpu: cpuPercent,
mem: Math.round(mem.rss / 1024 / 1024),
heapUsed: Math.round(mem.heapUsed / 1024 / 1024),
heapTotal: Math.round(mem.heapTotal / 1024 / 1024),
uptime: Math.round(process.uptime()),
tokens: vitalsTokenCount,
});
jsonReply(res, 200, getVitalsSnapshot());

} else if (url.pathname === "/" || url.pathname === "/test") {
res.writeHead(200, { "Content-Type": "text/html" });
Expand Down
15 changes: 11 additions & 4 deletions src/voice/ui.html
Original file line number Diff line number Diff line change
Expand Up @@ -1747,14 +1747,19 @@ <h3 style="margin:0;color:#8b949e;font-size:12px;text-transform:uppercase;letter
function sendMsg(m){if(ws&&ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify(m));}

// Agent Vitals
var vitalsStart=Date.now(), vitalsTokenCount=0, vitalsMaxTokens=200000;
var vitalsTokenCount=0, vitalsMaxTokens=200000, vitalsServerUptime=0;
var waveData=[], waveCanvas=document.getElementById('vitalsWaveCanvas'), waveCtx=waveCanvas?waveCanvas.getContext('2d'):null;

function updateUptime(){
var s=Math.floor((Date.now()-vitalsStart)/1000);
function formatUptime(s){
var h=Math.floor(s/3600), m=Math.floor((s%3600)/60), sec=s%60;
return (h>0?(h+':'):'')+String(m).padStart(2,'0')+':'+String(sec).padStart(2,'0');
}
function updateUptime(){
// Interpolate locally between API polls for smooth display,
// but base value always comes from the server snapshot
vitalsServerUptime++;
var el=document.getElementById('vitalUptime');
if(el) el.innerHTML=(h>0?(h+':'):'')+String(m).padStart(2,'0')+':'+String(sec).padStart(2,'0');
if(el) el.innerHTML=formatUptime(vitalsServerUptime);
}
setInterval(updateUptime,1000);

Expand Down Expand Up @@ -1782,6 +1787,8 @@ <h3 style="margin:0;color:#8b949e;font-size:12px;text-transform:uppercase;letter
if(memBar)memBar.style.width=Math.min(100,Math.round(mb/4096*100))+'%';
}
if(d.tokens!==undefined) updateVitalTokens(d.tokens);
// Sync uptime from server snapshot so UI matches API
if(d.uptime!==undefined) vitalsServerUptime=d.uptime;
// Push to wave
waveData.push(d.cpu||0);
if(waveData.length>60) waveData.shift();
Expand Down