ai web
Local developer console — chat with your agent, inspect traces, explore graph data, manage
sessions, and capture eval cases. Single command, opens in browser, under 100 KB of vanilla
JS and CSS, zero build step.
implemented
localhost:8888
Embedded in the ai binary. No separate install. Auto-connects to a running
ai serve, or spawns one if none is found.
Overview
ai web is a local reverse proxy and single-page app served from the
ai binary itself. It auto-detects a running ai serve instance
on localhost:8080 (configurable via --a2a) and proxies all
/a2a/* traffic there. If ai serve is not found and
--no-serve is not set, it spawns one as a subprocess.
Browser localhost:8888/ui/ │ ▼ HTTP ai web (reverse proxy + local APIs) ├─ /ui/* → embedded SPA (HTML + JS + CSS, ~80 KB) ├─ /api/web-config → mode, a2a url, debug flag, backend status ├─ /api/sessions/* → local session store (JSON files on disk) ├─ /api/evals/* → eval case capture + run ├─ /api/agents/*/trace-stream → SSE live trace events ├─ /api/data/* → toolbox source list + tool invocation ├─ /api/prompt-diff → live system prompt diff (when --config set) ├─ /a2a/* → proxied to upstream ai serve └─ /.well-known/* → proxied to upstream │ ▼ ai serve localhost:8080 (A2A server, agent execution, MCP tools)
Flags
All flags are optional. The simplest invocation is just ai web.
| Flag | Default | Description |
|---|---|---|
--port int |
8888 |
Listen port. 0 = OS-assigned. Auto-increments if default in use and --port not explicitly set. |
--host string |
127.0.0.1 |
Bind address. Use 0.0.0.0 with --token for team sharing on a LAN. |
--a2a string |
http://localhost:8080 |
Upstream ai serve endpoint to proxy to. |
--no-open |
false | Don't auto-open the browser. |
--no-serve |
false | Proxy-only mode — don't auto-start ai serve if backend is unreachable. |
--debug |
false | Show the trace panel by default on load. |
--restart |
false | Shut down an existing ai web instance on the same port, then start fresh. |
--token string |
"" | Bearer token for authentication. When set, all API calls require Authorization: Bearer <token>. Can also be set via AI_WEB_TOKEN env var. |
--config string |
agent.toml |
Path to agent.toml. When set, enables live system prompt diff watching. |
--evals-dir string |
evals |
Directory for saved eval case YAML files. |
--sessions-dir string |
.ai/sessions |
Directory for persistent session JSON files. |
--no-persist |
false | Disable session persistence — don't write session files to disk. |
solo dev (default) $ ai web $ ai web --debug team sharing on LAN $ ai web --host 0.0.0.0 --token mysecret connect to a remote agent $ ai web --no-serve --a2a http://my-agent.internal:8080 restart a stuck instance $ ai web --restart
Layout
Two-panel layout: chat on the left, trace on the right. A persistent footer bar shows live token counts, running cost, and context window utilization. The trace panel collapses behind a toggle on narrow viewports. Session tabs appear above the chat area when multiple sessions are open.
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ ai web · agent name · model · :8888 · [Chat] [Data] [Evals] ● connected │ ├────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────┤ │ session-1 session-2 ● + │ │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ CHAT PANEL │ TRACE PANEL │ │ │ │ │ message history · streaming response │ post-run ASCII trace │ │ system prompt toggle · prompt diff │ LLM call boxes │ │ inline tool call markers │ tool call nodes → click to expand │ │ │ cache hit/miss indicators │ │ input box · Ctrl+Enter to send │ │ │ ↑ message history │ │ ├────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────┤ │ FOOTER BAR · ↑ input / ↓ output tok · cost · turns · context utilization bar │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
The top nav bar tabs between Chat, Data (graph/SQL explorer), and Evals (capture and run eval cases). Backend connection status is live-polled every 5 seconds.
Chat Panel
Streaming SSE response from the A2A backend. Messages are tagged by role. Tool call
markers appear inline in the AI response. The system prompt is viewable via a toggle
above the history; when --config is set a live diff appears automatically
when the file changes. Press ↑ to cycle previous messages like shell history.
┌─────────────────────────────────────────────────────────────────────┐ │ CHAT ▶ system prompt ▶ session info [turn 3] │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ you analyze the Neo4j companies schema │ │ │ │ ai I'll start by examining the graph schema. Let me query │ │ the available node labels and relationship types. │ │ │ │ · called get_schema │ │ · analyzing 12 node types, 8 relationship types │ │ │ │ The schema has three main clusters: Organization is the │ │ central hub with edges to Article (847k), Investment │ │ (320k), and Person (94k). │ │ │ │ you which companies have the most article coverage? │ │ │ │ ai Let me run a query... ▌ │ │ │ ├─────────────────────────────────────────────────────────────────────┤ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ > _ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ Ctrl+Enter send · ↑ history · Ctrl+K palette │ └─────────────────────────────────────────────────────────────────────┘
System prompt diff. When --config agent.toml is set,
a background watcher polls the file every 500 ms. If the system_prompt
field changes, a unified diff appears in the system prompt toggle — useful during
prompt engineering sessions.
Trace Panel
Post-run ASCII box trace rendered after each full agent response. Each LLM call is a box with model name, latency, token count, stop reason, and a collapsible input message list. Cache hits are shown inline. Tool calls appear as connector nodes between boxes. Click any tool call to open the side panel with the full query and result.
┌───────────────────────────────────────────────────────────────────────┐ │ TRACE turn 3 / 3 │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ ── turn 1 ────────────────────────────────────────────────── │ │ │ │ ┌─ claude-sonnet-4-6 1.4s 832 tok stop: end_turn ─────┐ │ │ │ ▶ messages [1] [expand] │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ │ ── turn 2 ────────────────────────────────────────────────── │ │ │ │ ┌─ claude-sonnet-4-6 2.1s 1,842 tok stop: tool_use ────┐ │ │ │ ▶ messages [3] [expand] │ │ │ │ cache: hit · cache_read: 1,204 tok │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ├── get_schema ────────────────────────────── → 12 node types ───┤ │ │ │ │ │ ┌─ claude-sonnet-4-6 0.8s 312 tok stop: end_turn ────┐ │ │ │ ▶ messages [5] [expand] │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────────────────┘
Live trace events. The trace panel also subscribes to
GET /api/agents/{id}/trace-stream via SSE. This delivers
model_call_start events at inference time so the LLM box appears
immediately when the model starts — not after the full response lands.
Tool Side Panel
Clicking a tool call node in the trace opens a side panel. The trace panel shows only
the summary inline (tool_name → N rows). The side panel shows the full
query, parameters, result with syntax highlighting, and latency. Press
Escape to dismiss.
┌────────────────────────────────────────────────────────────────────────────────────────────┐ │ CHAT │ TOOL CALL execute_read_query [✕] │ ├──────────────────────────────────────┤ ├──────────────────────────────────────────────────┤ │ ai Let me run a coverage query. │ │ QUERY │ │ ├── execute_read_query → 10 rows │ │ MATCH (o:Organization)<-[:MENTIONS]-(a) │ │ ai The top 10 companies by... │ │ WHERE NOT EXISTS { (o)<-[:HAS_SUBSIDIARY]-() } │ │ │ │ RETURN o.name, count(a) AS articles │ │ │ │ ORDER BY articles DESC LIMIT 10 │ │ │ │ │ │ │ │ RESULT 10 rows · 42ms │ │ │ │ ┌──────────────────────┬────────────┐ │ │ │ │ │ name │ articles │ │ │ │ │ ├──────────────────────┼────────────┤ │ │ │ │ │ Apple Inc. │ 18,420 │ │ │ │ │ │ Microsoft │ 14,832 │ │ │ │ │ └──────────────────────┴────────────┘ │ │ ┌────────────────────────────────┐ │ │ Escape to close │ │ │ > _ │ │ │ │ └──────────────────────────────────────┘ └──────────────────────────────────────────────────┘
Sessions
Each browser tab runs an independent session. Sessions are persisted automatically as
JSON files under --sessions-dir (default .ai/sessions/)
after each turn. The latest session is restored on page load. The session browser
(Ctrl+K → "browse sessions") lets you search and restore past conversations.
session tabs (above chat) │ neo4j-research companies-analysis ● untitled-3 + │ │ (paused) (active, turn 4) (paused) │ session browser (Ctrl+K → browse sessions) ┌──────────────────────────────────────────────────────────────┐ │ SESSIONS │ ├──────────────────────────────────────────────────────────────┤ │ ┌────────────────────────────────────────────────────────┐ │ │ │ > search sessions... │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ companies-analysis 14 turns 3 min ago │ │ neo4j-research 9 turns yesterday │ │ movie-recs-test 4 turns 2 days ago │ │ │ └──────────────────────────────────────────────────────────────┘ persistence API (local to ai web, not proxied to ai serve) GET /api/sessions list sessions (id, created_at, turns, summary) GET /api/sessions/{id} full session JSON (messages array) PUT /api/sessions/{id} write/overwrite session (10 MB max) DELETE /api/sessions/{id} remove session file
Use --no-persist to disable disk writes — sessions live in browser memory
only. Session IDs are path-validated server-side to prevent directory traversal.
Data Explorer
Switch to the Data tab to explore the connected graph or SQL sources directly. Supports multiple source types (Neo4j, SQL, MongoDB, Redis, and any other mcp-toolbox source). Write queries are detected and require confirmation before execution.
┌───────────────────────────────────────────────────────────────────┐ │ DATA [ companies2 (neo4j) ▾ ] Schema Cypher Visual │ ├───────────────────────────────────────────────────────────────────┤ │ │ │ ── Cypher playground ────────────────────────────────────── │ │ │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ MATCH (o:Organization)<-[:MENTIONS]-(a:Article) │ │ │ │ RETURN o.name, count(a) AS articles │ │ │ │ ORDER BY articles DESC LIMIT 10 │ │ │ └───────────────────────────────────────────────────────────┘ │ │ [ Run Ctrl+Enter ] ↑ query history │ │ │ │ RESULT 10 rows · 38ms │ │ ┌──────────────────────────────┬────────────────────────────┐ │ │ │ name │ articles │ │ │ ├──────────────────────────────┼────────────────────────────┤ │ │ │ Apple Inc. │ 18,420 │ │ │ │ Microsoft │ 14,832 │ │ │ │ Google │ 13,291 │ │ │ └──────────────────────────────┴────────────────────────────┘ │ │ [Table] [JSON] [CSV ↓] │ │ │ └───────────────────────────────────────────────────────────────────┘
Schema view. Clicking a node label in the schema tab auto-inserts a
MATCH (n:Label) RETURN n LIMIT 25 template in the query editor.
Fulltext and vector indexes are listed separately.
SQL sources. When the connected mcp-toolbox source is a SQL database,
the playground switches to parameterized SQL with write-keyword detection
(INSERT, UPDATE, DELETE, DROP,
TRUNCATE) and requires explicit confirmation before execution. Results are
sortable by column; CSV download is available.
Source switcher. The dropdown at the top lists all sources configured
in toolbox.yaml. Switching sources reloads the schema and clears the query
editor.
Eval Integration
Press Ctrl+Shift+S to save the current conversation as an eval case.
A modal opens with the full conversation pre-filled so you can review and edit the
expected response before saving. Cases are written as YAML to evals/
(configurable via --evals-dir). The Evals tab lets you
list, run, and export cases. After a run, pass/fail badges appear inline in the chat
history.
save eval case (Ctrl+Shift+S) ┌──────────────────────────────────────────────────────────────────────┐ │ SAVE AS EVAL CASE │ ├──────────────────────────────────────────────────────────────────────┤ │ name │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ neo4j-schema-coverage-ranking │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ expected output (edit before saving) │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ you which companies have the most article coverage? │ │ │ │ ai Apple Inc. (18,420), Microsoft (14,832)... ▌ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ → saved to evals/neo4j-schema-coverage-ranking.yaml │ │ [ Save Ctrl+Enter ] [ Cancel Escape ] │ └──────────────────────────────────────────────────────────────────────┘ eval markers in chat after a run │ you which companies have the most article coverage? ✓ pass │ │ you what are the sentiment trends for Apple? ✗ fail │ eval YAML format name: neo4j-schema-coverage-ranking input: "which companies have the most article coverage?" expected_output: "Apple Inc. (18,420 articles)..." conversation: - role: user content: "which companies have the most article coverage?" - role: assistant content: "Apple Inc. leads with 18,420 articles..."
Eval runs are sent to the eval sidecar at
http://127.0.0.1:8093/eval/run (start with
ai sidecar start eval-bridge). Results and pass/fail scores are returned
per case and displayed in the Evals tab. Export all cases as a JSON array via
GET /api/evals/export.
Auth & Team Sharing
By default ai web binds to 127.0.0.1 with no authentication
— solo dev mode. To share with teammates on a LAN, add --host 0.0.0.0 and
a --token. Team members enter the token once in the browser; it is stored
in sessionStorage for the tab lifetime.
solo dev (default) $ ai web → no auth · binds 127.0.0.1 · all requests allowed team sharing on LAN $ ai web --host 0.0.0.0 --token mysecret → Bearer token required on all /api/* endpoints → browser prompts once, stores in sessionStorage → constant-time comparison prevents timing attacks env var alternative $ AI_WEB_TOKEN=mysecret ai web --host 0.0.0.0 auth-exempt paths (no token required) GET /ui/* static assets GET /api/web-config mode + connection status GET /api/health port conflict detection POST /api/reload signal proxy reload POST /api/shutdown graceful shutdown
CORS headers are currently echoed back for any origin, making ai web
safe for loopback and LAN use. For public internet deployment, a dedicated reverse
proxy with a stricter CORS policy and TLS termination is recommended.
Keyboard Shortcuts
Keyboard-first navigation — consistent with the terminal-first brand.
| Shortcut | Action |
|---|---|
Ctrl+Enter / Cmd+Enter | Submit message (chat) or run query (data explorer) |
↑ | Cycle previous messages in chat input (shell-style history) |
Ctrl+K | Command palette — new session, browse sessions, toggle trace, clear, save eval |
Ctrl+Shift+S | Save current conversation as eval case |
Escape | Close tool side panel / dismiss modal |
Ctrl+K command palette ┌──────────────────────────────────────────────────────────┐ │ ┌────────────────────────────────────────────────────┐ │ │ │ > _ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ │ new session │ │ browse sessions │ │ toggle trace panel │ │ clear conversation │ │ save as eval case │ │ view system prompt │ │ data explorer │ │ │ └──────────────────────────────────────────────────────────┘
Local API
ai web exposes a small HTTP API on its own port for tooling and scripting.
These endpoints are served locally and are distinct from the upstream A2A API on
:8080.
| Endpoint | Description |
|---|---|
GET /api/health | Server signature — used for port conflict detection. Returns {"server":"ai-web","port":N}. |
GET /api/web-config | Mode, upstream A2A URL, debug flag, backend status, auth enabled flag. |
GET /api/backend-status | Live connection status to ai serve. Polled every 5 s by the browser. |
GET /api/prompt-diff | Unified diff of system prompt since startup. Only populated when --config is set. |
GET /api/sessions | List sessions (id, created_at, updated_at, turn count, summary). |
GET /api/sessions/{id} | Full session JSON including messages array. |
PUT /api/sessions/{id} | Write session (10 MB max body). |
DELETE /api/sessions/{id} | Delete session file. |
POST /api/evals | Save eval case as YAML (512 KB max). |
GET /api/evals/list | List YAML files in evals directory. |
POST /api/evals/run | Run eval cases via eval sidecar at :8093. |
GET /api/evals/export | Download all eval cases as a JSON array. |
GET /api/data/sources | List toolbox sources (name, kind, status). |
POST /api/data/tool | Invoke an MCP tool directly (schema introspection or query execution). |
GET /api/graph/status | Whether a graph backend is configured and reachable. |
GET /api/agents/{id}/trace-stream | SSE stream of live trace events for a task. Query param: task_id. |
POST /api/reload | Signal the running instance to reload its proxy target. |
POST /api/shutdown | Graceful shutdown. |