C# Connector Internals

August 14, 2026 · View on GitHub

This document describes the Unity Editor-side C# codebase that receives CLI commands over HTTP and executes them.


Directory Structure

AgentConnector/
└── Editor/
    ├── HttpServer.cs                    # localhost HTTP listener
    ├── CommandRouter.cs                 # command dispatch + locking + batch
    ├── ToolDiscovery.cs                 # reflection-based tool scanning and schemas
    ├── Heartbeat.cs                     # instance state file writer
    ├── HeraAgentAssetConfigWindow.cs    # Settings window (Hera > Settings, Ultra Hera + asset config)
    ├── HeraAgentAssetConfigWindow.Model.cs
    ├── HeraAgentAssetConfigWindow.View.cs
    ├── Attributes/
    │   ├── HeraToolAttribute.cs          # [HeraTool], [ToolParameter]
    │   └── HeraActionAttribute.cs        # [HeraAction] action handler marker
    ├── Core/
    │   ├── Response.cs                  # SuccessResponse, ErrorResponse, ResponseTimings
    │   ├── ParamCoercion.cs             # bool coercion from JSON tokens
    │   ├── ToolParams.cs                # typed parameter access helpers + Result<T>
    │   ├── StringCaseUtility.cs          # PascalCase ↔ snake_case
    │   ├── ToolMetadata.cs              # schema metadata registry
    │   ├── SchemaUtility.cs             # C# type → JSON Schema type mapping
    │   ├── SerializedPropertyValue.cs   # JSON ↔ SerializedProperty bridge
    │   ├── ComponentTypeResolver.cs     # short/full component name → System.Type
    │   ├── HierarchyPath.cs             # Transform path build/find (inactive fallback)
    │   ├── TargetResolver.cs            # resolve GameObject/Component/Transform targets
    │   ├── EntityIdCompat.cs            # Unity 6000.5 EntityId shim
    │   ├── GameObjectComponents.cs      # stable component name list helper
    │   ├── Levenshtein.cs               # edit distance for "did you mean"
    │   ├── UnityDocsStore.cs            # bundled ScriptReference lookup data
    │   ├── UnityPitfalls.cs             # curated Unity API pitfalls for describe_type
    │   ├── HeraSettings.cs              # asset-config.json reader (juicy mode, csc/dotnet paths)
    │   ├── AssetConfigFile.cs            # shared locked JSON update helper
    │   ├── PackageJobState.cs           # async package job survival across domain reloads
    │   ├── AssetRefresh.cs              # AssetDatabase.Refresh + script compile request
    │   ├── AssetDetector.cs             # third-party asset detection + config sync
    │   ├── AssetReserializer.cs         # ForceReserializeAssets helper
    ├── Data/
    │   ├── unity_docs_*.jsonl.gz.bytes   # bundled Unity ScriptReference indexes
    │   ├── game_feel_1.0.jsonl.gz.bytes  # Game Feel Mode knowledge base
    │   └── ui_slop_1.0.jsonl.gz.bytes    # Unity De-slop Mode taxonomy
    ├── Tools/
    │   ├── ManageEditor.cs              # play, stop, pause, tags, layers
    │   ├── ExecuteCsharp.cs             # exec tool entry point (partial class)
    │   ├── ExecuteCsharp.SourceBuilder.cs   # snippet wrapping + using hoisting
    │   ├── ExecuteCsharp.Compilation.cs     # csc/dotnet invocation + error parsing
    │   ├── ExecuteCsharp.AssemblyLoader.cs  # collectible ALC assembly loading
    │   ├── ExecuteCsharp.Serializer.cs      # return value serialization + runtime error shaping
    │   ├── ExecuteCsharp.Serializer.UnityObjects.cs # UnityEngine.Object serializer branch
    │   ├── ExecuteMenuItem.cs           # Unity menu execution
    │   ├── ReadConsole.cs               # console log reading/clearing
    │   ├── RefreshUnity.cs              # asset database refresh + compile request
    │   ├── EditorScreenshot.cs          # screenshot capture
    │   ├── DetectAssets.cs              # auto-detect project assets / update asset config
    │   ├── ReserializeAssets.cs         # asset reserialization
    │   ├── ManageProfiler.cs            # profiler control
    │   ├── ManageScene.cs               # scene info/load/save/list/close
    │   ├── ManageComponents.cs          # component CRUD via SerializedProperty
    │   ├── ManageGameObject.cs          # GameObject CRUD + transform ops
    │   ├── ManageMaterial.cs            # material asset CRUD
    │   ├── ManagePrefab.cs              # prefab asset operations
    │   ├── ManageAssetImport.cs         # AssetImporter get/set
    │   ├── ManageUI.cs                  # uGUI create/get_rect/set_anchor/set_rect
    │   ├── ManagePackages.cs            # UPM list/add/remove/embed with async jobs
    │   ├── FindGameObjects.cs           # filtered scene search with pagination
    │   ├── FindMethod.cs                # method search across loaded assemblies
    │   ├── ListAssemblies.cs            # loaded assembly listing
    │   ├── DescribeType.cs              # loaded type introspection + pitfalls
    │   ├── DescribeShader.cs            # shader property inspection/search
    │   ├── UnityDocs.cs                 # offline ScriptReference lookup
    │   └── LogToConsole.cs              # write to Unity console
    └── TestRunner/
        ├── RunTests.cs                  # Unity Test Framework execution
        └── TestRunnerState.cs           # test-result persistence across reloads

