Files
agentmon/docs/plans/2026-01-17-ui-query-validation-design.md
T
William Valentin d748033851 docs: add UI, query API, and validation design
Design for Sessions/Run views, three new query-api endpoints,
and schema validation at ingest-gateway.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 01:43:04 -08:00

229 lines
5.2 KiB
Markdown

# 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