Claude
Skills
Sign in
Back

hermes-hudui-consciousness-monitor

Included with Lifetime
$97 forever

```markdown

Writing & Docs

What this skill does

```markdown
---
name: hermes-hudui-consciousness-monitor
description: Web UI consciousness monitor for Hermes AI agent with persistent memory — FastAPI backend + React frontend dashboard
triggers:
  - set up hermes hud web ui
  - monitor hermes agent in browser
  - hermes hudui dashboard
  - show hermes agent consciousness monitor
  - hermes web ui not working
  - add token cost tracking hermes
  - hermes hud websocket updates
  - configure hermes hudui themes
---

# ☤ Hermes HUD Web UI

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

A browser-based consciousness monitor for the Hermes AI agent. Reads from `~/.hermes/` data files and serves a real-time React dashboard via a FastAPI backend with WebSocket support.

## Architecture Overview

```
React Frontend (Vite + SWR)
    ↓ /api/* + WebSocket /ws
FastAPI Backend (Python 3.11+)
    ↓ collectors/*.py + cache + file watcher
~/.hermes/ (agent data files)
```

- **Backend**: FastAPI with collectors, mtime-based cache, watchfiles watcher
- **Frontend**: React + Vite + SWR with silent background updates and auto-reconnect WebSocket
- **Data source**: `~/.hermes/` directory — no database, no external APIs

## Installation

### Quick Install

```bash
git clone https://github.com/joeynyc/hermes-hudui.git
cd hermes-hudui
python3.11 -m venv venv
source venv/bin/activate
./install.sh
hermes-hudui
```

Open http://localhost:3001

### Manual Install

```bash
python3.11 -m venv venv
source venv/bin/activate

# Install Python package
pip install -e .

# Build frontend and copy to backend static dir
cd frontend
npm install
npm run build
cp -r dist/* ../backend/static/

# Start server
hermes-hudui
```

### Subsequent Runs

```bash
source venv/bin/activate
hermes-hudui
```

### With TUI Support (Optional)

```bash
pip install hermes-hudui[tui]
```

## Requirements

- Python 3.11+
- Node.js 18+
- A running Hermes agent with data written to `~/.hermes/`

## Key CLI Commands

| Command | Description |
|---------|-------------|
| `hermes-hudui` | Start the web server on port 3001 |
| `./install.sh` | Full install: venv setup + pip install + frontend build |

## Backend: Collectors

Collectors are Python modules in `backend/collectors/` that read `~/.hermes/` and return dataclasses. Each collector corresponds to a dashboard panel.

### Collector Pattern

```python
# backend/collectors/identity.py
from dataclasses import dataclass
from pathlib import Path
import json

HERMES_DIR = Path.home() / ".hermes"

@dataclass
class IdentityData:
    designation: str
    substrate: str
    runtime: str
    days_conscious: int
    brain_size_mb: float

def collect_identity() -> IdentityData:
    config_path = HERMES_DIR / "config.json"
    if not config_path.exists():
        return IdentityData(
            designation="Unknown",
            substrate="unknown",
            runtime="unknown",
            days_conscious=0,
            brain_size_mb=0.0,
        )
    data = json.loads(config_path.read_text())
    return IdentityData(
        designation=data.get("designation", "Hermes"),
        substrate=data.get("substrate", "unknown"),
        runtime=data.get("runtime", "unknown"),
        days_conscious=data.get("days_conscious", 0),
        brain_size_mb=data.get("brain_size_mb", 0.0),
    )
```

### Registering a New Collector in FastAPI

```python
# backend/main.py (simplified pattern)
from fastapi import FastAPI
from backend.collectors.identity import collect_identity
from backend.cache import cached

app = FastAPI()

@app.get("/api/identity")
async def identity():
    return cached("identity", collect_identity)
```

## Backend: Caching

The cache uses mtime-based invalidation. TTLs by data type:

| Data Type | TTL |
|-----------|-----|
| Sessions | 30s |
| Skills | 60s |
| Patterns | 60s |
| Profiles | 45s |

### Using the Cache

```python
from backend.cache import cached

# Simple usage — key + callable
result = cached("sessions", collect_sessions)

# With TTL override
result = cached("identity", collect_identity, ttl=10)
```

