From e094bba323689ee5902a69889873f893a3063418 Mon Sep 17 00:00:00 2001 From: Vivek Chand Date: Mon, 23 Mar 2026 10:12:52 +0100 Subject: [PATCH 1/3] feat: token velocity alert for runaway loop detection (closes #313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sliding 2-min window token threshold (default: 10,000 tokens/2min) - Consecutive tool-call chain detection (default: N=20 without human turn) - Cost velocity alert (default: $0.10/min over 5-min window) - In-dashboard banner with Kill Loop + Dismiss buttons (animated, red/orange) - Background thread checks velocity every 30s - Telegram notification on new alert types - GET /api/alerts/velocity โ€” current alert state - POST /api/alerts/velocity/config โ€” configure thresholds - POST /api/alerts/velocity/dismiss โ€” dismiss active alerts - Config stored in existing budget_config SQLite table (velocity_ prefix) --- dashboard.py | 441 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 424 insertions(+), 17 deletions(-) diff --git a/dashboard.py b/dashboard.py index 4aaf385..6d2748c 100755 --- a/dashboard.py +++ b/dashboard.py @@ -2765,6 +2765,92 @@ def get_local_ip(): + + + + + @@ -8197,6 +8202,11 @@ def get_local_ip():
+
๐Ÿ“… Activity Heatmap 30 days ยท hourly
+
+
Loading...
+
+
@@ -11392,36 +11402,60 @@ def get_local_ip(): } function startSystemHealthRefresh() { loadSystemHealth(); + loadHeatmap(); if (window._sysHealthTimer) clearInterval(window._sysHealthTimer); window._sysHealthTimer = setInterval(loadSystemHealth, 30000); + // Refresh heatmap every 5 minutes (data changes slowly) + if (window._heatmapTimer) clearInterval(window._heatmapTimer); + window._heatmapTimer = setInterval(loadHeatmap, 300000); } -// ===== Activity Heatmap ===== +// ===== Activity Heatmap (30-day hourly grid) ===== async function loadHeatmap() { + var gridEl = document.getElementById('heatmap-grid'); + var legendEl = document.getElementById('heatmap-legend'); + if (!gridEl) return; try { var data = await fetch('/api/heatmap').then(r => r.json()); - var grid = document.getElementById('heatmap-grid'); var maxVal = Math.max(1, data.max); + // Hour labels row var html = '
'; - for (var h = 0; h < 24; h++) { html += '
' + (h < 10 ? '0' : '') + h + '
'; } + for (var h = 0; h < 24; h++) { + html += '
' + (h % 6 === 0 ? (h < 10 ? '0' + h : String(h)) : '') + '
'; + } data.days.forEach(function(day) { - html += '
' + day.label + '
'; + // Show label only every 5 days to avoid crowding on 30-day view + var showLabel = day.label.endsWith('01') || day.label.endsWith('05') || + day.label.endsWith('10') || day.label.endsWith('15') || + day.label.endsWith('20') || day.label.endsWith('25') || + day.label.endsWith('30'); + html += '
' + (showLabel ? day.label : '') + '
'; day.hours.forEach(function(val, hi) { var intensity = val / maxVal; var color; if (val === 0) color = '#12122a'; - else if (intensity < 0.25) color = '#1a3a2a'; - else if (intensity < 0.5) color = '#2a6a3a'; - else if (intensity < 0.75) color = '#4a9a2a'; + else if (intensity < 0.2) color = '#1a3a2a'; + else if (intensity < 0.4) color = '#2a6a3a'; + else if (intensity < 0.6) color = '#3a8a2a'; + else if (intensity < 0.8) color = '#4a9a2a'; else color = '#6adb3a'; - html += '
'; + html += '
'; }); }); - grid.innerHTML = html; - var legend = document.getElementById('heatmap-legend'); - legend.innerHTML = 'Less
More'; + gridEl.innerHTML = html; + if (legendEl) { + legendEl.innerHTML = 'Less\u00a0' + + '
' + + '
' + + '
' + + '
' + + '
' + + '\u00a0More\u00a0\u00b7\u00a0' + data.days.length + ' days'; + } } catch(e) { - document.getElementById('heatmap-grid').innerHTML = 'No activity data'; + gridEl.innerHTML = 'No activity data'; } } diff --git a/tests/test_api.py b/tests/test_api.py index 7b2b678..9915eba 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -443,3 +443,58 @@ def test_memory_analytics_files_have_status(self, api, base_url): assert f["status"] in ("ok", "warning", "critical") + + +class TestHeatmap: + """Tests for the 30-day activity heatmap endpoint.""" + + def test_heatmap_returns_200(self, api, base_url): + """Heatmap endpoint returns 200.""" + r = api.get(f"{base_url}/api/heatmap", timeout=10) + assert r.status_code == 200, f"Expected 200, got {r.status_code}: {r.text[:200]}" + + def test_heatmap_has_required_keys(self, api, base_url): + """Response contains 'days' list and 'max' value.""" + d = assert_ok(get(api, base_url, "/api/heatmap")) + assert_keys(d, "days", "max") + assert isinstance(d["days"], list), "'days' must be a list" + assert isinstance(d["max"], (int, float)), "'max' must be numeric" + + def test_heatmap_returns_30_days(self, api, base_url): + """Heatmap covers exactly 30 days.""" + d = assert_ok(get(api, base_url, "/api/heatmap")) + assert len(d["days"]) == 30, f"Expected 30 days, got {len(d['days'])}" + + def test_heatmap_each_day_has_24_hours(self, api, base_url): + """Every day entry has exactly 24 hourly buckets.""" + d = assert_ok(get(api, base_url, "/api/heatmap")) + for day in d["days"]: + assert "hours" in day, f"Day entry missing 'hours': {day}" + assert len(day["hours"]) == 24, ( + f"Expected 24 hourly buckets, got {len(day['hours'])} for {day.get('label')}" + ) + + def test_heatmap_day_has_label_and_date(self, api, base_url): + """Every day entry has 'label' and 'date' fields.""" + d = assert_ok(get(api, base_url, "/api/heatmap")) + for day in d["days"]: + assert_keys(day, "label", "date", "hours") + + def test_heatmap_hours_are_non_negative_ints(self, api, base_url): + """All hourly counts are non-negative integers.""" + d = assert_ok(get(api, base_url, "/api/heatmap")) + for day in d["days"]: + for count in day["hours"]: + assert isinstance(count, int) and count >= 0, ( + f"Invalid hourly count {count!r} in {day.get('label')}" + ) + + def test_heatmap_max_matches_data(self, api, base_url): + """'max' equals the maximum hourly event count across all days.""" + d = assert_ok(get(api, base_url, "/api/heatmap")) + computed_max = max( + (max(day["hours"]) for day in d["days"]), default=0 + ) + assert d["max"] == computed_max, ( + f"'max' field {d['max']} does not match computed max {computed_max}" + ) From cfebd50e761bd67cd483031381907a006f01f109 Mon Sep 17 00:00:00 2001 From: Vivek Chand Date: Mon, 23 Mar 2026 10:19:33 +0100 Subject: [PATCH 3/3] content: ClawMetry vs NemoClaw + OpenClaw memory monitoring blog posts - docs/blog/nemo-vs-clawmetry.md: comparison targeting GTC buzz Angle: local-first free (ClawMetry) vs enterprise-priced (NemoClaw) Validates the market, highlights ClawMetry's zero-setup advantage - docs/blog/openclaw-memory-monitoring.md: SEO gap filler Targets 'openclaw memory monitoring' (zero existing content) Explains memory drift, context window consumption, ClawMetry analytics Both CTA to 'pip install clawmetry' --- docs/blog/nemo-vs-clawmetry.md | 84 ++++++++++++++++++++++ docs/blog/openclaw-memory-monitoring.md | 93 +++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 docs/blog/nemo-vs-clawmetry.md create mode 100644 docs/blog/openclaw-memory-monitoring.md diff --git a/docs/blog/nemo-vs-clawmetry.md b/docs/blog/nemo-vs-clawmetry.md new file mode 100644 index 0000000..0ca18fc --- /dev/null +++ b/docs/blog/nemo-vs-clawmetry.md @@ -0,0 +1,84 @@ +# ClawMetry vs NemoClaw: Which OpenClaw Observability Tool Is Right for You? + +**TL;DR:** NemoClaw is enterprise observability for OpenClaw with Kubernetes and cloud infrastructure. ClawMetry is free, open source, and runs on your laptop in 30 seconds. Different tools, different audiences. + +NVIDIA just announced NemoClaw at GTC, and the reaction in the OpenClaw community has been fascinating. Some folks are excited. Others are confused. And a surprising number are asking: "Wait, isn't that what ClawMetry already does?" + +Kind of. But not really. Let me break it down. + +## What NemoClaw Is + +NemoClaw is NVIDIA's enterprise observability layer for OpenClaw deployments. It's built on their NeMo Agent Toolkit and targets teams running OpenClaw at scale โ€” think Fortune 500 companies, research labs, and cloud-first organizations. + +Features include multi-node fleet management, cloud dashboards, compliance reporting, and deep integration with Kubernetes. It's designed to plug into existing enterprise observability stacks (Datadog, Dynatrace, OpenTelemetry). + +The target user: a platform engineering team managing dozens of OpenClaw nodes across production, staging, and dev environments. + +## What ClawMetry Is + +ClawMetry is an open source monitoring dashboard for OpenClaw that you can install in 30 seconds: + +```bash +pip install clawmetry +clawmetry +``` + +That's it. Open your browser, and you've got a full dashboard showing agent sessions, token usage, memory file health, brain activity, security posture, and cron job status. + +The target user: a solo developer or small team who wants to understand what their AI agent is actually doing. + +## The Core Difference: Local vs Cloud + +This is the real split. + +**ClawMetry** is local-first. Your data never leaves your machine. The dashboard reads directly from OpenClaw's log files and JSONL session records. No API keys, no accounts, no cloud subscription. Privacy by default. + +**NemoClaw** is cloud-first. It's designed for scenarios where you need centralized visibility across multiple nodes, cloud-hosted dashboards, and enterprise SLAs. That requires infrastructure, and infrastructure costs money. + +Neither approach is wrong. They solve different problems. + +## Feature Comparison + +| Feature | ClawMetry | NemoClaw | +|---|---|---| +| Price | Free, open source | Enterprise pricing | +| Setup time | 30 seconds | Days/weeks | +| Infrastructure | None (runs locally) | Kubernetes/cloud | +| Data privacy | 100% local | Cloud-hosted | +| Multi-node fleet | Basic | Full | +| Compliance reporting | No | Yes | +| Token cost tracking | Yes | Yes | +| Memory file analytics | Yes | Unknown | +| Brain/session visualization | Yes | Yes | +| Security posture scan | Yes | Unknown | + +## When to Use ClawMetry + +- You're a solo developer or small team +- You care about data privacy and don't want logs in the cloud +- You want something that works immediately with zero setup +- You're on a budget (free is good) +- You want open source you can inspect and modify + +## When to Consider NemoClaw + +- You're running OpenClaw at enterprise scale (20+ nodes) +- You need compliance reporting for auditors +- You're already using Kubernetes and want everything in one place +- Your organization requires vendor support and SLAs + +## The Bottom Line + +NemoClaw's announcement actually validates what ClawMetry has been saying for months: OpenClaw observability is a real problem worth solving. When NVIDIA builds an enterprise product in your space, it's a good sign. + +But enterprise tooling isn't right for everyone. If you want to understand what your AI agent is doing right now, without signing up for anything or setting up infrastructure, ClawMetry is your tool. + +```bash +pip install clawmetry +``` + +One command. Your dashboard is running in 30 seconds. + +--- + +*ClawMetry is free and open source. Star it on [GitHub](https://github.com/vivekchand/clawmetry) and try it today.* diff --git a/docs/blog/openclaw-memory-monitoring.md b/docs/blog/openclaw-memory-monitoring.md new file mode 100644 index 0000000..5f2919b --- /dev/null +++ b/docs/blog/openclaw-memory-monitoring.md @@ -0,0 +1,93 @@ +# OpenClaw Memory Monitoring: Why Your Agent's "Brain" Needs Watching + +**TL;DR:** OpenClaw agents persist their personality and context in files like SOUL.md, MEMORY.md, and AGENTS.md. If these drift silently, your agent changes behavior without you noticing. ClawMetry is the only tool that monitors this. + +Here's something most people running OpenClaw agents don't think about until something goes wrong: your agent has memory, and that memory can drift. + +Not dramatically. Not in ways that set off alarms. Just quietly, gradually, your agent's SOUL.md gets a new paragraph, your MEMORY.md grows by a few kilobytes every day, and six weeks later you're wondering why your agent feels "different" than when you first set it up. + +This is OpenClaw memory drift, and it's more common than you'd think. + +## What Is OpenClaw Memory? + +OpenClaw agents persist their identity and context across sessions through a set of files in the workspace: + +- **SOUL.md** โ€” The agent's personality, values, and behavioral guidelines +- **MEMORY.md** โ€” Long-term curated knowledge the agent has built up +- **AGENTS.md** โ€” Workspace conventions and task state rules +- **memory/YYYY-MM-DD.md** โ€” Daily raw notes and session logs + +These aren't configuration files in the traditional sense. They're more like a brain. The agent reads them at the start of each session to reconstruct who it is and what it knows. + +And like any brain, they need maintenance. + +## Why Memory Drift Is a Problem + +When memory files grow unchecked, a few things happen: + +**Context window consumption.** If your MEMORY.md is 32KB and your SOUL.md is another 16KB, that's 48KB of context before your agent has read a single user message. For a model with a 128K token context window, you've burned 10-15% before the conversation starts. This adds up fast. + +**Stale knowledge.** Daily memory files accumulate. An agent that's been running for 60 days has 60 daily log files. Most of that is outdated context that was relevant in January but is now just noise. + +**Silent personality changes.** When an agent updates its own SOUL.md or MEMORY.md (which it often does during conversations), the changes can be subtle. A new preference here, a modified rule there. Over weeks, the cumulative drift can meaningfully change how the agent behaves. + +None of this triggers an error. No alert fires. The agent just quietly becomes someone slightly different. + +## How ClawMetry Monitors OpenClaw Memory + +ClawMetry's Memory tab includes a built-in analytics panel that tracks all of this automatically. + +Open ClawMetry, click Memory, and you'll see: + +**Memory health status** โ€” A quick green/yellow/red indicator showing whether your memory files are healthy, growing large, or at risk of bloat. + +**Context budget bars** โ€” Visual indicators showing what percentage of common model context windows (Claude 200K, GPT-4 128K, Gemini 1M) your memory files are consuming. If your memory files are eating 25% of your Claude context before the conversation starts, you'll see it immediately. + +**Largest files chart** โ€” A bar chart showing which files are biggest, with color coding for files that need attention. + +**Daily growth sparkline** โ€” A 30-day chart of how your daily memory files are growing. A healthy pattern is steady. A hockey stick pattern means something is accumulating. + +**Recommendations** โ€” Specific suggestions for files that have grown too large, with guidance on what to prune. + +## Catching a Memory Change in Action + +Here's a real example of ClawMetry catching drift. An agent had been running for a month. The Memory tab showed MEMORY.md had grown from 8KB to 23KB over 30 days. + +Drilling in, the largest files were: MEMORY.md (23KB), memory/2026-02-14.md (18KB), SOUL.md (12KB). + +The SOUL.md flag was the interesting one. At 12KB, it had grown significantly from its original 4KB. Reviewing the file showed the agent had been adding detailed notes about every project it touched, slowly transforming what was meant to be a personality guide into a project wiki. + +Without ClawMetry's memory analytics, this would have been invisible until the agent started behaving strangely. + +## Setting Up OpenClaw Memory Monitoring + +```bash +pip install clawmetry +clawmetry +``` + +Navigate to the Memory tab. ClawMetry auto-discovers your OpenClaw workspace and starts monitoring immediately. + +No configuration. No API keys. No cloud setup. + +The memory analytics panel loads automatically and gives you an instant health snapshot of your agent's brain. + +## The Rule of Thumb + +A healthy OpenClaw memory setup: +- SOUL.md under 8KB (personality guide, not a wiki) +- MEMORY.md under 16KB (curated wisdom, not a log dump) +- Daily files older than 30 days archived or deleted +- Total memory context under 10% of your model's context window + +ClawMetry monitors all of this and flags when you're approaching thresholds before it becomes a problem. + +--- + +*ClawMetry is the only OpenClaw monitoring tool with built-in memory analytics. Free and open source.* + +```bash +pip install clawmetry +``` + +*[Star on GitHub](https://github.com/vivekchand/clawmetry) โ€” contributions welcome.*