> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reactor.inc/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To build and serve your own model, start at /deploy/development/quickstart and /deploy/development/overview. Deploying is the default path: reactor init scaffolds a workspace, reactor auth login authenticates, and reactor model deploy registers the model, publishes the release with the weights/ folder, and activates it on Reactor's GPUs, in one command from that workspace. Docker must be running, because the publish step builds the image locally. Bump model.version in reactor.yaml before redeploying a change, because a release that already has an image is reactivated as it is. Deployment access is granted per account, so contact team@reactor.inc if a deploy is refused. Every key in reactor.yaml is documented at /deploy/platform/reactor-yaml. Model code imports reactor_runtime; Python client code imports reactor_sdk. The runtime overview explains the model interface. Running the model on your own machine with reactor run is optional and needs a GPU you attach with --gpus; /deploy/development/local-testing covers that loop and pairs a complete brightness model with a Python client test in a separate brightness-test workspace.
> Reactor hosts multiple models, each with its own connect slug (modelName) and command/event schema. The video model catalog — slug, typed SDK package, and links to its schema — is at /model-api-reference/overview. Robotics policy documentation starts at /robotics/overview; X-WAM observations, actions, and client integration are under /robotics/xwam/; Cosmos3 Nano Policy DROID is under /robotics/cosmos/nano-policy-droid/. Some models expose one slug per experience (e.g. HappyOyster); always take the slug from the model's own pages, never guess it.
> Fastest path to a working app: `npx create-reactor-app my-app --model=<slug>` scaffolds a complete app with secure auth wired up. Typed TypeScript SDKs are published as @reactor-models/<model>; Python uses the base reactor-sdk package.
> Auth: exchange an API key (rk_...) for a JWT via POST https://api.reactor.inc/tokens from your server. Never put the API key in client-side code.
> Append .md to any docs URL for clean Markdown. Search these docs via the MCP server at https://docs.reactor.inc/mcp.

# EdenLive schema reference

> The complete EdenLive track, command, and state-update schema.

Connect to `reactor/eden-live` and send commands with `reactor.sendCommand()`. This reference lists
the media tracks, command parameters, and messages for a session. For a quick start, see the
[overview](/model-api-reference/eden-live/overview).

## Tracks

| Direction | Name | Type | Contract |
| - | - | - | - |
| Inbound | `source_video` | Video | Client-supplied draft video, at a steady rate from 10 to 60 fps. |
| Outbound | `main_video` | Video | Generated world video, 1280 × 704. |
| Outbound | `draft_video` | Video | The draft frame each generated frame was made from, with the same timing. |

The model returns its source frames on `draft_video` alongside the generated `main_video`. Use them
to compare the source and generated views.

With `source: "client"`, each chunk uses the latest `source_video` frames. EdenLive scales them to
cover 1280 × 704 and center-crops them. The output plays at the rate your frames arrive. With
`source: "game"`, EdenLive renders the selected game itself.

From connect until `start`, both outputs show the staged reference, or a black frame before one is
staged.

## Session lifecycle

Connect with the base SDK, then choose a draft source and stage an appearance reference and prompt.
Use `select_game` to stage an official game cover and prompt with either draft source. Use
`set_image` to stage an uploaded reference while keeping the current prompt.

`start` creates a new world and resumes generation. It replaces any existing world, including a
paused one. Both sources require a non-blank prompt. Game source also requires a selected game.
Client source requires a staged appearance reference.

Pause before calling `select_game` or `set_image`. Both commands end the current world. `set_prompt`
takes effect from the next chunk and keeps visual history. It leaves a paused world paused.

`set_source` keeps visual history when you switch sources. Switching to `game` starts the selected
game from its opening. Before the first `start`, resuming with `set_paused` fails. If a game ends,
call `start` to create a new world. You can also switch away from game source and resume with
`set_paused` and `paused: false`.

In game mode, steer the game with `set_keys` and `set_look`. Pausing, switching sources, and an
outcome release both.

Commands are valid at any time unless a precondition below prevents them. If you press and release a
key before the game captures its next frame, that press can have no effect.

## Commands

Use `reactor.sendCommand()` in JavaScript and React, or `reactor.send_command()` in Python. The
JavaScript method reports failures through the SDK error interface and resolves `undefined`. It does
not reject. Read `reactor.getLastError()` for the error. When EdenLive refuses a command, the
error's `code` is one of the codes listed under that command, such as `pause_required`, and its
message says why. A successful reply contains the snapshot in `reply.data`.

Use the snapshot to update your controls. Verify video playback on both output tracks. The only
model message is `state_update`.