HttpServer.cs

Role

Lightweight HTTP server on localhost. Receives CLI commands as POST /command, batch commands as POST /commands, dispatches via CommandRouter, returns JSON responses.

Key Characteristics

  • Uses ConcurrentQueue + EditorApplication.update for main-thread marshaling
  • Commands execute even when Unity is unfocused
  • Survives domain reloads via [InitializeOnLoad]
  • Reads request bodies incrementally with endpoint byte limits, caps batches at 50 commands, and admits at most 64 pending requests

Port Selection

const int DEFAULT_PORT = 8090;
const int FALLBACK_PORT = 8091;
const int MAX_PORT_ATTEMPTS = 10;

Tries 8090, then 8091, 8092, ... up to 10 attempts. First available port wins.

Request Handling Flow

ListenLoop (background thread)
    → await GetContextAsync()
    → HandleRequest()
        → Read a bounded JSON body incrementally
        → Extract command + parameters
        → Enqueue WorkItem to ConcurrentQueue
        → ForceEditorUpdate() (triggers EditorApplication.update)
        → await TCS.Task (blocks until main thread processes)
        → Serialize result to JSON
        → Write HTTP response

Endpoints

PathPurpose
POST /commandSingle command execution
POST /commandsBatch command execution (sequential, fail_fast)

/command accepts up to 1 MiB and /commands up to 4 MiB. Ingress rejections use a JSON ErrorResponse with a stable HTTP_* code and a matching 4xx/5xx status. The Go client preserves that envelope's code and data instead of flattening it into a transport string.

Domain Reload Survival

static HttpServer()
{
    Start();
    EditorApplication.quitting += Stop;
    AssemblyReloadEvents.beforeAssemblyReload += StopListener;
    AssemblyReloadEvents.afterAssemblyReload += Start;
    EditorApplication.update += ProcessQueue;
}
  • beforeAssemblyReload → stops the HTTP listener
  • afterAssemblyReload → restarts the HTTP listener
  • ProcessQueue runs on every EditorApplication.update tick

Security

  • Binds only to 127.0.0.1
  • Rejects browser CORS requests (HTTP 403 if Origin header present)
  • Blocks OPTIONS requests

CommandRouter.cs

Role

Routes incoming command requests to the appropriate tool handler. Serializes all requests through a single lock to prevent race conditions.

Locking

static readonly SemaphoreSlim s_Lock = new(1, 1);
static readonly TimeSpan s_LockTimeout = TimeSpan.FromSeconds(120);

public static async Task<object> Dispatch(string command, JObject parameters)
{
    if (!await s_Lock.WaitAsync(s_LockTimeout))
        return new ErrorResponse("COMMAND_LOCK_TIMEOUT",
            "[Hera] I waited 120s for the command lock but another command is still running.");
    // ... dispatch
}

The 120-second timeout is the lock-acquisition timeout, not the per-command execution budget. Long-running operations are polled from the CLI side via heartbeat files.

