packages/play/README.md

@clankie/play

Clankie’s shared turn-based play kernel, model transport and durable trail for Pokémon and Minecraft.

This package holds no emulator. His body is a seat in a hosted PokeAgents world (ADR 0145), and everything here sits above GbaDriverIo — one interface in src/body-seam.ts that the seat implements. The loop never learns what is behind it. The Pokémon extension owns its connector and execution composition through the shared game-extension lifecycle; the Minecraft adapter supplies its native play policy. This package retains the Pokémon adapter alongside their shared scheduler, transport and journal readers (ADR 0254).

What is in here

ModuleWhat it owns
play-kernel.tsShared numbered turns, bounded proposal replacement, failure backoff and speech cooldown.
play-model.tsShared streamed model requests, deadlines, cancellation and provider pricing.
play-journal.tsOne append-only identity/version envelope for native game payloads.
free-play.tsThe Pokémon adapter: observe, decide, act, diff, record. Progress, stall detection, learned transitions, and the interjection queue.
free-play-mind.tsThe model-backed decision-maker and the voice agent, built from the same persona so the two halves are one character.
free-play-voice.tsWhat he says out loud, and when volition lets him (ADR 0056).
free-play-journal.tsThe append-only journal and compatible Pokémon/Minecraft readers (ADR 0068).
play-journey.tsJourney identity, and the notes and objective the next sitting inherits (ADR 0126).
play-story.tsThe bounded story a journal projects for the console and captain.

Free play is a model, not an algorithm

ADR 0049 defines free-play agency. Each turn Clankie receives the decoded state and the action vocabulary and chooses; nothing here supplies a route. He returns a bounded monologue, intent, notes, and one catalogued action, which the world's own contract accepts or refuses.

The loop holds no save, load, or restart action. The world keeps the cartridge and persists it through its own catalog, so his notes and objective are what cross a sitting — never a rewindable savestate.

Sessions use the rolling evidence policy (ADR 0061): when the bounded evidence window fills, it is sealed and a fresh one starts, with the roll counted in the trace. Open-ended play never dies at a receipt-sized cap.

Pokémon sessions have a default 250,000 charged-token ceiling, configurable with clankie games budget (see CLI reference). Mind, voice and interrupted/repeated proposals all contribute to turn and summary usage. Missing provider usage reserves 16,000 tokens; unknown prices stay null, and a dollar cap fails closed. Thresholds are checked between calls, so one final call can exceed a limit; these are estimated model costs, not invoice guarantees.

Each turn allows two voice preemptions, then finishes with later speech held in a 32-slot FIFO. Overflow merges into its last bounded slot. The existing InterjectionQueue default remains a latest-line slot for other consumers. Before an action, decoded map/scene/battle/menu/dialog state is sampled again; changed decision state gets at most two fresh proposals, then no action that turn. Frame animation alone does not invalidate an action. Failed or invalid decisions back off 1/2/4/8 seconds and stop after five consecutive failures; success resets the counter. Retry waits observe a requested stop.

onNotable emits each kind once per sitting: stuck, two retired objectives, model unavailable, world ended (at the service boundary), and exhausted usage. The service queues these as information to the original conversation under its existing grant. Delivery never gates the motor; terminal admission waits at most two seconds. Clankie may guide the mind, speak to the room or stop. A missing conversation or delivery failure is logged without redirecting it elsewhere.

Running it

Clankie can start an installed local world through his machine tools, including an authorized Discord voice handoff. The shipped pokeagents skill covers checking the endpoint, keeping pokeagents start in a persistent terminal, and joining after readiness. The host, cartridges, and his own credentialed seat remain separate prerequisites; joining alone does not start the world.

The captain is the parent of a sitting; this package is the driver. The service's play host starts on the first join or explicit play observation, reconciles any stale session, and joins the world when asked. Service boot does not start its polling loop; observing alone does not join a world. The host hands the joined seat to the loop here. To watch a playthrough without a Discord ask, start @clankie/discord-activity, point WORLD_ADDRESS at a running world, and run:

CLANKIE_FREE_PLAY_TURNS=20 pnpm play:live

To read a journal after the fact:

pnpm --filter @clankie/play gameplay:evaluate-journal <journal.jsonl>
  • pnpm --filter @clankie/play test