# Agent Activity Monitoring Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Monitor all OpenClaw agent activity (tool calls, messages, sessions, errors) across three VMs and display it in a real-time dashboard. **Architecture:** An OpenClaw hook (TypeScript) on each VM captures agent events and POSTs them as agentmon envelopes to the ingest gateway. A new `/agents` page in the web UI renders a live activity timeline via WebSocket. A small backend addition filters events by framework. **Tech Stack:** TypeScript (hook), Go (backend filter), Vanilla JS/CSS (UI) **Design doc:** `docs/plans/2026-03-13-agent-monitoring-design.md` --- ## Task 1: Add framework filter to events query **Files:** - Modify: `internal/store/postgres/query.go` - Modify: `cmd/query-api/main.go` The agents UI needs to load recent events filtered by `source_framework = 'openclaw'`. The existing `ListRecentEvents` only takes `limit`. **Step 1: Add EventsFilter struct and update ListRecentEvents** In `internal/store/postgres/query.go`, replace the current function: ```go type EventsFilter struct { Limit int EventType string Framework string } func (d *DB) ListRecentEvents(ctx context.Context, f EventsFilter) ([]EventRow, error) { if f.Limit <= 0 { f.Limit = 100 } if f.Limit > 1000 { f.Limit = 1000 } query := "SELECT event_id, ts, type, payload FROM events WHERE 1=1" args := []any{} argN := 1 if f.EventType != "" { query += fmt.Sprintf(" AND type = $%d", argN) args = append(args, f.EventType) argN++ } if f.Framework != "" { query += fmt.Sprintf(" AND source_framework = $%d", argN) args = append(args, f.Framework) argN++ } query += fmt.Sprintf(" ORDER BY ts DESC LIMIT $%d", argN) args = append(args, f.Limit) rows, err := d.sql.QueryContext(ctx, query, args...) if err != nil { return nil, err } defer rows.Close() var out []EventRow for rows.Next() { var r EventRow if err := rows.Scan(&r.EventID, &r.TS, &r.Type, &r.Payload); err != nil { return nil, err } out = append(out, r) } return out, rows.Err() } ``` Add `"fmt"` to the import block. **Step 2: Update the query-api handler to pass filters** In `cmd/query-api/main.go`, update the `/v1/events` handler (around line 113): ```go r.Get("/v1/events", func(w http.ResponseWriter, r *http.Request) { limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) f := postgres.EventsFilter{ Limit: limit, EventType: r.URL.Query().Get("event_type"), Framework: r.URL.Query().Get("framework"), } events, err := db.ListRecentEvents(r.Context(), f) if err != nil { httpx.WriteJSON(w, http.StatusInternalServerError, map[string]any{"error": "db_error"}) return } httpx.WriteJSON(w, http.StatusOK, map[string]any{"events": events}) }) ``` **Step 3: Verify it compiles** Run: `cd /home/will/lab/agentmon && go build ./...` Expected: No errors **Step 4: Test the filter with curl** Run: `curl -s 'http://localhost:8081/v1/events?framework=openclaw&limit=5'` Expected: `{"events":null}` or `{"events":[]}` (no openclaw events yet, but no errors) **Step 5: Commit** ```bash git add internal/store/postgres/query.go cmd/query-api/main.go git commit -m "feat: add event_type and framework filters to events endpoint" ``` --- ## Task 2: Add /agents SPA route to Go server **Files:** - Modify: `cmd/web-ui/main.go:51` **Step 1: Add /agents to the SPA catch-all** In `cmd/web-ui/main.go`, update line 51 to include `/agents`: Change: ```go if r.URL.Path == "/" || strings.HasPrefix(r.URL.Path, "/sessions") || strings.HasPrefix(r.URL.Path, "/runs") || strings.HasPrefix(r.URL.Path, "/openclaw") { ``` To: ```go if r.URL.Path == "/" || strings.HasPrefix(r.URL.Path, "/sessions") || strings.HasPrefix(r.URL.Path, "/runs") || strings.HasPrefix(r.URL.Path, "/openclaw") || strings.HasPrefix(r.URL.Path, "/agents") { ``` **Step 2: Verify it compiles** Run: `cd /home/will/lab/agentmon && go build ./cmd/web-ui/` Expected: No errors **Step 3: Commit** ```bash git add cmd/web-ui/main.go git commit -m "feat: add /agents SPA route to web-ui server" ``` --- ## Task 3: Add Agents nav link and route in app.js **Files:** - Modify: `cmd/web-ui/static/index.html` - Modify: `cmd/web-ui/static/app.js` **Step 1: Add "Agents" nav link in index.html** In `cmd/web-ui/static/index.html`, update the `