Dispatch Flow

  1. If command == "list" → return names, summaries, or one full schema via ToolDiscovery.GetToolNames(), GetToolSummaries(), or GetToolSchema(tool)
  2. Extract action from parameters (action field or first positional arg)
  3. Resolve handler: action handler first (ToolDiscovery.FindActionHandler), then default HandleCommand (ToolDiscovery.FindDefaultHandler). Both [HeraAction] methods and legacy implicit action methods are considered.
  4. If handler is static → invoke directly; if instance → create via Activator.CreateInstance()
  5. If result is Task<object> → await it; if Task → await and return success message
  6. Return result (or success message if null)

Batch Dispatch

DispatchBatch holds the same lock while running a sequence of commands. This saves one HTTP round-trip per command and avoids releasing/re-acquiring the work queue between steps. fail_fast stops at the first ErrorResponse.

Error Handling

All exceptions are caught, logged via Debug.LogException, and returned as ErrorResponse. Structured errors carry a stable code field (e.g. EXEC_COMPILE_ERROR, UNKNOWN_COMMAND, MISSING_PARAM).

CommandRouter-level tool dispatch codes:

CodeWhen
COMMAND_LOCK_TIMEOUTLock acquisition timed out (another command is stuck)
UNKNOWN_COMMANDNo tool or action handler matched the command
UNKNOWN_TOOLlist --tool <name> referenced a missing tool
TOOL_TYPE_NOT_FOUNDHandler's declaring type could not be resolved
TOOL_MISSING_CONSTRUCTORTool class lacks a public parameterless constructor
TOOL_CONSTRUCTOR_INACCESSIBLETool constructor is not public
TOOL_INSTANCE_CREATE_FAILEDActivator.CreateInstance returned null
TOOL_ACTION_FAILEDAction-level handler threw an exception (an action was specified)
TOOL_FAILEDDefault handler threw an exception (no action specified)

Common Tool Error Codes

All tools now return stable code values. Branch on these rather than parsing message text.

CodeTypical Cause
MISSING_PARAMA required parameter is missing or null
INVALID_PARAMA parameter was supplied but malformed or out of range
UNKNOWN_ACTIONThe tool has no matching action for the requested action
TARGET_NOT_FOUNDinstance_id/path resolved to nothing
OBJECT_NOT_FOUNDThe supplied instance_id no longer points to a live object
NOT_A_GAMEOBJECTinstance_id exists but is not a GameObject
NOT_A_COMPONENTcomponent_id exists but is not a Component
INVALID_INSTANCE_IDinstance_id could not be parsed as an integer
INVALID_COMPONENT_IDcomponent_id could not be parsed as an integer
COMPONENT_NOT_FOUNDTarget GameObject does not have the requested component
COMPONENT_INDEX_OUT_OF_RANGEindex exceeds the number of matching components
UNKNOWN_COMPONENT_TYPEtype string does not resolve to a known component type
TRANSFORM_NOT_ADDABLETried to AddComponent<Transform>
ADD_COMPONENT_FAILEDUnity threw while adding a component
ADD_COMPONENT_NULLAddComponent returned null (likely DisallowMultipleComponent)
TRANSFORM_NOT_REMOVABLETried to remove the required Transform
REMOVE_COMPONENT_FAILEDUnity threw while removing a component
PROPERTY_NOT_FOUNDSerializedProperty path does not exist on the component
VALUE_COERCION_FAILEDCould not convert the supplied value to the property type
SCENE_NOT_FOUNDscene load target does not exist
SCENE_NOT_LOADEDTarget scene is not currently loaded
SCENE_DIRTYScene has unsaved changes and the operation requires a clean state
SCENE_CLOSE_FORBIDDENAttempted to close the only loaded scene
PREFAB_NOT_FOUNDPrefab asset path does not exist
PREFAB_SAVE_FAILEDUnity could not save the prefab
INSTANTIATE_FAILEDUnity could not instantiate the prefab
MATERIAL_NOT_FOUNDMaterial asset path does not exist
SHADER_NOT_FOUNDNamed shader is not loaded
SHADER_PROPERTY_NOT_FOUNDMaterial/shader does not expose the named property
VALUE_PARSE_ERRORCould not parse the value for a material property
UI_MISSING_UGUIRequired uGUI component could not be added
UI_MISSING_EVENTSYSTEMEventSystem type is unavailable
UI_EVENTSYSTEM_CREATE_FAILEDCould not create an EventSystem
TMP_NOT_INSTALLEDForced TextMeshPro but the package is missing
INVALID_PRESETUnrecognized anchor preset name
SCREENSHOT_FAILEDCould not capture the requested view
SCENEVIEW_NOT_FOUND / SCENEVIEW_CAMERA_NULL / CAMERA_NOT_FOUNDScene view / camera unavailable for screenshot
PROFILER_NO_DATAProfiler has no captured data
PROFILER_NO_FRAME_DATAProfiler frame/thread view is invalid
PROFILER_ITEM_NOT_FOUND--root name not present in the hierarchy
PROFILER_NO_FRAMES_IN_RANGERequested frame range is empty
EXEC_COMPILE_ERRORC# snippet did not compile
EXEC_RUNTIME_ERRORC# snippet threw an exception
EXEC_LOGGED_ERROR--strict mode and Debug.LogError/LogException/LogAssert was emitted
EXEC_LOAD_FAILEDCompiled assembly could not be loaded
EXEC_INTERNAL_ERRORUnexpected failure inside the exec pipeline
MENU_BLOCKEDMenu item is on the safety blocklist
MENU_EXECUTION_FAILEDEditorApplication.ExecuteMenuItem returned false
READCONSOLE_INIT_FAILEDUnity console reader could not initialize
PACKAGE_LIST_TIMEOUTPackage list request timed out
PACKAGE_JOB_START_FAILEDCould not start a package manager async job
DOCS_BUNDLE_UNAVAILABLEBundled Unity docs data is missing or unreadable
DOC_NOT_FOUNDQuery did not match any indexed docs entry
INVALID_LAYER_INDEXlayer integer is outside 0..31
UNKNOWN_LAYER_NAMElayer string is not a defined layer
INVALID_PATH_GLOBpath_glob regex conversion failed
TYPE_NOT_FOUNDdescribe_type could not resolve the type
TESTS_FAILEDOne or more tests failed
PLAYMODE_REFRESH_BLOCKEDrefresh_unity refused because Unity is entering/ in play mode
METHOD_NOT_ALLOWED / NOT_FOUND / INTERNAL_ERRORHTTP routing errors

