# Autopilot: AI playtesting for Unreal Engine Autopilot is an Unreal Engine 5 plugin that lets an AI agent play your game to test it. A prompt is one of four types: - **Exploration**, e.g. "Explore the crafting system in Lvl_Main.": the agent explores the game for a set number of minutes and reports likely bugs: engine errors, Blueprint runtime errors, things falling out of the world, teleports, values over their maximum, on-screen counts above their limit, and more. - **Testing**, e.g. "Go through the currently open level and tell me everything that looks wrong.": an exploration asked for in your own words, with a report of the problems found. - **Verification**, e.g. "Verify that the tree shakes when you hit it with an axe.": the agent plays the game to check it and answers **CONFIRMED**, **DENIED** or **INCONCLUSIVE**, with evidence. - **Goal**, e.g. "Kill the boss while keeping your health above 20% of its maximum value.": something to get done in play, achieved or not. For every prompt, Autopilot starts Play In Editor by itself, reads your project's code, tries things in the game and plays according to your prompt; when it is done, the play session ends by itself and the report opens. The plugin is only the game-side half. The decisions are made by the **agent service**, a separate program from this repository that runs the language models. In the editor, the plugin installs it once (from the installer it carries in a release download) and starts it itself, hidden, whenever it connects and none is running; it ends with the editor (see [The agent service](#the-agent-service)). You can also run it yourself with `autopilot serve` (`python -m autopilot serve` from the repository). The plugin connects out to it over a WebSocket (by default `ws://127.0.0.1:8765/v1/bridge`, or wherever it says it listens), and the service then reads the game and presses its controls through the plugin, in JSON messages (a versioned protocol, `protocol: 1`). In the code, the plugin's modules are still called `AutopilotPlaytest`, `AutopilotPlaytestEditor` and `AutopilotCodex`; the plugin itself (`Autopilot.uplugin`) and its editor tab are called Autopilot. ## Requirements - Unreal Engine 5.4-5.8, on Windows or macOS (the releases build Win64 and Mac for each). - The Enhanced Input plugin (enabled automatically). - The agent service: the release download's plugin carries its installer (`AgentService/`), which the first-run setup runs. From a repository checkout, set **Agent service command** (see [Settings](#settings)) or run `python -m autopilot serve` yourself; see [Running from a clone](../../../CONTRIBUTING.md#running-from-a-clone). ## Install 1. Copy this folder into your project as `/Plugins/Autopilot/`. 2. Build it: - **C++ project**: regenerate project files and build your editor target, or just open the project and let the editor build the plugin. - **Blueprint-only project**: the editor can't compile C++ for you, so build the plugin once with `RunUAT BuildPlugin` (see [Building](#building)) and copy the packaged output, which includes `Binaries/Win64`, into `Plugins/Autopilot/` instead. 3. Open the project. Click the orange **Autopilot** icon on the level editor toolbar (or **Window > Tools > Autopilot**) to open Autopilot. To update, put the new download's `Autopilot` folder in `Plugins/` (in place of the old one, or copied over it) and open the project: there is no need to delete `Binaries` or `Intermediate` (yours or the plugin's). The first time it connects, the plugin runs the agent service it carries: it installs it over an older one and replaces an older one still running (see [The agent service](#the-agent-service)). ## First-run setup The first time you open the **Autopilot** tab, a setup of three steps, or four when Autopilot doesn't know your game yet, takes its place (a stepper at the top shows where you are and what you chose). With Aura (recommended), in short: click **Create Account** if you don't have an Aura account yet, or **Sign in** if you do; the tab sets up the agent service, then your browser opens on Aura's sign-up or sign-in page; once you are signed in, **Agent service ready** shows: click **Continue**; choose whether to share your runs; then, optionally, let Autopilot explore your game (10 minutes by default; safe to skip) and send your first prompt. In detail: 1. **Account**: how to run the agent. - **Sign in with Aura** is billed to your Aura plan (the card says "Autopilot is maintained by the developers behind Aura, the multiagent AI solution to Unreal and Unity game development. You can use your Aura subscription to run Autopilot. New users get free Aura credits."). The agent service signs in for you on Aura's website: the tab installs and starts it if needed ("Setting up the agent service…", "Starting the agent service…"), asks it to sign in, and your browser opens on Aura's page. While it waits, the card shows the link (**Open again**, **Copy link**, **Cancel**); once you choose Allow it shows **Signed in as** *your email*, with **Continue** and **Use another account**. If the sign-in is denied, times out or Aura can't be reached, it says so in one line with **Try again**. While the sign-in goes on (getting ready, in your browser, waiting for the agent service) the BYOK card is not shown; **Cancel** brings it back. Once signed in it is folded and dimmed, with **Show**. The plugin never sees Aura's tokens: the agent service keeps the sign-in for the computer, so every editor here uses it. - The card has two buttons, the Aura app installed or not: **Sign in** signs in as above. **Create Account** (the default) opens Aura's account creation page in your browser, and the card then says "Finish creating your Aura account" and "Once you have created your account, sign in and start using Autopilot", with **Sign in** (as above). The page is on the Aura server the agent service signs in to (`AUTOPILOT_AURA_SERVER` in autopilot.env, else the download's), else https://app.tryaura.dev. - **Bring your own keys (BYOK)**, folded until **Show** (**Hide** folds it again): a **Decision provider API key** (e.g. TypeSafe), then the **Coding Agent** that writes and runs the harness (**Claude Code**, **Codex** or **OpenCode**, the default), then an **LLM provider API key**, with the LLM provider in a dropdown at the start of its row. Claude Code takes Anthropic's keys only and Codex OpenAI's, so for them the dropdown shows **Anthropic** or **OpenAI**, locked; with OpenCode you pick one of **Anthropic**, **OpenAI**, **OpenCode Zen**, **OpenRouter**, **DeepSeek** and **Gemini** (switching agents and back to OpenCode brings back the provider you picked for it). Both dropdowns start on what the agent service says it uses, else what is in `autopilot.env`, else OpenCode and Anthropic. Both keys are optional (leave them empty if they're already set where the agent service runs). **Use my keys** writes the agent (and OpenCode's provider) and the keys you fill in to `autopilot.env` and turns **Use with Aura** off. 2. **Agent service**: whether it is ready. Already running: **Agent service ready** at once. Not installed ("One-time installation takes approximately 1 minute."): **Install** runs the installer the plugin carries ("Installing the agent service…"), then starts it. A plugin without the installer (a repository checkout) says **No agent service found**, with an **Agent service command** field and **Start** (it saves the setting and starts it), or `autopilot serve` to run yourself (**Copy**). If installing or starting fails, the step says why, with **View logs** and **Try again**. **I'll do it later** goes on. A release download always runs the agent service it carries (see [The agent service](#the-agent-service)): a newer download over an earlier one installs its own before it first starts it, and replaces an older one still running. An **Agent service command** setting is never replaced. 3. **Privacy settings**: whether to share your runs' data (**Share my runs to improve Autopilot**, on by default: "Enabling this sends recordings, screenshots, and agent traces and memories to the developers of Autopilot."; see [Data sharing](#data-sharing)), and a recap of steps 1 and 2 (e.g. **Account: Aura · you@studio.com**, **Account: BYOK**, **Agent service: Ready**; the stepper says **1 ACCOUNT · AURA**, **· AURA APP** or **· BYOK**). 4. **First run**, only when the game's memory is empty (no earlier runs, notes or library: what the agent service says when the tab connects). Step 3's button then reads **Next**; it saves your choices and connects, and step 4 waits for the connection ("Connecting to the agent service…", then "Checking what Autopilot remembers about this game…", with **Skip**). With the memory empty it recommends an Explore run so Autopilot learns your game first: **Minutes** (10 by default, the composer's value) and **Explore and finish**, which starts the run and shows it live, or **Skip and finish** (the card: "Autopilot still works if you skip this step."). When the memory holds something, the stepper has three steps, step 3's button reads **Finish**, and step 4 is skipped by itself. **Finish** (or **Next**) saves your choices and connects; the tab shows once the setup is done. The choices are kept for every project and engine on this machine, in `Autopilot/preferences.json` in your user settings folder (`%LOCALAPPDATA%` on Windows, `~/Library/Application Support` on macOS, `~/.config` elsewhere), next to `autopilot.env`; if Unity's or Godot's Autopilot already asked, the Unreal tab doesn't ask again. **Rerun setup**, on the Settings page (the gear beside **New run**), opens it again at step 1. While a run is going it is disabled ("Finish or stop the run first"): changing the account reconnects, which would end the run. Command-line runs never show it. The **Settings** page starts with the **Account** corner: how runs go (**Aura** with your email and, when Aura says it, what Autopilot billed your Aura account this billing period, e.g. **Billing period (Oct 1 – Oct 31): $12.34 USD**; **Aura app**; **BYOK** (**Your own keys**) with the last characters of the keys the agent service has, if any, e.g. **Saved on this computer: Decision provider ••••a91f · OpenCode (Anthropic) ••••3c2e**; or **Not set up**), whether the agent service runs (**Agent service: running** with its address; starting or installing; **Agent service: not installed** with **Install**; **The agent service didn’t start.** with **View logs**; **Agent service: starts when Autopilot connects**), **Rerun setup** (the setup from its account step), and for Aura **Sign out** (red; it asks first, in place: every editor on the computer uses the sign-in). When the Aura sign-in has ended it says **Signed out**, with **Sign in again** right there. The tab asks the agent service for the account again after the setup, after a sign-out, at every connection and when the Settings page opens. The Settings page ends with **Uninstall Autopilot** (see [Removing Autopilot](#removing-autopilot)). The tab's look is Aura's "The Flatness" (dark only): flat fills and 1px rules, one amber accent (`#f0873a`), IBM Plex Sans and IBM Plex Mono, 8px corners on cards and square controls. Its last row, on every page but the setup and the removal, is a **status line** that says what just happened for 8 seconds, in one line (its whole text as its tooltip): **Copied the report**, **Copied**, **Told the agent: … is not a bug**, **Showing …**, **Open the level to show this finding in it**, why something was refused (**Not connected: connect first**, **A run is going: wait for its result, or stop it**, **Not connected: connect first to reset the memory**, **A run is going: reset the memory once it is done**), **Couldn’t start Aura**, **Couldn’t save the setup to … (error n)**, **A bundled font couldn’t be read: using the editor’s font**, and the plugin's own notices (a run started, stopping, the connection). Set up before, but the agent service isn't on this computer any more (removed by hand, e.g. `uv tool uninstall autopilot`): the first time the tab opens in an editor session, the setup opens at step 2, **Agent service**, in place of the connect card, so **Install** (or, from a repository checkout, how to set it up) is right there. Your other choices are kept. This happens only when the editor starts the agent service itself (see [The agent service](#the-agent-service)) and none is running. Every page of the tab reflows with its size: rows wrap or stack when it is narrow (down to about 320 px), content is at most 620 px wide (New run; Settings 640, the setup 600, the connect card 540) and centered when it is wide, and pages scroll when it is short. Below 700 px the history becomes a button over the page (the run picked, or **New** and how many runs this session) that opens the list, with the **Memory** row, under it. ## The agent service Once the first-run setup is done, the editor connects by itself (when it starts and when the tab opens; not after you press **Disconnect**, for the rest of the editor session, until **Reconnect** on the connect card). With the Agent Service URL left at its default and **Use with Aura** off, connecting starts the agent service when none is running: - **Finding it**: the **Agent service command** setting if set (a full command line, e.g. `C:\autopilot\.venv\Scripts\python.exe -m autopilot`); it is used as it is, never updated or replaced. Else, in a release download (its `AgentService/` folder has the installer and `agent-service.json`), the agent service it carries, where its installer puts it (`%USERPROFILE%\.local\bin\autopilot.exe`, `~/.local/bin/autopilot`): when the one there was installed from another download (the `agent-service.json` the installer copies next to `autopilot.env` names another wheel), or it isn't there but an `autopilot` is on `PATH`, the plugin installs its own first, then starts it (once per editor session; if updating fails, the one installed starts). An `autopilot` elsewhere on `PATH` is never used. Nothing installed at all: the tab offers **Install** (the first-run setup's step 2). Without the installer (a repository checkout): `autopilot` on `PATH`, else the installer's place; none: the tab says how to set it up. - **Starting it**: one already running (it says where it listens in `Autopilot/brain.json`) is used: a second one is never started. In a release download, only if it was built from the same commit as the one the plugin carries (`brain.json` and `agent-service.json` both name it), unless **Agent service command** is set: an older one is stopped (only if its program is `autopilot` or a Python and its process is the one that wrote `brain.json`: created before the `started` time it wrote there, so a process id reused since by another program is never stopped; anything else, or a `brain.json` that doesn't say when it started, is left running and used), its `brain.json` removed, and the plugin's own installed if needed and started. The log says so (`[Autopilot] An older agent service was running (pid N): replacing it with this plugin's`). This happens once per editor session (one that comes back is used), never to the one the plugin is connected to. So installing a new download is only: unzip it into `Plugins/` and open the project. Else the plugin runs `autopilot serve --port 0 --exit-with `, hidden, with its output in `Autopilot/logs/agent-service-.log` (the 5 newest are kept), waits up to 30 s for it to say where it listens, and connects. If it stops before that, the tab says **The agent service stopped** with **View logs**. It ends with the editor that started it; other editors on the computer use the same one, and once it has ended the next connect starts a new one. One that ends within a minute of starting isn't started again until you press **Try again**. - **Installing it**: a release download carries the installer in `Plugins/Autopilot/AgentService/` (a `autopilot-*.whl`, `install.ps1`, `install.sh`). **Install** runs `powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1` (`sh install.sh` elsewhere), hidden, with its output in `Autopilot/logs/install-.log`. It is needed once per computer. The processes are watched on threads of their own: nothing waits on the editor's game thread. ## Quick start (editor) 1. In the editor, open the **Autopilot** tab: the orange **Autopilot** icon on the toolbar, or **Window > Tools > Autopilot**, and finish the [first-run setup](#first-run-setup) the first time. 2. The tab connects by itself, and the plugin starts the agent service if none is running (see [The agent service](#the-agent-service)); **New run** then says **● CONNECTED** and the address at its foot, with **Disconnect**. Until then the connect card says what is happening (**Agent service**, **Not connected**: connecting, installing, starting, or why it can't, with what to do: **Install**, **Try again**, **Try now**, **View logs**, **Get Aura**, **Start Aura**, **Stop**; disconnected after a run was shown: **Back to the last run**), and **Advanced** unfolds the **Agent service URL**, the **Access token** and the **Agent service command** (kept when you press Enter or leave the field; the same settings as in Editor Preferences). A run shown while there is no connection says so in a bar over it (**Reconnecting to the agent service…** with **Details**, or **Not connected to the agent service** with **Reconnect**). To run it yourself instead (e.g. from the repository: `python -m autopilot serve`), start it first: the plugin uses one already running. 3. Type what to do in the box, in your own words, and press **Run** (in the box, at its right). The **Time limit** beside it (minutes; it starts at the one you last set, 30 the first time, kept per project) bounds every kind of run. As the run starts, the decision model reads which of the four types the words ask for, and the run shows that type from then on: - **Exploration**: explore the game or a part of it for bugs, e.g. "Explore the crafting system in Lvl_Main.", "Find bugs in the shop". The agent plays for the Time limit's minutes, with the **Design Brief** setting as what the game should do. Its report's status is **EXPLORED**. - **Testing**: a test in your own words, asking for the problems to be reported, e.g. "Go through the currently open level and tell me everything that looks wrong.". It plays as an exploration; its report's status is **GAME TESTED**. - **Verification**: a claim or a question about the game, e.g. "Verify that the tree shakes when you hit it with an axe.", "Can the player swim?". The Time limit is the whole run's. - **Goal**: something to get done, e.g. "Kill the boss while keeping your health above 20% of its maximum value.". The Time limit is the whole run's. The agent service reads the command line's prompts the same way. Runs are real time: the game runs on while the agent decides. The agent starts Play In Editor by itself when it needs the game running, and ends it when the run is done. Play In Editor plays the level open in the editor, unless the prompt means another ("In Lvl_Arena, verify that ...", "Explore the Harbor level", "in the desert one", `/Game/Maps/Harbor`): the agent picks it from the project's levels before it starts playing. That level plays without being opened in the editor (the play session's map override): the open level and its unsaved changes stay as they are. While a run is going, the tab shows: - **Live**: the stages of the run (Launch, Analyze, Probe env, Build harness, Rollout · H*n*, Process result, Report; each name always whole: the row wraps onto a second line when the tab is narrow; under each, how long it took, m:ss, the current one's so far in the accent; the stage follows the agent service's activity frames, or its status lines for an older one), the feed's last 10 lines (one line each, no folds; the Feed tab has them all), what the agent is doing now, its **Harness attempts** so far (a square each, in one row that never wraps: the newest that fit, at most 24, so the current one, outlined, is always last; the others filled; then the count of them all), and **Stop** to finish early (it says **Stopping…** until the result; the agent still writes its report). "Saving what it learned to memory", at the end, is part of the report. If the agent service supports it, a box at the foot ("Send a steering prompt to Autopilot") lets you nudge the run, e.g. "go round the rocks to the left first": you can type at any stage, and **Steer** sends it while the game is playing (the Rollout stage; before that its tooltip says "You can steer once the game is playing"). While it stops, both are locked. The Feed tab has the same box, with the same words. - **Feed**: the run as a transcript, grouped by stage ("Nothing yet." before the first line): a heading per stage (Launch, Analyze, Probe, Build harness, Play, Results, Report) with its count of lines, and under it what the agent did and said, one line per event of the agent service's activity feed (its `activity` frames): its time (m:ss), a small mark for its kind (? the agent's words, R read, S searched, W wrote, P tried in the game, E episode, L looked, ! report or alert, > what you steered, * restart, M the game's memory refined), its words and, for a call that came back (a probe, an episode, a look), the result's words on the same line, quieter. Under a line, a fold for each detail ("+ the code", "the output", "what was written", "the summary", "the edit and its evidence", "the whole message"…) opens a mono block with it, at most 320 px tall, scrollable and selectable. The status lines, engine findings, what you steered ("You: …") and the result ("Result: Goal hit") are among them, each under its stage; the agent's findings are tagged red, the engine's by severity. An agent service too old to send activity frames gives the status lines alone, as before. When a run changed what the agent remembers about the game, its last line says so (**Memory updated**). A run's feed is saved with it, so a run reopened from the History shows it whole. A tab that connects again while a run goes asks the agent service for the events it missed (`activity_since`); an event sent twice is shown once. While the run goes and the agent service is on this computer, the tab bar says **● LOCAL AGENT**. The prompt box stays editable during a run. - **Result** once it ends: the verdict above its reason, the run's recording when it has one, the evidence and the findings (no findings section when there are none). A run without a recording says why in one muted line where the player would be, **No recording:** followed by the agent service's reason (for example "the game can't be recorded here: it runs without rendering"; nothing when it gave none). The line is saved with the run, so the history shows it too. A run whose recording file is gone keeps the player, dimmed, with nothing to press, and says **The recording isn’t there any more.** A stopped run's reason is "Stopped before a result."; one whose connection closed first says **NO RESULT**. Evidence lines that start with their harness and time ("H1 12.4 s") show those in the accent. The recording plays in a screen under the verdict (230 px high, 200 in a narrow tab; a play/pause button, a time bar to scrub, the time and the length, and a button (a folder) that shows the file in File Explorer, Finder or your file manager); nothing plays until you press play. The agent service records every run it can (a game that renders: not `-nullrhi`); the plugin copies the recording into the run's report folder as `recording.mp4`, off the editor's game thread, so it stays with the run (a Reset memory doesn't take it, and runs opened from the history show it). It is played with the engine's Media Framework (Windows Media Foundation or Electra, both on by default); if it can't be played there, that button still shows you the file to open in another player. Each finding's severity tag says **ERROR**, **WARNING** or **INFO**. For each finding, **Show in level** moves the level viewport camera to where it happened (**Showing *actor***, **Showing the place in the open level**, or **Open the level to show this finding in it**), **Not a bug** tells the agent not to report it again in this project (then it says **Marked not a bug**), and **Verify a fix** fills in a new Verify run that checks the bug is gone ("Verify that this is fixed: …"). **Run again** (a circular arrow, filled), **Copy report** (a clipboard) and **Run folder** (a folder) are icons at the right of the run's id line, above its prompt; their names are their tooltips; **Run folder** is disabled when the folder is gone. **Copy report** copies Markdown: the prompt, the verdict, **Duration:** · **Harnesses:** · **Policy steps:** · **Alerts:**, the alerts as quotes, **## Reason**, the evidence and the findings. The direct changes' heading is **Changed directly · not through play · n** (**Changed directly · n** when narrow). The tab bar says the run's id, kind, **DURATION** and attempts when the tab is wide. - **Policy log**: every step the agent's policy took, with the observation it was sent, the action probabilities it returned, the action it chose and how long the step took. Filter by harness and by what the step did, or find an action (**Find an action…**: the chosen or the current action). Picking any step, the newest too, pauses on it (**PAUSED ON H*n* #*step***); step with the ◀ ▶ buttons (disabled at the ends), or press **Follow live** to follow new steps again (**● FOLLOWING**). The legend lists the actions that ran in the harness shown (at most 8); the footer counts every step of that harness (or of all), whatever the filters keep. **Copy step JSON** copies the frame as the agent service sent it (its questions' instructions left out), and **Raw JSON** shows it. The agent service only sends these to an editor on the same machine (unless it is started with `AUTOPILOT_INSPECT=1`), and you can turn the log off with the **Show Policy Log** setting. Each run in the history says when it started, how long it took and its harness attempts (e.g. **Oct 19 13:25 · 03:26 · 2 attempts**). The run history (the last 50 runs) is kept in `Saved/Autopilot/Runs/`, so closing the tab or the editor loses nothing. Each run's report files are written to `Saved/Playtest//`. **Settings** (the gear beside **New run**; **Back to runs** closes it) also lists **This project**'s runs with usage or a bill (**Time**, **Prompt**, **Billed**; **No usage yet.** when there are none; plain rows, nothing to pick): what Autopilot was billed for each through Aura (the agent service says it when the run is finished; two decimals, **< $0.01** below a cent, **—** for a BYOK run), and **Billed by Aura $X.XX**, their sum. The tokens the models used are still counted and saved with each run, for the agent service's reports, but the page no longer shows them. It also holds the [Data sharing](#data-sharing) choice, then the card **Autopilot in Claude Code & Codex** (see [Runs from Claude Code or Codex](#runs-from-claude-code-or-codex)) and, last, **Uninstall Autopilot**. Those two settings are laid out alike: the setting's name, what it does, then the switch (with **Data sharing on** or **Data sharing off** beside it) or the commands. A run's time limit is the prompt box's own (**Time limit**, beside **Run**). ### Runs from Claude Code or Codex You can also run Autopilot from your own Claude Code or Codex, on your Claude or ChatGPT plan: the Settings page's card **Autopilot in Claude Code & Codex** says how ("One-time CLI setup to run Autopilot through Claude Code or Codex using your Claude or ChatGPT subscriptions, respectively."), with `autopilot connect claude` and `autopilot connect codex`, each with **Copy**. Then ask it, for example, "Use Autopilot to verify that the door opens with the red key." Runs still use your decision provider API key, or Aura. Keep this project open in the editor with the tab connected: such a run shows in the tab as one started from the prompt box would (from the Settings page too; the first-run setup, when open, stays until you finish it), with **Started from Claude Code** (or **Started from Codex**, **Started from your coding agent**) under its words, live and in its result, and in the history like any run. **Stop** and steering work on it as on any run. The agent service runs one run at a time: while one goes, another can't start. **Memory** at the bottom of the history shows how many earlier runs the agent remembers about this game (e.g. **· 12 runs**, or **· empty**). **Reset memory…** asks first, in place ("Reset memory?" / "The agent forgets what it has learned about this game.", **Reset memory** or **Cancel**), then makes it forget, so the next run starts fresh; under the row it says **Resetting memory…** (the button disabled), then for 8 seconds **Memory reset successful** or **Memory not reset:** and why. A reset also takes the runs before it off the history: their files move to `Saved/Autopilot/Runs/.reset-backup/