Supervise several agents at once

One daemon already saw every session; the dashboard only ever showed one of
them well. Under four parallel agents it failed in specific ways, each fixed
here:

- Sessions were identified by workspace basename, so two agents in one repo
  were indistinguishable. The daemon now hands out a small ordinal badge in
  arrival order and the web UI colours each agent by it — rows, timeline
  lines, and approval cards, which previously asked you to approve `rm -rf`
  "in remote-grok" without saying which one.
- `currentTool` held a single call, so parallel tools overwrote each other.
  It is now a list; durations are matched FIFO per tool name, since hook
  payloads carry no call id.
- One 400-event ring, evicted oldest-first, let a chatty agent blank
  everyone else's history. Eviction now takes from whichever session holds
  the most of the ring.
- Sessions sorted by recency jumped under a moving thumb. They are ordered
  waiting -> error -> working -> idle -> ended, ties on badge, so an agent
  keeps its slot.
- Events carry no workspace root, so a restart came back with a full
  timeline and an empty roster. The session map is persisted to
  sessions.json (debounced, flushed on shutdown, 12h cutoff on load).
  In-flight tools are dropped on the way out: they belonged to a process
  that no longer exists.
- Snapshot pushes now back off to 1s once a snapshot exceeds 24KB, since
  full-snapshot SSE cost scales with agent count.
- Pending approvals are ordered by which expires first, not by arrival.

`glance status` and /local/status report the roster by state. e2e suite:
220 passed, 0 failed.
This commit is contained in:
iceBear67
2026-08-09 05:27:00 +00:00
parent 5eec1940be
commit 6d34a17d9d
18 changed files with 637 additions and 94 deletions
+58 -20
View File
@@ -1,32 +1,37 @@
# grok-glance
A Grok Build plugin that puts a small web dashboard behind a passkey, so you can glance at what
an agent is doing from your phone — and tap **approve** or **deny** when it wants to run something
risky.
your agents are doing from your phone — and tap **approve** or **deny** when one wants to run
something risky. Several agents at once is the normal case, not an edge case.
It is deliberately small: read-only, plus remote approve/deny. It cannot send prompts, edit files,
or drive a session.
```
┌────────────────────────────┐
│ ● grok-glance 2 sessions
│ ● grok-glance 2 working ·
│ 1 waiting │
├────────────────────────────┤
│ Waiting on you 62s │
│ Bash · in remote-grok
│ Bash · in ●2 remote-grok │
│ rm -rf ./dist │
│ ▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░ │
│ [ Deny ] [ Approve ] │
├────────────────────────────┤
remote-grok working
~/src/remote-grok
Last asked: fix the flaky
Read 4s
server/src/state.ts
12 tools 0 failed 0 ✗
Agents 3 live
All agents 3
●1 remote-grok working
Read 4s state.ts +2
31 tools now
●2 remote-grok waiting
│ Bash 1m rm -rf ./dist │
│ 12 tools 1 failed 4s │
│ ●3 docs-site error │
│ build failed 2m │
├────────────────────────────┤
│ Activity │
│ ● Read state.ts 14:22 │
│ ● Bash npm test 14:21 │
│ ● Read state.ts ●1 14:22 │
│ ● Bash npm test ●3 14:21 │
└────────────────────────────┘
```
@@ -119,18 +124,50 @@ Repeat for each device you want. `node bin/glance devices` lists them; `node bin
## What the dashboard shows
- **Now** — the workspace, its state (working / waiting on you / idle / error / ended), the last
thing you asked, the tool currently running with a live elapsed timer, and running counts of
tools, failures and denials.
- **Sessions** — one row per live session when there is more than one; tap to filter.
- **Activity** — a timeline of prompts, tool calls with durations, failures, permission denials,
notifications, subagents, compactions, session start/end.
- **Pending approvals** — a card per waiting tool call, with the command, a countdown, and two
large buttons.
- **Agents** — one row per agent whenever there is more than one: badge, workspace, state, what it
is running right now, and how long ago it last did anything. Tap one to focus it; tap **All
agents** to come back. Ended sessions are folded away behind a toggle.
- **Now** — the focused agent: its workspace, state (working / waiting on you / idle / error /
ended), the last thing you asked, **every** tool it currently has in flight with a live elapsed
timer each, and running counts of tools, failures and denials.
- **Activity** — a merged timeline of prompts, tool calls with durations, failures, permission
denials, notifications, subagents, compactions, session start/end. Each row is stamped with the
badge of the agent it came from; focusing an agent filters it down to that one.
- **Pending approvals** — a card per waiting tool call, with the command, which agent is asking, a
countdown, and two large buttons.
Updates arrive over Server-Sent Events. The server sends whole snapshots rather than deltas, so a
phone that slept through twenty events still wakes up showing the truth.
## Several agents at once
Watching four agents on a phone is a different problem from watching one, so a few things are not
what you might assume:
- **Badges, not names.** Labels are workspace basenames, so two agents in the same repo are both
"remote-grok". The daemon hands each session a small ordinal in arrival order — `●1`, `●2` — and
the dashboard colours everything belonging to that agent with it: its row, its timeline lines, its
approval cards. The ordinal survives a daemon restart.
- **Sorted by who needs you, then fixed.** Rows are ordered *waiting → error → working → idle →
ended*, and ties break on badge. Within a state an agent never changes position, because a list
that re-sorts on every event moves the row out from under a thumb already heading for it.
- **A chatty agent cannot bury the others.** The event ring is global, but eviction always takes
from whichever session currently holds the most of it. One agent in a tight loop trims itself
rather than blanking everyone else's history.
- **Parallel tool calls are all shown.** An agent runs several tools at once; the focused card lists
them and the overview row shows the first with a `+2`. Durations are matched oldest-first per tool
name, since hook payloads carry no call id.
- **A restart does not lose the roster.** Events carry no workspace root, so the session map is
persisted separately (`sessions.json`) and reloaded on boot — otherwise every agent would come
back nameless until it happened to speak again. In-flight tools are deliberately *not* restored:
they belonged to a process that no longer exists.
`glance status` shows the same breakdown from a terminal:
```
sessions : 4 (1 waiting on you, 1 error, 2 working)
```
## Remote approve / deny
Off by default. Turn it on from the phone's settings panel, or:
@@ -186,6 +223,7 @@ Everything lives in `~/.grok/glance` (mode 0700), or `$GLANCE_HOME` if you set i
| `admin.token` | Rotated every daemon start; authenticates the CLI |
| `hook.secret` | Shared secret the hook scripts present on `/hook/*`. Created once, mode 0600, never rotated — a rotation mid-session would 403 whatever was already in flight. Delete it and the daemon mints a new one on its next start; hooks then need that restart to agree again, which `glance status` will tell you about. |
| `events.jsonl` | Append-only event log, one JSON object per line, rotated at 5 MB |
| `sessions.json` | The agent roster — label, badge, workspace, state, counts — so a restart comes back with the overview intact. Written debounced, flushed on shutdown; sessions older than 12 hours are dropped on load. |
| `daemon.log` | Daemon stdout/stderr |
Three environment variables override `config.json`, which is mostly useful for testing a second