Skip to main content
A policy is a Docker image that the runner launches once per slot. This page is the contract your image must satisfy, independent of any particular game.

Episode lifecycle

A Coworld episode runs one game container and one player container per slot:
  1. The runner generates per-slot tokens and starts the game container.
  2. The runner waits for the game to become healthy (/healthz).
  3. The runner starts a player container for each slot, injecting that slot’s WebSocket URL.
  4. Each player reads its runner-injected URL, connects, and speaks the game-defined protocol.
  5. A player exits when the game ends (the server closes the socket).
  6. The runner collects results, replay, and per-slot logs.
The runner does not restart a player container that exits.

Environment variables the runner injects

COWORLD_PLAYER_WS_URL
string
The primary player WebSocket URL: a fully-formed address pointing at the game runnable’s /player route, with the slot’s slot and token query params already encoded. Use it exactly as provided — do not rewrite or drop query parameters, and expect extra game-owned parameters to be present.
COGAMES_ENGINE_WS_URL
string
The engine WebSocket URL used by the runner integration and by several existing players (for example, the Crewrift bridge reads this). Like COWORLD_PLAYER_WS_URL, it is a full URL with slot and token; pass it through unchanged.
Two variable names appear across the codebase for the player’s WebSocket URL: COWORLD_PLAYER_WS_URL (current player-role documentation) and COGAMES_ENGINE_WS_URL (runner integration and existing players). Read whichever your target game and its reference player use, and prefer whichever the game’s current docs specify. TODO: confirm the canonical variable and any compatibility aliasing for your target game against its current source before relying on one name.
COWORLD_PLAYER_ARTIFACT_UPLOAD_URL
string
Optional destination for a per-slot artifact upload. It is either a local file:// URL (local runs) or a hosted presigned http(s):// PUT URL. Handle both schemes; skip if absent. At most one .zip per slot, ≤200 MB. Artifact upload is best-effort and must never fail the episode.
AWS_ENDPOINT_URL_BEDROCK_RUNTIME
string
The hosted Bedrock sidecar endpoint. Any Bedrock call must go here — see Bedrock contract.
BEDROCK_MODEL
string
The model ID supplied at upload time (--bedrock-model). Read your model from this variable.
USE_BEDROCK
string
Set when the policy was uploaded with --use-bedrock.

Stdout logging

  • The runner captures stdout and stderr into per-slot logs.
  • Emit structured newline-delimited JSON on stdout if you want machine- readable traces.
  • Flush eagerly and do not rely only on stderr — a crash can happen before buffered output is written.
  • The game’s results and replay are the source of truth for episode success, not your logs.

Reconnect behavior

  • The same slot/token can reconnect while the episode is still running; game state survives short disconnects.
  • During a disconnect the game applies a no-op or its documented default.
  • The runner does not restart your container, so handle transient socket drops inside your process if you want to recover.

Robustness

  • On malformed or unexpected input, return a legal default action — never raise through the game loop.
  • Any LLM/provider failure must fall back to a legal scripted action immediately. A crashing or timing-out player scores as a loss.
Sources: adapted from Metta-AI/players docs/coworld-integration-guide.md and Metta-AI/metta packages/coworld/src/coworld/docs/roles/PLAYER.md, LIFECYCLE.md.