ToolDiscovery.cs

Role

Finds [HeraTool] handlers via reflection. Result is cached per assembly-reload — a fresh scan happens only when Unity reloads the domain, which is also when new tools could appear.

Tool Name Resolution

C# Class NameTool Name
ManageEditormanage_editor
ExecuteCsharpexec (explicit Name =)
EditorScreenshotscreenshot (explicit Name =)
ManageUImanage_ui
Custom: [HeraTool(Name = "my_tool")]my_tool (explicit)

No explicit Name=StringCaseUtility.ToSnakeCase(ClassName).

Action-Level Handlers

Action handlers are public static methods on a [HeraTool] class that take exactly one JObject parameter and return object, Task<object>, or Task. They are registered under <tool>:<snake_case_method_name>. Discovery ignores invalid [HeraAction] declarations and emits a diagnostic during the scan; a partially loadable assembly still contributes its non-null ReflectionTypeLoadException.Types entries.

Explicit registration (preferred):

[HeraAction]
public static object GetRect(JObject raw) { ... }

Legacy implicit registration is still supported for backward compatibility: any public static method with the right signature that is not named Handle or HandleCommand is auto-registered. New code should use [HeraAction] to make intent explicit and avoid accidental registration of helper methods.

The CLI sends manage_ui get_rect directly without a monolithic HandleCommand switch.

Schema Generation

GetToolSchema() returns JSON schema for a discovered tool, including:

  • Tool name, description, group(s), examples
  • Deterministically ordered action descriptors (name, description)
  • Parameter schema (from the nested Parameters class + [ToolParameter] attributes)
  • Output schema
  • Metadata flags (enum support, default support, custom types)

GetToolSummaries() returns name + description only (cheap). GetToolNames() returns names only (cheapest) and is also used for list --compact.

"Did you mean"

SuggestSimilarCommands() uses Levenshtein.DistanceBounded() to suggest up to 3 tool names within edit distance 2 of a typo'd command.


ExecuteCsharp

The exec tool is implemented as a partial static class split across six files under Tools/:

