docs › Spectator view (watch the agent cook)
Spectator view (watch the agent cook)
Watch a connected agent work in real time. An AGENT ACTIVITY window with pause, take over and kill, a read-only loopback stream, and redacted save and replay. Covers what is redacted, the security model, how to capture it for a stream, and its limits.
Generated against dexterm/app at commit e8b8d0f. The spectator view lets you watch an agent work: each time a connected agent calls a tool (opens a token, runs a LARP check, scans new pairs, reads a bubble map, makes a proposal, gets refused), the call shows up with its result, how long it took and how old its data was. It is for watching and for demos ("watch the agent cook"). It adds no new power: agents stay untrusted, and nothing in the spectator view can arm, sign, confirm or send anything.
It works with agents connected to the loopback agent bus that dexterm agent serve runs (see bring your own agent), and — since 0.1.4 — with agents connected over MCP (dexterm mcp install): run the app with dexterm --spectate-mcp and the running terminal follows the agent as it works (see follow an MCP agent). Read limits before you rely on it.
Quick start#
$ dexterm agent add scanbot --tier observe # once: register the agent, prints a one-time token
$ export DEXTERM_AGENT_TOKEN=dxa_… # in the agent's environment, never on a command line
$ dexterm agent serve --spectator-popup --record session.jsonl
$ # now start your agent; it connects to the bus with its name and token
$ dexterm spectate --replay session.jsonl # later: replay what it diddexterm agent serve runs on fixtures (canned data, no network) unless you pass --live for keyless live reads. Fixtures are the right choice for a first try and for any recording you plan to show.
The three surfaces#
All three read the same events from one in-memory hub, which agent serve creates only when you ask for one of them. Each surface redacts its own copy (see what is redacted). The hub keeps the original events, and they never leave the process.
| surface | start it with | what you get |
|---|---|---|
| AGENT ACTIVITY window | dexterm agent serve --spectator-popup | a window titled "watch the agent cook" with live activity and the pause, take over and kill controls |
| read-only stream | dexterm agent serve --spectate | a loopback WebSocket that sends events out and accepts no commands |
| save and replay | dexterm agent serve --record FILE, then dexterm spectate --replay FILE | a redacted JSONL file of the session that you can replay offline |
The flags combine: --spectator-popup --spectate --record FILE runs all three at once, and --roster adds the agent roster popup.
1. The AGENT ACTIVITY window (follow mode)#
--spectator-popup opens the spectator window through dexterm's popup host. It is the docked native window on macOS when the native shell is built, and a browser tab on loopback otherwise, as with the chart popup. agent serve prints which one it opened. The window refreshes once a second and shows:
- screen: the screen the agent asked to show (screener, token, watchlist, ticket) with the token, feed, filter and highlight it picked. This line comes from the agent's navigation events. See limits for what it is not.
- agents: each connected agent with an
agentbadge, its tier (observe,annotateorpropose), its status (active,pausedorkilled) and when it last did something. - activity: newest first, up to 80 lines (the window keeps the last 200 events). Each line shows how long ago it happened, the agent, the phase, the tool, a short summary, the time the call took in ms and the age of the data it returned. Refused calls are marked.
| phase | meaning |
|---|---|
connect / disconnect | an agent opened or closed a session (with its client and any mandate) |
result | a read tool returned (ok or an error) |
nav | a navigation tool asked the screen to move (open_token, show_in_screener, switch_feed, add_to_watchlist, open_ticket) |
proposal | the agent made a proposal you would have to confirm (an alert, an order-plan draft, a watchlist add) |
refused | the gate refused the call, with its reason (never, tier, mandate, spoof, …) |
note / subscribe | the agent wrote a note, or subscribed to a live stream |
control | you paused, resumed or killed the agent |
Controls. Each agent row has buttons:
- pause: the gate refuses the agent's next call. The agent stays connected and visible.
- take over: pauses the agent and marks you as the driver. The window shows
YOU HAVE TAKEN OVER, and the agent stays frozen but visible. - resume: shown for a paused agent. Its calls go through again and control goes back to the agent.
- kill (the panic button): closes every live session for that agent now and leaves it paused, so a reconnect stays frozen. To revoke the agent for good, run
dexterm agent rotate <name>ordexterm agent remove <name>.
These four are the only messages the window can send, and each carries just an agent name. The agent serve process maps them to the bus's own pause, resume and kick, the same controls the roster popup uses, and checks them again before acting. The window has no button, message or path that can arm, sign, confirm, send or raise a cap.
2. The read-only stream (--spectate)#
dexterm agent serve --spectate starts a WebSocket server on 127.0.0.1 on a random port and prints the port. The per-session token goes to ~/.dexterm/spectator.json (mode 0600). It is never printed and never on a command line. dexterm spectate --info shows the endpoint (--json for scripts).
A viewer connects, then sends {"method":"hello","token":"…"} with the token from that file within 10 seconds, or the connection is closed. After a good hello it receives:
{"type":"welcome","protocol":"dexterm-spectator/1","redacted":true,"t":…}
{"type":"event","snapshot":true,"event":{…}} # the recent backlog, oldest first
{"type":"event","snapshot":false,"event":{…}} # then each new event as it happens
Each event has the same fields the window uses: seq, t, phase, agent, tier, transport, tool, summary, ok, ms, dataAgeSec, refusal, nav, and redacted: true. The server honours exactly two inbound messages: hello and ping (answered with pong). Anything else is counted as dropped and ignored. dexterm does not ship a viewer for the raw stream. It is meant for your own overlay or tooling on the same machine.
3. Save and replay (--record FILE, dexterm spectate)#
dexterm agent serve --record FILE appends every event to FILE as one JSON line, redacted, with the file created mode 0600. The redaction makes the file fit to share in a bug report or a demo.
$ dexterm spectate --replay session.jsonl # SCREEN, AGENTS and ACTIVITY as text
$ dexterm spectate --replay session.jsonl --json # {"meta":…,"view":…} for scriptsReplay is offline and deterministic. It folds the saved events back into the same screen picture and activity list the window uses, then prints them in the terminal. It does not reopen the window. Don't confuse this with dexterm --record FILE on the main app, which saves raw data responses for --replay (see the CLI reference). The two flags share a name and do different things.
Follow over MCP in the running terminal#
An agent connected over MCP (Claude Code, Claude Desktop, Cursor, Codex — set up with dexterm mcp install) runs the dexterm mcp server as its own process, which the app cannot see into. Since 0.1.4, run the app with dexterm --spectate-mcp and the running terminal follows that agent:
- While the flag is on, the app writes a small local rendezvous under
~/.dexterm(mode 0600). Thedexterm mcpserver notices it and starts emitting one record per tool call to a local channel the app tails. When you are not following, the file is absent and the server writes nothing — there is no cost when nobody is watching. - A navigation call (open a token, switch screen or feed, set a filter, highlight a row, open the paper ticket) moves the real screen, with a status line like
agent <name>: opened BONK. These are the same read-only moves the analyst makes; nothing an agent does here can size, confirm, sign, send or raise a cap. - The controls are at your keyboard: ctrl+p pauses and resumes the mirroring, ctrl+k stops following (it removes the rendezvous, so the server stops emitting), and any other key hands control back to you — the agent can never move the screen out from under you.
- A read-only, loopback, token-gated stream opens too, so
dexterm spectate --infoshows its endpoint while you follow. Every record is authenticated by the rendezvous token before it is folded, and every shared copy is redacted (see what is redacted). This is on the owner's own machine, so follow mode itself shows full detail.
The built-in analyst (the in-app ask Claude) already drives the terminal through its own action channel and shows analyst did X; this is the same idea for an agent you connected yourself over MCP.
What is redacted#
Redaction runs on every copy that leaves the hub: the stream, the saved file and the AGENT ACTIVITY window.
- Wallet and token addresses: any base58 run of 32 to 44 characters becomes
head4…tail4(7xKX…gAsU), the same short form the app uses elsewhere. A full address never appears. - Key material: any base58 run of 45 or more characters, or a hex run of 64 or more, becomes
[key]with nothing kept. The gate already refuses every key and signer tool, so key material should never reach an event, and this rule covers it anyway. - Every string field, not just the summary. The tool name, the refusal reason, the agent's name and every navigation field (screen, token, feed, filter, highlight, side) are scrubbed too. An untrusted agent might put an address or a key in an unknown tool name to smuggle it into the stream. The bus cuts a refused tool name to 64 characters and redaction masks what is left. A test drives that exact attack through the real bus and checks the stream and the saved file.
- Sensitive events: an event marked sensitive keeps only
[sensitive]as its summary.
What stays readable: phases, tool names, tiers, timings, data ages, refusal codes and verdict labels, which is what makes the stream worth watching. A token symbol, a filter or a number in a summary is shown as it is. If you don't want real coin names in a capture, run on fixtures (the default).
Security model#
- Loopback only. The stream binds
127.0.0.1on a random port and refuses any connection whose remote address is not loopback. It never binds a LAN address or::. - Per-session token. 32 random bytes, generated when the stream starts, compared in constant time, stored in
~/.dexterm/spectator.json(0600) and never passed as an argument. A connection without the token in its first 10 seconds is closed. - No inbound command path. The stream server holds only the event hub. It has no reference to the bus, the agent registry, the tool executor or any control, so no code path leads from the socket to anything that can change state. A test sends
tools/call,pauseandroster.kickat it and checks that the bus is untouched and the socket only ever sent events. - The window uses the popup host's security unchanged: a single-use ticket and a one-time WebSocket token per window, loopback bind, strict Host and Origin checks, a nonce CSP, and an allow-list that admits only the four
spectator.*control events. Every string is drawn as text, never as HTML. - Agents stay untrusted and cannot sign. The spectator view only observes calls the gate already allowed or refused. No agent at any tier has a tool that can arm, sign, confirm, send, raise a cap, change the gate or touch a wallet, a mandate or the signer, and the spectator view adds no event or control that could. Watching an agent never gives it more power.
The trust page has the short version.
Streaming it#
- Capture the window, not a port. Point your capture tool's window capture at the "watch the agent cook" window, or at the browser window if you're on the browser fallback. "Shareable" here means you capture a window on your own screen. Nothing binds a public address, and you shouldn't forward or tunnel the stream port.
- Keep secrets off screen. Don't show
~/.dexterm/spectator.json, theDEXTERM_AGENT_TOKENline ordexterm agent addoutput (it prints the agent's token once).agent serveprints the token's file path, never the token. - Use fixtures for a show.
agent servewithout--liveuses canned data, so no real coin names or market numbers appear. With--live, a capture shows whatever real coins the agent looks at. - Record and replay. Add
--record FILEso you can share the session afterwards. The file is redacted, so you can attach it to a bug report. - The window and the stream show model and agent activity, not advice. Whatever an agent concludes is its own reading of tool results.
Limits#
Stated plainly, as of e8b8d0f:
- The AGENT ACTIVITY window is a separate window with its own screen model. Its
screenline shows the screen the agent asked for, built from its navigation events, and the bus runs in its own process (dexterm agent serve), so that window is not a mirror of your terminal. The running terminal itself does move when you follow withdexterm --spectate-mcp(follow mode) — that is a different surface from the popup window. - MCP agents are shown in follow mode, which you turn on. Agents connected through
dexterm mcp(Claude Code, Claude Desktop, Cursor or Codex, set up withdexterm mcp install) appear once you run the app withdexterm --spectate-mcp; the terminal then follows them and a read-only stream opens. Without the flag the MCP server emits nothing. The built-in analyst drives the terminal through its own action channel and showsanalyst did X. - Replay is text.
dexterm spectate --replayprints the session in the terminal. It does not reopen the window. - No stream viewer ships. The raw stream is for your own tooling.
- Redaction matches patterns. It masks anything shaped like an address or a key. Other text, such as a token symbol, a filter or a number, is shown as it is.
- The window keeps the last 200 events and shows 80.
- The native window is macOS-only and needs the native shell built. Everywhere else the window opens in your browser on loopback.
See also#
- Bring your own agent: connecting an agent, tiers, mandates and the untrusted-by-default model.
- Trust: what any connected agent can never do.
dexterm help spectate: the same topic from the command line, offline.