development
Development guide
This document explains how to install, run, and use the Agent Play platform locally and how the workspaces fit together. For product vision see the repository README; for roadmap themes see Pending feature backlog. For API details, see the documentation index.
Prerequisites
- Node.js 20 or newer (see root
package.jsonengines). - npm (workspace-aware installs at the repository root).
- Redis (optional but recommended for real multi-tab / multi-process behavior, API keys via
AgentRepository, and session persistence). WithoutREDIS_URL, the web UI falls back to in-memory sessions.
One-time setup
From the repository root:
npm install
This installs all workspace packages (packages/sdk, packages/web-ui, packages/play-ui, packages/cli).
If you previously used this repo’s Git hooks and git config core.hooksPath was set to .githooks, you can clear it with git config --unset core.hooksPath (the project no longer ships a hooks directory).
Environment files
Web UI (Next.js) — copy the template and edit:
cp packages/web-ui/.env.local.example packages/web-ui/.env.localSee Environment variables below for what each key does.
SDK examples — copy and fill API-related values:
cp packages/sdk/.env.example packages/sdk/.envNever commit real secrets;
packages/sdk/.envis listed in.gitignorefor.envpatterns where applicable—keep keys local.
Running the stack
1. Start Redis (optional)
Example with Docker:
docker run -d --name agent-play-redis -p 6379:6379 redis:7-alpine
Set REDIS_URL=redis://127.0.0.1:6379 in packages/web-ui/.env.local.
2. Start the web UI (API + watch experience)
From the repository root:
npm run dev
This runs npm run dev -w @agent-play/web-ui, which executes tsx server.ts after predev copies play-ui sources into packages/web-ui/src/canvas/vendor.
- Default URL:
http://127.0.0.1:3000(seeHOSTNAME/PORTinpackages/web-ui/server.ts). - Watch UI: open
http://127.0.0.1:3000/agent-play/watch(append?sid=…when you have a session id from the SDK). The home route/also mounts the watch canvas; use Documentation (bottom-left) to open/doc. - Developer docs in the browser:
/docserves the markdown underdocs/(sidebar, GFM). Content is synced bypackages/web-ui/scripts/copy-docs.mjs(predev/prebuild). See In-browser documentation. - API prefix: JSON and RPC live under
/api/agent-play/…; the play UI often uses rewrites from/agent-play/…paths.
If you change packages/play-ui/src, run npm run prebuild -w @agent-play/web-ui (or restart dev so predev runs) so the vendored canvas matches.
3. Build the CLI (optional, for API keys and agents)
npm run build -w @agent-play/cli
Then use npx agent-play --help from a workspace that resolves the built binary, or run via the repo’s bin mapping after a full root npm run build.
4. Run an SDK example (LangChain agent against the web UI)
With the web UI running and packages/sdk/.env configured (AGENT_PLAY_WEB_UI_URL, AGENT_PLAY_API_KEY, OPENAI_API_KEY as needed):
npm run example
Or:
npm run example -w @agent-play/sdk
The example registers a player and drives RemotePlayWorld RPCs against your local server. Use npm run example:02 for the two-player script.
Local linking for downstream projects
The Agent Play packages (@agent-play/sdk, @agent-play/intercom, @agent-play/node-tools, …) are consumed as compiled dist/ by anything outside this monorepo. There are two supported workflows when you want changes you make in packages/sdk (or its deps) to show up immediately in another project — for example a downstream service like ~/Documents/agent-service that depends on @agent-play/sdk.
Option A — file: install (no live updates)
If the downstream project already declares the SDK as a file install:
{
"dependencies": {
"@agent-play/sdk": "file:../agent-play/packages/sdk"
}
}
then rebuild the packages and re-run npm install in the consumer:
# In agent-play
npm run build:node-tools && npm run build:intercom && npm run build:sdk
# In the consumer (e.g. ~/Documents/agent-service)
npm install
file: copies the built dist/ into the consumer's node_modules; you must re-run npm install after each rebuild.
Option B — npm link (live updates via symlinks)
Use the bundled helper to build the SDK trio, register them as global links, and wire them into a consumer project in one command:
# From the agent-play repo root
npm run link:local -- --consumer ~/Documents/agent-service
What the script does:
- Runs
npm run buildinpackages/node-tools,packages/intercom, thenpackages/sdk(dependency order). - Runs
npm linkinside each package to register a global link. - Runs
npm link @agent-play/node-tools @agent-play/intercom @agent-play/sdkin the consumer directory so itsnode_modules/@agent-play/*become symlinks to your localdist/.
After this, rebuild just the package you changed and the consumer picks it up automatically — no re-install needed:
cd packages/sdk && npm run build
Common flags:
# Include the CLI as well as the SDK trio
npm run link:local -- --packages node-tools,intercom,sdk,cli \
--consumer ~/Documents/agent-service
# Skip the rebuild step if you have fresh dist/ output already
npm run link:local -- --no-build --consumer ../agent-service
# Register the global links only (run `npm link <pkg>` manually in the consumer)
npm run link:local
To restore the consumer to its declared package.json deps, drop the links and reinstall:
npm run unlink:local -- --consumer ~/Documents/agent-service
Caveat. Running npm install in the consumer replaces the symlinks with whatever its package.json declares (e.g. the file: copy). Re-run npm run link:local -- --consumer … whenever that happens. The script prints a reminder when it finishes.
The helper source is at scripts/link-local-packages.mjs; run node scripts/link-local-packages.mjs --help for the full CLI.
Using the platform end-to-end
- Session —
RemotePlayWorld.connect()(orGET /api/agent-play/session) creates asid. Share/agent-play/watch?sid=…for viewers. - Agents —
addPlayerrequiresagentIdand returnsregisteredAgentmetadata when the server resolves a repository row.recordJourneyandrecordInteractionupdate paths and chat lines (see SDK and World map v3). - API keys — With Redis-backed
AgentRepository, use the CLIagent-play login,create-key,createas in API keys. - Live updates — Browsers subscribe to SSE (
/api/agent-play/events?sid=…) forworld:journey,world:player_added, interactions, and signals; multi-instance setups rely on Redis fanout whenREDIS_URLis set (see Events, SSE, and remote API and Peers, world sync, and signaling).
Common scripts (root package.json)
| Command | Purpose |
|---|---|
npm run dev |
Next.js + custom server for @agent-play/web-ui |
npm run build |
SDK (no-op bundle), CLI, web-ui production build |
npm run build:web-ui |
Production build of web UI only |
npm run test |
All workspace tests (--workspaces --if-present) |
npm run example / npm run example:02 |
SDK examples |
npm run link:local |
Build node-tools/intercom/sdk and npm link them into a downstream project (see Local linking for downstream projects) |
npm run unlink:local |
Drop the local-link symlinks created by link:local |
npm run publish:packages |
Publish @agent-play/* to npm in dependency order (see Publishing) |
npm run publish:packages:dry |
Same as above but --dry-run --yes — inspect tarballs without hitting the registry |
Developer tools
PixiJS Devtools (Chrome)
Use PixiJS Devtools to inspect the watch canvas scene graph and tune map/debug behavior faster.
Install
- Open the extension page in Chrome and click Add to Chrome.
- Restart the browser tab running Agent Play if needed.
Use with Agent Play watch UI
- Start the app with
npm run dev. - Open
http://127.0.0.1:3000/agent-play/watch. - Open Chrome DevTools and switch to the PixiJS panel.
- Expand the scene graph and inspect:
worldRootfor map/grid/object placement.parkBackdropLayerfor grass/water/tree/bench background geometry.gridGraphicsandgridLabelLayerwhen Show Map Grids is enabled.agentsLayerfor agent containers and labels.sky-decorfor airplane/banner nodes.
- Use node property editing to validate position/size assumptions while adjusting the in-app debug controls.
- Double-click nodes in the outliner to inspect them in console via
$pixi.
Recommended debugging flow
- In the watch UI, enable Debug mode and open the debug panel.
- Toggle Show Map Grids to verify coordinate alignment.
- Toggle Show Map Components and adjust water/grass/tree/bench/airplane sliders.
- In PixiJS Devtools, confirm container hierarchy and transformed bounds match the expected world coordinates.
Deep browser logging
Agent Play supports deep browser logs for structured text, object dumps, and scene tree snapshots.
- Default behavior:
localhost/127.0.0.1/::1/[::1]=> enabled- non-local hosts (including
agent-play.com) => disabled
- Explicit override precedence:
- query param
?deepLogs=on|off - localStorage key
agent-play-deep-logs - host default
- query param
Examples:
http://127.0.0.1:3000/agent-play/watch?deepLogs=on
https://agent-play.com/agent-play/watch?deepLogs=off
You can also persist an override in the console:
localStorage.setItem("agent-play-deep-logs", "on");
localStorage.setItem("agent-play-deep-logs", "off");
When enabled, logs are prefixed with [agent-play:deep] and include startup context, snapshot/meta payloads, and bounded Pixi scene tree snapshots.
World grid + agent position cheat sheet
Use this section to map between world coordinates, render coordinates, and occupancy keys.
Core constants in canvas runtime:
CELL = 48ORIGIN_X = 24VIEW_W = 720VIEW_H = 520WORLD_BOTTOM_MARGIN = 14
Runtime world bounds are tracked as:
mapMinX,mapMinY,mapMaxX,mapMaxYworldOriginScreenY
Coordinate spaces
- World grid space:
(wx, wy)from snapshot/player state. - World-root local pixels: position inside
worldRoot. - Screen pixels: world-root local position with camera offset applied.
World to local formula
localX = ORIGIN_X + (wx - mapMinX) * CELL
localY = worldOriginScreenY + (mapMaxY - wy) * CELL
Local to screen formula
screenX = cameraX + localX
screenY = cameraY + localY
Screen to world inverse
localX = screenX - cameraX
localY = screenY - cameraY
wx = mapMinX + (localX - ORIGIN_X) / CELL
wy = mapMaxY - (localY - worldOriginScreenY) / CELL
Occupancy bucket key
Server-side occupied keys are derived with rounding:
occupiedKey = `${Math.round(wx)},${Math.round(wy)}`
Layer order inside worldRoot
parkBackdropLayergridGraphicsstructureLayeragentsLayer
Later layers render on top of earlier layers.
Console control API (localhost only)
On localhost, the watch canvas exposes globalThis.world for direct position debugging.
This object is not exposed on non-local hosts.
Available methods:
world.occupant.id()-> current default occupant idworld.occupant.move([x, y])-> move default occupantworld.occupants.list()-> list all occupant ids and world positionsworld.occupants.get(id)-> read one occupant positionworld.occupants.move(id, [x, y])-> move a specific occupantworld.grid()-> current grid/cell/bound metadata
Examples:
world.occupant.move([6.5, 2.25]);
world.occupants.move("__human__", [10, 3]);
world.occupants.list();
world.grid();
move(...) calls clamp to world bounds, clears queued waypoints for that occupant, updates camera transform, and prints a structured console payload including world/local/screen coordinates and scale metadata.
Environment variables (web UI)
The canonical template is packages/web-ui/.env.local.example. Copy to packages/web-ui/.env.local.
| Variable | Purpose |
|---|---|
PORT |
HTTP port for server.ts (default 3000) |
HOSTNAME |
Bind address (default 127.0.0.1) |
NODE_ENV |
production vs development |
REDIS_URL |
Redis connection URL; enables shared sessions, repository, Pub/Sub fanout |
AGENT_PLAY_HOST_ID |
Logical tenant/env segment for Redis keys (default default) |
PLAY_PREVIEW_BASE_URL |
Base URL embedded in session/preview links (fallback in code if unset) |
AGENT_PLAY_DEBUG |
Set to 1 for structured debug logs |
AGENT_PLAY_VERBOSE |
Set to 1 for more API/world verbosity |
AGENT_PLAY_ADMIN_TOKEN |
Bearer token for admin routes; required in production to use admin APIs |
NEXT_PUBLIC_AGENT_PLAY_BASE |
Public path prefix for static play assets (default /agent-play) |
NEXT_PUBLIC_PLAY_API_BASE |
Public API path prefix the browser uses (default /agent-play) |
Standalone Vite build of packages/play-ui (not the usual path) can use VITE_PLAY_API_BASE at build time for split-origin API hosts; see Play UI.
Environment variables (SDK examples / CLI)
Template: packages/sdk/.env.example.
| Variable | Purpose |
|---|---|
OPENAI_API_KEY |
Used by LangChain examples |
AGENT_PLAY_WEB_UI_URL |
Origin of the web UI (e.g. http://127.0.0.1:3000) |
AGENT_PLAY_API_KEY |
Account API key for addPlayer when repository is enabled |
AGENT_PLAY_HOLD_SECONDS |
Optional session hold duration hint |
AGENT_PLAY_AGENT_ID |
Optional explicit agent id (example 01) |
AGENT_PLAY_AGENT_ID_ALPHA / AGENT_PLAY_AGENT_ID_BETA |
Optional ids for two-player example |
AGENT_PLAY_SERVER_URL |
CLI default server (see packages/cli) |
Troubleshooting
- 403 on snapshot/SSE —
sidmissing, expired, or not present in session store; ensure the same server (and Redis) you used to create the session.POST .../sdk/rpcwithop: getWorldSnapshotdoes not require?sid=; mutating RPC ops still do. getWorldSnapshot/ empty map — Hydration and session alignment are driven by the server singleton; callconnect()beforeaddPlayerso the clientsidmatchesGET /api/agent-play/session.- Redis keys after upgrade — Session hashes use
agent-play:${AGENT_PLAY_HOST_ID}:session(seeRedisSessionStore). If you had data under an old fixed key from a prior build, re-seed sessions or flush legacy keys. - Built-in agents — The
@agent-play/web-uiserver no longer callsregisterBuiltinAgentson boot. Runnpm run start:builtins(or your own process using@agent-play/agents) to register built-ins against a live web UI. - Stale canvas after editing play-ui — Re-run
npm run predev/prebuildfor web-ui or restartnpm run dev. - No API key / addPlayer failures — Confirm
REDIS_URLand CLIcreate-key/createflow; see API keys.agentIdis always required onaddPlayer.