The examples assume a ready session. In React, call `useReactor` from `@reactor-team/js-sdk` inside
a component under `ReactorProvider`. Put awaited calls in an async event handler. The typed SDK,
`@reactor-models/eden-live`, wraps each command as a method, such as `selectGame` and `setLook`. The
[tutorial](/model-api-reference/eden-live/tutorial#handle-refusals) shows error handling.

| Command | Description |
| - | - |
| `start` | Create or replace a world and resume generation. |
| `set_keys` | Replace the keys currently held in the selected game. |
| `set_look` | Hold a turn and tilt rate in the selected game. |
| `set_image` | Stage an uploaded appearance reference. |
| `set_paused` | Pause or resume the current world. |
| `set_prompt` | Change the scene prompt. |
| `set_source` | Choose game or client draft video. |
| `select_game` | Stage one official game, its cover, and its prompt. |

### `start`

Creates a new world and resumes generation. It replaces any current world, including a paused one.
The same seed, appearance reference, and draft repeat a world. The first chunk arrives on
`main_video` and `draft_video` after the source delivers its draft frames.

| Parameter | Type | Required | Default | Range / notes |
| - | - | - | - | - |
| `seed` | integer | No | `2026092901` | `0` through `2147483647` |

Errors:

* `game_required`: Game source has no selected game.
* `image_required`: Client source has no staged appearance reference.
* `prompt_required`: The prompt is blank.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const reply = await reactor.sendCommand("start", { seed: 2026092901 });
  if (!reply) console.error(reactor.getLastError());
  else console.log(reply.data);
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const sendCommand = useReactor((s) => s.sendCommand);
  const reply = await sendCommand("start", { seed: 2026092901 });
  if (!reply) console.error("The command failed; read session error state.");
  else console.log(reply.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reply = await reactor.send_command("start", {"seed": 2026092901})
  if reply is not None:
      print(reply["data"])
  ```
</CodeGroup>

### `set_keys`

Replaces the full set of held keys for the next game frame EdenLive captures. EdenLive ignores the
keys while paused or when the source is `client`. A short press can fall between captured frames.
Check `state_update` and the output video to see which inputs took effect.

| Parameter | Type | Required | Default | Range / notes |
| - | - | - | - | - |
| `keys` | string array or `null` | No | `null` | Empty or `null` releases all keys. The schema declares `maxLength: 14`. Allowed strings: `"w"`, `"a"`, `"s"`, `"d"`, `"arrowup"`, `"arrowdown"`, `"arrowleft"`, `"arrowright"`, `"f"`, `" "`, `"shift"`, `"e"`, `"r"`, `"g"`. |

The schema declares no command-specific error for `set_keys`. A paused world or client source
ignores the keys. With client source, your own game handles the keyboard.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const reply = await reactor.sendCommand("set_keys", { keys: ["w", " "] });
  if (!reply) console.error(reactor.getLastError());
  else console.log(reply.data);
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const sendCommand = useReactor((s) => s.sendCommand);
  const reply = await sendCommand("set_keys", { keys: ["w", " "] });
  if (!reply) console.error("The command failed; read session error state.");
  else console.log(reply.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reply = await reactor.send_command("set_keys", {"keys": ["w", " "]})
  if reply is not None:
      print(reply["data"])
  ```
</CodeGroup>

### `set_look`

Holds a look rate in the game, like a mouse turning the view. Each game frame applies the latest
rate until the next call replaces it, so send `0, 0` when the movement stops. The rate adds to the
arrow keys' look, up to the game's own turn speed. EdenLive ignores it while paused or when the
source is `client`.

| Parameter | Type | Required | Default | Range / notes |
| - | - | - | - | - |
| `yaw` | number | No | `0` | `-1` to `1`. Positive turns right, negative left. |
| `pitch` | number | No | `0` | `-1` to `1`. Positive tilts up, negative tilts down. |

The schema declares no command-specific error for `set_look`. Values outside the range fail
validation.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const reply = await reactor.sendCommand("set_look", { yaw: 0.5, pitch: 0 });
  if (!reply) console.error(reactor.getLastError());
  else console.log(reply.data);
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const sendCommand = useReactor((s) => s.sendCommand);
  const reply = await sendCommand("set_look", { yaw: 0.5, pitch: 0 });
  if (!reply) console.error("The command failed; read session error state.");
  else console.log(reply.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reply = await reactor.send_command("set_look", {"yaw": 0.5, "pitch": 0})
  if reply is not None:
      print(reply["data"])
  ```
</CodeGroup>

### `set_image`

Stages an appearance reference in place of the game cover. Pause the world before calling it. The
command ends the current world and keeps the current prompt.

Upload a PNG, JPEG, or WebP image at least 32 pixels wide and 32 pixels high, up to 25 MiB and 16
megapixels. When the world starts, EdenLive scales and center-crops the reference to 1280 × 704. Use
`uploadFile` and pass the returned `FileRef` to the command. A data URL cannot replace the upload
reference.

| Parameter | Type | Required | Default | Range / notes |
| - | - | - | - | - |
| `image` | `FileRef` | Yes | Not specified | Uploaded PNG, JPEG, or WebP. Minimum 32 pixels per dimension; maximum 25 MiB and 16 MP. |

Errors:

* `pause_required`: The world is not paused.
* `game_required`: Game source has no selected game.
* `invalid_image`: The file is empty, too small, too large, or undecodable.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const image = await reactor.uploadFile(referenceFile);
  const reply = await reactor.sendCommand("set_image", { image });
  if (!reply) console.error(reactor.getLastError());
  else console.log(reply.data);
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const { uploadFile, sendCommand } = useReactor((s) => ({
    uploadFile: s.uploadFile,
    sendCommand: s.sendCommand,
  }));
  const image = await uploadFile(file);
  const reply = await sendCommand("set_image", { image });
  if (!reply) console.error("The command failed; read session error state.");
  else console.log(reply.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  image = await reactor.upload_file("reference.png")
  reply = await reactor.send_command("set_image", {"image": image})
  if reply is not None:
      print(reply["data"])
  ```
</CodeGroup>

The SDK serializes the upload reference with these wire fields:

| Field | Type | Required |
| - | - | - |
| `upload_id` | UUID string | Yes |
| `name` | string | Yes |
| `mime_type` | string | Yes |
| `size` | integer | Yes |

### `set_paused`

Pauses or resumes the current world. Pausing holds both draft and generation and releases held keys
and look. Resuming continues the same world. Use `start` when you want a new world.

| Parameter | Type | Required | Default | Range / notes |
| - | - | - | - | - |
| `paused` | boolean | No | `true` | `true` pauses. `false` resumes. |

Errors:

* `start_required`: You tried to resume before `start`.
* `game_ended`: You tried to resume a game that reached an outcome.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const reply = await reactor.sendCommand("set_paused", { paused: true });
  if (!reply) console.error(reactor.getLastError());
  else console.log(reply.data);
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const sendCommand = useReactor((s) => s.sendCommand);
  const reply = await sendCommand("set_paused", { paused: true });
  if (!reply) console.error("The command failed; read session error state.");
  else console.log(reply.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reply = await reactor.send_command("set_paused", {"paused": True})
  if reply is not None:
      print(reply["data"])
  ```
</CodeGroup>

### `set_prompt`

Changes the scene description from the next chunk. The world keeps its visual history, so the look
changes gradually. A paused world stays paused. The reply gives the accepted prompt, and the output
video shows its effect. With source `game`, the reply can take about two seconds before a world
starts. Sending the current prompt again changes nothing and replies at once.

| Parameter | Type | Required | Default | Range / notes |
| - | - | - | - | - |
| `prompt` | string | Yes | Not specified | Non-empty, maximum 4096 characters. |

Errors: `prompt_required` for blank text.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const reply = await reactor.sendCommand("set_prompt", { prompt: "A frozen moonlit fortress." });
  if (!reply) console.error(reactor.getLastError());
  else console.log(reply.data);
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const sendCommand = useReactor((s) => s.sendCommand);
  const reply = await sendCommand("set_prompt", { prompt: "A frozen moonlit fortress." });
  if (!reply) console.error("The command failed; read session error state.");
  else console.log(reply.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reply = await reactor.send_command("set_prompt", {"prompt": "A frozen moonlit fortress."})
  if reply is not None:
      print(reply["data"])
  ```
</CodeGroup>

### `set_source`

Chooses the draft source for `start`. In an existing world, the switch takes effect from the next
chunk and keeps visual history. Switching to `game` starts the selected game from its opening. After
a game ends, switch away from game source to allow a resume. The switch releases held keys and look.
Read the state in the reply, then check the output video for the source change.

| Parameter | Type | Required | Default | Range / notes |
| - | - | - | - | - |
| `source` | string | Yes | Not specified | `"game"` or `"client"`. Client source reads `source_video` at 10 to 60 fps. |

Errors: `game_required` when a started world switches to `game` before `select_game`.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const reply = await reactor.sendCommand("set_source", { source: "client" });
  if (!reply) console.error(reactor.getLastError());
  else console.log(reply.data);
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const sendCommand = useReactor((s) => s.sendCommand);
  const reply = await sendCommand("set_source", { source: "client" });
  if (!reply) console.error("The command failed; read session error state.");
  else console.log(reply.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reply = await reactor.send_command("set_source", {"source": "client"})
  if reply is not None:
      print(reply["data"])
  ```
</CodeGroup>

### `select_game`

Stages an official game with its cover as the appearance reference and its official prompt as the
scene description. Pause the world before calling it. The command ends the current world. The cover
and prompt can also style client video. With source `game`, the reply arrives once the game is
loaded, about two seconds later, so a `start` after the reply begins at once.

| Parameter | Type | Required | Default | Range / notes |
| - | - | - | - | - |
| `game` | string | Yes | Not specified | One of `"freeze"`, `"spiders"`, `"tracer"`, `"bioshock"`, `"godzilla"`, `"arcadeduel"`, or `"dust2"`. |

Errors: `pause_required` while a world plays.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const reply = await reactor.sendCommand("select_game", { game: "freeze" });
  if (!reply) console.error(reactor.getLastError());
  else console.log(reply.data);
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  const sendCommand = useReactor((s) => s.sendCommand);
  const reply = await sendCommand("select_game", { game: "freeze" });
  if (!reply) console.error("The command failed; read session error state.");
  else console.log(reply.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reply = await reactor.send_command("select_game", {"game": "freeze"})
  if reply is not None:
      print(reply["data"])
  ```
</CodeGroup>

## Messages

| Message | Description |
| - | - |
| `state_update` | Complete observable session state, emitted on connect, after accepted commands, and after every chunk. |

Replace the displayed state with each complete `state_update` snapshot. Clear it when you
disconnect. Use these snapshots as the source of state instead of reconstructing it from transition
events.

### `StateUpdate` payload

| Field | Type | Description |
| - | - | - |
| `source` | string | `game` or `client`, the current draft source. |
| `game` | string | Selected game, or an empty string before `select_game`. |
| `image_name` | string | Appearance-reference file name, or an empty string before staging one. |
| `prompt` | string | Scene description in effect. In game mode, each chunk also adds game event sentences. |
| `seed` | integer | Seed of the current world. |
| `keys` | string array | Keys held now. Frames already captured retain their captured keys. |
| `look` | number array | Look rate held now, as `[yaw, pitch]`. `[0, 0]` when none. |
| `paused` | boolean | Whether the world is on hold. |
| `started` | boolean | Whether `start` created the current world. |
| `ended` | boolean | Whether the game reached an outcome. Only `start` or `set_source` continues it. |
| `game_state` | string | The game's state after the latest chunk, such as `RUNNING`, `WON`, or `LOST`. |
| `outcome` | string array | The game's result card once it ends: result, reason, and advice. Empty during play. |
| `health` | number or `null` | Player health after the latest chunk, or `null` when the game has none. |
| `health_max` | number or `null` | Maximum player health, or `null` when the game has none. |
| `events` | string array | What happened in the game during the latest chunk. |
| `world_id` | integer | World identity. `start` increments it. |
| `chunks` | integer | Number of generated chunks in the current world. |
| `seconds` | number or `null` | Seconds the latest chunk took to generate, or `null` before the first chunk. |

`game_state`, `outcome`, `health`, `health_max`, and `events` describe the game. They are empty or
`null` with source `client` and before a world's first chunk. Every `state_update` repeats `events`
until the next chunk, so add them to a log once for each new `chunks` value.

## Complete example

The following sequence starts a built-in game with the base SDK.

Configure `reactor` as in the [tutorial](/model-api-reference/eden-live/tutorial) and wait for
`ready`. This sequence pauses the world, selects `freeze`, and starts a new world with the game as
its draft source. The example logs the state from accepted commands. Keep the message and track
handlers from the quick start active to display subsequent snapshots and video.

```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
function applyCommandResult(reply: Awaited<ReturnType<typeof reactor.sendCommand>>) {
  if (!reply) {
    console.error(reactor.getLastError());
    return false;
  }
  console.log(reply.data);
  return true;
}

if (!applyCommandResult(await reactor.sendCommand("set_paused", { paused: true }))) {
  throw new Error("Could not pause the world");
}
if (applyCommandResult(await reactor.sendCommand("select_game", { game: "freeze" }))) {
  if (applyCommandResult(await reactor.sendCommand("set_source", { source: "game" }))) {
    applyCommandResult(await reactor.sendCommand("start", { seed: 2026092901 }));
  }
}

// Keep the state_update and output-track listeners active for the session.
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.