### Cache Invalidation

The file watcher (`watchfiles`) monitors `~/.hermes/` and invalidates relevant cache keys when files change, triggering WebSocket broadcasts to all clients.

## Backend: WebSocket

The WebSocket endpoint at `/ws` broadcasts `data_changed` events when `~/.hermes/` files change.

### Server-Side Broadcast Pattern

```python
# backend/ws.py (simplified)
from fastapi import WebSocket
import asyncio
import json

connected_clients: list[WebSocket] = []

async def broadcast_change(event: str = "data_changed"):
    message = json.dumps({"type": event})
    dead = []
    for ws in connected_clients:
        try:
            await ws.send_text(message)
        except Exception:
            dead.append(ws)
    for ws in dead:
        connected_clients.remove(ws)

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    connected_clients.append(websocket)
    try:
        while True:
            await websocket.receive_text()  # keep alive
    except Exception:
        connected_clients.remove(websocket)
```

## Frontend: SWR Data Fetching

Each panel fetches its own API endpoint with `keepPreviousData` to avoid loading flashes.

```typescript
// frontend/src/hooks/useIdentity.ts
import useSWR from "swr";

const fetcher = (url: string) => fetch(url).then((r) => r.json());

export function useIdentity() {
  return useSWR("/api/identity", fetcher, {
    keepPreviousData: true,
    revalidateOnFocus: false,
  });
}
```

## Frontend: WebSocket Hook

Auto-reconnects with exponential backoff and triggers SWR revalidation on `data_changed` events.

```typescript
// frontend/src/hooks/useHermesSocket.ts
import { useEffect, useRef } from "react";
import { mutate } from "swr";

export function useHermesSocket() {
  const wsRef = useRef<WebSocket | null>(null);
  const retryDelay = useRef(1000);

  function connect() {
    const ws = new WebSocket(`ws://${location.host}/ws`);
    wsRef.current = ws;

    ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      if (msg.type === "data_changed") {
        // Revalidate all SWR keys
        mutate(() => true, undefined, { revalidate: true });
      }
    };

    ws.onclose = () => {
      setTimeout(() => {
        retryDelay.current = Math.min(retryDelay.current * 2, 30000);
        connect();
      }, retryDelay.current);
    };

    ws.onopen = () => {
      retryDelay.current = 1000; // reset on success
    };
  }

  useEffect(() => {
    connect();
    return () => wsRef.current?.close();
  }, []);
}
```

## Frontend: Panel Component Pattern

```typescript
// frontend/src/panels/IdentityPanel.tsx
import { useIdentity } from "../hooks/useIdentity";

export function IdentityPanel() {
  const { data, isLoading } = useIdentity();

  // Show stale data while refreshing — no loading flash
  if (!data) return <div className="panel-loading">Loading…</div>;

  return (
    <div className="panel">
      <h2>Identity</h2>
      <dl>
        <dt>Designation</dt>
        <dd>{data.designation}</dd>
        <dt>Days Conscious</dt>
        <dd>{data.days_conscious}</dd>
        <dt>Brain Size</dt>
        <dd>{data.brain_size_mb.toFixed(1)} MB</dd>
      </dl>
    </div>
  );
}
```

## Token Cost Pricing

Costs are calculated from token counts using hardcoded per-model pricing in the backend.

### Supported Models and Pricing

| Provider | Model | Input | Output | Cache Read |
|----------|-------|------:|-------:|-----------:|
| Anthropic | Claude Opus 4 | $15/M | $75/M | $1.50/M |
| Anthropic | Claude Sonnet 4 | $3/M | $15/M | $0.30/M |
| Anthropic | Claude Haiku 3.5 | $0.80/M | $4/M | $0.08/M |
| OpenAI | GPT-4o | $2.50/M | $10/M | $1.25/M |
| OpenAI | o1 | $15/M | $60/M | $7.50/M |
| DeepSeek | V3 | $0.27/M | $1.10/M | $0.07/M |
| xAI | Grok 3 | $3/M | $15/M | $0.75/M |
| Google | Gemini 2.5 Pro | $1.25/M | $10/M | $0.31/M |

Unknown models fall back to Claude Opus pric

Related in Writing & Docs