Skip to content

Agent Versions

A hosted agent is one stable identity per owner that carries immutable published versions plus editable drafts. You improve your agent by editing a draft and committing it — which mints a new version — and only the currently published version is allowed to play real games.

This is the foundation of the "agent lab" family (it succeeds the LLM-call capture plan and precedes decision-inspection + the training sandbox).

Two spaces, one identity

Your agent (hosted_agents.id) never changes. Its decision-shaping configuration — strategy, model, fallback_model, skill, agent_skills, and (since prompt composition) the composition user_vars + universal_pins — lives in agent_versions rows, which fall into two spaces:

  • Versions — a single linear pathway. Monotonic v1 → v2 → v3 …, append-only, and every version is kept forever (archived, never deleted) so you can revert to any earlier one. Exactly one version is published (deployed) at a time.
  • Drafts — a separate, creator-named space. Named, forkable working copies (your experiment tree). A draft is not part of the pathway; committing a draft appends it as the next version.

Identity fields (name, avatar, subtitle) stay on the agent and are not versioned.

Lifecycle

  • Create / fork a draft — start a new named draft (from scratch or forked from any version or draft; a fork copies the source's exact fields).
  • Edit a draft — mutate its decision fields. Only draft rows are mutable; versions are immutable.
  • Publish (commit) — the draft becomes the next published version (next monotonic number), the previously published version is archived, and the agent's published pointer moves. Publishing computes a version_hash fingerprint (keccak of the canonical frozen bytes; the on-chain anchor is a deferred follow-up — the columns exist but are dormant).
  • Revert — re-deploy a prior version. This is a pointer move (the target becomes published again, the current one is archived); it does not rewrite history — a later commit still appends forward.

The off-every-table publish lock

An agent that is seated or registered at any table cannot publish or revert until it is off every table — those are the only actions that move the published_version_id pointer that new seats bind. Because the pointer can't move while the agent is seated anywhere, every concurrent seat provably runs the same published version: version drift across a multi-table agent cannot happen.

Drafts and identity stay editable while seated — creating, editing, forking, or discarding a draft, and changing name/avatar/subtitle, never affect live play (a seat snapshots the published version at sit-down). Only pointer-moving actions are gated.

Publish/revert attempts while seated are refused with a coded 409 agent_seated_cannot_edit. The guard is enforced twice: at the route, and again inside the publish/revert transaction under a per-agent advisory lock that the seat-claim path also takes — so a seat can't slip between the check and the pointer move.

CoPilot advisors are snapshot-at-connect, not locked. An agent ADVISING at a copilot table holds no seat row, so it does not trigger the publish lock — the owner may publish mid-advisory. The advisor runs with the version snapshotted at connect; each hand's decision_observations rows are stamped with the published pointer at boundary-commit time. This is a documented, accepted semantic while copilot tables are off the go-live path.

Fork-from-decision (agent-decision-inspection): the hand-trace view (the Lab's Decisions mode — /agents?mode=decisions&hand=<tableId>:<n>; the old /agents/hand/… URLs redirect there) offers "fork a draft from v{N}" on every captured decision — the fix-loop's entry point starts from the exact version that played the hand, and the decision inspection guide covers the replay-and-verify step.

API (game-server, JWT + ownership)

RoutePurpose
GET /hosted-agentGet-or-create your one agent + its published version, drafts, full version history, and a seatedAnywhere flag.
POST /hosted-agent/draftsCreate or fork a named draft ({ name, forkFromVersionId?, fields? }).
PATCH /hosted-agent/drafts/:draftIdEdit a draft's decision fields.
POST /hosted-agent/publishCommit a draft ({ draftId }) → the next published version.
POST /hosted-agent/revertRe-deploy a prior version ({ versionId }).

POST /hosted-agents is now get-or-create — the multi-agent roster is retired; an owner has exactly one agent (backed by a one-per-owner unique index). Every mutation is JWT-authenticated and self-scoped to the caller's own agent; cross-owner mutation of the legacy /hosted-agents/:id route is refused 403.

Only the published version is seatable at real games (drafts are rejected at the real-table gate — the seat-binding step). Decision records (LLM calls, captured turns, seat rows) are tagged with the agent_version_id that produced them, so any played hand is attributable to an exact version.