FileResponsibility
ExecuteCsharp.cs[HeraTool] entry point, Parameters, HandleCommand, PreWarmCompiler, CompileAndExecute orchestration
ExecuteCsharp.SourceBuilder.csDefault usings, snippet wrapping, leading-using hoisting, line-offset math
ExecuteCsharp.Compilation.csCompileToBytes, csc/dotnet/Mono launcher, error parsing/formatting, temp-file cleanup
ExecuteCsharp.AssemblyLoader.csCollectible AssemblyLoadContext load with Assembly.Load fallback
ExecuteCsharp.Serializer.csReturn-value serialization, including the depth-1/2 compact Unity-object {name,type,instanceID} contract, --stacktrace modes, and --strict log capture
ExecuteCsharp.Serializer.UnityObjects.csDedicated UnityEngine.Object shallow/deep serializer branch

Splitting keeps each file under ~300 lines and makes the compile/load/invoke/serialize pipeline easier to navigate. The public contract (HandleCommand, PreWarmCompiler) does not change.


Heartbeat.cs

Role

Writes the instance state JSON file every 1.0 second so the Go CLI can discover and monitor Unity.

File Location

~/.hera-agent-unity/instances/<md5(projectPath).Substring(0,16)>.json

Example: ~/.hera-agent-unity/instances/a1b2c3d4e5f67890.json

State Determination

static string GetState()
{
    if (EditorApplication.isCompiling) return "compiling";
    if (EditorApplication.isUpdating) return "refreshing";
    if (EditorApplication.isPlaying)
        return EditorApplication.isPaused ? "paused" : "playing";
    return "ready";
}

Forced States

Certain operations force a temporary state to prevent the CLI from seeing premature "ready":

EventForced StateDuration
beforeAssemblyReload"reloading"Until next tick
ExitingEditMode"entering_playmode"Until next tick
MarkCompileRequested()"compiling"Up to 30 seconds while compilation begins; clears once compile activity has completed

Instance File Format

{
  "state": "ready",
  "projectPath": "/Users/admin/Unity/MyProject",
  "port": 8090,
  "pid": 12345,
  "unityVersion": "6000.3.5f2",
  "docsVersion": "6000.3",
  "compiler": { "cscKind": "unity_dotnet_sdk_roslyn", "dotnetKind": "unity_netcore_runtime" },
  "timestamp": 1714372800000,
  "compileErrors": false
}

compileErrors is read from EditorUtility.scriptCompilationFailed and lets waitForReady() report compilation errors without an extra console read.

Current heartbeats also advertise capability strings including tool_catalog_v1, domain_epoch_v1, approval_v1, operation_ledger_v1, and task_bridge_v1. The Go MCP adapter uses these capabilities independently: an older Connector falls back to Compact legacy discovery, while missing approval or ledger support rejects risky work instead of weakening policy. MCP is not implemented in this assembly and opens no additional Unity listener. See MCP.md.


Core Utilities

Response.cs

  • SuccessResponsesuccess, message, data, optional agent_hint, timings
  • ErrorResponsesuccess=false, message, optional code, suggestions, data, timings
  • ResponseTimings — attaches compile_ms / execute_ms / serialize_ms / total_ms to responses

ToolParams.cs + ParamCoercion.cs

ToolParams wraps a JObject and provides typed accessors: Get, GetRequired, GetInt, GetFloat, GetBool, GetRaw. ParamCoercion handles permissive bool parsing (true/1/yes/on, etc.).

SerializedPropertyValue.cs

JSON ↔ SerializedProperty bridge used by manage_components, manage_asset_import, and any future tool that reads or sets typed Unity object properties. Supports:

  • Primitives, enums, colors, vectors, quaternions, rects, bounds
  • Object references via InstanceID, asset path, or {instance_id|asset_path} envelope
  • Public parsers: TryParseFloats, TryParseColor

ComponentTypeResolver.cs

Resolves short (Rigidbody) or fully-qualified (UnityEngine.Rigidbody) component names. The derived-type scan is snapshotted into dictionaries after each domain reload so repeated lookups avoid walking TypeCache every time. Provides SuggestSimilar() for "did you mean" hints.

HierarchyPath.cs

Build(Transform)/Root/Child. Find(string)GameObject.Find first, then a fallback walk over loaded scenes including inactive roots/children.

