thinknagent 0.1.23 → 0.1.25

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/lib/history.js CHANGED
@@ -56,6 +56,7 @@ class HistoryManager {
56
56
  load: metrics.cpu?.loadAvg ?? 0,
57
57
  mem: metrics.memory?.usedPct ?? 0,
58
58
  memUsedMB: Math.round((metrics.memory?.used || 0) / (1024 * 1024)),
59
+ buffcache: metrics.memory?.buffcache ?? 0,
59
60
  rxSec: metrics.network?.[0]?.rxSec ?? 0,
60
61
  txSec: metrics.network?.[0]?.txSec ?? 0,
61
62
  procs: metrics.processes?.total ?? 0,
package/lib/metrics.js CHANGED
@@ -47,6 +47,12 @@ class MetricsPoller {
47
47
  si.processes(),
48
48
  ]);
49
49
 
50
+ // True active process memory (excluding OS buffer / page cache)
51
+ const realUsedBytes = (typeof mem.available === 'number' && mem.available > 0)
52
+ ? (mem.total - mem.available)
53
+ : (mem.active || (mem.used - (mem.buffcache || 0)));
54
+ const realUsedPct = parseFloat(Math.min(100, Math.max(0, (realUsedBytes / mem.total) * 100)).toFixed(1));
55
+
50
56
  const payload = {
51
57
  ts: Date.now(),
52
58
  cpu: {
@@ -55,10 +61,15 @@ class MetricsPoller {
55
61
  loadAvg: cpu.avgLoad ?? 0,
56
62
  },
57
63
  memory: {
58
- total: mem.total,
59
- used: mem.used,
60
- free: mem.free,
61
- usedPct: parseFloat(((mem.used / mem.total) * 100).toFixed(1)),
64
+ total: mem.total,
65
+ active: realUsedBytes,
66
+ activePct: realUsedPct,
67
+ buffcache: mem.buffcache || Math.max(0, mem.used - realUsedBytes),
68
+ rawUsed: mem.used,
69
+ rawUsedPct: parseFloat(((mem.used / mem.total) * 100).toFixed(1)),
70
+ free: mem.available || mem.free,
71
+ used: realUsedBytes,
72
+ usedPct: realUsedPct,
62
73
  },
63
74
  disk: disk.map(d => ({
64
75
  fs: d.fs,
@@ -130,10 +141,18 @@ class MetricsPoller {
130
141
 
131
142
  // ─── Automated Spike Diagnostic & Root Cause Engine ────────────────────────
132
143
  const curCpuUsage = parseFloat(cpu.currentLoad.toFixed(1));
133
- const curMemPct = parseFloat(((mem.used / mem.total) * 100).toFixed(1));
134
- const sortedProcs = (procs.list || []).slice().sort((a, b) => (b.pmem || 0) - (a.pmem || 0));
135
- const topMemProc = sortedProcs[0] || null;
136
- const topCpuProc = (procs.list || []).slice().sort((a, b) => (b.pcpu || 0) - (a.pcpu || 0))[0] || null;
144
+ const curMemPct = realUsedPct;
145
+
146
+ // Filter real user/application processes with actual resource footprint
147
+ const sortedMemProcs = (procs.list || [])
148
+ .filter(p => p && p.pid > 1 && (p.pmem > 0.5 || p.pcpu > 0.5))
149
+ .sort((a, b) => (b.pmem || 0) - (a.pmem || 0));
150
+ const topMemProc = sortedMemProcs[0] || (procs.list || []).sort((a, b) => (b.pmem || 0) - (a.pmem || 0))[0] || null;
151
+
152
+ const sortedCpuProcs = (procs.list || [])
153
+ .filter(p => p && p.pid > 1 && (p.pcpu > 0.5 || p.pmem > 0.5))
154
+ .sort((a, b) => (b.pcpu || 0) - (a.pcpu || 0));
155
+ const topCpuProc = sortedCpuProcs[0] || (procs.list || []).sort((a, b) => (b.pcpu || 0) - (a.pcpu || 0))[0] || null;
137
156
 
138
157
  let diagnostics = {
139
158
  hasSpike: false,
@@ -150,35 +169,44 @@ class MetricsPoller {
150
169
  this._lastMemPct = curMemPct;
151
170
  this._lastCpuUsage = curCpuUsage;
152
171
 
153
- if (curMemPct >= 80 || memDelta >= 3.5) {
172
+ const topMemMB = topMemProc ? Math.round(((topMemProc.pmem || 0) / 100) * (mem.total / (1024 * 1024))) : 0;
173
+ const topMemPct = topMemProc ? parseFloat((topMemProc.pmem || 0).toFixed(1)) : 0;
174
+ const hasRealMemCulprit = topMemProc && (topMemPct >= 3.0 || topMemMB >= 35);
175
+
176
+ // Trigger spike only on sudden surge OR critically high memory with an identified culprit
177
+ if (memDelta >= 5.0 || (curMemPct >= 90 && hasRealMemCulprit)) {
154
178
  diagnostics.hasSpike = true;
155
179
  diagnostics.type = 'memory';
156
180
  diagnostics.deltaPct = memDelta;
157
- if (topMemProc) {
158
- const procMemMB = Math.round(((topMemProc.pmem || 0) / 100) * (mem.total / (1024 * 1024)));
181
+ if (hasRealMemCulprit) {
159
182
  diagnostics.culprit = {
160
183
  pid: topMemProc.pid,
161
184
  name: topMemProc.name,
162
185
  command: topMemProc.command ? topMemProc.command.slice(0, 80) : topMemProc.name,
163
- memPct: parseFloat((topMemProc.pmem || 0).toFixed(1)),
164
- memMB: procMemMB,
186
+ memPct: topMemPct,
187
+ memMB: topMemMB,
165
188
  cpu: parseFloat((topMemProc.pcpu || 0).toFixed(1))
166
189
  };
167
- diagnostics.explanation = `Memory surge detected (${curMemPct}% RAM, delta: ${memDelta >= 0 ? '+' : ''}${memDelta}%). Primary consumer is "${topMemProc.name}" (PID ${topMemProc.pid}) utilizing ${diagnostics.culprit.memPct}% (${procMemMB} MB). Probable cause: Rapid memory allocation, large dataset buffer in memory, or memory leak.`;
190
+ diagnostics.explanation = `Memory surge detected (${curMemPct}% RAM, delta: ${memDelta >= 0 ? '+' : ''}${memDelta}%). Primary consumer is "${topMemProc.name}" (PID ${topMemProc.pid}) utilizing ${topMemPct}% (${topMemMB} MB). Probable cause: Rapid memory allocation, large dataset buffer in memory, or memory leak.`;
191
+ } else {
192
+ diagnostics.explanation = `Memory load is elevated (${curMemPct}% RAM, delta: ${memDelta >= 0 ? '+' : ''}${memDelta}%), distributed across system caches and background services. No single runaway process.`;
168
193
  }
169
- } else if (curCpuUsage >= 80 || cpuDelta >= 25.0) {
194
+ } else if (cpuDelta >= 30.0 || (curCpuUsage >= 85 && topCpuProc && (topCpuProc.pcpu || 0) >= 10.0)) {
195
+ const topCpuVal = topCpuProc ? parseFloat((topCpuProc.pcpu || 0).toFixed(1)) : 0;
170
196
  diagnostics.hasSpike = true;
171
197
  diagnostics.type = 'cpu';
172
198
  diagnostics.deltaPct = cpuDelta;
173
- if (topCpuProc) {
199
+ if (topCpuProc && topCpuVal >= 5.0) {
174
200
  diagnostics.culprit = {
175
201
  pid: topCpuProc.pid,
176
202
  name: topCpuProc.name,
177
203
  command: topCpuProc.command ? topCpuProc.command.slice(0, 80) : topCpuProc.name,
178
- cpu: parseFloat((topCpuProc.pcpu || 0).toFixed(1)),
204
+ cpu: topCpuVal,
179
205
  memPct: parseFloat((topCpuProc.pmem || 0).toFixed(1))
180
206
  };
181
- diagnostics.explanation = `High CPU workload spike detected (${curCpuUsage}% CPU, delta: ${cpuDelta >= 0 ? '+' : ''}${cpuDelta}%). Primary consumer is "${topCpuProc.name}" (PID ${topCpuProc.pid}) utilizing ${diagnostics.culprit.cpu}% CPU. Probable cause: Intensive computation, complex query/loop execution, or heavy background processing.`;
207
+ diagnostics.explanation = `High CPU workload spike detected (${curCpuUsage}% CPU, delta: ${cpuDelta >= 0 ? '+' : ''}${cpuDelta}%). Primary consumer is "${topCpuProc.name}" (PID ${topCpuProc.pid}) utilizing ${topCpuVal}% CPU. Probable cause: Intensive computation, complex query/loop execution, or heavy background processing.`;
208
+ } else {
209
+ diagnostics.explanation = `High CPU utilization (${curCpuUsage}% CPU, delta: ${cpuDelta >= 0 ? '+' : ''}${cpuDelta}%) spread across multiple threads.`;
182
210
  }
183
211
  }
184
212
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinknagent",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "ThinkNCollab server agent & MCP server — metrics, logs, alerts, shell bridge, AI planning",
5
5
  "main": "lib/agent.js",
6
6
  "author": "ThinkNCollab Team <raman@thinkncollab.com>",