what you see
the live view
Open the dashboard link and the first thing on screen is the live view: the directories people are working in, laid out as places, and every agent as a named cursor standing on the file it is changing — the way a design tool shows collaborators, for a repository. A cursor glides when its agent moves to another file, pulses while it is typing (an edit in the last few seconds), and carries a one-line bubble saying what the edit did. Two cursors on one file is a collision you can see forming. A ⚑ on a file is a claim; a ✓ is a reconciled one.
The column on the right is who is here now — working, typing, thinking (a prompt arrived after the last edit), idle — with their task and the file they are on. The peek under it follows whatever just happened, or whichever file or cursor you pick, by click or by key.
A crew agent is drawn as itself. When the edit was made by one of the crew,
a subagent Claude Code ran from the crew's plugin or from a team agentmash team committed,
the cursor becomes that agent's mascot and the tag reads alice lv3 · pipe, the level staying
with the person; the rail shows its head. It keeps the person's colour, tag and glow: the crew
robot may be the very robot another person in the room is drawn as, and those three are what
tell the two apart. The person's own mascot comes back with the next edit the main agent
makes, or as soon as they send a new prompt, since a prompt always goes to the main agent.
Two crew agents editing in parallel in one session share its one cursor: the newest edit
wins. The hooks send the agent's catalog name (agent_type: "pipe-backend") and nothing more;
Claude Code's own agents and any agent you gave a name of your own are sent as null (see
Threat model). It is sent by the Claude Code hooks from the release after
0.4.0, and by repos installed through the web installer after this deploy.
Presence is the default; code is opt-in. Out of the box the live view shows who is where
and what changed in a sentence (+12/-3 lines, touching User) — the same summary the hooks
already send. Nobody's code leaves their machine. A room that wants more runs:
npx agentmash diffs onFrom the next edit on, each agent's hooks also send the text the edit replaced and the text it wrote (clipped to 4 000 characters a side), and the live view shows it beside the file and the first line of it in the cursor's bubble. Three things are true of streamed diffs:
- They are never written to disk on the server. They ride beside the event on the
WebSocket and sit in a small in-memory ring so a dashboard that opens late sees the last
few; a server restart,
agentmash diffs offoragentmash purge eventsforgets them. - The room's switch is enforced by the server, not trusted from the hook. A diff sent to a room that has not opted in is dropped.
- Every developer keeps a veto.
AGENTMASH_DIFFS=0(or"stream_diffs": falsein the committed config) keeps that developer's text at home whatever the room says; their presence and summaries still show.
The other pages are the same room from other angles: people is the same hour person by person; changes is the code; collisions is who stood on whom, now and by place; history looks back.
Presence: who is actually here
The board's whole claim is that it shows who is working right now, so it has to be able to say when someone isn't. Every developer carries one of four states:
| State | What it means | What it's based on |
|---|---|---|
| ● working | Their agent edited something in the last couple of minutes. | An event just arrived. |
| ◐ idle | Their session is still open, but nothing has happened for a while. Probably reading, thinking, or in a meeting. | Silence, inside the idle window. |
| ◌ silent — never said it finished | Nothing for longer than the idle window, and the session never said goodbye. Not a lost connection — it is still open, and nothing is coming out of it. We don't know whether they're thinking or whether the laptop is shut, so the board stops claiming they're working. | Silence, past the idle window. |
| ○ session ended | Their Claude Code session ended and the hook said so. | Reported, not guessed. |
Why there is no heartbeat. Hooks only run when Claude Code does something. Between two
edits ten minutes apart, no AgentMash code is running on that machine at all — the only way
to change that is a background timer on a developer's laptop, which is a far bigger
imposition than a hook and still says nothing when the machine is asleep. So the client
reports what it can know for free (a session that ends in an orderly way) and the server
infers the rest by decay on its own clock. A closed laptop, a killed terminal and a crashed
process all send nothing; all three land in silent, which is exactly what we know
about them. Nothing was added to the edit path: an edit is still one POST /events, and a
pre-edit check is still one GET /check.
Clock skew. Clients send their own timestamps and the server is told to trust them, but never for presence. Every event is also stamped with the moment the server received it, and that stamp — not the client's — is what presence, the activity window and the dashboard's elapsed times are computed from. A timestamp from the future is clamped to receipt time, so a machine whose clock runs fast can't buy itself an extra half hour of looking busy. The dashboard ages the board on the server's clock too, so a viewer's own wrong clock doesn't empty the screen.
The advisory inherits this: instead of "alice is working in this file" twenty minutes
after alice left, /check now returns a presence label per holder and the agent is told
"alice has sent nothing for 22 min and appears to have stopped."