# UI, Query API, and Schema Validation Design Date: 2026-01-17 ## Overview This design covers three related improvements to agentmon: 1. **Web UI** — Sessions list, session detail, and run detail views 2. **Query API** — New endpoints to support the UI 3. **Schema Validation** — Required field validation at ingest-gateway ## URL Structure | URL | View | Description | |-----|------|-------------| | `/` | Redirect | Redirects to `/sessions` | | `/sessions` | Sessions List | Recent sessions, filterable | | `/sessions/:session_id` | Session Detail | Runs within a session | | `/runs/:run_id` | Run Detail | Spans within a run | Query parameters for filtering: - `/sessions?from=2026-01-17&to=2026-01-18` — time range - `/sessions?framework=opencode` — filter by framework - `/sessions?host=willlaptop` — filter by host - Parameters combine: `/sessions?framework=claude-code&host=willlaptop` ## Query API Endpoints ### GET /v1/sessions List sessions with filters. Query params: - `from`, `to` — ISO timestamps (default: last 24h) - `framework` — filter by source framework - `host` — filter by source host - `limit` — max results (default 50, max 200) - `cursor` — pagination token (opaque, timestamp-based) Response: ```json { "sessions": [ { "session_id": "s1", "started_at": "2026-01-17T08:00:00Z", "ended_at": "2026-01-17T09:30:00Z", "framework": "claude-code", "host": "willlaptop", "run_count": 5 } ], "next_cursor": "..." } ``` ### GET /v1/sessions/:session_id Session detail with runs. Response: ```json { "session": { "session_id": "...", "started_at": "...", "ended_at": "...", "framework": "...", "host": "..." }, "runs": [ { "run_id": "r1", "started_at": "...", "ended_at": "...", "status": "success", "span_count": 12 } ] } ``` ### GET /v1/runs/:run_id Run detail with spans. Response: ```json { "run": { "run_id": "...", "session_id": "...", "started_at": "...", "ended_at": "...", "status": "..." }, "spans": [ { "span_id": "sp1", "name": "Bash", "kind": "tool", "started_at": "...", "duration_ms": 1234, "status": "success", "payload": {} } ] } ``` ## UI Views ### Sessions List Default view showing last 24 hours, most recent first. Layout: - Filter bar: time range (from/to), framework dropdown, host dropdown - Table columns: Session ID, Framework, Host, Run Count, Time (relative) - Click row to navigate to session detail - "Load more" button for pagination ### Session Detail Shows session metadata and all runs within it. Layout: - Header: Session ID, framework, host, start time, total duration - Back link to sessions list - Runs table: Run ID, Status, Span Count, Duration, Start Time - Click row to navigate to run detail Status indicators: - Green checkmark for success - Red X for error - Yellow circle for running/unknown ### Run Detail Shows run metadata and all spans within it. Layout: - Header: Run ID, status, start time, duration - Back link to session detail - Spans table: Name, Kind, Status, Duration - Click row to expand/collapse details Expanded span shows: - Model name, tokens, cost (for LLM spans) - Error details (for failed spans) - Raw payload JSON ## Schema Validation Validation performed at ingest-gateway before publishing to NATS. ### Required Fields All events must include: ``` schema.name = "agentmon.event" schema.version = 1 event.id — non-empty string event.type — one of: session.start, session.end, run.start, run.end, span.start, span.end, error, metric.snapshot event.ts — valid timestamp (RFC3339 or unix-ms) event.source.framework — non-empty string event.source.client_id — non-empty string event.source.host — non-empty string ``` ### Validation Responses | Scenario | HTTP | WebSocket | |----------|------|-----------| | Valid | 202 Accepted | `{"ack": {"seq": N}}` | | Missing field | 400 + error | `{"error": "missing_field", "field": "..."}` | | Invalid type | 400 + error | `{"error": "invalid_type", "value": "..."}` | | Unknown fields | Allow | Allow | Non-strict mode: unknown fields in `correlation`, `attributes`, and `payload` are stored as-is. ## Frontend Implementation Technology: Vanilla JavaScript (no framework, no build step). File structure: ``` cmd/web-ui/static/ ├── index.html # Shell + router ├── app.js # Router + page loader ├── pages/ │ ├── sessions.js │ ├── session.js │ └── run.js └── style.css ``` Routing: History API with catch-all handler serving index.html for all routes. Data fetching: Native fetch() to query-api endpoints via /api proxy. ## Implementation Order 1. Query API endpoints (sessions, session detail, run detail) 2. Schema validation at ingest-gateway 3. Frontend pages (sessions list, session detail, run detail) 4. Rebuild and deploy ## Future Considerations - Waterfall visualization for spans (SVG/canvas) - Token/cost aggregations in session/run summaries - Free-text search across sessions - Real-time updates via WebSocket subscription