Initial commit: AIGame Unity client with URP, HybridCLR, YooAsset and tooling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-09-21 14:21:07 +08:00
co-authored by Cursor
commit f3f66ab063
931 changed files with 112366 additions and 0 deletions
@@ -0,0 +1,191 @@
# Animation commands
Author animation assets: AnimationClips (and their float curves), AnimatorControllers (parameters, layers, states, transitions), and Timeline assets. Create commands write under the authoring root and follow the shared `confirm`/`dry_run` convention.
## AnimationClips
### `create_animation_clip`
Create an empty .anim AnimationClip asset under the authoring root, with an optional frame rate and loop flag.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | `–` | Asset path ending in .anim, relative to the authoring root. The Assets/ prefix is optional. |
| `frameRate` | no | `60` | Sampling frame rate of the clip. |
| `loop` | no | `false` | If true, set the clip's loop-time flag in its AnimationClipSettings. |
| `confirm` | no | `false` | Required (true) only when overwriting an existing asset at the path. |
| `dry_run` | no | `false` | If true, validate inputs and report what would be created without writing anything. |
**Returns:** `AuthoringResult`
### `set_animation_curve`
Add or replace a single float curve binding on an AnimationClip (via AnimationUtility.SetEditorCurve). Replacing an existing binding overwrites it rather than duplicating.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `clip` | yes | `–` | Reference to the AnimationClip to edit (path / guid / globalId). |
| `path` | no | `""` | GameObject path relative to the animated root the property lives on. Empty string targets the root. |
| `type` | yes | `–` | Component type the property lives on, e.g. "Transform", "UnityEngine.Light". Resolved via the component TypeResolver. |
| `property` | yes | `–` | Curve property name, e.g. "m_LocalPosition.x", "m_LocalScale.y", "localEulerAnglesRaw.z". |
| `keys` | yes | `–` | Keyframes: `[{ time, value, inTangent?, outTangent?, weightedMode?: "None"\|"In"\|"Out"\|"Both" }]`. Omitted tangents default to 0 (flat); this is NOT Unity's Auto tangent mode. |
| `dry_run` | no | `false` | If true, validate type/property/keys without writing the curve. |
**Returns:** `SetAnimationCurveResult`
### `get_animation_clip`
Read an AnimationClip's metadata and all float curve bindings (optionally with keyframes).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `clip` | yes | `–` | Reference to the AnimationClip to read (path / guid / globalId). |
| `includeKeys` | no | `false` | If true, include each binding's keyframes. |
**Returns:** `AnimationClipInfo`
### `remove_animation_curve`
Remove a float curve binding from an AnimationClip (SetEditorCurve(clip, binding, null)). Destructive: requires confirm=true.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `clip` | yes | `–` | Reference to the AnimationClip to edit (path / guid / globalId). |
| `path` | no | `""` | GameObject path relative to the animated root the binding lives on. Empty string targets the root. |
| `type` | yes | `–` | Component type of the binding to remove, e.g. "Transform". Resolved via the component TypeResolver. |
| `property` | yes | `–` | Curve property name to remove, e.g. "m_LocalPosition.x". |
| `confirm` | no | `false` | Must be true to actually remove the binding (destructive guard). |
| `dry_run` | no | `false` | If true, report the binding that would be removed without removing it. |
**Returns:** `SetAnimationCurveResult`
## AnimatorControllers
### `create_animator_controller`
Create an .controller AnimatorController asset (with a default Base Layer) under the authoring root.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | `–` | Asset path ending in .controller, relative to the authoring root. The Assets/ prefix is optional. |
| `confirm` | no | `false` | Required (true) only when overwriting an existing asset at the path. |
| `dry_run` | no | `false` | If true, validate inputs and report what would be created without writing anything. |
**Returns:** `AuthoringResult`
### `add_animator_parameter`
Add a parameter (Float | Int | Bool | Trigger) to an AnimatorController. A duplicate name returns code 'duplicate_parameter'.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `controller` | yes | `–` | Reference to the AnimatorController to edit (path / guid / globalId). |
| `name` | yes | `–` | Parameter name. |
| `type` | yes | `–` | Parameter type: Float \| Int \| Bool \| Trigger. |
| `defaultValue` | no | `–` | Default value for Float/Int/Bool (ignored for Trigger). |
| `dry_run` | no | `false` | If true, validate inputs without writing the parameter. |
**Returns:** `object`
### `add_animator_layer`
Add a layer to an AnimatorController.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `controller` | yes | `–` | Reference to the AnimatorController to edit (path / guid / globalId). |
| `name` | yes | `–` | Layer name. |
| `weight` | no | `1` | Layer weight. |
| `blendingMode` | no | `Override` | Blending mode: Override \| Additive. |
| `dry_run` | no | `false` | If true, validate inputs without writing the layer. |
**Returns:** `object`
### `add_animator_state`
Add a state to a layer, optionally with a motion (AnimationClip or BlendTree) and as the layer default. A layer name with no match returns code 'layer_not_found'.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `controller` | yes | `–` | Reference to the AnimatorController to edit (path / guid / globalId). |
| `layer` | no | `0` | Layer index (int) or name (string). Default 0 (Base Layer). |
| `name` | yes | `–` | State name. |
| `motion` | no | `–` | Optional AnimationClip or BlendTree asset to assign as the state's motion. |
| `isDefault` | no | `false` | If true, set this state as the layer's default state. |
| `position` | no | `–` | Optional [x, y] node position in the graph (cosmetic). |
| `dry_run` | no | `false` | If true, validate inputs without writing the state. |
**Returns:** `object`
### `add_animator_transition`
Add a transition between two states (or from AnyState/Entry, to Exit) on a layer, with optional conditions. Validates that the states exist and each condition's parameter exists and its mode matches the parameter type.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `controller` | yes | `–` | Reference to the AnimatorController to edit (path / guid / globalId). |
| `layer` | no | `0` | Layer index (int) or name (string). Default 0 (Base Layer). |
| `fromState` | yes | `–` | Source state name, or the special "AnyState" / "Entry". |
| `toState` | yes | `–` | Destination state name, or the special "Exit". |
| `conditions` | no | `–` | Optional conditions: `[{ parameter, mode: "If"\|"IfNot"\|"Greater"\|"Less"\|"Equals"\|"NotEqual", threshold? }]`. |
| `hasExitTime` | no | `false` | If true, the transition uses exit time. |
| `exitTime` | no | `0` | Normalized exit time (0..1) when hasExitTime is set. |
| `duration` | no | `0.25` | Transition duration in seconds. |
| `hasFixedDuration` | no | `true` | If true, duration is in seconds; otherwise normalized. |
| `dry_run` | no | `false` | If true, validate everything (states, parameters, mode/type) without writing the transition. |
**Returns:** `object`
### `get_animator_controller`
Read an AnimatorController's full structure: parameters, layers, states (with motion / default), and transitions (with conditions).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `controller` | yes | `–` | Reference to the AnimatorController to read (path / guid / globalId). |
**Returns:** `AnimatorControllerInfo`
## Timeline
The Timeline commands require the `com.unity.timeline` package.
### `create_timeline`
Create a .playable TimelineAsset under the authoring root (optional frame rate).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | `–` | Asset path ending in .playable, relative to the authoring root. The Assets/ prefix is optional. |
| `frameRate` | no | `60` | Timeline frame rate. |
| `confirm` | no | `false` | Required (true) only when overwriting an existing asset at the path. |
| `dry_run` | no | `false` | If true, validate inputs and report what would be created without writing anything. |
**Returns:** `object`
### `add_timeline_track`
Add a track (Animation | Audio | Activation | Control | Playable | Signal | Marker) to a TimelineAsset, optionally nested under a parent group/track.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `timeline` | yes | `–` | Reference to the TimelineAsset to edit (path / guid / globalId). |
| `trackType` | yes | `–` | Track type: Animation \| Audio \| Activation \| Control \| Playable \| Signal \| Marker. |
| `name` | no | `–` | Optional track display name. |
| `parentTrack` | no | `–` | Optional name of an existing group/track to nest the new track under. |
| `dry_run` | no | `false` | If true, validate inputs without writing the track. |
**Returns:** `object`
### `add_timeline_clip`
Add a clip to a named track on a TimelineAsset. For Animation tracks pass an AnimationClip asset; for Audio tracks an AudioClip.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `timeline` | yes | `–` | Reference to the TimelineAsset to edit (path / guid / globalId). |
| `track` | yes | `–` | Target track name. |
| `start` | yes | `–` | Clip start time in seconds. |
| `duration` | yes | `–` | Clip duration in seconds. |
| `asset` | no | `–` | Source asset: for Animation tracks an AnimationClip; for Audio tracks an AudioClip. Required for those track types. |
| `dry_run` | no | `false` | If true, validate inputs without writing the clip. |
**Returns:** `object`
### `get_timeline`
Read a TimelineAsset's structure: frame rate, duration, and its tracks with their clips.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `timeline` | yes | `–` | Reference to the TimelineAsset to read (path / guid / globalId). |
**Returns:** `object`
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,150 @@
# Asset & file commands
Commands for creating, importing, moving, copying, renaming, deleting and finding project assets, plus folder and text-file operations. Every agent-supplied path is funnelled through the authoring-root sandbox (it rejects `../` and out-of-project writes); destructive or overwriting operations require `confirm=true` and support `dry_run`. AssetDatabase create/move/copy/rename/delete are not part of Unity's Undo system.
### `create_asset`
Create a new ScriptableObject (or other UnityEngine.Object) asset of the given type at a path under the authoring root.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Asset path relative to the authoring root, including extension (e.g. Data/Config.asset or Materials/Wall.mat). The Assets/ prefix is optional. |
| `type` | yes | – | Fully-qualified or short type name to instantiate (e.g. UnityEngine.Material, MyGame.GameConfig). Must derive from UnityEngine.Object and be creatable. |
| `shader` | no | `–` | Material-only (ignored otherwise): shader name to assign (e.g. Standard, "Universal Render Pipeline/Lit"). When omitted, defaults to "Universal Render Pipeline/Lit" if a Scriptable Render Pipeline is active, otherwise the built-in "Standard" shader (falling back to "Standard" if URP/Lit is unavailable). |
| `confirm` | no | `false` | Required (true) only when overwriting an existing asset at the path. Ignored when the path is empty. |
| `dry_run` | no | `false` | If true, validate inputs and report what would be created without writing anything. |
**Returns:** `AuthoringResult`
**Notes:** `confirm=true` required to overwrite an existing asset; supports `dry_run`.
### `import_asset`
Import an external file (e.g. a texture, model, audio clip) into the project by copying it to a path under the authoring root, then importing it.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `source` | yes | – | Absolute filesystem path to the external file to import. |
| `path` | yes | – | Destination asset path relative to the authoring root, including extension. The Assets/ prefix is optional. |
| `confirm` | no | `false` | Required (true) only when overwriting an existing asset at the destination path. |
| `dry_run` | no | `false` | If true, validate inputs and report what would be imported without writing anything. |
**Returns:** `AuthoringResult`
**Notes:** `confirm=true` required to overwrite an existing asset; supports `dry_run`.
### `move_asset`
Move (or rename via a new path) an asset to a new location under the authoring root. Preserves the asset's GUID.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `asset` | yes | – | Reference to the asset to move (path / guid / globalId). |
| `destination` | yes | – | Destination asset path relative to the authoring root, including extension. The Assets/ prefix is optional. |
| `dry_run` | no | `false` | If true, validate the move (via AssetDatabase.ValidateMoveAsset) without performing it. |
**Returns:** `AuthoringResult`
**Notes:** Supports `dry_run`.
### `copy_asset`
Copy an asset to a new path under the authoring root. The copy gets a fresh GUID.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `asset` | yes | – | Reference to the asset to copy (path / guid / globalId). |
| `destination` | yes | – | Destination asset path relative to the authoring root, including extension. The Assets/ prefix is optional. |
| `confirm` | no | `false` | Required (true) only when overwriting an existing asset at the destination path. |
| `dry_run` | no | `false` | If true, validate inputs and report what would be copied without writing anything. |
**Returns:** `AuthoringResult`
**Notes:** `confirm=true` required to overwrite an existing asset; supports `dry_run`.
### `rename_asset`
Rename an asset in place (keeps it in the same folder, keeps its GUID).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `asset` | yes | – | Reference to the asset to rename (path / guid / globalId). |
| `new_name` | yes | – | New file name WITHOUT a folder path. The extension is preserved if omitted. |
| `dry_run` | no | `false` | If true, validate the rename without performing it. |
**Returns:** `AuthoringResult`
**Notes:** Supports `dry_run`.
### `delete_asset`
Delete an asset from the project. Destructive: requires confirm=true.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `asset` | yes | – | Reference to the asset to delete (path / guid / globalId). |
| `confirm` | no | `false` | Must be true to actually delete. Without it the command refuses (destructive guard). |
| `dry_run` | no | `false` | If true, report the asset that would be deleted without deleting it. |
**Returns:** `AuthoringResult`
**Notes:** Destructive — `confirm=true` required; supports `dry_run`. Not undoable via Unity's Undo.
### `find_assets`
Find assets by type and/or name and/or label, returning their path, GUID and type. At least one filter is required.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `type` | no | `–` | Type name to filter by (e.g. Material, GameObject, ScriptableObject, MyGame.GameConfig). Resolved to a System.Type and matched against each asset's actual main type. |
| `name` | no | `–` | Name substring to filter by (AssetDatabase name filter). |
| `label` | no | `–` | Asset label to filter by (AssetDatabase 'l:' filter). |
| `search_in` | no | `–` | Folder to scope the search to, relative to the authoring root (default: the authoring root). |
| `limit` | no | `200` | Maximum number of results to return (default 200). |
**Returns:** `FindAssetsResult`
**Notes:** At least one of `type`, `name`, or `label` is required.
### `set_import_settings`
Set import settings on an asset's AssetImporter (e.g. a texture's isReadable) and re-import it.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `asset` | yes | – | Reference to the asset whose importer to edit (path / guid / globalId). |
| `settings` | yes | – | JSON object of importer property/field names to values, e.g. {"isReadable": true, "textureType": "NormalMap"}. |
| `dry_run` | no | `false` | If true, validate which settings would apply (and which are unknown) without writing or re-importing. |
**Returns:** `SetImportSettingsResult`
**Notes:** Supports `dry_run`. Import settings live in the asset's .meta file and are not part of Unity's Undo system.
### `get_import_settings`
Read an asset's import settings, structured by importer type (texture/model/audio), including the default-platform fields and (for textures/audio) one platform override block.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `asset` | yes | – | Reference to the asset whose importer to read (path / guid / globalId). |
| `platform` | no | `Default` | Platform whose override to read: Default \| Standalone \| iOS \| Android \| WebGL \| tvOS. |
**Returns:** `GetImportSettingsResult`
### `create_folder`
Create a folder under the authoring root (creates intermediate folders).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Folder path relative to the authoring root (default Assets/); the Assets/ prefix is optional. e.g. Gameplay/Enemies or Assets/Gameplay/Enemies |
**Returns:** `AuthoringResult`
### `read_text_file`
Read a UTF-8 text file under the authoring root and return its contents.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Text file path relative to the authoring root. The Assets/ prefix is optional. |
| `max_bytes` | no | `1048576` | Reject files larger than this many bytes (default 1048576 = 1 MiB) to avoid huge payloads. |
**Returns:** `ReadTextFileResult`
**Notes:** `MainThreadRequired = true`.
### `write_text_file`
Write UTF-8 text to a file under the authoring root, then import it. Overwriting an existing file requires confirm=true.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Text file path relative to the authoring root, including extension. The Assets/ prefix is optional. |
| `contents` | yes | – | The full text content to write (replaces the file). |
| `confirm` | no | `false` | Required (true) only when overwriting an existing file at the path. |
| `dry_run` | no | `false` | If true, validate inputs and report what would be written without writing anything. |
**Returns:** `AuthoringResult`
**Notes:** `confirm=true` required to overwrite an existing file; supports `dry_run`. Filesystem writes are not part of Unity's Undo system.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,140 @@
# Baking commands
Bake and clear precomputed scene data: lightmaps, NavMesh, and occlusion culling. Each `bake_*` runs asynchronously (returns immediately) — poll the matching `*_bake_status` until `completed`. The `clear_*` commands are destructive and require `confirm=true`. The `get_*_settings` / `set_*_settings` pairs read and tune the bake parameters.
## Lighting
### `bake_lighting`
Trigger an async lightmap bake of the open scene(s) via Lightmapping.BakeAsync(). Returns immediately; poll `lighting_bake_status` until completed.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `confirm` | no | `false` | Recommended (true): a bake overwrites existing lightmap data. Accepted for parity; not required. |
| `dry_run` | no | `false` | If true, validate there is an open bakeable scene and return the current lighting settings without baking. |
**Returns:** `object`
### `lighting_bake_status`
Get the status of the last lighting bake: idle | baking | completed.
**Returns:** `string`
### `cancel_lighting_bake`
Cancel an in-progress lighting bake (Lightmapping.Cancel()).
**Returns:** `object`
### `clear_baked_lighting`
Clear baked lightmap data for the open scene(s). Destructive: requires confirm=true.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `confirm` | no | `false` | Must be true to actually clear (destructive, not undoable via Unity's Undo). |
| `include_disk_cache` | no | `false` | If true, also clear the GI disk cache (Lightmapping.ClearDiskCache()). |
| `dry_run` | no | `false` | If true, report what would be cleared without clearing. |
**Returns:** `object`
### `get_lighting_settings`
Read the active LightingSettings (lightmapper, bounces, resolution, directional mode, AO, etc.).
**Returns:** `LightingSettingsResult`
### `set_lighting_settings`
Apply a subset of lighting settings to the active LightingSettings. Returns `{ applied[], unknown[] }`.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | yes | `–` | JSON object with a subset of lighting fields to set (same names/enums as get_lighting_settings). |
| `dry_run` | no | `false` | If true, validate the keys and report applied/unknown without changing anything. |
**Returns:** `object`
## NavMesh
### `bake_navmesh`
Trigger an async legacy NavMesh bake of the open scene(s) via UnityEditor.AI.NavMeshBuilder. Returns immediately; poll `navmesh_bake_status` until completed.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `confirm` | no | `false` | Accepted for parity (a bake overwrites the existing NavMesh); not required. |
| `dry_run` | no | `false` | If true, validate there is an open scene and return current NavMesh settings without baking. |
**Returns:** `object`
### `navmesh_bake_status`
Get the status of the last NavMesh bake: idle | baking | completed.
**Returns:** `string`
### `cancel_navmesh_bake`
Cancel an in-progress NavMesh bake (NavMeshBuilder.Cancel()).
**Returns:** `object`
### `clear_navmesh`
Clear the baked NavMesh for the open scene(s). Destructive: requires confirm=true.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `confirm` | no | `false` | Must be true to actually clear (destructive, not undoable via Unity's Undo). |
| `dry_run` | no | `false` | If true, report what would be cleared without clearing. |
**Returns:** `object`
### `get_navmesh_settings`
Read the default agent's legacy NavMesh bake settings (agentRadius/Height/Slope/Climb, minRegionArea, voxelSize).
**Returns:** `NavMeshSettingsResult`
### `set_navmesh_settings`
Apply a subset of legacy NavMesh bake settings to the default agent. Returns `{ applied[], unknown[] }`.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | yes | `–` | JSON object with a subset of NavMesh fields to set (same names as get_navmesh_settings). |
| `dry_run` | no | `false` | If true, validate the keys and report applied/unknown without changing anything. |
**Returns:** `object`
### `bake_navmesh_surfaces`
Bake NavMeshSurface components (AI Navigation package). v1 stub: returns package_not_found when the package is absent.
**Returns:** `object`
## Occlusion culling
### `bake_occlusion_culling`
Trigger an async occlusion-culling bake of the open scene(s) via StaticOcclusionCulling.GenerateInBackground(). Returns immediately; poll `occlusion_bake_status` until completed.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `smallest_occluder` | no | `–` | Smallest object that will occlude others (meters). Defaults to Unity's current value. |
| `smallest_hole` | no | `–` | Smallest gap geometry can have that the view can see through (meters). Defaults to Unity's current value. |
| `backface_threshold` | no | `–` | Backface threshold (1-100); lower trims more backfaces. Defaults to Unity's current value. |
| `confirm` | no | `false` | Accepted for parity (a bake overwrites existing occlusion data); not required. |
| `dry_run` | no | `false` | If true, validate there is an open scene and report the parameters that would be used without baking. |
**Returns:** `object`
### `occlusion_bake_status`
Get the status of the last occlusion bake: idle | baking | completed.
**Returns:** `string`
### `cancel_occlusion_bake`
Cancel an in-progress occlusion bake (StaticOcclusionCulling.Cancel()).
**Returns:** `object`
### `clear_occlusion_culling`
Clear baked occlusion-culling data for the open scene(s). Destructive: requires confirm=true.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `confirm` | no | `false` | Must be true to actually clear (destructive, not undoable via Unity's Undo). |
| `dry_run` | no | `false` | If true, report what would be cleared without clearing. |
**Returns:** `object`
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,102 @@
# Batch command
The `batch` command runs several existing pipeline commands in a single request, with cross-operation result references, atomic Undo, and dry-run validation. It exists to collapse the round-trips of a "create → add components → wire fields" job (otherwise 10–90 sequential calls) into one request, and to add what `eval` structurally lacks: **transactionality and rollback**, so a failure mid-way never leaves partial state on a user-owned scene.
**Scope guard:** `batch` is transactional object CRUD with references. It intentionally has **no** `invoke`/loop/conditional op types — arbitrary logic belongs in code-on-disk (`run_script`/`eval`), not in JSON. This keeps the command surface auditable and allowlistable.
### `batch`
Run multiple registered commands in one transactional request.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `operations` | yes | – | Ordered list of operations, each `{ id?, command, params }`. Max 200. |
| `transactional` | no | `true` | Group every op into one Editor Undo step and revert all applied ops if any op fails. Forced `false` when `on_error=continue`. Rejects commands whose mutations Undo cannot revert (see [Transactional restrictions](#transactional-restrictions)). |
| `on_error` | no | `abort` | `abort`: stop at the first failing op. `continue`: run every op, collecting per-op errors (forces `transactional=false`). |
| `dry_run` | no | `false` | Validate command names, parameters, reference topology, and excluded/non-batchable commands without mutating anything. |
| `result_fields` | no | – | Optional per-op result projection: a map of op id-or-index → array of result field paths to keep (context economy). |
| `time_budget_ms` | no | `50000` | Cooperative time budget for the whole batch, in milliseconds (max 3600000). Checked before each op after the first; when exhausted, the remaining ops are skipped and a transactional batch rolls back. |
**Returns:** `BatchResult` — `{ results: [{ id?, command, success, result? | error?, skipped?, resultTruncated?, revertible? }], applied, reverted, transactional, onError, error?, undoGroup?, dryRun?, valid? }`. The batch-level `error` is set only for aborts not attributable to a single op ("batch time budget exceeded", "batch canceled").
**Notes:** Runs on the main thread. Each sub-operation goes through the same dispatch path as `/api/exec`, so its result is shaped identically to running that command standalone.
#### Operation shape
Each entry of `operations` is:
```json
{ "id": "createHead", "command": "add_component", "params": { "target": "$root.instanceId", "type": "Rigidbody" } }
```
- `id` (optional) — a name for referencing this op's result from a later op. Must be unique within the batch and match `^[A-Za-z_][A-Za-z0-9_-]*$` (purely numeric ids would be ambiguous with index selectors, and `.` collides with the reference path separator).
- `command` — the registered command to run (any command except the non-batchable ones below).
- `params` — the command's parameters, same shape as `/api/exec` `parameters`. Operations are **always** `command` + `params`; the `argv`/`commandLine` raw forms are exec-only and are not accepted inside a batch operation.
#### Cross-operation references
Any **string** value in a later op's `params` may reference an earlier op's result:
```
"$<id-or-index>.<jsonPath>"
```
- `<id-or-index>` names an earlier op by its `id` or its 0-based index (an `id` match takes precedence).
- `<jsonPath>` is a Newtonsoft SelectToken path into that op's result, e.g. `instanceId`, `components[0].instanceId`.
- `"$$"` escapes a literal `$` (so `"$$5"` is the literal string `"$5"`).
- References are **backward-only** and resolved as **whole values** (the entire string must be the reference). The substituted JSON token keeps its type, so a numeric `instanceId` deserializes into an `ObjectRef` parameter exactly as a literal would. References may appear anywhere in the parameter tree (including inside nested objects/arrays).
- Referencing an op that **failed** or was **skipped** errors explicitly (this can happen under `on_error=continue`); referencing a succeeded op that **returned no result** errors with "returned no result". A reference never silently resolves to `null`.
- References always resolve against the **full** result of the earlier op, even when the reported copy was truncated or projected via `result_fields`.
- **Result size bounds** (both measured in serialized UTF-8 bytes, i.e. wire size): a single op's reported result above **16 KiB** is replaced by a truncation marker (`resultTruncated: true` + a preview), and the **aggregate** of all reported results in one batch is bounded to **256 KiB** — past that, later results are elided the same way (the ops still execute; only the echoed copy is dropped). Use `result_fields` to project results down when a batch reads large objects.
- A malformed `result_fields` path never fails its op (the op has already executed); the bad path is reported inside the projection as `invalidResultFields`.
Example — create a GameObject, add a Rigidbody to it, then set the Rigidbody's mass:
```json
{
"operations": [
{ "command": "create_gameobject", "params": { "name": "Ball" } },
{ "command": "add_component", "params": { "target": "$0.instanceId", "type": "Rigidbody" } },
{ "command": "set_serialized_field", "params": { "target": "$1.instanceId", "field": "m_Mass", "value": 2.5 } }
]
}
```
#### Atomicity
The whole batch runs inside one `AuthoringUndoScope`, so every op (each of which registers its own Undo operations) collapses into a **single** Editor Undo step. When `transactional` (the default) and the batch aborts — an op fails under `on_error=abort`, the time budget runs out, or the job is canceled — every applied op is reverted with `Undo.RevertAllDownToGroup` (including partial mutations registered by the failing op itself), the scene returns to its pre-batch state, and nothing is left on the Undo stack. `reverted: true` and the failing op's `error` are reported.
#### Transactional restrictions
Rollback is only real for **Undo-tracked** mutations (scene objects, components, serialized fields). Commands that mutate state **outside** the Undo system — asset and file writes, scene saves, Build Settings, project settings, baked data — cannot be rolled back, so a transactional batch **rejects** them with a structured `not_batchable_transactional` error. Run them with `transactional: false`, or call them standalone. Their per-op result reports `revertible: false` so the limitation is visible.
The non-revertible set (v1 — a curated list, pending per-command `Revertible` metadata):
- **Assets & files:** `create_asset`, `import_asset`, `move_asset`, `copy_asset`, `rename_asset`, `delete_asset`, `create_folder`, `set_import_settings`, `write_text_file`, `create_script`
- **Prefab assets:** `create_prefab`, `create_prefab_variant`, `apply_prefab_overrides`, `save_prefab_contents` (scene-side prefab ops — `instantiate_prefab`, `unpack_prefab`, `revert_prefab_overrides` — are Undo-tracked and stay fully batchable)
- **Scene persistence & build list:** `save_scene`, `save_all`, `add_scene_to_build`, `remove_scene_from_build`, `set_build_settings`
- **Settings:** `set_audio_settings`, `set_graphics_settings`, `set_input_settings`, `set_lighting_settings`, `set_navmesh_settings`, `set_physics_settings`, `set_player_settings`, `set_quality_settings`, `set_tags_layers`, `set_time_settings`, `set_authoring_root`
- **Animation / Timeline assets:** `create_animation_clip`, `set_animation_curve`, `remove_animation_curve`, `create_animator_controller`, `add_animator_layer`, `add_animator_parameter`, `add_animator_state`, `add_animator_transition`, `create_timeline`, `add_timeline_track`, `add_timeline_clip`
- **Materials:** `set_material_properties` (persists the `.mat` asset to disk)
- **Baking:** `bake_lighting`, `clear_baked_lighting`, `bake_navmesh`, `bake_navmesh_surfaces`, `clear_navmesh`, `bake_occlusion_culling`, `clear_occlusion_culling`
`open_scene` and `create_scene` are **always** rejected inside a batch (even with `transactional: false`): they clear the Editor Undo stack, which would silently destroy the batch's ability to revert anything that ran before them.
#### Timing, budget and cancellation
The batch executes in a **single main-thread turn** — the Editor UI is frozen for its duration, bounded by `time_budget_ms` (frame-spanning execution is future work). The elapsed time is checked cooperatively before each op after the first; when the budget is exhausted, the remaining ops are reported `skipped: true`, the batch-level `error` is set to "batch time budget exceeded", and a transactional batch rolls back. For long batches, submit the request as a detached job (`"job": true` on `/api/exec`) and raise `time_budget_ms` — jobs also make the batch cancelable: cooperative cancellation (`POST /api/job/cancel`) is checked between ops and aborts the batch the same way ("batch canceled"). Per-op progress is reported (`op i/n: <command>`) and visible via `GET /api/progress`.
#### Dry run
`dry_run: true` validates without executing anything: it catches unknown commands, excluded/non-batchable (`not_batchable`) commands, transactional rejections (`not_batchable_transactional`), unknown/missing parameters, and unresolvable reference topology (unknown or forward references). It mutates nothing and reports `valid` plus a per-op `success`/`error`.
#### Excluded commands
The following are rejected with a structured `not_batchable` error:
- **Out of scope:** `build`, `switch_build_target`, `package_*`, `editor_play`/`editor_stop`/`editor_pause`, `recompile`
- **Arbitrary code:** `eval`, `eval_file`, `run_script`, and nested `batch`
- **Modal-dialog hazard:** `menu` (menu items can open modal dialogs that wedge an unattended batch)
- **Cannot make progress:** synchronous `wait_for` — the batch holds the main thread for its whole turn, so the editor state a condition watches cannot change; the wait would just burn its timeout inside the batch. Run an async `wait_for` before/after the batch instead (`wait_status`/`wait_cancel` are quick reads and stay batchable)
- **Undo-stack wipe:** `open_scene`, `create_scene`
- **Async commands** (`run_tests`, `list_tests`, and any command returning `Task`): they are completed by `EditorApplication.update` callbacks, which cannot fire while the batch holds the main thread — dispatching one would freeze the Editor permanently
- **Runtime-only commands** (e.g. `set_timescale`): they belong to the Player server surface and cannot execute on the editor server
@@ -0,0 +1,188 @@
# Build, compilation & test commands
These commands drive the build/compile/test dev loop, plus build configuration: triggering Player builds and reading the resulting BuildReport, switching the active build target, enumerating targets, reading/writing EditorUserBuildSettings, and listing Build Profiles. Several commands are asynchronous: trigger the operation, then poll a matching `*_status` command until it reports completion. The client must tolerate connection errors during a domain reload (recompile, target switch) or a long blocking build.
### `build`
Trigger an async Player build and report the full BuildReport. Returns immediately (queued); poll build_status until status is 'completed'. DetailedBuildReport is included by default unless 'options' is supplied. Use dry_run to validate without building.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | no | `–` | BuildTarget name (e.g. StandaloneWindows64). Defaults to the active target. Must be installed. |
| `outputPath` | no | `–` | Output path (absolute, or relative to the project root). Defaults to the last/auto path. |
| `profileName` | no | `–` | Build Profile name to activate before building (Unity 6 only; ignored otherwise). |
| `options` | no | `–` | BuildOptions names. Omit to get just DetailedBuildReport; supplying any disables that default. |
| `scenes` | no | `–` | Scene asset paths to build (e.g. Assets/Scenes/Main.unity). Defaults to EditorBuildSettings. |
| `confirm` | no | `false` | Acknowledge and run the build; without it the call is refused. Use `dry_run` to validate only. |
| `dry_run` | no | `false` | Validate target/outputPath/scenes without building. |
Supported `options` values (case-insensitive): `Development`, `AllowDebugging`, `ConnectWithProfiler`, `EnableHeadlessMode`, `SymlinkSources`, `BuildAdditionalStreamedScenes`, `CleanBuildCache`, `DetailedBuildReport`. Any unrecognized value is a validation error.
**Returns:** `object` (transient payloads: `queued` / `busy` / `dry_run` / `error`; the finished report is read via `build_status`).
**Notes:** `MainThreadRequired = false`. Async — validates + queues, returns `queued` immediately, then an editor tick runs the (blocking) build; poll `build_status` until status is `completed`. Only one build at a time (returns `busy` otherwise). Mutating: gated by the `confirm`/`dry_run` convention — pass `confirm=true` for a real build, or `dry_run=true` to validate target/outputPath/scenes without building (nothing is queued on a dry run or on validation failure).
### `build_status`
Status of the current/most recent build: idle | queued | building | completed, with the full BuildReport (files, packedAssets, buildSteps, errors, warnings) once completed. Retained until the next build.
No parameters.
**Returns:** `string` (JSON). `idle` when no build has run; `queued`/`building` (the latter with live `elapsedMs`) while in progress; a full `BuildReportResult` once `completed`.
`BuildReportResult` fields (returned when `status` is `completed`):
| Field | Description |
|-------|-------------|
| `status` | Always `completed` here. |
| `buildId` | Id returned by the triggering `build` call. |
| `result` | `Succeeded` \| `Failed` \| `Cancelled` \| `Unknown`. |
| `platform` | Build target the report is for. |
| `outputPath` | Final output location. |
| `totalSizeBytes` | Total build size in bytes. |
| `buildTimeMs` | Total build duration in milliseconds. |
| `buildStartedAt` / `buildEndedAt` | ISO-8601 timestamps (omitted when unset). |
| `totalWarnings` / `totalErrors` | Aggregate counts from the report summary. |
| `files` | Output files (path, role, sizeBytes). Present only on success. |
| `packedAssets` | Packed bundles with per-asset breakdown. Present only on success and requires DetailedBuildReport. |
| `buildSteps` | Per-step name, durationMs, depth, and messages. |
| `errors` / `warnings` | Build issues, each with `message` and best-effort `file`. |
**Notes:** `MainThreadRequired = false`. Reads a Temp status file off-thread, so polling keeps working while a build holds the main thread. The file also survives the domain reload a build can incur, so the last report is retained until the next build.
### `switch_build_target`
Switch the active build target (destructive, long-running: triggers a full reimport + domain reload). Requires confirm=true. Returns immediately; poll switch_build_target_status.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | `–` | BuildTarget name to switch to (must be installed; see list_build_targets). |
| `confirm` | no | `false` | Apply the switch. Without it the call is refused. |
**Returns:** `object` (`switching` / `busy` / `completed` / `error`). Returns `completed` immediately if already on the requested target.
**Notes:** `MainThreadRequired = false`. Async — validates + queues, returns `switching` immediately, then an editor tick performs the (blocking) switch; poll `switch_build_target_status` until `completed`. Only one switch at a time (returns `busy` otherwise). Mutating and confirm-gated: without `confirm=true` the call is refused. The status file survives the domain reload the switch causes, and is reconciled against the active target on the next load.
### `switch_build_target_status`
Status of the last target switch: idle | switching | completed (with success + activeBuildTarget).
No parameters.
**Returns:** `string` (JSON). `idle`, `switching`, or `completed` (with `success` and `activeBuildTarget`, or `errors` on failure).
**Notes:** `MainThreadRequired = false`. Reads a Temp status file off-thread, so it keeps answering while the switch holds the main thread.
### `list_build_targets`
List the known BuildTarget values with their group and whether build support is installed.
No parameters.
**Returns:** `object` (list of `BuildTargetInfo`: `name`, `displayName`, `targetGroup`, `isInstalled`), sorted by group then name. Obsolete and sentinel targets are excluded.
**Notes:** `MainThreadRequired = true`.
### `get_build_settings`
Read the current build configuration from EditorUserBuildSettings / EditorBuildSettings.
No parameters.
**Returns:** `object` (`BuildSettingsResult`).
`BuildSettingsResult` fields:
| Field | Description |
|-------|-------------|
| `activeBuildTarget` | Active BuildTarget name. |
| `activeBuildTargetGroup` | Active BuildTargetGroup name. |
| `developmentBuild` | Whether a Development Player is configured. |
| `allowDebugging` | Whether script debugging is allowed. |
| `connectWithProfiler` | Whether the Profiler auto-connects. |
| `buildScriptsOnly` | Whether only scripts are built (skip data). |
| `symlinkSources` | Whether runtime/plugin sources are symlinked. |
| `il2CppCodeGeneration` | `OptimizeSpeed` \| `OptimizeSize` for the active target. |
| `scenes` | Build Settings scene list (each: `path`, `guid`, `enabled`). |
**Notes:** `MainThreadRequired = true`.
### `set_build_settings`
Set mutable EditorUserBuildSettings fields. Does NOT manage scenes (use add_scene_to_build / remove_scene_from_build) or switch target (use switch_build_target). Use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Fields to change; omitted fields are left unchanged. |
| `confirm` | no | `false` | Apply the changes. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields (a structured input DTO; every field is optional — only supplied fields change):
| Field | Description |
|-------|-------------|
| `developmentBuild` | Build a Development Player (enables the debugger/profiler). |
| `allowDebugging` | Allow script debugging (only effective with developmentBuild=true). |
| `connectWithProfiler` | Auto-connect the Profiler (only effective with developmentBuild=true). |
| `buildScriptsOnly` | Build only the scripts (skip data) for faster iteration. |
| `symlinkSources` | Symlink runtime/plugin sources instead of copying (where supported). |
| `il2CppCodeGeneration` | IL2CPP code generation for the active target: OptimizeSpeed \| OptimizeSize. |
**Returns:** `object` (`SetBuildSettingsResult`: `success`, `dryRun`, `applied` map of fields that changed, `skipped` map of supplied fields that already matched, `message`).
**Notes:** `MainThreadRequired = true`. Mutating: refused unless `confirm=true` (or `dry_run=true` to preview). Fields already at the requested value are reported under `skipped` rather than re-applied. Fails if no `settings` object is provided.
### `list_build_profiles`
List Build Profile assets in the project (Unity 6 only). Returns feature_unavailable on earlier versions.
No parameters.
**Returns:** `object` (list of `BuildProfileInfo`: `name`, `guid`, `platform`, `isActive`), or `{ error, code = "feature_unavailable" }` on editors older than Unity 6.
**Notes:** `MainThreadRequired = true`.
### `recompile`
Force a script recompile (works while unfocused/minimized). Poll recompile_status for completion.
No parameters.
**Returns:** `object`
**Notes:** `MainThreadRequired = true`. Async — poll `recompile_status` until status is `completed` or `up_to_date`. A successful compile triggers a domain reload, so the triggering request cannot stay open.
### `recompile_status`
Get the status of the last recompile: idle | triggered | compiling | completed | up_to_date.
No parameters.
**Returns:** `string`
**Notes:** `MainThreadRequired = false`.
### `list_tests`
List all available tests (EditMode and/or PlayMode) without running them.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `mode` | no | `all` | Test mode: all, editor, playmode (default: all) |
**Returns:** `TestListResponse`
**Notes:** `MainThreadRequired = true`.
### `run_tests`
Execute Unity tests with filtering options.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `mode` | no | `all` | Test mode: all, editor, playmode (default: all) |
| `filter` | no | `–` | Test name filter pattern (case-insensitive partial match) |
| `filter_type` | no | `testName` | Filter type: testName, assembly, category (default: testName) |
| `include_explicit` | no | `false` | Include tests marked with [Explicit] attribute |
| `async_tests` | no | `false` | Run asynchronously - return immediately, poll /test-status for results |
| `timeout` | no | `300` | Test execution timeout in seconds (default: 300) |
**Returns:** `TestExecutionResponse`
**Notes:** `MainThreadRequired = true`. Synchronous by default; with `async_tests=true` it returns immediately — poll `test_status` for results.
### `test_status`
Get status of running async test execution.
No parameters.
**Returns:** `string`
**Notes:** `MainThreadRequired = false`.
### `cancel_tests`
Cancel running test execution.
No parameters.
**Returns:** `object`
**Notes:** `MainThreadRequired = true`.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,61 @@
# Capture commands
Commands that render a camera, the Scene View, or a UI Toolkit element to a PNG and return it base64-encoded so an agent can "see" the editor (or a running player) without a display.
### `capture_game_view`
Render the game view to a PNG. Returns it inline as base64, unless `save_path` is set — then the result is **path-only** (no base64) so agent tool results stay small; pass `include_inline_image=true` to get both.
`source` selects what is captured. `camera` (default) renders a camera and **does not include Screen Space - Overlay UI** (HUDs, menus, damage numbers on overlay canvases) — overlay canvases composite straight to the screen and never render through a camera. `screen` captures the composited game view backbuffer, so overlay UI is included; because overlay UI only exists at runtime, `source=screen` **requires Play Mode** (in Edit Mode it returns a clear error — use `source=camera`). The screen capture reads the game view at its true pixel resolution — independent of OS display scaling — and preserves the on-screen color tone, so the PNG matches what the Game window shows.
Parameter interactions: `include_inline_image` is only meaningful together with `save_path` (without one, the inline image is always returned). `max_resolution` only applies when an inline image is actually returned — i.e. no `save_path`, or `save_path` + `include_inline_image=true`; it has **no effect** when `save_path` is set without `include_inline_image=true`. `camera` is ignored when `source=screen`.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `width` | no | `1280` | Output width in px (default 1280; capped 4096). |
| `height` | no | `720` | Output height in px (default 720; capped 4096). |
| `camera` | no | `–` | Optional camera name (`source=camera` only); defaults to Camera.main, else the first enabled camera. |
| `save_path` | no | `–` | Optional project-relative path to write the PNG (e.g. Screenshots/foo.png). When set, the result omits the inline base64 image unless `include_inline_image=true`. |
| `include_inline_image` | no | `false` | Also return the image inline as base64 when `save_path` is set. |
| `max_resolution` | no | `0` | Cap on the inline image's longest edge (e.g. 512), preserving aspect ratio. No effect when `save_path` is set without `include_inline_image=true`. The `save_path` file keeps the requested resolution; a downscaled inline copy is reported via `inlineWidth`/`inlineHeight`. |
| `source` | no | `camera` | `camera` renders a camera (misses Screen Space - Overlay UI); `screen` captures the composited game view incl. overlay canvases (**Play Mode only**). |
**Returns:** `CaptureResult`
### `capture_scene_view`
Render the active Scene View to a PNG. Same result contract and parameter interactions as `capture_game_view`: inline base64, or path-only when `save_path` is set (opt back in with `include_inline_image=true`; `max_resolution` has no effect when `save_path` is set without it).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `width` | no | `1280` | Output width in px (default 1280; capped 4096). |
| `height` | no | `720` | Output height in px (default 720; capped 4096). |
| `save_path` | no | `–` | Optional project-relative path to write the PNG (e.g. Screenshots/foo.png). When set, the result omits the inline base64 image unless `include_inline_image=true`. |
| `include_inline_image` | no | `false` | Also return the image inline as base64 when `save_path` is set. |
| `max_resolution` | no | `0` | Cap on the inline image's longest edge (e.g. 512), preserving aspect ratio. No effect when `save_path` is set without `include_inline_image=true`. The `save_path` file keeps the requested resolution; a downscaled inline copy is reported via `inlineWidth`/`inlineHeight`. |
**Returns:** `CaptureResult`
### `capture_editor_element`
Capture a UI Toolkit VisualElement (by selector) from an EditorWindow to a PNG; returns path + base64.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `window` | yes | `–` | EditorWindow type name (e.g. InspectorWindow) or window title to capture from. |
| `selector` | yes | `–` | Element selector: '#name', '.class', a type name (e.g. Button), descendant (space) / child ('>') chains, optional pseudo-states (:checked,:hover,:focus,:active,:enabled,:disabled,:not(...)). |
| `output` | no | `–` | Output PNG path (absolute, or relative to the project root). Defaults to a timestamped file under <project>/Temp/pipeline-screenshots/. |
**Returns:** `CaptureElementResponse`
**Notes:** `MainThreadRequired = true`. Unity 6000.7+ only.
### `capture_runtime_element`
Capture a UI Toolkit VisualElement (by selector) from a live runtime panel (UIDocument or PanelRenderer) to a PNG; returns path + base64.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `panel` | no | `–` | Name of the target panel: matches the PanelSettings asset name or the host GameObject name (UIDocument or PanelRenderer). Optional when exactly one panel exists. |
| `selector` | yes | `–` | Element selector: '#name', '.class', a type name (e.g. Button), descendant (space) / child ('>') chains, optional pseudo-states (:checked,:hover,:focus,:active,:enabled,:disabled,:not(...)). |
| `output` | no | `–` | Output PNG path (absolute, or relative to Application.persistentDataPath). Defaults to a timestamped file under Application.persistentDataPath. |
**Returns:** `CaptureElementResponse`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`. Unity 6000.7+ only.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,187 @@
# Editor lifecycle & observability commands
Commands to control Editor play mode, focus, and menus, and to observe the Editor's state, console, and performance.
### `editor_play`
Enter Unity Editor play mode.
No parameters.
**Returns:** `string`
### `editor_stop`
Exit Unity Editor play mode.
No parameters.
**Returns:** `string`
### `editor_pause`
Toggle pause state of Unity Editor play mode (calling it again while paused resumes play mode).
No parameters.
**Returns:** `string`
### `editor_status`
Get detailed Unity Editor status and state information.
No parameters.
**Returns:** `StatusResponse`
### `editor_focus`
Bring the Unity Editor window to the foreground.
No parameters.
**Returns:** `string`
**Notes:** `MainThreadRequired = true`.
### `menu`
Execute an Editor menu item by path, or list available items when no path is given.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | no | `–` | Menu item path to execute, e.g. "Assets/Reimport All". Omit to list available menu items. |
**Returns:** `MenuResponse`
**Notes:** `MainThreadRequired = true`.
### `screenshot`
Capture the Scene or Game view as a PNG and return its file path.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `view` | no | `game` | Which view to capture: 'game' (default) or 'scene' |
| `output` | no | `–` | Output PNG path (absolute, or relative to the project root). Defaults to a timestamped file under <project>/Temp/pipeline-screenshots/. |
| `width` | no | `0` | Output width in pixels. 0 (default) uses the view camera's current width. |
| `height` | no | `0` | Output height in pixels. 0 (default) uses the view camera's current height. |
**Returns:** `ScreenshotResponse`
**Notes:** `MainThreadRequired = true`.
### `set_autotick`
Keep the editor ticking while unfocused by forcing EditorApplication.SignalTick at a throttled rate.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `enable` | no | `true` | Enable (true) or disable (false) auto-tick mode |
| `interval_ms` | no | `16` | Minimum milliseconds between forced ticks. 0 = every update (max rate, pegs a CPU core). Default 16 (~60Hz). |
| `persist` | no | `true` | Persist this choice to SessionState so it survives a domain reload. Set `false` for a one-off/expensive setting (e.g. `interval_ms=0`) that should revert to the last persisted choice (or the default) after the next recompile instead of sticking for the rest of the session. |
**Returns:** `string`
**Notes:** `MainThreadRequired = true`. Persisted in SessionState by default: your enabled/interval choice survives a domain reload (dies with the editor process/session).
Use `persist=false` for a change you only want in effect until the next unrelated recompile — e.g. a short profiling run at `interval_ms=0` (pegs a CPU core) or a one-off test/CI script — so it can't outlive its purpose and silently burn CPU for the rest of the session. Leave `persist` at its default `true` for a setting you want to remain in effect across recompiles, such as the normal always-on 16ms tick.
### `get_performance_stats`
Read render, memory, and frame-timing stats (structured, read-only).
No parameters.
**Returns:** `PerformanceStats`
### `audit`
Run a Project Auditor static-analysis scan. Returns immediately; poll `audit_status` until status is `completed`, then read the CSV.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `categories` | no | `–` | Comma-separated issue categories to scan (e.g. `Code,ProjectSetting,Texture`). Default: all categories. An unknown name is rejected with an error listing the valid values. |
| `output` | no | `–` | CSV output path (absolute, or relative to the project root). Defaults to `<project>/Temp/pipeline-audit/<scanId>.csv`. |
**Returns:** `object` — `{ status, scanId, csvPath }` on start, or `{ status: "unavailable" | "error" | "busy", message }`.
**Notes:** Requires Project Auditor in the Editor. Only one scan runs at a time (a second call returns `busy`). Not cancellable: stop polling to abandon a scan.
### `audit_status`
Get the status of the last audit: `idle` | `scanning` | `completed` | `failed` | `interrupted` | `unavailable`.
No parameters.
**Returns:** `AuditStatus` (JSON) — `issueCount` and `csvPath` are populated when `completed`; `error` when `failed`; `message` when `unavailable`.
**Notes:** `MainThreadRequired = false`, so it answers while a scan holds the main thread (Code analysis compiles assemblies). `interrupted` means a domain reload killed an in-flight scan — re-run `audit`.
#### Project Auditor prerequisites
`audit` needs both Project Auditor **and** its rules:
- **Project Auditor absent** — `audit` returns `unavailable` immediately.
- **Rules absent** — when Project Auditor ships as a built-in editor module, its rules (descriptors,
API/obsolete databases, Roslyn analyzers) come from the separate `com.unity.project-auditor-rules`
package. Without it Project Auditor registers no analysis modules and cannot analyze anything, so
`audit` returns `scanning` and the first `audit_status` poll reports `unavailable` with a message
naming the package to install. It never reports an empty `completed` scan, which would read as a
clean project.
The CSV columns are `Category, Severity, Areas, Description, RelativePath, Line, DescriptorId, Recommendation`;
only diagnostics (things to fix) are emitted, not raw inventory rows.
### `report_evals`
Aggregate the local eval-usage telemetry into a ranked report: API fingerprint frequency, one-liner percentage, error rate, and command-coverage suggestions (read-only). See [Eval usage telemetry](#eval-usage-telemetry) below.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `top` | no | `50` | Maximum entries in each ranked list: the fingerprint ranking and the uncovered patterns suggested as commands. Accepted range: 0 or greater; negative values fall back to the default. |
**Returns:** `EvalUsageReport`
**Notes:** `MainThreadRequired = false` (stays servable while the Editor is settling). Reads `<project>/Library/Pipeline/eval-usage.jsonl` merged with the rotated `eval-usage.old.jsonl` (oldest first), so the report covers the full retained history — bounded to roughly 2× the size cap. Returns a zeroed report if no evals have been recorded. The report's `distinctFingerprints` field carries the uncapped total of distinct fingerprints so truncation of the ranked list is always visible; coverage suggestions are computed from the full ranking, not the capped list.
## Eval usage telemetry
The `eval`/`eval_file` commands are the workhorse of autonomous agent sessions, but they are opaque: raw C# through the protocol, unauditable, and hard to displace with typed commands without knowing what agents actually run. This telemetry closes that loop by recording, **locally**, what each eval does — so the [eval-displacement epic (AUTHAPI-24)](https://jira.unity3d.com/browse/AUTHAPI-24) can be triaged from data instead of anecdotes.
### What is recorded, and where
Every `eval`/`eval_file` invocation appends one JSON object (one line, JSONL) to `<project>/Library/Pipeline/eval-usage.jsonl`:
| Field | Meaning |
|-------|---------|
| `time` | ISO-8601 UTC timestamp. |
| `command` | `eval` or `eval_file`. |
| `success` / `error` | Whether it succeeded; short error category on failure. |
| `payloadLength` / `lineCount` | Size/shape of the eval body. |
| `executionTimeMs` | How long it took. |
| `classification` | `single-expression` (a one-liner: one invoke / read / assignment) or `statements` (multi-statement bodies, e.g. polling loops, bulk setup). |
| `fingerprints` | The top-level member-access paths from the syntax tree — e.g. `AssetDatabase.Refresh`, `PlayerSettings.SetScriptingBackend`, `Object.FindFirstObjectByType<T>`, `MatchManager.Instance.State`. Rendering is a strict whitelist: the only source text that can appear in a fingerprint is an identifier, type name, or member name. Literals, interpolated strings, and any other expression form are replaced by the neutral `<expr>` placeholder (`"…".Length` records as `<expr>.Length`); object creations record as `new TypeName` (constructor arguments never appear); casts unwrap to the inner expression; generic type arguments normalise to `<T>`. A fingerprint that would be nothing but the placeholder is dropped. |
| `source` | The raw eval source — **absent by default**; present only when the opt-in below is enabled, and truncated at 64 K characters with an explicit `…[truncated]` marker (`payloadLength` still reports the full length). |
**Local-first, privacy-first, editor-only.** Nothing is transmitted off the machine, and nothing is recorded outside the Editor: player builds — including development players, where eval itself is available — never write telemetry (recording compiles to a no-op there). The fingerprint is derived from the C# syntax tree, so the log captures *which APIs* agents reach for without capturing *what data* they operate on. Conditional-access chains (`a?.B.C`) are fingerprinted with `?.` normalised to `.` so they rank with their unconditional form. The fingerprint deliberately undercounts a few forms (arguments nested inside a chain's receiver call, bare object creations) — see the `EvalUsageFingerprinter` class doc for the full blind-spot list before treating absence as proof of non-use.
**Zero eval latency.** Recording is fire-and-forget: when an eval completes, the source and outcome plus the current settings are snapshotted, and the parse → fingerprint → append runs on a background thread from that snapshot — the eval response is never delayed by telemetry. The JSONL is bounded by a size cap and rotates to `eval-usage.old.jsonl`; the rotated file is merged back into `report_evals`, so retained history is bounded to roughly 2× the cap.
### Settings
Configured on the `EditorPipelineManager` settings asset (`Window/Pipeline/Settings...`). Edits apply live from the inspector, and the stored values are re-applied on every server start:
- **Eval Telemetry Enabled** (default on) — master switch. When off, nothing is recorded (and, as always, nothing is transmitted).
- **Store Eval Source** (default off) — the explicit opt-in to also persist the raw eval `source` in each record, for local debugging only.
### The feedback loop: report → backlog triage
Run `report_evals` to aggregate the log into an `EvalUsageReport`:
- **Ranked fingerprints** with counts — what agents actually eval, most-used first.
- **One-liner percentage** and **error rate** — the shape of eval usage (the evidence base for AUTHAPI-24 is that ~80% of eval calls are one-liners).
- **Coverage suggestions** — each fingerprint is cross-referenced against the live command catalog:
- `covered`: patterns already served by an existing command (e.g. `PlayerSettings.SetScriptingBackend` → `set_player_settings`) — these are habit, and should move off eval.
- `topUncovered`: the highest-frequency patterns with no covering command (e.g. `AssetDatabase.Refresh` → proposed `refresh_assets`) — these are the next typed commands to build ([AUTHAPI-17](https://jira.unity3d.com/browse/AUTHAPI-17)).
This turns backlog triage into a lookup: "12× `AssetDatabase.Refresh`, no command → build it; 60× state-read loops → `wait_for`". Re-run across sessions to demonstrate eval's shrinking share as typed commands land.
### `get_authoring_root`
Get the base folder (under Assets/) that bare authoring paths resolve against.
No parameters.
**Returns:** `object`
### `set_authoring_root`
Set the base folder (under Assets/) that bare authoring paths resolve against and are confined to. Use 'Assets' for full project access.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `root` | yes | `–` | Project-relative folder under Assets/, e.g. Assets/AgentWork. Use 'Assets' to allow the whole project. |
**Returns:** `object`
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,169 @@
# GameObject & component commands
Commands for creating GameObjects, querying the scene hierarchy, mutating the core GameObject/Transform surface, and adding/removing components and editing their serialized properties. Scene mutations are wrapped in Unity's Undo system (so a multi-step action reverts as one collapsible step) and are blocked during Play mode. Objects are referenced by an `ObjectRef` handle (globalId/path/guid/instanceId/hierarchyPath), and results come back as `AuthoringResult` identities you can chain into a follow-up call.
### `create_gameobject`
Create an empty GameObject or a built-in primitive (cube/sphere/capsule/cylinder/plane/quad) in the active scene.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `name` | no | `–` | Name for the new GameObject. Defaults to 'GameObject' (or the primitive name). |
| `primitive` | no | `–` | Optional primitive type: cube, sphere, capsule, cylinder, plane, quad. Omit for an empty GameObject. |
| `parent` | no | `–` | Optional parent handle (globalId/path/guid/instanceId/hierarchyPath). The new object becomes a child of it. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `create_gameobjects`
Batch-create N empty GameObjects or primitives in one call. Optional positions/rotations/scales are arrays of [x,y,z] (length must equal count). Returns the created identities.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `name` | no | `–` | Base name. With count>1 and no explicit names, objects are suffixed Name1..NameN. |
| `primitive` | no | `–` | Optional primitive type: cube, sphere, capsule, cylinder, plane, quad. Omit for empty GameObjects. |
| `parent` | no | `–` | Optional parent handle. Every created object becomes a child of it. |
| `count` | no | `1` | How many GameObjects to create. Default 1. |
| `positions` | no | `–` | Local positions, one [x,y,z] per object. Length must equal count when supplied. |
| `rotations` | no | `–` | Local Euler rotations (degrees), one [x,y,z] per object. Length must equal count when supplied. |
| `scales` | no | `–` | Local scales, one [x,y,z] per object. Length must equal count when supplied. |
**Returns:** `CreateGameObjectsResult`
**Notes:** Undo-able (the whole batch reverts as one step); blocked during Play mode.
### `find_gameobjects`
Find GameObjects in loaded scenes by name, tag, component type, and/or hierarchy path (filters are combined). Returns structured identities.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `name` | no | `–` | Exact name to match. |
| `tag` | no | `–` | Tag to match (e.g. 'Player'). |
| `type` | no | `–` | Component type name to match (e.g. 'Rigidbody', 'UnityEngine.Camera'). |
| `hierarchy_path` | no | `–` | Exact hierarchy path to match (e.g. '/Root/Child'). |
| `include_inactive` | no | `true` | Include inactive GameObjects. Default true. |
**Returns:** `FindGameObjectsResult`
### `set_transform`
Set a GameObject's local position/rotation(euler)/scale. Omitted channels are left unchanged.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the GameObject to modify. |
| `position` | no | `–` | Local position as [x,y,z]. |
| `rotation` | no | `–` | Local rotation as Euler angles [x,y,z] in degrees. |
| `scale` | no | `–` | Local scale as [x,y,z]. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `set_parent`
Reparent a GameObject under a new parent, or detach it to scene root when no parent is given.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the GameObject to reparent. |
| `parent` | no | `–` | Handle of the new parent. Omit (or empty) to move the object to the scene root. |
| `world_position_stays` | no | `true` | Keep the object's world position when reparenting. Default true. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `set_active`
Set a GameObject's active self-state (activeSelf).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the GameObject. |
| `active` | yes | – | Desired active state. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `set_tag`
Set a GameObject's tag (the tag must already exist in the project).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the GameObject. |
| `tag` | yes | – | Tag to assign (must exist in the Tag Manager). |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `set_layer`
Set a GameObject's layer by name or numeric index (0-31).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the GameObject. |
| `layer` | yes | – | Layer name (e.g. 'UI') or numeric index 0-31. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `rename_gameobject`
Rename a GameObject.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the GameObject. |
| `name` | yes | – | New name. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `delete_gameobject`
Delete a GameObject from the scene (reversible via Undo).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the GameObject to delete. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode. The result describes the object's identity captured before destruction.
### `add_component`
Add a component (by type name) to a GameObject.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the GameObject. |
| `type` | yes | – | Component type name (e.g. 'Rigidbody' or 'UnityEngine.Camera'). |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `remove_component`
Remove a component from a GameObject. Provide either a component handle (target) or a GameObject handle (target) plus a type name.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the component to remove, OR of the GameObject when 'type' is given. |
| `type` | no | `–` | Component type name to remove from the target GameObject (omit when 'target' already points at a component). |
**Returns:** `AuthoringResult`
**Notes:** Undo-able; blocked during Play mode.
### `get_component_properties`
Get a component's serialized properties as a JSON map. Address the component by handle, or by GameObject handle + type.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the component, OR of the GameObject when 'type' is given. |
| `type` | no | `–` | Component type name on the target GameObject (omit when 'target' is a component handle). |
**Returns:** `ComponentPropertiesResult`
### `set_component_properties`
Set serialized properties on a component (one Undo step). 'properties' maps property name -> value; object references accept an ObjectRef handle.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Handle of the component, OR of the GameObject when 'type' is given. |
| `properties` | yes | – | Map of serialized property name to value. Vectors/colors are arrays; object refs are handle objects. |
| `type` | no | `–` | Component type name on the target GameObject (omit when 'target' is a component handle). |
**Returns:** `ComponentPropertiesResult`
**Notes:** Undo-able (one Undo step); blocked during Play mode. An unknown property name fails the whole batch (no partial apply).
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,55 @@
# Material & shader commands
Read and edit material shader properties, and discover/introspect shaders so an agent can pick a valid shader name and property set.
## Materials
### `get_material_properties`
Read a material's shader, render queue, enabled keywords, and all shader properties with their current values (Color as [r,g,b,a], Vector as [x,y,z,w], Texture as an object reference).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `material` | yes | `–` | Reference to the .mat asset (or a loaded material) to read (path / guid / globalId / instanceId). |
**Returns:** `MaterialPropertiesResult`
### `set_material_properties`
Set shader properties on a material (Float/Range/Int=number; Color=[r,g,b,a] or "#RRGGBBAA" hex; Vector=[x,y,z,w]; Texture=an object reference or null to clear), optionally reassign the shader, set the render queue, and toggle keywords. Unknown names / type mismatches are reported in `unknown[]`.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `material` | yes | `–` | Reference to the .mat asset (or a loaded material) to edit (path / guid / globalId / instanceId). |
| `shader` | no | `–` | Reassign the material's shader by name (e.g. "Standard", "Universal Render Pipeline/Lit", or a Shader Graph shader name). Applied before properties so new property names resolve against the new shader. |
| `properties` | no | `–` | JSON object of shader property name -> value. Names must include the leading underscore (e.g. _BaseColor). Float/Range/Int=number; Color=[r,g,b,a] or hex string; Vector=[x,y,z,w]; Texture=an object reference {guid/path} or null. |
| `renderQueue` | no | `–` | Explicit render queue, or -1 to inherit from the shader. Omit to leave unchanged. |
| `enableKeywords` | no | `–` | Shader keywords to enable (e.g. _NORMALMAP, _EMISSION). |
| `disableKeywords` | no | `–` | Shader keywords to disable. |
| `confirm` | no | `false` | Reserved for parity; editing an existing material is non-destructive and undoable, so it is not required. |
| `dry_run` | no | `false` | If true, validate the shader, resolve property names and texture refs, and report applied[]/unknown[] without writing anything. |
**Returns:** `SetMaterialPropertiesResult`
## Shaders
### `list_shaders`
Discover available shaders so an agent can pick a valid name for set_material_properties / create_asset. Returns `[{ name, assetPath, isBuiltin, isSupported }]` — `assetPath` is omitted for built-in shaders in the default lean reply (pass `verbose: true` on the request for an explicit `assetPath: null`); use `isBuiltin` to classify.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `filter` | no | `–` | Case-insensitive substring matched against the shader name (e.g. "URP", "Lit"). |
| `includeBuiltin` | no | `true` | Include built-in/engine shaders (those with no project asset path). |
| `limit` | no | `200` | Maximum number of shaders to return. |
**Returns:** `object` (list of `ShaderInfo`)
### `get_shader_properties`
Introspect a shader's declared property list (name, description, type Color|Vector|Float|Range|TexEnv|Int, range, textureDimension, flags). Provide `shader` (by name) OR `material` (read the shader off that material).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `shader` | no | `–` | Shader name (e.g. "Universal Render Pipeline/Lit"). Provide this OR 'material'. |
| `material` | no | `–` | Reference to a material to read the shader from instead of naming it. Provide this OR 'shader'. |
**Returns:** `ShaderPropertiesResult`
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,32 @@
# Navigation & selection commands
Commands to read and drive the Editor selection, and to run Unity Search queries. All commands are read-only or non-destructive and run on the main thread.
### `get_selection`
Read the current Editor selection as structured object identities.
No parameters.
**Returns:** `SelectionResult`
### `set_selection`
Set the Editor selection to the given assets/scene objects.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `instance_ids` | no | `–` | Scene/loaded object instance IDs to select. |
| `paths` | no | `–` | Asset paths to select (e.g. Assets/Foo.prefab). |
**Returns:** `SelectionResult`
### `search`
Run a Unity Search query and return structured results.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `query` | yes | `–` | Unity Search query string, e.g. 't:Material', 'p: my asset', 'h: Main Camera'. |
| `limit` | no | `50` | Max results to return (capped 200). |
**Returns:** `SearchResult`
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,70 @@
# Package Manager commands
These commands query and modify the project's UPM package set (list, search, add, remove, resolve, status) over `UnityEditor.PackageManager`. Read-only queries block until the registry responds and return synchronously; mutating commands (`package_add`/`package_remove`) require `confirm=true`, support `dry_run` previews, and are async by default (poll `package_status`) with an optional `wait=true` to block until done.
### `package_list`
List packages by scope: installed (default) | available (registry) | all (both). Returns the full result synchronously — available/all block until the registry query completes.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `scope` | no | `installed` | Which packages to list: installed (default) | available | all. |
| `include_indirect` | no | `true` | Include indirect (transitive) installed dependencies (applies to scope=installed/all). |
| `offline` | no | `false` | For available/all: query the local cache instead of the registry. |
**Returns:** `object`
**Notes:** `MainThreadRequired = false`. Synchronous — `available`/`all` start a registry query and block until it completes (up to a 25s timeout); `installed` reads the resolved set inline on the main thread.
### `package_search`
Search packages available in the registry. Provide a name (e.g. com.unity.foo) or omit to list all. Returns the full result synchronously (blocks until the registry query completes).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `query` | no | `–` | Package name to search for. Omit/empty to list all available packages. |
| `offline` | no | `false` | Search the local cache only. |
**Returns:** `object`
**Notes:** `MainThreadRequired = false`. Synchronous — blocks until the registry query completes (up to a 25s timeout). Each result is flagged with whether it is currently installed.
### `package_add`
Add a UPM package by name@version, git URL, or 'file:' local path. Async by default (returns in_progress; poll package_status); pass wait=true to block until added. A recompile/domain reload follows — poll recompile_status. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `identifier` | yes | `–` | Package to add: 'com.unity.foo@1.2.3', a git URL, or 'file:../Path'. |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
| `wait` | no | `false` | Block until the operation completes and return the result (synchronous). Default: return immediately and poll package_status. |
**Returns:** `object`
**Notes:** `MainThreadRequired = false`. Mutating — requires `confirm=true`; `dry_run=true` returns a preview without applying. Async by default (returns `in_progress`; poll `package_status`); with `wait=true` it blocks until completion (up to a 25s timeout). A successful add triggers a recompile/domain reload — poll `recompile_status`. Returns `busy` if another package operation is already in progress.
### `package_remove`
Remove a UPM package by name. Async by default (returns in_progress; poll package_status); pass wait=true to block until removed. A recompile/domain reload follows — poll recompile_status. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `name` | yes | `–` | Package name to remove (e.g. com.unity.foo). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
| `wait` | no | `false` | Block until the operation completes and return the result (synchronous). Default: return immediately and poll package_status. |
**Returns:** `object`
**Notes:** `MainThreadRequired = false`. Mutating — requires `confirm=true`; `dry_run=true` returns a preview without applying. Async by default (returns `in_progress`; poll `package_status`); with `wait=true` it blocks until completion (up to a 25s timeout). A successful remove triggers a recompile/domain reload — poll `recompile_status`; the reload can drop a synchronous reply, so `package_status` confirms the outcome. Returns `busy` if another package operation is already in progress.
### `package_resolve`
Resolve/refresh packages from the manifest (re-fetch and re-link). May trigger a recompile/domain reload — poll recompile_status. Its outcome is recorded for package_status.
No parameters.
**Returns:** `object`
**Notes:** `MainThreadRequired = true`. Fire-and-forget — `Client.Resolve()` schedules a resolve on a later tick, so the response returns before any reload. May trigger a recompile/domain reload — poll `recompile_status`. Its outcome is recorded in the status file, so `package_status` validates it.
### `package_status`
Status of the last async package operation (add/remove/resolve): idle | in_progress | completed | failed, with the added package, manifest, and any error.
No parameters.
**Returns:** `string`
**Notes:** `MainThreadRequired = false`. Reads a Temp status file that survives the domain reload an add/remove/resolve triggers, so a lost synchronous reply or an interrupted async op is recoverable by polling. Returns `{"status":"idle"}` when no operation has run.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,84 @@
# Prefab commands
Commands covering the prefab lifecycle an agent needs to build content procedurally: saving a configured GameObject as a prefab asset, instantiating it into a scene, deriving a variant, applying/reverting instance overrides, unpacking, and editing prefab contents through the prefab stage. Asset paths are sandboxed to the authoring root; scene-side mutations are Undo-able, but prefab asset writes are AssetDatabase operations and are not part of Unity's Undo system.
### `create_prefab`
Save a GameObject as a prefab asset at a project path; the source becomes a connected instance.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `source` | yes | – | Reference to the source GameObject to save as a prefab (globalId/path/guid/instanceId/hierarchyPath). |
| `path` | yes | – | Prefab asset path relative to the authoring root (the Assets/ prefix is optional and the .prefab extension is added if missing). e.g. Prefabs/Enemy or Prefabs/Enemy.prefab |
**Returns:** `AuthoringResult`
**Notes:** Scene-side effects are Undo-able; the prefab asset write is not undoable.
### `instantiate_prefab`
Instantiate a prefab asset into a loaded scene and return the created instance.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `prefab` | yes | – | Reference to the prefab asset to instantiate (path/guid/globalId). |
| `scene_path` | no | `–` | Optional path of a loaded scene to instantiate into; defaults to the active scene. |
| `name` | no | `–` | Optional name for the created instance; defaults to the prefab name. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able.
### `create_prefab_variant`
Create a prefab variant asset that inherits from a base prefab.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `base` | yes | – | Reference to the base prefab asset (path/guid/globalId). |
| `path` | yes | – | Variant prefab asset path relative to the authoring root (.prefab added if missing). |
**Returns:** `AuthoringResult`
**Notes:** Prefab asset write is not undoable.
### `apply_prefab_overrides`
Apply a prefab instance's overrides back to its source prefab asset.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `instance` | yes | – | Reference to a prefab instance GameObject in a scene (instanceId/hierarchyPath/globalId). |
**Returns:** `AuthoringResult`
**Notes:** Scene-side effects are Undo-able; the asset write is not. The instance's source prefab asset must live under the authoring root.
### `revert_prefab_overrides`
Revert a prefab instance's overrides so it matches its source prefab asset.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `instance` | yes | – | Reference to a prefab instance GameObject in a scene (instanceId/hierarchyPath/globalId). |
**Returns:** `AuthoringResult`
**Notes:** Undo-able.
### `unpack_prefab`
Unpack a prefab instance into plain GameObjects (outermost level or completely).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `instance` | yes | – | Reference to a prefab instance GameObject in a scene (instanceId/hierarchyPath/globalId). |
| `completely` | no | `false` | If true, unpack all nested prefab levels (Completely); if false, only the outermost level (OutermostRoot). |
**Returns:** `AuthoringResult`
**Notes:** Undo-able.
### `save_prefab_contents`
Open a prefab asset in an isolated prefab stage, apply a declarative edit, and save it back (nested-prefab safe).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `prefab` | yes | – | Reference to the prefab asset to edit (path/guid/globalId). |
| `rename_child` | no | `–` | Optional child name (relative path under the root, e.g. 'Body/Head') to rename. |
| `new_name` | no | `–` | New name for the child identified by rename_child. |
| `set_active_child` | no | `–` | Optional child name (relative path under the root) whose active state to set. |
| `active` | no | `true` | Active state to apply when set_active_child is provided. |
**Returns:** `AuthoringResult`
**Notes:** Prefab asset write (prefab-stage save) is not part of Unity's Undo system. The prefab asset must live under the authoring root.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,270 @@
# Project settings commands
Read and change a representative slice of Unity's project-wide settings (Audio, Graphics, Input, Physics, Player, Quality, Runtime Pipeline, Tags & Layers, Time). `get_*` commands read a group's current values into a `ProjectSettingsResponse`; `set_*` commands mutate them through the shared `confirm`/`dry_run` convention, so `confirm=true` is required to apply and `dry_run=true` previews the change without applying it. Every command in this group is `MainThreadRequired = true`. On a `set_*`, omitted fields are left unchanged, and the group is re-read after the write so the response reflects the resulting state. Settings writes are not undoable via Ctrl+Z.
### `get_audio_settings`
Read project Audio settings (volume, rolloff scale, doppler factor).
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_audio_settings`
Change project Audio settings. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Fields to change; omitted fields are left unchanged (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `volume` | no | `–` | Global audio volume (0..1). |
| `rolloffScale` | no | `–` | Global rolloff scale. |
| `dopplerFactor` | no | `–` | Global doppler factor. |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`.
### `get_graphics_settings`
Read GraphicsSettings (default render pipeline).
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_graphics_settings`
Set the default render pipeline asset. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Fields to change; omitted fields are left unchanged (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `renderPipelineAsset` | no | `–` | Reference (path / guid / globalId) to a RenderPipelineAsset to set as the default. Pass an empty reference (`{}`) to select the built-in pipeline; omit to leave unchanged. |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`.
### `get_input_settings`
Read the legacy Input Manager axes (names and count).
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_input_settings`
Tune a legacy Input Manager axis (sensitivity/gravity/dead) by name. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Axis change. 'axis' selects the axis by name; omitted numeric fields are left unchanged (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `axis` | yes | – | Name of the axis to modify (e.g. 'Horizontal'). |
| `sensitivity` | no | `–` | New sensitivity. |
| `gravity` | no | `–` | New gravity. |
| `dead` | no | `–` | New dead-zone size. |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`.
### `get_physics_settings`
Read Physics settings (gravity, solver iterations, bounce threshold).
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_physics_settings`
Change Physics settings. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Fields to change; omitted fields are left unchanged (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `gravityX` | no | `–` | Gravity X component. |
| `gravityY` | no | `–` | Gravity Y component (e.g. -9.81). |
| `gravityZ` | no | `–` | Gravity Z component. |
| `defaultSolverIterations` | no | `–` | Default solver iteration count. |
| `bounceThreshold` | no | `–` | Bounce threshold velocity. |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`.
### `get_player_settings`
Read PlayerSettings (company/product/version, scripting backend, API level).
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_player_settings`
Change PlayerSettings. Requires confirm=true; use dry_run to preview. Scripting backend / API level changes trigger a domain reload.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Fields to change; omitted fields are left unchanged (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `companyName` | no | `–` | Company name. |
| `productName` | no | `–` | Product name. |
| `bundleVersion` | no | `–` | Bundle/application version string. |
| `scriptingBackend` | no | `–` | Scripting backend (e.g. Mono2x, IL2CPP). Triggers a domain reload. |
| `apiCompatibilityLevel` | no | `–` | API compatibility level (e.g. NET_Standard_2_0, NET_Unity_4_8). Triggers a domain reload. |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`. Changing `scriptingBackend` or `apiCompatibilityLevel` triggers a domain reload (`requiresDomainReload`); scripting-backend / API-level changes are read and written against the active build target.
### `get_quality_settings`
Read QualitySettings (current level, level names, vSync, anti-aliasing).
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_quality_settings`
Change QualitySettings. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Fields to change; omitted fields are left unchanged (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `level` | no | `–` | Quality level index (see levelNames from get_quality_settings). |
| `vSyncCount` | no | `–` | VSync count (0 = off, 1, 2). |
| `antiAliasing` | no | `–` | MSAA sample count (0, 2, 4, 8). |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`.
### `get_runtime_pipeline_settings`
Read Pipeline Runtime settings (enableInBuilds, port, requestTimeoutMs, enableAuditLogging, autoStart, maxWorkItemsPerFrame). Reports the settings in effect, falling back to built-in defaults when none have been authored; never creates the settings file.
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_runtime_pipeline_settings`
Change Pipeline Runtime settings. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Fields to change; omitted fields are left unchanged (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `enableInBuilds` | no | `–` | Enable the Pipeline HTTP server in Player builds. SECURITY WARNING: only enable in development/QA builds. |
| `port` | no | `–` | HTTP port for the Pipeline server. 0 = auto-assign from range 7900-7999. |
| `requestTimeoutMs` | no | `–` | Request timeout in milliseconds. |
| `enableAuditLogging` | no | `–` | Log every remote request for security auditing. |
| `autoStart` | no | `–` | Start the server automatically when the Player boots (or Play Mode is entered). |
| `maxWorkItemsPerFrame` | no | `–` | Maximum work items the dispatcher processes per frame. |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`. Refused while in Play Mode — the running driver already loaded its own settings, so a change would silently not apply until the next Play session or build. `port` (0-65535), `requestTimeoutMs` (1000-60000), and `maxWorkItemsPerFrame` (1-50) are rejected outright with a `Fail` response if outside these bounds — the same ones the Project Settings page enforces — rather than being clamped or silently applied.
### `get_tags_layers`
Read the project's tags and (named) layers.
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_tags_layers`
Add/remove tags and assign user layer names (index 8-31). Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Tag/layer changes to make (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `addTags` | no | `–` | Tag names to add. |
| `removeTags` | no | `–` | Tag names to remove. |
| `setLayers` | no | `–` | User layer assignments (index 8-31); array of layer-assignment objects (fields below). |
`setLayers[]` (layer-assignment) fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `index` | yes | – | Layer index (8-31 for user layers). |
| `name` | yes | – | Layer name (empty string clears the slot). |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`. User layers are indices 8-31 (0-7 are reserved).
### `get_time_settings`
Read Time settings (fixedDeltaTime, maximumDeltaTime, timeScale).
No parameters.
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`.
### `set_time_settings`
Change Time settings. Requires confirm=true; use dry_run to preview.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `settings` | no | `–` | Fields to change; omitted fields are left unchanged (fields below). |
| `confirm` | no | `false` | Apply the change. Without it the call is refused. |
| `dry_run` | no | `false` | Preview the change without applying it. |
`settings` fields:
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `fixedDeltaTime` | no | `–` | Fixed timestep in seconds (e.g. 0.02). |
| `maximumDeltaTime` | no | `–` | Maximum allowed timestep in seconds. |
| `timeScale` | no | `–` | Time scale (1 = real-time). |
**Returns:** `ProjectSettingsResponse`
**Notes:** `MainThreadRequired = true`. `confirm=true` required to apply; supports `dry_run`.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,213 @@
# Runtime commands
Commands served by the Player / dev-build server. Many require a Development build with the runtime Pipeline enabled — see [Runtime setup](../runtime-setup.md). Commands marked `RuntimeOnly` are only registered in a player build.
### `runtime_status`
Get comprehensive runtime application status.
No parameters.
**Returns:** `RuntimeStatusResponse`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`.
### `quit`
Gracefully quit the Unity application.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `exitCode` | no | `0` | Exit code for the application |
**Returns:** `string`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`.
### `set_target_framerate`
Set the target frame rate for the application.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `frameRate` | yes | `–` | Target frame rate (-1 for platform default, 0 for unlimited) |
**Returns:** `string`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`.
### `set_timescale`
Set the time scale for the application.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `scale` | yes | `–` | Time scale multiplier (0.0 to pause, 1.0 for normal speed) |
**Returns:** `string`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`.
### `simulate_key`
Simulate a keyboard key event (Input System). Drives the running app.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `key` | yes | `–` | Input System Key name, e.g. Space, W, Enter, LeftArrow. |
| `action` | no | `press` | down \| up \| press (down+up). |
**Returns:** `InputSimulationResponse`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`. Requires the Input System package (`ENABLE_INPUT_SYSTEM`); reports unavailable otherwise.
### `simulate_pointer`
Simulate a mouse/pointer event at screen coordinates (Input System).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `x` | yes | `–` | Screen X in pixels (origin bottom-left). |
| `y` | yes | `–` | Screen Y in pixels (origin bottom-left). |
| `action` | no | `click` | move \| down \| up \| click (down+up). |
| `button` | no | `left` | left \| right \| middle. |
**Returns:** `InputSimulationResponse`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`. Requires the Input System package (`ENABLE_INPUT_SYSTEM`); reports unavailable otherwise.
### `log`
Write a message to Unity console.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `message` | yes | `–` | Message to log to console |
| `level` | no | `info` | Log level: info, warning, error |
**Returns:** `string`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`.
### `console`
Get captured Unity console output (Editor or Player; supports tail, level filtering, and follow via a cursor).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `tail` | no | `100` | Maximum number of most-recent entries to return |
| `level` | no | `log` | Minimum severity to include: log | warn | error |
| `since` | no | `-1` | Cursor: only return entries newer than this seq. Use the 'cursor' from a previous response to follow. |
| `since_session` | no | `–` | Session the `since` cursor came from: pass the `session` from a previous response. A cursor from another session is refused and the tail returned with `reset=true`. |
**Returns:** `ConsoleLogResponse`
**Notes:** `MainThreadRequired = false`. Available in both the Editor and player builds.
A cursor is only meaningful paired with the `session` that issued it. Sequence numbers restart from
zero when the Editor restarts (the buffer is persisted under `Temp/`, which Unity deletes at every
launch), so a cursor from an earlier session names different entries. Always echo `session` back as
`since_session`; when the pair cannot be honored the response sets `reset=true`, returns the tail
instead of nothing, and gives you a fresh `cursor`/`session` to adopt.
Beyond the entries, the response carries:
- `counts` — errors/warnings/logs the buffer currently retains, before the level filter and `tail`.
- `groundTruth` — what the Editor itself reports: `compilationFailed`, `compiling`, the Editor
console's own `consoleErrors`/`consoleWarnings`/`consoleLogs`, whether the buffer has been
`seeded` from the console's store, and `ageMs` for how stale the sample is. Null in a player.
`groundTruth.consoleErrors` exceeding `counts.error` means the Editor console is showing entries the
buffer does not hold. That is expected for output produced before the package was first imported;
otherwise it is the signal that something was missed.
### `console_status`
Console ground truth and buffer counters without pulling entries: compile-failure flag, Editor
console counts, and the buffer's retained counts and cursor.
No parameters.
**Returns:** `ConsoleLogResponse` with an empty `entries` array.
**Notes:** `MainThreadRequired = false`. Cheap enough to poll in a loop while a compile runs; use
`console` when you want the entries themselves.
### `clear_console`
Clear the captured log buffer and the Unity Editor console.
No parameters.
**Returns:** `object`
**Notes:** Available in both the Editor and player builds; in a player only the captured buffer is
cleared, since there is no Editor console. Editor compile errors are logged as sticky entries, which
Unity's own clear skips, so they remain in the Console window until the next compile completes.
Clearing keeps the cursor, so a client following `console` sees `dropped=true` on its next poll
rather than silently skipping the window that was cleared.
### `eval`
Evaluate C# code dynamically using Roslyn compiler.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `code` | yes | `–` | C# code to evaluate |
| `timeout` | no | `5000` | Timeout in milliseconds |
**Returns:** `EvalResponse`
**Notes:** `MainThreadRequired = true`. Available on both editor and runtime.
### `eval_file`
Evaluate C# code read from a `.cs` file on disk. A convenience alternative to `eval` for code
too long to pass inline — edit the file, then evaluate it. The resolved source is run through the
same evaluation path as `eval`.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `file` | yes | `–` | Path to a `.cs` file to evaluate |
| `timeout` | no | `5000` | Timeout in milliseconds |
The `file` is read on the Unity side (relative paths resolve against the Unity process working
directory) and must end in `.cs`. A missing file, a non-`.cs` extension, or an empty file returns
a `Bad Request`.
**Returns:** `EvalResponse`
**Notes:** `MainThreadRequired = true`. Available on both editor and runtime.
> For anything beyond an ad-hoc one-liner — bulk construction, builder scripts — prefer the
> Editor-side [`run_script`](scripts.md#run_script) command, which compiles a versioned project
> `.cs` file in memory and runs a named entry point instead of carrying code through the protocol.
### `reload_file`
Compile and apply [CodeReload] edits from a source file.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `filename` | yes | `–` | Source file containing [CodeReload] methods (e.g. Assets/Scripts/Player.cs) |
| `timeout` | no | `30000` | Compilation timeout in milliseconds |
| `assemblyDir` | no | `–` | Directory to save compiled assemblies to disk (optional, default is in-memory only) |
| `pdb` | no | `false` | Emit debug symbols (portable PDB) mapped to the original source so breakpoints bind in your editor. Compiles unoptimized. |
**Returns:** `CodeReloadResponse`
**Notes:** `MainThreadRequired = true`. See [Reloading code](../code-reload.md).
### `reload_file_editor_interpreter`
Compile [CodeReload] edits and run them through the IlInterpreter VM in this process instead of `Assembly.Load` (IL2CPP-safe; only a minimal host API + the target type are available).
Same parameters as [`reload_file`](#reload_file).
**Returns:** `CodeReloadResponse`
**Notes:** `MainThreadRequired = true`. See [Reloading code](../code-reload.md).
### `reload_file_player_interpreter`
Compile a file's (or folder's) [CodeReload] methods in the Editor and push the IL to connected development players over PlayerConnection (IL2CPP-safe). Folders are walked recursively; files that do not mention `CodeReload` are skipped.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `filename` | yes | `–` | Source `.cs` file or a folder containing [CodeReload] methods (e.g. Assets/pong.cs or Assets/Scripts) |
| `player` | no | `-1` | Target connected player id; `-1` broadcasts to all connected players |
**Returns:** `{ success, message, diagnostics }` for a file; `{ success, message, pushed, failed }` for a folder.
**Notes:** `MainThreadRequired = true`. Served by the **Editor** server (it needs Roslyn and PlayerConnection), not the player; the player only needs the runtime Pipeline driver, not its HTTP server. See [Reloading code](../code-reload.md#reload_file_player_interpreter).
### `codereload_status`
Show current code reload registry status and statistics.
No parameters.
**Returns:** `CodeReloadResponse`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`.
### `cleanup_codereload`
Remove old code reload DLL versions and clear registry.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `assemblyDir` | yes | `–` | Directory containing assemblies to cleanup |
| `force_domain_reload` | no | `true` | Force Unity domain reload after cleanup |
**Returns:** `CodeReloadResponse`
**Notes:** `MainThreadRequired = true`, `RuntimeOnly`.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,93 @@
# Scene commands
Commands for creating, opening, saving and inspecting scenes, controlling the active scene, snapshotting a scene's hierarchy, and managing the Build Settings scene list. Mutating scene operations are blocked while the editor is entering or in play mode (they fail with a recoverable error before touching any state); read-only commands are safe in play mode.
### `create_scene`
Create a new scene and save it to the given path under the authoring root.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Scene path relative to the authoring root (default Assets/); the Assets/ prefix and the .unity extension are optional. e.g. Scenes/Level1 |
| `additive` | no | `false` | Open the new scene additively alongside currently open scenes instead of replacing them. |
| `template` | no | `empty` | Initial contents: 'empty' (default) for a blank scene, or 'default' to seed a Main Camera + Directional Light matching Unity's built-in 3D template. |
**Returns:** `AuthoringResult`
**Notes:** Blocked during Play mode.
### `open_scene`
Open an existing scene from the given path.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Scene path relative to the authoring root (default Assets/); the Assets/ prefix and the .unity extension are optional. |
| `additive` | no | `false` | Open additively alongside currently open scenes instead of replacing them. |
**Returns:** `AuthoringResult`
**Notes:** Blocked during Play mode.
### `save_scene`
Save an open scene. Saves the active scene when no path is given.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | no | `–` | Path of the open scene to save (authoring-root relative; Assets/ prefix and .unity optional). Omit to save the active scene. |
**Returns:** `AuthoringResult`
**Notes:** Blocked during Play mode.
### `save_all`
Save all open scenes that have unsaved changes.
No parameters.
**Returns:** `object`
**Notes:** Blocked during Play mode.
### `list_open_scenes`
List all currently open scenes with their load/active/dirty state.
No parameters.
**Returns:** `object`
### `set_active_scene`
Set which open scene is the active scene (new objects are created in the active scene).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Path of an already-open scene to make active (authoring-root relative; Assets/ prefix and .unity optional). |
**Returns:** `AuthoringResult`
**Notes:** Blocked during Play mode.
### `get_scene_hierarchy`
Return the GameObject tree of an open scene (or the active scene). Each node carries instanceId + hierarchyPath usable by GameObject commands.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | no | `–` | Path of the open scene to snapshot (authoring-root relative; Assets/ prefix and .unity optional). Omit for the active scene. |
**Returns:** `SceneHierarchy`
### `add_scene_to_build`
Add a scene to the Build Settings scene list (idempotent). Optionally enable it.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Scene path to add (authoring-root relative; Assets/ prefix and .unity optional). |
| `enabled` | no | `true` | Whether the scene is enabled in the build list. |
**Returns:** `object`
**Notes:** Blocked during Play mode.
### `remove_scene_from_build`
Remove a scene from the Build Settings scene list (idempotent).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `path` | yes | – | Scene path to remove (authoring-root relative; Assets/ prefix and .unity optional). |
**Returns:** `object`
**Notes:** Blocked during Play mode.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,111 @@
# Script commands
Commands for creating C# script files, attaching MonoBehaviours to GameObjects, reading/writing serialized fields, and running compiled project entry points in memory. Writing a `.cs` file does not make its type available — Unity must import and compile it (a domain reload) first. The flow is: `create_script` → `recompile` → poll `recompile_status` (until completed/up_to_date) → `attach_script`. For bulk construction that does **not** need a new persistent project type, skip the recompile entirely: [`run_script`](#run_script) compiles a file in memory and runs it with no asset import and no domain reload.
### `create_script`
Create a new C# script (default base class MonoBehaviour) from a template under the authoring root. NOTE: the type does not exist until a recompile completes — to attach it, call recompile, poll recompile_status, then attach_script.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `name` | yes | – | Class/file name without extension, e.g. PlayerController. Must be a valid C# identifier. |
| `path` | no | `–` | Folder (relative to the authoring root; the Assets/ prefix is optional) to write the .cs into. Defaults to the authoring root. |
| `namespace` | no | `–` | Optional namespace to wrap the class in. Omit for the global namespace. |
| `base_class` | no | `MonoBehaviour` | Base class to derive from. Defaults to MonoBehaviour. |
| `overwrite` | no | `false` | Overwrite the file if it already exists. Defaults to false (an existing file is an error). |
**Returns:** `AuthoringResult`
**Notes:** Does not trigger a recompile; the created type is not usable until a recompile completes (poll `recompile_status`).
### `attach_script`
Add a MonoBehaviour to a GameObject by its (compiled) type name OR by its script asset path. Provide exactly one of 'type' or 'script'. If the type isn't compiled yet, returns a recoverable error: recompile, poll recompile_status, then retry.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Reference to the GameObject to add the component to (globalId/path/guid/instanceId/hierarchyPath). |
| `type` | no | `–` | Component type name to add, e.g. PlayerController or Game.Player.PlayerController. Must already be compiled. Mutually exclusive with 'script'. |
| `script` | no | `–` | Script asset path, e.g. 'Assets/Pool/Scripts/CueShooter.cs'. The backing class is resolved via MonoScript.GetClass(), so the class name may differ from the filename. Mutually exclusive with 'type'. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able. Provide exactly one of `type` or `script`. A not-yet-compiled type returns a recoverable error (recompile, poll `recompile_status`, then retry).
### `set_serialized_field`
Set a serialized field on a component/asset. Supports primitives, enums, Vector/Color/Rect/Bounds, object references (value = an ObjectRef: asset by guid/fileId/path or scene object by instanceId/hierarchyPath), and array elements via 'name.Array.data[i]' (or 'name.Array.size' to resize).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Reference to the component or asset to modify (globalId/path/guid/instanceId/hierarchyPath). May be a GameObject when 'component' is given. |
| `field` | yes | – | SerializedProperty path, e.g. 'speed', 'settings.speed', or 'waypoints.Array.data[0]'. |
| `value` | yes | – | JSON value to assign. For object references pass an ObjectRef object (or null to clear). For enums pass the value name. |
| `component` | no | `–` | Component type name on the target GameObject (e.g. 'Rigidbody'). Use when 'target' is a GameObject; omit when 'target' is already a component handle. |
**Returns:** `AuthoringResult`
**Notes:** Undo-able.
### `get_serialized_fields`
Read serialized fields of a component/asset. Returns each top-level field's name, type and value (object references are returned as re-usable handles). Pass 'field' to read a single SerializedProperty path.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `target` | yes | – | Reference to the component or asset to read (globalId/path/guid/instanceId/hierarchyPath). May be a GameObject when 'component' is given. |
| `field` | no | `–` | Optional single SerializedProperty path to read (e.g. 'speed' or 'items.Array.data[0]'). Omit to read all top-level fields. |
| `component` | no | `–` | Component type name on the target GameObject (e.g. 'Rigidbody'). Use when 'target' is a GameObject; omit when 'target' is already a component handle. |
**Returns:** `object`
### `run_script`
Compile a single project `.cs` file in memory (no domain reload, no code carried through the
protocol) and execute a named `static` entry point. This is the **builder-pattern** path for bulk
construction — see the note below. Editor command; still arbitrary code execution, so it is grouped
under the same `scripts/eval` tag as `eval` and is disabled wherever the eval family is disabled.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `file` | yes | `–` | Path to a `.cs` file. Relative paths resolve against the **project root** (the parent of `Assets/`), not the process working directory. May live **outside `Assets/`** (e.g. `AgentScripts/`) so writing it never triggers an asset import or domain reload. |
| `entry` | no | `–` | Entry point as `Namespace.Type.Method` (or `Type.Method`, or a bare `Method`). Nested types may be written with dots (`Outer.Inner.Method`). Default: the single public static method if unambiguous, else a method named `Main`. Static, non-generic methods only in v1. Ephemeral mode only — rejected with `mode=hotpatch`. |
| `args` | no | `–` | JSON array of arguments, coerced to the entry's parameter types: primitives, enum names, `string[]`, and a string/object handle (`ObjectRef`) for `UnityEngine.Object` parameters. JSON `null` is only valid for reference-type and nullable parameters (never a silent `default(T)`). Ephemeral mode only — rejected with `mode=hotpatch`. |
| `mode` | no | `ephemeral` | `ephemeral`: compile to an in-memory assembly, run the entry, discard. `hotpatch`: apply in-place `[CodeReload]` method replacements to already-loaded types (delegates to `reload_file`). |
| `references` | no | `–` | Extra assembly name prefixes to reference. In the Editor all loaded assemblies are already referenced, so this is effectively a no-op there. Ephemeral mode only; **not applied** by hotpatch (the compile is `reload_file`'s). |
| `defines` | no | `–` | Extra scripting define symbols, **appended to the project's active editor defines** (`EditorUserBuildSettings.activeScriptCompilationDefines`) — so `UNITY_EDITOR`, version and platform symbols are already set and sources behave like project code; your defines extend that set rather than replace it. Ephemeral mode only; **not applied** by hotpatch. |
| `pdb` | no | `false` | Emit a portable PDB mapped to the source file so breakpoints bind. Ephemeral runs always emit source-mapped symbols so exception stack traces carry `file:line`. Compiles unoptimized. |
| `timeout_ms` | no | `30000` | Timeout in milliseconds (governs the main-thread wait budget end-to-end, like `eval`). |
| `dry_run` | no | `false` | Compile only: return diagnostics; the emitted assembly is **not loaded** into the domain and nothing executes (the response carries no `assemblyName`). Rejected with `mode=hotpatch` — a hotpatch "dry run" would still apply live method replacements. |
**Returns:** `RunScriptResponse` — `{ result, diagnostics, compileMs, executeMs, assemblyName }` on top
of the standard envelope. The result carries no echo of the source (path + diagnostics only). Compile
errors return full Roslyn diagnostics (with line/column) and execute nothing; runtime exceptions
surface as structured errors with `file:line` mapped to the source file.
**Notes:** `MainThreadRequired = true`. Editor command.
- Executing runs always compile **unoptimized (Debug codegen)** so the emitted symbols keep full
sequence points for `file:line` mapping — expect the generated code to run somewhat slower than
regular project assemblies.
- **Async entry points** (`async Task` / `Task<T>`) are awaited to completion; `Task<T>.Result`
becomes the command result and exceptions in the async work surface as runtime errors. The await
is **asynchronous** — the Editor main thread keeps pumping, so awaits that resume on Unity's
main-thread context (`Task.Yield()`, `Task.Delay()`, Awaitable, …) work naturally; no
`ConfigureAwait(false)` contortions are required. The wait is bounded by what remains of
`timeout_ms`; on expiry the command returns a `Timeout` error and the task keeps running
detached (its effects may still land), like an abandoned `eval`.
- **hotpatch specifics:** `entry`, `args` and `dry_run` are rejected (Bad Request); `references` and
`defines` are not applied. Path resolution follows `reload_file`, which probes under `Assets/`
for bare filenames (unlike ephemeral's project-root resolution). Timing: `executeMs` carries the
whole reload duration (compile + apply, not split by `reload_file`); `compileMs` is `0`. Advisory
reload diagnostics are surfaced as severity-`warning` entries on success.
- Under Unity's Mono runtime the in-memory assembly emitted by an executing `ephemeral` run is
**not** collectible/unloadable, so each run retains one small assembly (the same behavior as
`eval`); prefer a single builder entry over many tiny runs. `dry_run` loads nothing.
#### The builder pattern (bulk construction)
For bulk construction — creating many objects, wiring private fields, generating content — put the
logic in a **versioned project script** and invoke it with `run_script`. Write the file with
`write_text_file` (ideally under a non-`Assets/` folder such as `AgentScripts/` so the write triggers
no asset import), then call `run_script --file AgentScripts/Build.cs --entry Build.All`. Iterating the
script and re-running it costs an in-memory compile (< ~2s), **not** a 15–20s domain reload.
The anti-patterns this replaces: carrying construction code as escaped C# strings through `eval`
(verbose, unreviewable, shell-escaping hazards), and editing a builder under `Assets/` in a loop
(each edit pays a full domain reload). Hold the line: code goes in files on disk via `run_script`;
`eval` stays for genuinely ad-hoc one-liners.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).
@@ -0,0 +1,169 @@
# Wait commands
Server-side condition waiting (AUTHAPI-25). `wait_for` waits until a runtime/editor member condition holds — evaluated on the Editor's per-frame loop — and can act in the **same frame** the condition first becomes true (e.g. screenshot the moment a match-end screen appears). It replaces the dominant agent anti-pattern of client-side polling loops: read a value via `eval`, sleep in the client, repeat. That loop is token-expensive and slow, and its client round-trips can miss short-lived states entirely (a 2-second screen is easy to miss at ~1–2 s per poll). A server-side condition is a cheap per-frame delegate that can act atomically the frame it fires.
## Sync vs async: pick the right mode first
`/api/exec` executes **one command at a time** — a synchronous `wait_for` holds that queue for its entire duration. The Editor itself never freezes (each poll is marshaled onto the main thread between sleeps), but **every other command queues behind the wait until it resolves**. That includes any command whose effect your condition is waiting for: if the condition needs a mutation you haven't issued yet, the mutation can never run, and the wait self-deadlocks until its timeout.
Use a **synchronous** wait only when BOTH hold:
- the wait is short — comfortably under your client's HTTP timeout (typically **< 30 s**), and
- the condition does **not** depend on any command you still need to issue (it will be satisfied by something already in motion: play-mode gameplay, a running bake, a timer, ...).
For **everything else**, pass `async=true`: you get a `waitId` back immediately, other commands keep executing, and you poll `wait_status` (and cancel with `wait_cancel`). This is the default choice for long waits and for any "issue command, then wait for its effect" flow.
A synchronous wait submitted as a detached job (`POST /api/exec` with `"job": true`) remains cancelable: `POST /api/job/cancel` interrupts the sleep loop and resolves the wait as `canceled`.
> Member resolution, coercion, and the read-only security policy are intended to be shared with the invoke/query surface (AUTHAPI-16). Until that shared resolver lands, `wait_for` ships a self-contained resolver with the same rules that the two will converge on. Type and member-chain resolution is cached per wait — only the live target instance (an `ObjectRef` target or a `findType` instance) is re-resolved each poll.
### `wait_for`
Wait until a member condition holds, then optionally act in the same frame.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `condition` | yes | `–` | The condition object (see below). |
| `timeout_s` | no | `30` | Maximum seconds to wait (clamped to 0–600; NaN/Infinity are rejected). |
| `poll_interval_ms` | no | `100` | How often to re-check (clamped to 16–5000 ms; NaN/Infinity are rejected). The sleep never exceeds the remaining budget, so a large interval cannot stretch a short timeout. |
| `on_met` | no | `–` | Follow-up run in the same frame the condition first holds: `{ capture: { view, source, save_path, width, height, include_image }, pause }`. `capture.source` (game view only): `camera` (default) renders a camera and **misses Screen Space - Overlay UI**; `screen` captures the composited game view including overlay canvases (HUDs), Play Mode only. |
| `return_history` | no | `false` | Include the observed `{ value, elapsedMs }` samples. A ring buffer keeps the **last 200** samples, so the tail near the transition is retained (the interesting part), not the first 200 identical observations. |
| `tolerate_missing` | no | `false` | Treat resolution failures (type/member/instance not found) as condition-not-met and retry until timeout, instead of failing fast. Enables "wait until X exists" — e.g. waiting for a spawned object via `findType`. On timeout the last resolution error is included. |
| `async` | no | `false` | Return a `waitId` immediately and evaluate in the background; poll `wait_status`, cancel with `wait_cancel`. **Use for long waits and whenever the condition depends on a command you still need to issue.** |
**The `condition` object**
| Field | Required | Description |
|-------|----------|-------------|
| `member` | yes | Dotted member path. With no `target`/`findType` it is a fully-qualified **static** path (e.g. `UnityEditor.EditorApplication.isPlaying`); with `target`/`findType` it walks **instance** members (e.g. `State`, `Health.CurrentHealth`). Read-only fields/properties only — write-only members and methods are rejected. |
| `target` | no | Object handle (`instanceId` / `hierarchyPath` / `guid` / `path` / `globalId`) to read instance members from. Re-resolved every poll, so the referenced object may appear mid-wait (combine with `tolerate_missing`). |
| `findType` | no | Fully-qualified `UnityEngine.Object` type name; the member walks from a **live** instance. Persistent objects (prefab/asset files on disk) and hidden editor internals (`hideFlags != None`) are excluded, so the wait never silently binds to a prefab asset instead of the scene instance. Scene instances are preferred; if several candidates remain, the lowest instance id is chosen deterministically and a `note` on the result reports the ambiguity — pass `target` to select one explicitly. |
| `op` | no | `equals` (default) \| `notEquals` \| `greaterThan` \| `lessThan` \| `contains` (strings) \| `changed` (met on the first change from the initially observed value; needs no `value`). |
| `value` | conditional | Comparison operand, coerced to the member's type (primitive, enum name, or string). Required for every op except `changed`. When either side is a `float`, `equals`/`notEquals` compare at **float precision** (widening a float to double manufactures noise digits — `0.9f` would otherwise never equal `0.9`). |
**`op: changed` is for value-like members** — primitives, enums, and strings. It compares the member's current value against the first observed value, so content mutations *inside* a reference-typed member (a list gaining an element, a field on a referenced object changing) are **not** detected: the reference itself must change. Likewise, `value`/`initialValue`/`history` snapshots of mutable reference types reflect the object's state at serialization time, not at observation time.
**Returns:** `WaitResult`
```
{
"state": "met", // pending | met | timedOut | interrupted | canceled | failed | not_found
"met": true,
"timedOut": false,
"member": "MatchManager.Instance.State",
"op": "equals",
"value": "Ended", // most recently observed value
"initialValue": "Playing", // first observed value (baseline for op=changed)
"elapsedMs": 512, // frozen at the terminal transition
"framesObserved": 6,
"note": "…", // present only for non-fatal resolution notes (e.g. ambiguous findType)
"capture": { "savedPath": "Screenshots/victory.png" }, // present only with on_met.capture
"waitId": "…" // present only for async submissions / status snapshots
}
```
- A **timeout** returns `met:false, timedOut:true` with the last observed `value` — distinct from a resolution/evaluation error, which returns `state:"failed"` with an `error` message (unless `tolerate_missing` turns resolution errors into retries; the wait then times out with the last resolution error attached).
- If the main thread stalls past the per-poll marshal budget (~60 s: a huge import, a modal bake), the poll is counted as **missed** and the wait keeps going until its own budget expires — a stall does not turn the wait into a generic error.
- The sync result is well under 2 KB without `history`.
### `wait_status`
Get the status/result of an async wait (`wait_for` with `async=true`).
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `wait_id` | yes | `–` | The id returned by `wait_for` when `async=true`. |
**Returns:** `WaitResult` (`state:"not_found"` if the id is unknown — async waits do not survive a domain reload; completed waits are retained up to a bounded count and pruned oldest-first the next time a `wait_for` is submitted, so a session that never submits another wait keeps its last results queryable).
### `wait_cancel`
Cancel an async wait.
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `wait_id` | yes | `–` | The id to cancel. |
**Returns:** `WaitResult`. If the wait was still active, it resolves to `state:"canceled"`. If it had already reached a terminal state (`met`/`timedOut`/`failed`/`interrupted`) before the cancel request arrived, that actual state is returned unchanged — the cancel had no effect, and reporting a fake `canceled` here would misreport whether an `on_met` follow-up (capture/pause) actually ran.
## Migration: replace a client polling loop with one call
Before — an agent screenshots a match-end screen by polling with `eval` (dozens of round-trips, and the 2-second screen is easy to miss):
```
# ~60 client round-trips, each ~1–2s:
loop:
state = eval "MatchManager.Instance.State" # read
if state == "Ended": break
sleep 1 # client-side sleep
capture_game_view --source screen --save_path Screenshots/victory.png
```
After — one **async** wait that captures atomically in the frame the condition first holds, while other commands stay available (the match may take minutes, and you may still need to issue commands that drive it):
```
# 1. Arm the wait; returns { waitId } immediately:
wait_for --condition '{"member":"MatchManager.Instance.State","op":"equals","value":"Ended"}' \
--timeout_s 300 \
--on_met '{"capture":{"view":"game","source":"screen","save_path":"Screenshots/victory.png"}}' \
--async true
# 2. Keep working: start the match, drive gameplay, run other commands...
# 3. Collect the result (poll until state != "pending"):
wait_status --wait_id <waitId>
```
Only use the synchronous form when the wait is short and self-contained — e.g. play mode is already running and the state flips within a few seconds:
```
wait_for --condition '{"member":"MatchManager.Instance.State","op":"equals","value":"Ended"}' --timeout_s 10
```
## Waiting for something to exist (spawn waits)
By default the first failed resolution fails the wait (`state:"failed"`) — that catches typos fast. To wait for an object or member that does not exist *yet*, pass `tolerate_missing:true`:
```
wait_for --condition '{"findType":"MyGame.BossController","member":"IsActivated","op":"equals","value":true}' \
--tolerate_missing true --timeout_s 120 --async true
```
Resolution failures are then treated as condition-not-met and retried every poll until the object appears (or the wait times out, with the last resolution error attached).
## Interruption and lifecycle
- Concurrent waits are capped (8); a further submission is rejected with a structured error telling you to `wait_cancel` one or let one finish.
- A **domain reload** (`beforeAssemblyReload`) or **exiting play mode** resolves every active wait to `state:"interrupted"` rather than hanging or leaking. **Entering play mode does not interrupt**: with domain reload disabled ("Enter Play Mode Options"), a wait armed in edit mode survives into play mode — the natural way to arm a capture before pressing play. (With domain reload enabled, the reload interrupts it anyway.)
- `on_met.pause` pauses play mode on the frame the condition holds (no-op outside play mode).
- If the server is stopping while a synchronous wait is pending, the wait resolves as `interrupted` ("server stopping") instead of evaluating against a shutting-down editor.
## Result contract details
- `error` is set only when the wait itself ends abnormally (`failed`, `interrupted`, `canceled`,
or the surfaced last resolution error on a tolerated timeout). A failed `on_met` follow-up
(capture/pause) is reported separately as **`onMetError`** while `state` stays `met` — the
condition did hold; check `onMetError` before trusting `capture`.
- `condition.target` and `condition.findType` are **mutually exclusive** (rejected up front).
- Member paths resolve **public** readable fields/properties only.
- A **destroyed** Unity object mid-path fails structurally; as the **leaf** value it is observed
as `null` (Unity's own `==` semantics), so "wait until destroyed" is
`{ "op": "equals", "value": null }` — the `value` key must be present and explicitly `null`;
omitting it is rejected for every op except `changed`.
- `tolerate_missing` retries the evaluator's STRUCTURED failures: resolution failures
(type/member/instance not found, unless permanently malformed) and evaluation errors
("cannot compare yet" — e.g. an ordering op while the member is still null); the last error is
surfaced if the wait times out. A member getter that THROWS fails the wait immediately,
tolerated or not.
- A bare type name matching several loaded types binds deterministically (lowest full name) and
surfaces a `note` — use the fully-qualified name to disambiguate.
## Interaction with `batch`
`wait_for` is rejected inside a `batch` (`not_batchable`): the batch holds the main thread for its
whole turn, so the editor state a condition watches cannot change while the wait polls — it would
just burn its timeout. Run an async `wait_for` before or after the batch and poll `wait_status`
(both `wait_status` and `wait_cancel` are quick reads and stay batchable).
## Interaction with the startup settle gate
`wait_for` is a background command (`MainThreadRequired=false`), so it is **exempt from the settling busy-gate** (AUTHAPI-35): it is accepted even while the Editor is still importing/compiling after a cold start. A wait polling during that window observes a half-ready editor — types may not be loaded and objects may not exist yet. Combine with `tolerate_missing:true` for a deliberate wait-until-ready pattern, or gate on `/api/status` reporting `ready` first if you want fully settled reads.
See [Creating commands](../creating-commands.md) and [Connectivity](../connectivity.md).