TargetResolver.cs

Shared target resolution: instance_id (highest priority) or path, with optional altPathKey. Also resolves Transform from a raw string and generic GetComponent<T>.

EntityIdCompat.cs

Unity 6000.5 renamed InstanceIDToObject/GetInstanceID to EntityIdToObject/GetEntityId and made the old API obsolete-as-error. This shim chooses the right API per compile-time Unity version and preserves the existing int-based instance_id contract.

GameObjectComponents.cs

GameObjectComponents.GetNames(GameObject) returns the type names of all non-null components on a GameObject, in GetComponents order and with missing scripts skipped. Shared by manage_ui and manage_prefab so both tools report component lists the same way.

Levenshtein.cs

Edit-distance helper with a bounded early-exit variant used by command/type/docs suggesters.

UnityDocsStore.cs

Selects the bundled unity_docs_<version>.jsonl.gz.bytes file for the current Unity version, falling back to the 6000.0 bundle when an exact bucket is not present. Loads it into a dictionary keyed by class/property/method name. Provides exact lookup and prefix-bucketed Levenshtein suggestions.

BundleStore.cs

BundleStore<TEntry> loads one bundled gzipped-JSONL knowledge file into a dictionary keyed by a caller-supplied selector, once per domain (the bundle is immutable UPM content). Provides Lookup, Count, LoadError, Values, and full-scan Levenshtein SuggestSimilar. Package-relative path resolution falls back to an AssetDatabase search so in-project checkouts still resolve. UnityDocsStore is deliberately not a consumer — it resolves a Unity-version bucket and runs a 3-layer prefix/length/bounded suggest, both locked decisions.

GameFeelStore.cs

Owns a BundleStore<Entry> over game_feel_1.0.jsonl.gz.bytes (67 topics), keyed by topic. Adds the category-grouped index with ethics first.

UiSlopStore.cs

Owns a BundleStore<Entry> over ui_slop_1.0.jsonl.gz.bytes (49 Unity UI-slop tells), keyed by tell id. Adds the area-grouped index (A→E, the fixed fix order) and CheckFor(id), which returns the uGUI predicate.

UnityPitfalls.cs

Curated catalog of Unity API pitfalls attached to describe_type responses. Entries can carry a minimum docs bucket so Unity 6-only advice is hidden on 2022.3/2023.2.

HeraSettings.cs

Reads ~/.hera-agent-unity/asset-config.json by last-write-time cache. Exposes:

  • GameFeelUiMode → drives manage_ui juice hints (legacy ui_juicy_mode key read as fallback)
  • GameFeelMode → drives manage_components add game-feel topic hints
  • UiSlopMode → drives manage_components add UI-slop tell hints and the doctor --agent-rules de-slop section
  • DotweenPreferred → tween backend hint
  • DefaultCscPath / DefaultDotnetPath → compiler defaults for exec; the resolver compares the configured path on every call, so a saved change is observed without a domain reload

PackageJobState.cs

Survives domain reloads for async manage_packages add/remove/embed operations. Writes a result file to ~/.hera-agent-unity/status/package-result-<port>-<job_id>.json that the CLI polls.

AssetRefresh.cs

Wrapper around AssetDatabase.Refresh and CompilationPipeline.RequestScriptCompilation. Used by refresh_unity; returns a structured result so the tool layer only has to build the response envelope.

AssetDetector.cs

Owns the shared Odin/DOTween detection rules used by both Hera Settings and detect_assets. It checks product-specific folders, package paths, and DLLs. Loaded-assembly fallback is product-specific and is used only for the active Unity project; an explicit different project path is scanned from disk only. Detected installed flags are mirrored into ~/.hera-agent-unity/asset-config.json.

AssetConfigFile.cs

Coordinates the Settings window and asset detector with the Go CLI through a sibling asset-config.json.lock file. Updates read the latest JSON while holding that lock and publish through a flushed temporary-file replacement, so readers never observe a partial document. Unknown top-level fields, asset fields, and asset entries are retained. Conflicting edits to recognized fields are last-writer-wins; the format has no revision-based merge protocol.

AssetReserializer.cs

Thin wrapper around AssetDatabase.ForceReserializeAssets. Handles the "whole project" vs "specific paths" branching and logging. Used by reserialize.

Built-in Tools Summary

