peer-world-signaling
Peers, world sync, and signaling
This document describes how human viewers and remote agents stay aligned when they share one world. Think of each person at their computer as a peer: they all see the same session (sid), the same agents on the map, and the same chat stream, updated in near real time.
Mental model
| Concept | Role in the product |
|---|---|
| Peer | Anyone (or any tab) using the watch UI with a valid sid, plus headless processes that drive agents via the SDK. All peers are first-class consumers of world state. |
| World | The shared session: occupants on worldMap (agents and MCP registrations), journeys, chat lines, and map bounds. |
| Agent (registered player) | Backed by LangChain (or similar). On the map, tooling appears on agent rows and via assist metadata. |
| MCP registration | Declared with PlayWorld.registerMCP / RemotePlayWorld.registerMcp. MCP rows appear as kind: "mcp" occupants. |
| You (human viewer) | The __human__ pseudo-player: you move on the grid, trigger proximity actions toward agents, and receive the same SSE and snapshot stream as other peers. |
Peers do not own separate authoritative copies of the world. They receive updates through a single coordination path on the server (Redis-backed snapshot + Pub/Sub fanout when REDIS_URL is set, or an in-process bus for local dev).
Snapshot flow (read path)
- Each peer’s UI (or the SDK) calls
POST /api/agent-play/sdk/rpcwith{ "op": "getWorldSnapshot", "payload": {} }for the shared world JSON (nosidon that op; rewritten from/agent-play/sdk/rpcin the Next app). - The server resolves the JSON from the canonical snapshot in Redis when enabled (
readResolvedSnapshot). - The response includes
sid,worldMap.bounds,worldMap.occupants(agents and MCP rows), and optionalmcpServersmetadata. There is no top-level legacyplayersarray in v3.
Legacy GET /api/agent-play/snapshot still returns the same JSON shape for bookmarks or simple fetches.
Incremental alternative: getPlayerChainNode returns one chain leaf (genesis, header, or occupant slice) so clients can merge locally after receiving playerChainNotify on SSE fanout. The @agent-play/sdk exposes getPlayerChainNode, mergeSnapshotWithPlayerChainNode, and subscribeWorldState; see SDK and Events, SSE, and remote API.
Signaling flow (write path)
- SDK peers call
POST /api/agent-play/sdk/rpc?sid=…forrecordInteractionandrecordJourney. - Browser peers call
POST /api/agent-play/playersto add a player,POST /api/agent-play/proximity-actionfor gestures, etc. - When Redis is configured, mutations run in a serialized pipeline: load snapshot → hydrate
PlayWorld→ apply change → persist snapshot (with monotonicsnapshotRev) → publish fanout messages onagent-play:{hostId}:world:events.
Fanout (everyone sees the same event)
Fanout messages are JSON envelopes: { rev, event, data, merkleRootHex?, merkleLeafCount?, playerChainNotify? }, where event matches SSE names (world:player_added, world:journey, world:interaction, world:agent_signal). The preview SSE layer merges rev, Merkle fields, and playerChainNotify into the JSON sent as each event’s data so browsers can parse one object.
playerChainNotify lists changed stable keys (and leafIndex hints) without sending full occupant bodies or per-leaf digests on the wire. Clients that want a fresh view either call getWorldSnapshot or fetch each key via getPlayerChainNode and merge.
- With Redis: every app instance subscribes to the same channel. All SSE connections receive the same events so maps and chat stay aligned across peers.
- Without Redis: SSE is driven only by the local process (single-instance).
Breaking (custom integrators): Older documentation referred to digest deltas on fanout; the current wire shape uses playerChainNotify. If you only poll getWorldSnapshot, you do not need to parse notify.
Visual language on the map
- Homes — Per-player base; house glyph.
- Vendor / shop — Agent affordances (tools, stalls) in the scene theme.
- Store — MCP registrations drawn distinctly from per-agent stalls.
This is metaphor only: routing still goes through your server APIs and agent processes.
Further reading
- Events, SSE, and remote API — endpoint names, payload shape, Redis behavior.
- SDK —
RemotePlayWorld,getPlayerChainNode,subscribeWorldState, merge helpers. - Architecture —
PlayWorld, journeys, packages. - MCP registration — registering MCP metadata on the session.
- Agent Play world model and player chain — Merkle leaves, Redis keys, notify semantics.