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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user