ToolClassKey Actions
manage_editorManageEditor.csplay, stop, pause, set_active_tool, add_tag, remove_tag, add_layer, remove_layer
execExecuteCsharp.*.csCompile and run C# code inside Unity (partial class split)
menuExecuteMenuItem.csExecute Unity menu items by path (File/Quit blocked)
consoleReadConsole.csRead/filter/clear console logs
refresh_unityRefreshUnity.csAssetDatabase.Refresh, optional compile request (→ AssetRefresh.cs)
screenshotEditorScreenshot.csCapture scene/game view
detect_assetsDetectAssets.csAuto-detect project assets / update asset config (→ AssetDetector.cs)
reserializeReserializeAssets.csForce asset reserialization (→ AssetReserializer.cs)
profilerManageProfiler.csenable/disable/capture profiler data
run_testsRunTests.csExecute Unity Test Framework tests
sceneManageScene.csinfo, load, save, list, close
manage_componentsManageComponents.csadd, remove, list, get, set via SerializedProperty
manage_gameobjectManageGameObject.cscreate, destroy, move, set_parent, set_active, set_name, get_transform
manage_materialManageMaterial.cscreate, get, set, set_shader
manage_prefabManagePrefab.cscreate, instantiate, add_component, remove_component
manage_asset_importManageAssetImport.csget/set AssetImporter properties
manage_uiManageUI.cscreate, get_rect, set_anchor, set_rect
manage_packagesManagePackages.cslist, add, remove, embed (async job file)
find_gameobjectsFindGameObjects.csfiltered scene search with pagination
find_methodFindMethod.csmethod search across loaded assemblies
list_assembliesListAssemblies.csloaded assembly listing
describe_typeDescribeType.cstype introspection + Unity pitfalls
describe_shaderDescribeShader.csshader property inspection/search
unity_docsUnityDocs.csoffline ScriptReference lookup
game_feelGameFeel.csoffline game-feel/juice recipe lookup (ethics built in)
ui_slopUiSlop.csoffline UI-slop tell lookup (uGUI checks, fixes, exceptions)
logLogToConsole.cswrite to Unity console

Data Bundle

AgentConnector/Editor/Data/unity_docs_<version>.jsonl.gz.bytes files are gzipped JSONL imported as TextAssets. The current checkout still includes the legacy unity_docs_6.0.jsonl.gz.bytes file, which UnityDocsStore treats as the 6000.0 fallback. Regenerate a versioned bundle with:

go run ./tools/build-unity-docs \
    --in  <path-to-Documentation/en> \
    --out AgentConnector/Editor/Data/unity_docs_6000.0.jsonl.gz.bytes \
    --unity-version 6000.0

game_feel_1.0.jsonl.gz.bytes is the Game Feel Mode knowledge base. Its checked-in source of truth is tools/build-game-feel-docs/game_feel.jsonl; regenerate with go run ./tools/build-game-feel-docs.

ui_slop_1.0.jsonl.gz.bytes is the Unity De-slop Mode taxonomy. Its checked-in source of truth is tools/build-ui-slop-docs/ui_slop.jsonl; regenerate with go run ./tools/build-ui-slop-docs. The builder validates ids, areas, severities, and deep_topic values before writing.


TestRunner

RunTests.cs starts both EditMode and PlayMode runs asynchronously through the Unity Test Framework. Each mode persists its final result to ~/.hera-agent-unity/status/test-results-<port>-<run_id>.json; TestRunnerState keeps the result path alive across a domain reload. The CLI polls that file until the final result, so test has no --wait flag.

The connector also best-effort writes test-results-<port>.json for an older CLI that only understands port-scoped PlayMode results.

A current CLI sends async_results=true to opt into run-scoped asynchronous EditMode results. Without that capability, EditMode keeps the legacy synchronous response contract; PlayMode remains asynchronous.


Domain Reload Notes

When Unity compiles scripts, the entire AppDomain is reloaded:

  • All static variables reset
  • All instances destroyed
  • HTTP listener must be stopped before reload, restarted after

Components marked [InitializeOnLoad] automatically re-initialize after reload. This is why HttpServer, Heartbeat, TestRunnerState, ToolDiscovery, ExecCompileCache, and PackageJobState all use this attribute or subscribe to AssemblyReloadEvents.