Web UI¶
agent6 web serves a browser front-end for driving agent6 from a desktop or a phone: start and watch runs, steer them, answer their prompts, and run state machines.
The same UI on a phone (single column, bottom nav):
Start the server¶
agent6 web # serve the hub on http://127.0.0.1:7658
agent6 web <session-id> # open a session on load
agent6 web <machine> # open a machine instance on load
agent6 web <draft> # open a `machine create` draft on load (read-only)
--host / --port override the [web] config for one invocation.
Stop it with Ctrl-C.
Pages¶
Every page docks its text entry at the bottom, like a chat: Enter sends, Shift+Enter inserts a newline. Buttons do what the CLI command of the same name does, and prompts are answered inline on every page that shows a run.
- Sessions page: every session (mode, status, last activity, cost), a fan-out's lanes folded under its row
- the composer starts a run, plan or ask under a chosen preset and model (the model box shows what the config resolves; a pick overrides it)
more…holds Prune merged runs (sessions prune, or with--delete-squashedafter a confirm) and Clear saved asks
- Machines page: instances,
machine createdrafts, and cards that run an authored machine file; the composer creates a new one - Session view (live over SSE): the conversation, the same folded transcript the CLI and TUI render, with the turn in flight streaming underneath; a detail toggle cycles collapsed, expanded, hidden
- a resizable drawer holds the run's context: overview, plan.md, task graph, budget, tool calls, background shells, latest commit diff, event log
- the Latest commit widget selects any per-step commit (cumulative toggle) and the other widgets follow it; a model-controlled run has no chain and says so
- the composer steers a live run or resumes an ended one under the preset and model picked above it, and takes the steer directives; Ctrl+Enter sends a live steer as
/now, Ctrl-R searches past messages - buttons: stop now or after the step, compact, fork (a new run at the latest checkpoint, started from its composer), merge, delete history, run a finished plan (
run --from), and review (sessions review, minutes; refused while the run is live) - "Allow all" on a prompt appears only where it would grant more than the one call
- Machine view: the state overview, the path taken and the current agent state's conversation; the entry submits as Steer (into the current state) or Message (a
pokea waiting machine's next tool reads); Stop parks the instance at its next transition andmachine runresumes it - Config page: every setting with value and source, filterable; click a row to set it, with the choices an enum or a provider's model listing offers; Add provider… adds or updates a
[providers.<name>]block; secrets never shown
Start a machine on the Machines page and watch the current state stream, answering its approvals and questions in place:
Layout¶
- desktop: a nav rail, the run view a fixed pane with the drawer and conversation scrolling inside it
- phone: a top bar (theme toggle), bottom tab nav, the composer docked above it, and the run view one widget at a time, switched from the top-bar menu
Notifications and installing (PWA)¶
The page installs as an app (phone home-screen icon or desktop window).
🔔 Notifications on a machine view grants permission; machine.notify and a machine's end then pop OS notifications, except on a backgrounded phone.
For a phone not open on the page, point [machine.notify].on_event at a push service.
The HTTP API¶
The page reads the same wire form as agent6 attach --json:
curl -s localhost:7658/api/hub # hub state
curl -s localhost:7658/api/session/<id> # a run's state, as JSON
curl -s localhost:7658/api/session/<id>/conversation # the folded conversation
curl -s localhost:7658/api/machine/<name> # a machine's state, as JSON
curl -s localhost:7658/api/config # effective config
curl -sN localhost:7658/api/session/<id>/events # SSE: a snapshot per change
- reads:
/api/meta,/api/hub,/api/routes?mode=&preset=,/api/configwith/suggest/<key>and/provider_choices,/api/session/<id>(whatattach --jsonprints;?step=<sha>folds up to that commit) with/conversation,/restate,/diff(?sha=&cumulative=1) and/events,/api/machine/<name>with/reasoning,/conversationand/events,/api/draft/<name>with/conversation,/diffand/events - writes, small JSON
POSTs:/api/new,/api/session/<id>/{steer,approve,answer,merge,undo,fork,resume,run_plan,review,stop,compact,rm},/api/machine/<name>/{poke,stop,steer,approve,answer},/api/sessions/{prune,rm_asks},/api/config,/api/config/provider,/api/machine/{create,run} - every write goes through the same spawn and answer-file contracts as the CLI; machine names and answer ids validate to one path component
- the page and its PWA assets:
/,/manifest.webmanifest,/sw.js,/icon.svg,/favicon.svg
Remote access (Tailscale)¶
The server binds 127.0.0.1 by default and has no app-level auth.
For remote access, put Tailscale in front of the loopback bind:
agent6 web # keep it on 127.0.0.1:7658
tailscale serve --bg 7658 # HTTPS + WireGuard, reachable on your tailnet
- the tailnet identity is the access control and
tailscale serveterminates HTTPS; agent6 handles no tokens or passwords - a non-loopback bind exposes the write surface (spawn runs, answer prompts) to anyone reaching the port, so it refuses without the opt-in:
[web].allow_non_loopback = truefor[web].host,--allow-non-loopbackfor--host