/ˈkrɒn.əˌlɪt.ɪks/ (noun)
- Cron analytics and observability.
- The dashboard for agentic automations in Hermes.
Observe. Measure. Optimize.
Cronalytics is a Hermes Agent plugin that attributes session-level usage and estimated cost to every cron-originated run, so you can see what your scheduled jobs are costing you. It hooks into on_session_end, stores derived analytics in a local SQLite fact database, and surfaces them in the Hermes dashboard via a dedicated /cronalytics tab.
Turn hidden automation into visible spend.
Built for Hermes Agent, the autonomous agent framework by Nous Research.
YouTube: short video showing basic install and usage.
- Captures every cron job run as it completes via the
on_session_endhook - Persists cost, token counts, model, duration, and success state to a local fact database
- Backfills historical data automatically on plugin load and on demand via reconciliation scanner
- Surfaces a dashboard with:
- Summary cards (total runs, estimated cost, tokens, pace)
- Leader board (top runs, top cost, top tokens, top pace)
- Cost-by-model breakdown with proportional bars
- Per-job table with runs, cost, duration, projections, and sortable columns
- Expandable detail rows showing token breakdown, schedule, and success/failure split
- Job detail modal with full run history (sortable, 200-run limit)
- Outcome filter (All / Success / Failure) with conditional card colors
- Mode filter (All / Agent / No agent) for script-only job visibility
- Sync Now button to trigger backfill on demand
- Educational modals explaining Pace, Nominal, Trend, and cost math
- docs/INSTALL.md — Detailed installation guide
- docs/UNINSTALL.md — Clean removal instructions
- docs/USAGE.md — Dashboard usage guide
- dev/BRIEF.md — Product opportunity brief & positioning
- dev/DESIGN.md — Architecture, data flow, and technical decisions
- dev/FEATURES.md — Complete feature catalog with formulas
- dev/LAUNCH_PLAN.md — V1.0 launch timeline
- dev/AGENTS.md — Contributor conventions & release gates
- dev/DEV_SETUP.md — Development environment setup
- PLAN.md — Phased build plan and backlog (root)
Cost data is estimated, not exact. Cronalytics reports the estimated cost that Hermes computed and stored in state.db. Your actual invoice may differ due to rate changes, credits, or rounding. Use this for directional awareness, not accounting.
Single-profile cron by default. Cronalytics monitors the Hermes profile where it is installed. Most users — even those with multiple profiles configured — run cron jobs in the default profile. For them, Cronalytics works fully.
The edge case: if you explicitly create a cron job under a non-default profile (hermes --profile <name> cron create ...), that job runs in an isolated gateway with its own state.db. Cronalytics, installed in the default profile, cannot see it. To monitor those jobs, install Cronalytics in that profile's plugins/ directory as well.
Multi-profile cron support is on our roadmap.
Open the Hermes dashboard, navigate to the Plugins tab, and use the Install from GitHub / Git URL field. Enter:
owner/reposhorthand (e.g.8bit64k/cronalytics)- Or a full
https://orgit@clone URL
Check Enable after install, then click Install.
Hard-refresh your browser (Ctrl+Shift+R or Cmd+Shift+R) to clear cached JS.
Open the Cronalytics tab in the dashboard sidebar.
Reverse proxy users: If you run the Hermes dashboard behind Caddy or Nginx, ensure
/api/*routes are forwarded directly to the dashboard backend. A misconfigured proxy will return HTML instead of JSON for plugin API calls. Seedocs/INSTALL.mdfor a minimal Caddy example.For development setup, see
dev/DEV_SETUP.md.
After install, the plugin needs data:
- Wait for a cron job to run — the
on_session_endhook captures it automatically. - Or trigger a manual backfill — click Sync Now in the dashboard, or run:
curl -H "X-Hermes-Session-Token: <token>" -X POST http://localhost:9119/api/plugins/cronalytics/syncIf the dashboard shows "No cron jobs captured," click Sync Now.
Note: The sync endpoint requires the dashboard's ephemeral session token for security (injected into the SPA at startup). Most users should use the dashboard Sync Now button instead of curl.
name: cronalytics
version: 1.0.0
description: Cost and operational observability for Hermes cron jobs
provides_hooks:
- on_session_endAll current settings are hardcoded defaults. There is no user-editable config file yet (planned for v1.1).
| Setting | Default | Meaning |
|---|---|---|
RETRY_DELAYS |
[3.0, 8.0, 15.0] |
Seconds to wait before each worker retry |
JITTER_MAX |
2.0 |
Max random seconds added to each retry delay |
MAX_RETRIES |
3 |
Total attempts to read a session from state.db |
Paths are resolved automatically:
STATE_DB:~/.hermes/state.db(Hermes core session store)FACT_DB:~/.hermes/plugins/cronalytics/facts.db(plugin-owned SQLite)WATERMARK_FILE:~/.hermes/plugins/cronalytics/watermark.jsonPENDING_FILE:~/.hermes/plugins/cronalytics/pending.jsonl
Four cards showing aggregate metrics for the selected window:
- Job Runs — total executions with vs-prior-period delta (↑/↓ %)
- Cost — total estimated cost in amber; vs-prior delta + ✓/✗ breakdown (Actual cost placeholder suppressed — partial coverage creates misleading comparisons)
- Tokens — total tokens in blue; In/Out/Cached proportion micro-bars
- Pace — aggregate
trend_monthly / nominal_monthlyas a multiplier:< 1.0×green — under scheduled budget1.0–2.0×neutral — on track≥ 2.0×red — over budget
Click any card to open an educational modal explaining the metric.
Four spotlight cards surfacing the highest-value job in each dimension, with the leader's share of the window total:
- Top Runs — highest execution count;
% of total runssub-line - Top Cost — highest cumulative spend;
% of total costsub-line - Top Tokens — highest token consumption;
% of total tokenssub-line - Top Pace — highest pace multiplier (most at risk of exceeding budget)
Click any card to open a detail modal with job metadata.
Proportional bar chart showing the top 5 models by cost, with run counts. Remaining models collapsed with "and N more."
Eight sortable columns: Job, Runs, Avg Time, Total Cost, Avg Cost, Nominal/mo, Trend/mo, Pace.
- Click a column header to sort ascending/descending
- Click any row to expand a detail panel showing:
- Token breakdown (total, in, out, cached)
- Success/failure split with cost attribution
- Schedule display, last run, model, next run
- See Runs button opening a full modal
Full run history for the selected job:
- 95% width modal with sticky headers
- Sortable by run time, cost, duration, success, model
- 200-run default limit (backend ceiling: 500)
- Mode column showing Agent vs No agent
- Outcome toggle —
All | Success | Failure(persists in localStorage) - Mode toggle —
All | Agent | No agent(persists in localStorage) - Day selector —
7D | 30D | 90Dpresets + custom input (0–365 days, Enter/Go) - Refresh — re-fetches summary and jobs
- Sync Now — triggers reconciliation scan with spinner + completion toast
Cronalytics tracks two different notions of "success":
| Signal | What It Means | Source |
|---|---|---|
Wrapper Success (success toggle in dashboard) |
The cron wrapper finished without error — the job ran, the agent responded, and the wrapper exited cleanly. | end_reason field |
| Payload Success | The agent's actual output was correct, useful, or achieved the intended goal. | Not tracked |
- Success = high, Failure = low → Your cron jobs are mechanically reliable.
- Success = high, but output quality is poor → The infrastructure is fine; the issue is in the prompt, model choice, or task definition.
- Failure = high → Investigate timeouts, API errors, or wrapper crashes.
The Success/Failure toggle is a reliability signal, not a correctness signal.
Cron Job Due
│
▼
run_job() ──▶ agent.run_conversation()
│
▼
Hook: on_session_end(platform="cron")
│
▼
Enqueue session_id ──▶ Deferred worker retries
│ (waits for DB flush)
▼
Query state.db ──▶ Insert into facts.db
│
▼
Dashboard queries facts.db via plugin API
All endpoints are mounted at /api/plugins/cronalytics/.
| Endpoint | Method | Description |
|---|---|---|
/health |
GET |
Plugin health + sync metadata |
/summary?days=N&outcome=both&mode=all |
GET |
Aggregated totals with projections |
/jobs?days=N&outcome=both&mode=all |
GET |
Per-job rolled-up stats with projections |
/jobs/{job_id}/runs |
GET |
Individual runs for a specific job |
/models?days=N&outcome=both&mode=all |
GET |
Cost breakdown by model |
/trends?days=N&outcome=both&mode=all |
GET |
Daily cost + runs time series |
/sync |
POST |
Run reconciliation scanner manually |
The fact database (facts.db) is append-only. Rows are inserted once and never updated or deleted.
Key fields captured per run:
session_id— unique run keyjob_id— stable job definition IDrun_time/ended_at/duration_secondsmodelinput_tokens/output_tokens/reasoning_tokens/cache_read_tokens/cache_write_tokensestimated_cost_usd— primary cost metricactual_cost_usd— ground-truth when availablecost_status,cost_source,billing_providerapi_call_count,message_count,tool_call_countend_reason,successjob_mode—agentorno_agentingested_at
cronalytics/
├── plugin.yaml # Plugin manifest (hooks, version)
├── __init__.py # Register hook + bootstrap scanner
├── config.py # Paths + defaults
├── facts.py # SQLite fact DB: schema, insert, queries
├── ingester.py # Deferred ingestion worker + crash recovery
├── scanner.py # Reconciliation scanner + watermark I/O
├── schedule.py # Cron parsing + projection math
├── cli.py # Standalone terminal interface
├── logger.py # Shared logger
├── checkpoint.py # Session state persistence
├── dashboard/
│ ├── manifest.json # Dashboard plugin manifest
│ ├── plugin_api.py # FastAPI router
│ ├── build.js # esbuild bundler script
│ ├── src/ # Modular frontend source
│ │ ├── index.js # Entry point
│ │ ├── lib/ # SDK, formatters, icons, validators
│ │ ├── hooks/ # useApi, useModal
│ │ └── components/ # 13 React components
│ └── dist/
│ └── index.js # Bundled IIFE frontend
└── tests/ # 83 pytest tests
- Wrapper-level success only. The
successboolean reflects whether the session wrapper completed, not whether the agent task succeeded. - Abandoned sessions are invisible. Sessions where the gateway crashed or the job got stuck are never ingested (they never reach
ended_at). - No user-editable config file yet. All tuning values are hardcoded in
config.py. - Actual cost is often null. Most runs only populate
estimated_cost_usd;actual_cost_usddepends on provider billing data. - Plugin directory is a static copy. Changes in the build directory are not reflected in
~/.hermes/plugins/cronalytics/unless manually copied or symlinked. - Dashboard server caches plugins per-process. Changes to
manifest.jsonorplugin_api.pyrequire a full dashboard restart. - Mobile layout tested but not optimized. The table may require horizontal scroll on narrow viewports.
- Job detail modal capped at 200 runs. High-frequency jobs show full count in the table but the drill-down is limited.
This is an independent project built by a solo developer with help from an AI agent, and I'm grateful you are willing to try Cronayltics. I hope it helps optimize your cron activity. I use it daily and will fix bugs as I find them, but support and bug fixes will be on my best effort time schedule.
Found a bug? Open a GitHub issue with reproduction steps.
Have a feature idea? Open a discussion or fork it.
Caveat: The cost estimates are approximate and as recorded by the Hermes Agent framework. The success/failure signal is wrapper-level only (see Understanding Success). Verify anything mission-critical independently.
If you are running Hermes Agent you have everything you need:
- Hermes Agent with plugin hook support (
on_session_end) - Hermes dashboard server for UI components
- SQLite (bundled with Python)
MIT — see LICENSE for full text.
- Leader Board '% of total' — spotlight cards show the leader's share of the window total (e.g. "42% of total cost")
- Cost card: suppressed Actual — partial
actual_cost_usdcoverage creates misleading comparisons. The line now readsActual: —until provider billing data coverage is reliable. - Backend fix — synthetic script-only rows now insert
NULLforactual_cost_usd(was 0.0), eliminating phantom$0.00aggregates.
- Dashboard: Summary Board, Leader Board, Per-Model Breakdown, Jobs Breakdown table
- Sortable 8-column jobs table with expandable detail rows
- Job Detail Modal with full run history, sticky headers, inherited sorting
- Outcome toggle (All/Success/Failure) with conditional Cost card colors
- Mode toggle (All/Agent/No agent) with script job visibility
- Pace, Nominal, and Trend projections with educational modals
- Reconciliation scanner with watermark-based backfill
- Bootstrap scanner on plugin load (catches post-restart gaps)
- 83 pytest tests covering facts, parser, scanner, schedule, ingester, plugin API
- Lint/type check:
ruff+mypyclean - Keyboard-accessible cards and table headers (a11y)
- Large-font theme resilience
- API validation layer (JSDoc typedefs + runtime guards)
- Initial release: real-time ingestion, fact DB, reconciliation scanner, dashboard API, React frontend with summary cards, jobs table, cost-by-model, sync button.
Plugin path: ~/.hermes/plugins/cronalytics/
Fact DB: ~/.hermes/plugins/cronalytics/facts.db
API base: /api/plugins/cronalytics/
