# GemTavern Game Link Protocol v1

Game Link lets a local game mod send a character card, current scene, and prompt directives to GemTavern over a local WebSocket.

The game mod owns game-specific interpretation:

- Which character is speaking.
- What scene is happening.
- Whether a live event should trigger a reply.
- The hidden prompt directive for automatic events and user messages.

GemTavern owns generic runtime behavior:

- Pairing and local WebSocket endpoints.
- Character import and chat binding.
- Prompt assembly from `promptDirective`.
- Model calls, voice, UI, storage, and TTS.

## Transport

First version uses local WebSocket:

```text
ws://{device-host}:38471/game-link/socket
```

The mod sends one JSON object per WebSocket message.

## Payload Envelope

Every v1 payload uses:

```json
{
  "protocol": "gemtavern.game_link",
  "protocolVersion": 1,
  "type": "character_card_upsert",
  "integration": {
    "id": "rimworld",
    "gameId": "rimworld",
    "gameName": "RimWorld",
    "modName": "GemTavern Game Link",
    "modVersion": "1.0.0"
  },
  "character": {
    "id": "stable-game-character-id",
    "name": "Max",
    "cardJSON": "{...}",
    "avatarPNGBase64": "..."
  },
  "scene": {
    "kind": "wedding",
    "family": "wedding",
    "priority": 50,
    "line": "Max is marrying Ballard."
  },
  "promptDirective": {
    "protocolVersion": 1,
    "sceneContext": "[Game scene context]\nMax is marrying Ballard.",
    "autoEventGuide": "[Game event reply guide]\n...",
    "directUserGuide": "[Game direct user message]\n...",
    "promptVersion": "rimworld-prompts-2026-07-02"
  },
  "event": {
    "id": "optional-stable-event-id",
    "visibleText": "Max is marrying Ballard.",
    "replyPolicy": "auto",
    "createdAtUnix": 1780000000
  },
  "state": {}
}
```

## Types

### `character_card_upsert`

Creates or updates a Game Link character in GemTavern.

Required:

- `integration`
- `character.id`
- `character.name`
- `character.cardJSON`
- `promptDirective`

Recommended:

- `character.avatarPNGBase64`
- `scene`
- `state`

### `live_event`

Sends a live game moment.

Required:

- `integration`
- `character.id`
- `character.name`
- `event.visibleText`
- `event.replyPolicy`
- `promptDirective`

If `event.replyPolicy == "auto"`, GemTavern may generate a character reply. If `event.replyPolicy == "silent"`, GemTavern updates state without asking the model for an automatic reply.

## Prompt Rules

GemTavern assembles prompts from mod-provided directives:

- Automatic event: `promptDirective.sceneContext + promptDirective.autoEventGuide`
- User text or voice: `promptDirective.directUserGuide + promptDirective.sceneContext`

GemTavern may append app-owned runtime settings, such as how the character should address the user. GemTavern does not infer game-specific scene routing from `state`.

## Field Rules

- `integration.id`: stable lowercase integration identifier, such as `rimworld`.
- `integration.gameId`: stable game identifier.
- `character.id`: stable ID from the game save, not a display name.
- `scene.line`: natural user-facing scene sentence.
- `state`: opaque game-specific JSON. GemTavern stores it but does not interpret it for scene routing.
- `promptDirective`: required for product flows. Missing directives should be treated as integration errors.

## QR Pairing

QR payload type:

```text
gemtavern_game_link_pair_v1
```

Required QR fields:

- `protocol`: `gemtavern.game_link`
- `protocolVersion`: `1`
- `type`: `gemtavern_game_link_pair_v1`
- `integration`
- `token`
- `hosts`
- `responsePort`
- `expiresAtUnix`

GemTavern responds with candidate WebSocket endpoints. Mods should try all candidates and save the first successful endpoint.
