unity-open-mcp 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-identity.d.ts +52 -0
- package/dist/agent-identity.js +115 -0
- package/dist/agent-identity.js.map +1 -0
- package/dist/batch-spawn.d.ts +7 -0
- package/dist/batch-spawn.js +155 -29
- package/dist/batch-spawn.js.map +1 -1
- package/dist/capabilities/build-capabilities.d.ts +45 -0
- package/dist/capabilities/build-capabilities.js +262 -27
- package/dist/capabilities/build-capabilities.js.map +1 -1
- package/dist/capabilities/cost-hints.d.ts +82 -0
- package/dist/capabilities/cost-hints.js +185 -0
- package/dist/capabilities/cost-hints.js.map +1 -0
- package/dist/capabilities/lifecycle.d.ts +40 -0
- package/dist/capabilities/lifecycle.js +353 -0
- package/dist/capabilities/lifecycle.js.map +1 -0
- package/dist/capabilities/rule-catalog.d.ts +13 -1
- package/dist/capabilities/rule-catalog.js +620 -75
- package/dist/capabilities/rule-catalog.js.map +1 -1
- package/dist/capabilities/tool-groups.d.ts +21 -0
- package/dist/capabilities/tool-groups.js +411 -14
- package/dist/capabilities/tool-groups.js.map +1 -1
- package/dist/cli/args.d.ts +32 -1
- package/dist/cli/args.js +201 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/cli.d.ts +21 -0
- package/dist/cli/cli.js +68 -5
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/commands.d.ts +33 -0
- package/dist/cli/commands.js +400 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/exit-codes.d.ts +43 -0
- package/dist/cli/exit-codes.js +78 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/compile-verify.d.ts +65 -0
- package/dist/compile-verify.js +94 -0
- package/dist/compile-verify.js.map +1 -0
- package/dist/compressible-router.js +64 -11
- package/dist/compressible-router.js.map +1 -1
- package/dist/compression/asset-model.d.ts +15 -0
- package/dist/compression/compact.d.ts +5 -4
- package/dist/compression/compact.js +13 -10
- package/dist/compression/compact.js.map +1 -1
- package/dist/dialog-dismiss.d.ts +127 -74
- package/dist/dialog-dismiss.js +427 -170
- package/dist/dialog-dismiss.js.map +1 -1
- package/dist/dialog-policy.d.ts +141 -0
- package/dist/dialog-policy.js +345 -0
- package/dist/dialog-policy.js.map +1 -0
- package/dist/gate-error.d.ts +6 -0
- package/dist/gate-error.js +13 -2
- package/dist/gate-error.js.map +1 -1
- package/dist/hub-control.d.ts +247 -0
- package/dist/hub-control.js +703 -0
- package/dist/hub-control.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -12
- package/dist/index.js.map +1 -1
- package/dist/live-client.d.ts +123 -3
- package/dist/live-client.js +526 -69
- package/dist/live-client.js.map +1 -1
- package/dist/offline.d.ts +105 -0
- package/dist/offline.js +816 -23
- package/dist/offline.js.map +1 -1
- package/dist/output-profile.d.ts +110 -0
- package/dist/output-profile.js +255 -0
- package/dist/output-profile.js.map +1 -0
- package/dist/retry-policy.d.ts +72 -0
- package/dist/retry-policy.js +146 -0
- package/dist/retry-policy.js.map +1 -0
- package/dist/routers.d.ts +4 -0
- package/dist/routers.js +2 -2
- package/dist/routers.js.map +1 -1
- package/dist/server-instructions.d.ts +18 -0
- package/dist/server-instructions.js +145 -0
- package/dist/server-instructions.js.map +1 -0
- package/dist/tool-router.d.ts +38 -1
- package/dist/tool-router.js +690 -37
- package/dist/tool-router.js.map +1 -1
- package/dist/tool-session-state.d.ts +37 -0
- package/dist/tool-session-state.js +78 -1
- package/dist/tool-session-state.js.map +1 -1
- package/dist/tools/apply-fix.js +16 -2
- package/dist/tools/apply-fix.js.map +1 -1
- package/dist/tools/asmdef-create.d.ts +2 -0
- package/dist/tools/asmdef-create.js +114 -0
- package/dist/tools/asmdef-create.js.map +1 -0
- package/dist/tools/asmdef-get.d.ts +2 -0
- package/dist/tools/asmdef-get.js +31 -0
- package/dist/tools/asmdef-get.js.map +1 -0
- package/dist/tools/asmdef-list.d.ts +2 -0
- package/dist/tools/asmdef-list.js +41 -0
- package/dist/tools/asmdef-list.js.map +1 -0
- package/dist/tools/asmdef-modify.d.ts +2 -0
- package/dist/tools/asmdef-modify.js +108 -0
- package/dist/tools/asmdef-modify.js.map +1 -0
- package/dist/tools/audio-listener-get.d.ts +2 -0
- package/dist/tools/audio-listener-get.js +16 -0
- package/dist/tools/audio-listener-get.js.map +1 -0
- package/dist/tools/audio-mixer-get-parameter.d.ts +2 -0
- package/dist/tools/audio-mixer-get-parameter.js +25 -0
- package/dist/tools/audio-mixer-get-parameter.js.map +1 -0
- package/dist/tools/audio-mixer-set-parameter.d.ts +2 -0
- package/dist/tools/audio-mixer-set-parameter.js +44 -0
- package/dist/tools/audio-mixer-set-parameter.js.map +1 -0
- package/dist/tools/audio-source-add.d.ts +2 -0
- package/dist/tools/audio-source-add.js +94 -0
- package/dist/tools/audio-source-add.js.map +1 -0
- package/dist/tools/audio-source-modify.d.ts +2 -0
- package/dist/tools/audio-source-modify.js +52 -0
- package/dist/tools/audio-source-modify.js.map +1 -0
- package/dist/tools/bridge-status.js +16 -6
- package/dist/tools/bridge-status.js.map +1 -1
- package/dist/tools/build-get-active-target.js +1 -2
- package/dist/tools/build-get-active-target.js.map +1 -1
- package/dist/tools/build-get-defines.js +1 -1
- package/dist/tools/build-get-defines.js.map +1 -1
- package/dist/tools/build-get-scenes.js +1 -2
- package/dist/tools/build-get-scenes.js.map +1 -1
- package/dist/tools/build-get-targets.js +1 -1
- package/dist/tools/build-get-targets.js.map +1 -1
- package/dist/tools/build-set-defines.js +1 -2
- package/dist/tools/build-set-defines.js.map +1 -1
- package/dist/tools/build-set-scenes.js +1 -1
- package/dist/tools/build-set-scenes.js.map +1 -1
- package/dist/tools/build-set-target.js +1 -1
- package/dist/tools/build-set-target.js.map +1 -1
- package/dist/tools/build-start.js +0 -1
- package/dist/tools/build-start.js.map +1 -1
- package/dist/tools/cinemachine-brain-ensure.d.ts +2 -0
- package/dist/tools/cinemachine-brain-ensure.js +27 -0
- package/dist/tools/cinemachine-brain-ensure.js.map +1 -0
- package/dist/tools/cinemachine-camera-list.d.ts +2 -0
- package/dist/tools/cinemachine-camera-list.js +15 -0
- package/dist/tools/cinemachine-camera-list.js.map +1 -0
- package/dist/tools/cinemachine-create-camera.d.ts +2 -0
- package/dist/tools/cinemachine-create-camera.js +55 -0
- package/dist/tools/cinemachine-create-camera.js.map +1 -0
- package/dist/tools/cinemachine-set-body.d.ts +2 -0
- package/dist/tools/cinemachine-set-body.js +33 -0
- package/dist/tools/cinemachine-set-body.js.map +1 -0
- package/dist/tools/cinemachine-set-lens.d.ts +2 -0
- package/dist/tools/cinemachine-set-lens.js +41 -0
- package/dist/tools/cinemachine-set-lens.js.map +1 -0
- package/dist/tools/cinemachine-set-noise.d.ts +2 -0
- package/dist/tools/cinemachine-set-noise.js +31 -0
- package/dist/tools/cinemachine-set-noise.js.map +1 -0
- package/dist/tools/cinemachine-set-targets.d.ts +2 -0
- package/dist/tools/cinemachine-set-targets.js +30 -0
- package/dist/tools/cinemachine-set-targets.js.map +1 -0
- package/dist/tools/console-log.js +2 -2
- package/dist/tools/console-log.js.map +1 -1
- package/dist/tools/constraint-add.d.ts +2 -0
- package/dist/tools/constraint-add.js +85 -0
- package/dist/tools/constraint-add.js.map +1 -0
- package/dist/tools/dependencies.d.ts +2 -0
- package/dist/tools/dependencies.js +76 -0
- package/dist/tools/dependencies.js.map +1 -0
- package/dist/tools/editor-add-tag.js +2 -3
- package/dist/tools/editor-add-tag.js.map +1 -1
- package/dist/tools/editor-clear-history.d.ts +2 -0
- package/dist/tools/editor-clear-history.js +25 -0
- package/dist/tools/editor-clear-history.js.map +1 -0
- package/dist/tools/editor-get-layers.js +3 -3
- package/dist/tools/editor-get-layers.js.map +1 -1
- package/dist/tools/editor-get-tags.js +2 -2
- package/dist/tools/editor-get-tags.js.map +1 -1
- package/dist/tools/editor-redo.js +2 -3
- package/dist/tools/editor-redo.js.map +1 -1
- package/dist/tools/editor-undo-history.d.ts +2 -0
- package/dist/tools/editor-undo-history.js +21 -0
- package/dist/tools/editor-undo-history.js.map +1 -0
- package/dist/tools/editor-undo.js +3 -3
- package/dist/tools/editor-undo.js.map +1 -1
- package/dist/tools/editorprefs-delete.d.ts +2 -0
- package/dist/tools/editorprefs-delete.js +23 -0
- package/dist/tools/editorprefs-delete.js.map +1 -0
- package/dist/tools/editorprefs-get.d.ts +2 -0
- package/dist/tools/editorprefs-get.js +28 -0
- package/dist/tools/editorprefs-get.js.map +1 -0
- package/dist/tools/editorprefs-set.d.ts +2 -0
- package/dist/tools/editorprefs-set.js +33 -0
- package/dist/tools/editorprefs-set.js.map +1 -0
- package/dist/tools/find-references.js +24 -4
- package/dist/tools/find-references.js.map +1 -1
- package/dist/tools/frame-debugger.d.ts +2 -0
- package/dist/tools/frame-debugger.js +48 -0
- package/dist/tools/frame-debugger.js.map +1 -0
- package/dist/tools/gameobject-modify.js +69 -2
- package/dist/tools/gameobject-modify.js.map +1 -1
- package/dist/tools/gate-budget-estimate.js +2 -1
- package/dist/tools/gate-budget-estimate.js.map +1 -1
- package/dist/tools/hub-available-releases.d.ts +2 -0
- package/dist/tools/hub-available-releases.js +23 -0
- package/dist/tools/hub-available-releases.js.map +1 -0
- package/dist/tools/hub-get-install-path.d.ts +2 -0
- package/dist/tools/hub-get-install-path.js +20 -0
- package/dist/tools/hub-get-install-path.js.map +1 -0
- package/dist/tools/hub-install-editor.d.ts +2 -0
- package/dist/tools/hub-install-editor.js +47 -0
- package/dist/tools/hub-install-editor.js.map +1 -0
- package/dist/tools/hub-install-modules.d.ts +2 -0
- package/dist/tools/hub-install-modules.js +44 -0
- package/dist/tools/hub-install-modules.js.map +1 -0
- package/dist/tools/hub-list-editors.d.ts +2 -0
- package/dist/tools/hub-list-editors.js +25 -0
- package/dist/tools/hub-list-editors.js.map +1 -0
- package/dist/tools/hub-set-install-path.d.ts +2 -0
- package/dist/tools/hub-set-install-path.js +30 -0
- package/dist/tools/hub-set-install-path.js.map +1 -0
- package/dist/tools/impact-preview.js +2 -2
- package/dist/tools/impact-preview.js.map +1 -1
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.js +534 -24
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/light-add.d.ts +2 -0
- package/dist/tools/light-add.js +73 -0
- package/dist/tools/light-add.js.map +1 -0
- package/dist/tools/light-modify.d.ts +2 -0
- package/dist/tools/light-modify.js +36 -0
- package/dist/tools/light-modify.js.map +1 -0
- package/dist/tools/light-set.d.ts +2 -0
- package/dist/tools/light-set.js +54 -0
- package/dist/tools/light-set.js.map +1 -0
- package/dist/tools/list-assets-of-type.d.ts +2 -0
- package/dist/tools/list-assets-of-type.js +54 -0
- package/dist/tools/list-assets-of-type.js.map +1 -0
- package/dist/tools/lod-add-level.d.ts +2 -0
- package/dist/tools/lod-add-level.js +66 -0
- package/dist/tools/lod-add-level.js.map +1 -0
- package/dist/tools/lod-group-configure.d.ts +2 -0
- package/dist/tools/lod-group-configure.js +64 -0
- package/dist/tools/lod-group-configure.js.map +1 -0
- package/dist/tools/manage-tools.js +7 -5
- package/dist/tools/manage-tools.js.map +1 -1
- package/dist/tools/memory-snapshot-capture.d.ts +2 -0
- package/dist/tools/memory-snapshot-capture.js +61 -0
- package/dist/tools/memory-snapshot-capture.js.map +1 -0
- package/dist/tools/mutation-explain.js +2 -2
- package/dist/tools/mutation-explain.js.map +1 -1
- package/dist/tools/playerprefs-delete.d.ts +2 -0
- package/dist/tools/playerprefs-delete.js +24 -0
- package/dist/tools/playerprefs-delete.js.map +1 -0
- package/dist/tools/playerprefs-get.d.ts +2 -0
- package/dist/tools/playerprefs-get.js +28 -0
- package/dist/tools/playerprefs-get.js.map +1 -0
- package/dist/tools/playerprefs-set.d.ts +2 -0
- package/dist/tools/playerprefs-set.js +37 -0
- package/dist/tools/playerprefs-set.js.map +1 -0
- package/dist/tools/profiler-capture-frame.d.ts +2 -0
- package/dist/tools/profiler-capture-frame.js +75 -0
- package/dist/tools/profiler-capture-frame.js.map +1 -0
- package/dist/tools/profiler-clear-data.js +1 -2
- package/dist/tools/profiler-clear-data.js.map +1 -1
- package/dist/tools/profiler-enable-module.js +4 -5
- package/dist/tools/profiler-enable-module.js.map +1 -1
- package/dist/tools/profiler-get-status.js +5 -6
- package/dist/tools/profiler-get-status.js.map +1 -1
- package/dist/tools/profiler-load-data.js +4 -5
- package/dist/tools/profiler-load-data.js.map +1 -1
- package/dist/tools/profiler-save-data.js +3 -3
- package/dist/tools/profiler-save-data.js.map +1 -1
- package/dist/tools/profiler-set-config.js +5 -5
- package/dist/tools/profiler-set-config.js.map +1 -1
- package/dist/tools/profiler-start.js +4 -4
- package/dist/tools/profiler-start.js.map +1 -1
- package/dist/tools/profiler-stop.js +2 -3
- package/dist/tools/profiler-stop.js.map +1 -1
- package/dist/tools/read-asset.js +21 -3
- package/dist/tools/read-asset.js.map +1 -1
- package/dist/tools/reflection-probe-bake.d.ts +2 -0
- package/dist/tools/reflection-probe-bake.js +47 -0
- package/dist/tools/reflection-probe-bake.js.map +1 -0
- package/dist/tools/reflection-probe-get.d.ts +2 -0
- package/dist/tools/reflection-probe-get.js +19 -0
- package/dist/tools/reflection-probe-get.js.map +1 -0
- package/dist/tools/reimport-package.d.ts +2 -0
- package/dist/tools/reimport-package.js +53 -0
- package/dist/tools/reimport-package.js.map +1 -0
- package/dist/tools/scan-paths.js +19 -0
- package/dist/tools/scan-paths.js.map +1 -1
- package/dist/tools/scene-focus.js +1 -1
- package/dist/tools/scene-focus.js.map +1 -1
- package/dist/tools/scene-get-data.js +31 -13
- package/dist/tools/scene-get-data.js.map +1 -1
- package/dist/tools/scene-get-dirty-summary.js +1 -1
- package/dist/tools/scene-get-dirty-summary.js.map +1 -1
- package/dist/tools/sceneview-get-camera.d.ts +2 -0
- package/dist/tools/sceneview-get-camera.js +16 -0
- package/dist/tools/sceneview-get-camera.js.map +1 -0
- package/dist/tools/sceneview-set-camera.d.ts +2 -0
- package/dist/tools/sceneview-set-camera.js +58 -0
- package/dist/tools/sceneview-set-camera.js.map +1 -0
- package/dist/tools/script-write.js +2 -2
- package/dist/tools/script-write.js.map +1 -1
- package/dist/tools/scriptableobject-create.d.ts +2 -0
- package/dist/tools/scriptableobject-create.js +86 -0
- package/dist/tools/scriptableobject-create.js.map +1 -0
- package/dist/tools/search-assets.js +20 -3
- package/dist/tools/search-assets.js.map +1 -1
- package/dist/tools/settings-get-lighting.js +3 -4
- package/dist/tools/settings-get-lighting.js.map +1 -1
- package/dist/tools/settings-get-player.js +2 -2
- package/dist/tools/settings-get-player.js.map +1 -1
- package/dist/tools/settings-get-quality.js +1 -2
- package/dist/tools/settings-get-quality.js.map +1 -1
- package/dist/tools/settings-get-render-pipeline.d.ts +2 -0
- package/dist/tools/settings-get-render-pipeline.js +19 -0
- package/dist/tools/settings-get-render-pipeline.js.map +1 -0
- package/dist/tools/settings-get-time.d.ts +2 -0
- package/dist/tools/settings-get-time.js +18 -0
- package/dist/tools/settings-get-time.js.map +1 -0
- package/dist/tools/settings-set-lighting.js +5 -5
- package/dist/tools/settings-set-lighting.js.map +1 -1
- package/dist/tools/settings-set-physics.js +2 -2
- package/dist/tools/settings-set-physics.js.map +1 -1
- package/dist/tools/settings-set-player.js +2 -2
- package/dist/tools/settings-set-player.js.map +1 -1
- package/dist/tools/settings-set-quality-level.d.ts +2 -0
- package/dist/tools/settings-set-quality-level.js +44 -0
- package/dist/tools/settings-set-quality-level.js.map +1 -0
- package/dist/tools/settings-set-quality.js +2 -2
- package/dist/tools/settings-set-quality.js.map +1 -1
- package/dist/tools/settings-set-time.d.ts +2 -0
- package/dist/tools/settings-set-time.js +53 -0
- package/dist/tools/settings-set-time.js.map +1 -0
- package/dist/tools/shader-get-data.js +3 -3
- package/dist/tools/shader-get-data.js.map +1 -1
- package/dist/tools/shader-graph-create.d.ts +2 -0
- package/dist/tools/shader-graph-create.js +40 -0
- package/dist/tools/shader-graph-create.js.map +1 -0
- package/dist/tools/shader-graph-node-add.d.ts +2 -0
- package/dist/tools/shader-graph-node-add.js +50 -0
- package/dist/tools/shader-graph-node-add.js.map +1 -0
- package/dist/tools/shader-graph-node-connect.d.ts +2 -0
- package/dist/tools/shader-graph-node-connect.js +60 -0
- package/dist/tools/shader-graph-node-connect.js.map +1 -0
- package/dist/tools/shader-graph-open.d.ts +2 -0
- package/dist/tools/shader-graph-open.js +29 -0
- package/dist/tools/shader-graph-open.js.map +1 -0
- package/dist/tools/skybox-get.d.ts +2 -0
- package/dist/tools/skybox-get.js +13 -0
- package/dist/tools/skybox-get.js.map +1 -0
- package/dist/tools/skybox-set.d.ts +2 -0
- package/dist/tools/skybox-set.js +34 -0
- package/dist/tools/skybox-set.js.map +1 -0
- package/dist/tools/spriteatlas-add-packable.d.ts +2 -0
- package/dist/tools/spriteatlas-add-packable.js +40 -0
- package/dist/tools/spriteatlas-add-packable.js.map +1 -0
- package/dist/tools/spriteatlas-create.d.ts +2 -0
- package/dist/tools/spriteatlas-create.js +41 -0
- package/dist/tools/spriteatlas-create.js.map +1 -0
- package/dist/tools/spriteatlas-delete.d.ts +2 -0
- package/dist/tools/spriteatlas-delete.js +32 -0
- package/dist/tools/spriteatlas-delete.js.map +1 -0
- package/dist/tools/spriteatlas-get.d.ts +2 -0
- package/dist/tools/spriteatlas-get.js +23 -0
- package/dist/tools/spriteatlas-get.js.map +1 -0
- package/dist/tools/spriteatlas-list.d.ts +2 -0
- package/dist/tools/spriteatlas-list.js +21 -0
- package/dist/tools/spriteatlas-list.js.map +1 -0
- package/dist/tools/spriteatlas-modify.d.ts +2 -0
- package/dist/tools/spriteatlas-modify.js +55 -0
- package/dist/tools/spriteatlas-modify.js.map +1 -0
- package/dist/tools/spriteatlas-remove-packable.d.ts +2 -0
- package/dist/tools/spriteatlas-remove-packable.js +39 -0
- package/dist/tools/spriteatlas-remove-packable.js.map +1 -0
- package/dist/tools/terrain-create.d.ts +2 -0
- package/dist/tools/terrain-create.js +73 -0
- package/dist/tools/terrain-create.js.map +1 -0
- package/dist/tools/terrain-paint-layer.d.ts +2 -0
- package/dist/tools/terrain-paint-layer.js +85 -0
- package/dist/tools/terrain-paint-layer.js.map +1 -0
- package/dist/tools/terrain-place-trees.d.ts +2 -0
- package/dist/tools/terrain-place-trees.js +92 -0
- package/dist/tools/terrain-place-trees.js.map +1 -0
- package/dist/tools/terrain-set-heights.d.ts +2 -0
- package/dist/tools/terrain-set-heights.js +68 -0
- package/dist/tools/terrain-set-heights.js.map +1 -0
- package/dist/tools/terrain-set-neighbors.d.ts +2 -0
- package/dist/tools/terrain-set-neighbors.js +65 -0
- package/dist/tools/terrain-set-neighbors.js.map +1 -0
- package/dist/tools/texture-get-importer.d.ts +2 -0
- package/dist/tools/texture-get-importer.js +25 -0
- package/dist/tools/texture-get-importer.js.map +1 -0
- package/dist/tools/texture-get.d.ts +2 -0
- package/dist/tools/texture-get.js +23 -0
- package/dist/tools/texture-get.js.map +1 -0
- package/dist/tools/texture-reimport.d.ts +2 -0
- package/dist/tools/texture-reimport.js +33 -0
- package/dist/tools/texture-reimport.js.map +1 -0
- package/dist/tools/texture-set-import.d.ts +2 -0
- package/dist/tools/texture-set-import.js +52 -0
- package/dist/tools/texture-set-import.js.map +1 -0
- package/dist/tools/tilemap-box-fill.d.ts +2 -0
- package/dist/tools/tilemap-box-fill.js +32 -0
- package/dist/tools/tilemap-box-fill.js.map +1 -0
- package/dist/tools/tilemap-create-rule-tile.d.ts +2 -0
- package/dist/tools/tilemap-create-rule-tile.js +39 -0
- package/dist/tools/tilemap-create-rule-tile.js.map +1 -0
- package/dist/tools/tilemap-create-tile-asset.d.ts +2 -0
- package/dist/tools/tilemap-create-tile-asset.js +35 -0
- package/dist/tools/tilemap-create-tile-asset.js.map +1 -0
- package/dist/tools/tilemap-create.d.ts +2 -0
- package/dist/tools/tilemap-create.js +32 -0
- package/dist/tools/tilemap-create.js.map +1 -0
- package/dist/tools/tilemap-set-tile.d.ts +2 -0
- package/dist/tools/tilemap-set-tile.js +33 -0
- package/dist/tools/tilemap-set-tile.js.map +1 -0
- package/dist/tools/timeline-clip-add.d.ts +2 -0
- package/dist/tools/timeline-clip-add.js +43 -0
- package/dist/tools/timeline-clip-add.js.map +1 -0
- package/dist/tools/timeline-create.d.ts +2 -0
- package/dist/tools/timeline-create.js +32 -0
- package/dist/tools/timeline-create.js.map +1 -0
- package/dist/tools/timeline-director-bind.d.ts +2 -0
- package/dist/tools/timeline-director-bind.js +34 -0
- package/dist/tools/timeline-director-bind.js.map +1 -0
- package/dist/tools/timeline-modify.d.ts +2 -0
- package/dist/tools/timeline-modify.js +44 -0
- package/dist/tools/timeline-modify.js.map +1 -0
- package/dist/tools/timeline-track-add.d.ts +2 -0
- package/dist/tools/timeline-track-add.js +37 -0
- package/dist/tools/timeline-track-add.js.map +1 -0
- package/dist/tools/ui-canvas-add.d.ts +2 -0
- package/dist/tools/ui-canvas-add.js +66 -0
- package/dist/tools/ui-canvas-add.js.map +1 -0
- package/dist/tools/ui-element-add.d.ts +2 -0
- package/dist/tools/ui-element-add.js +65 -0
- package/dist/tools/ui-element-add.js.map +1 -0
- package/dist/tools/ui-element-modify.d.ts +2 -0
- package/dist/tools/ui-element-modify.js +59 -0
- package/dist/tools/ui-element-modify.js.map +1 -0
- package/dist/tools/ui-layout-group-add.d.ts +2 -0
- package/dist/tools/ui-layout-group-add.js +80 -0
- package/dist/tools/ui-layout-group-add.js.map +1 -0
- package/dist/tools/validate-edit.js +22 -1
- package/dist/tools/validate-edit.js.map +1 -1
- package/dist/tools/vfx-block-edit.d.ts +2 -0
- package/dist/tools/vfx-block-edit.js +51 -0
- package/dist/tools/vfx-block-edit.js.map +1 -0
- package/dist/tools/vfx-list.d.ts +2 -0
- package/dist/tools/vfx-list.js +31 -0
- package/dist/tools/vfx-list.js.map +1 -0
- package/dist/tools/vfx-open.d.ts +2 -0
- package/dist/tools/vfx-open.js +29 -0
- package/dist/tools/vfx-open.js.map +1 -0
- package/package.json +1 -1
package/dist/live-client.js
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
import { deriveIsError } from "./gate-error.js";
|
|
2
|
-
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
|
-
import {
|
|
6
|
-
import { readInstanceLock, classifyInstance, lockPath } from "./instance-discovery.js";
|
|
5
|
+
import { pollAndDismissDialogs, readDismissConfig, } from "./dialog-dismiss.js";
|
|
6
|
+
import { readInstanceLock, classifyInstance, lockPath, isPidAlive } from "./instance-discovery.js";
|
|
7
7
|
import { makeErrorResult } from "./results.js";
|
|
8
8
|
import { checkBridgeCompat, isVersionCheckSuppressed, } from "./compat.js";
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// M23 Plan 3 — structured retry policy + env-overridable tunables. Replaces
|
|
10
|
+
// the hardcoded module constants that previously bounded the compile-wait and
|
|
11
|
+
// transient-retry loops. The policy is keyed by lifecycle class (see
|
|
12
|
+
// retryConfigFor); the tunables are resolved once at construction.
|
|
13
|
+
import { readRetryTunables, } from "./retry-policy.js";
|
|
14
|
+
// M23 Plan 3 — compile-verify failure-code detection (compile_noop /
|
|
15
|
+
// dll_stale). Applied as an additive annotation on compile-reload results so
|
|
16
|
+
// an agent can branch when a recompile reported success but the compiled state
|
|
17
|
+
// did not advance.
|
|
18
|
+
import { detectCompileVerify, buildCompileVerifyAnnotation, } from "./compile-verify.js";
|
|
19
|
+
import { lifecycleFor } from "./capabilities/lifecycle.js";
|
|
20
|
+
// M23 Plan 3 — per-process agent identity (sent as X-Agent-Id so the bridge's
|
|
21
|
+
// fair round-robin queue can schedule across agents).
|
|
22
|
+
import { PROCESS_AGENT_ID } from "./agent-identity.js";
|
|
11
23
|
const PING_TIMEOUT_MS = 5_000;
|
|
24
|
+
// The bridge's default per-tool wait before IT gives up and returns a timeout
|
|
25
|
+
// envelope (packages/bridge/Editor/Bridge/BridgeRequestBody.cs
|
|
26
|
+
// DefaultTimeoutMs). The client fetch timeout must never preempt this — if the
|
|
27
|
+
// client aborts first it re-POSTs while the bridge is still processing the
|
|
28
|
+
// original Work, manufacturing duplicate side-effects (specs/feedback.md entry
|
|
29
|
+
// 4B/4A). postTool floors its fetch timeout at BRIDGE_DEFAULT_TIMEOUT_MS +
|
|
30
|
+
// slack so a small/absent timeout_ms can't make the client give up before the
|
|
31
|
+
// bridge. Kept as a literal here (not imported from the C# side) because the
|
|
32
|
+
// bridge assembly isn't readable from the TS server; the cross-reference above
|
|
33
|
+
// is the contract — bump both together.
|
|
34
|
+
const BRIDGE_DEFAULT_TIMEOUT_MS = 30_000;
|
|
12
35
|
const DIRECT_RESPONSE_TOOLS = new Set([
|
|
13
36
|
"unity_open_mcp_validate_edit",
|
|
14
37
|
"unity_open_mcp_checkpoint_create",
|
|
@@ -40,6 +63,45 @@ const DIRECT_RESPONSE_TOOLS = new Set([
|
|
|
40
63
|
function sleep(ms) {
|
|
41
64
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
42
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Decide whether {@link LiveClient#postTool}'s catch block should re-POST after
|
|
68
|
+
* a transient fetch failure. Exported so the retry policy is unit-testable in
|
|
69
|
+
* isolation (driving a real response-timeout through postTool takes 40s because
|
|
70
|
+
* the fetch timeout is floored at BRIDGE_DEFAULT_TIMEOUT_MS + slack).
|
|
71
|
+
*
|
|
72
|
+
* undici (Node's fetch) throws a DISTINGUISHABLE error class for the two
|
|
73
|
+
* failure modes that reach this catch:
|
|
74
|
+
* - `TypeError` ("fetch failed") → CONNECTION failure: the socket never
|
|
75
|
+
* connected, was reset before the request body flushed, or the listen port
|
|
76
|
+
* is dead (ECONNREFUSED). No bytes reached the server → no Work was queued
|
|
77
|
+
* → no side effect is possible. Re-POSTing is ALWAYS safe, even on an
|
|
78
|
+
* unchanged endpoint (the same live bridge never received the first POST).
|
|
79
|
+
* - `AbortError` (AbortController timer) → RESPONSE timeout: the request
|
|
80
|
+
* connected and the server is still processing it. A side effect may have
|
|
81
|
+
* happened or be queued. Re-POSTing is only safe when the endpoint CHANGED
|
|
82
|
+
* during recovery (a restart → new bridge that never saw the original POST);
|
|
83
|
+
* on an unchanged endpoint the original call may still complete there, so
|
|
84
|
+
* re-POSTing would risk running the mutation a second time.
|
|
85
|
+
*
|
|
86
|
+
* This distinction matters because a burst of parallel /tools/* calls can leave
|
|
87
|
+
* a dead/reset connection in the pool: the next POST throws a TypeError on
|
|
88
|
+
* connect, and the old "unchanged endpoint → no retry" guard wedged the whole
|
|
89
|
+
* typed-editor surface even though the bridge was healthy and idle
|
|
90
|
+
* (specs/feedback.md entry 2026-07-02-b). The bug was that the guard conflated
|
|
91
|
+
* "endpoint unchanged" with "request in flight," which is only true when the
|
|
92
|
+
* fetch actually connected (AbortError), not when it failed to connect
|
|
93
|
+
* (TypeError).
|
|
94
|
+
*/
|
|
95
|
+
export function shouldRetryPostAfterFailure(err, endpointChanged) {
|
|
96
|
+
// An endpoint change (Unity restart detected during recovery) always makes a
|
|
97
|
+
// retry safe regardless of failure class — the new bridge never saw the POST.
|
|
98
|
+
if (endpointChanged)
|
|
99
|
+
return true;
|
|
100
|
+
// Unchanged endpoint: only retry on a connection failure (TypeError). A
|
|
101
|
+
// response timeout (AbortError or anything else) on the same endpoint means
|
|
102
|
+
// the request may have reached the bridge → suppress the retry.
|
|
103
|
+
return err instanceof TypeError;
|
|
104
|
+
}
|
|
43
105
|
const OFFLINE_HINT = "Ensure the Unity Editor is open with the Agent Bridge running. " +
|
|
44
106
|
"The bridge port is per-project (20000 + sha256(projectPath) % 10000), not " +
|
|
45
107
|
"fixed — if Unity is open the MCP server may be aimed at the wrong port. " +
|
|
@@ -74,6 +136,13 @@ export class LiveClient {
|
|
|
74
136
|
dismissEnabled;
|
|
75
137
|
dismissTimeoutMs;
|
|
76
138
|
dismissIntervalMs;
|
|
139
|
+
/** M23 Plan 2 — active dialog policy (auto/manual/ignore/recover/safe-mode/
|
|
140
|
+
* cancel). Stored so waitForCompile can thread it into the dismiss loop
|
|
141
|
+
* without re-reading the env on every stall. */
|
|
142
|
+
dialogPolicy;
|
|
143
|
+
/** M23 Plan 2 — opt-in for the Project Upgrade Required dialog (irreversible,
|
|
144
|
+
* off by default). */
|
|
145
|
+
allowProjectUpgrade;
|
|
77
146
|
/** M14 — per-session bearer token auto-discovered from the instance lock.
|
|
78
147
|
* Undefined when no live lock was found (older bridge / env port override);
|
|
79
148
|
* in that case no Authorization header is sent and the bridge must be in
|
|
@@ -87,20 +156,44 @@ export class LiveClient {
|
|
|
87
156
|
/** One-shot guard so the version-compat warning (server vs bridge) is emitted
|
|
88
157
|
* at most once per process — every /ping would otherwise re-warn. */
|
|
89
158
|
compatWarned;
|
|
90
|
-
|
|
159
|
+
/** M23 Plan 3 — env-overridable retry/compile-wait tunables. Resolved once
|
|
160
|
+
* at construction (the env does not change mid-process). Replaces the
|
|
161
|
+
* hardcoded MAX_COMPILE_WAIT_MS / COMPILE_POLL_INTERVAL_MS /
|
|
162
|
+
* TRANSIENT_RETRY_* module constants. */
|
|
163
|
+
retry;
|
|
164
|
+
/** M23 Plan 3 — agent identity sent as X-Agent-Id on every request so the
|
|
165
|
+
* bridge's fair round-robin queue can schedule across agents sharing one
|
|
166
|
+
* bridge. Defaults to the process-wide id; a transient LiveClient built for
|
|
167
|
+
* a per-request port override may carry a per-call id. */
|
|
168
|
+
agentId;
|
|
169
|
+
/** Parsed UNITY_OPEN_MCP_BRIDGE_PORT, or undefined. When set, the env
|
|
170
|
+
* override is authoritative and refreshEndpointFromLock() is a no-op (there
|
|
171
|
+
* is no lock to read — matches resolvePort/resolveAuthToken semantics).
|
|
172
|
+
* Stored so a mid-session refresh can respect the same override precedence
|
|
173
|
+
* the constructor used; without it a refresh would silently switch from an
|
|
174
|
+
* env-pinned port to the lock port. */
|
|
175
|
+
envPort;
|
|
176
|
+
constructor(port, pingCache, authToken, projectPath, agentId = PROCESS_AGENT_ID, envPort) {
|
|
91
177
|
this.baseUrl = `http://127.0.0.1:${port}`;
|
|
92
178
|
this.pingCache = pingCache;
|
|
93
179
|
this.authToken = authToken;
|
|
94
180
|
this.projectPath = projectPath;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
//
|
|
99
|
-
//
|
|
181
|
+
this.retry = readRetryTunables();
|
|
182
|
+
this.agentId = agentId;
|
|
183
|
+
this.envPort = envPort;
|
|
184
|
+
// M13 T4.5 + M23 Plan 2 — startup dialog auto-dismissal. Resolved once at
|
|
185
|
+
// construction; the env vars do not change mid-process. The feature is
|
|
186
|
+
// enabled by default and runs concurrently with every compile/bridge
|
|
187
|
+
// readiness wait so it ticks on the same stall points agents hit, not
|
|
188
|
+
// only at process spawn. M23 Plan 2 generalizes T4.5's single Ignore
|
|
189
|
+
// click into the full 6-variant policy taxonomy (dialog-policy.ts) so
|
|
190
|
+
// different workflows can pick different buttons on the same dialog.
|
|
100
191
|
const dismissCfg = readDismissConfig();
|
|
101
192
|
this.dismissEnabled = dismissCfg.enabled;
|
|
102
193
|
this.dismissTimeoutMs = dismissCfg.timeoutMs;
|
|
103
194
|
this.dismissIntervalMs = dismissCfg.intervalMs;
|
|
195
|
+
this.dialogPolicy = dismissCfg.policy;
|
|
196
|
+
this.allowProjectUpgrade = dismissCfg.allowProjectUpgrade;
|
|
104
197
|
this.compatWarned = false;
|
|
105
198
|
}
|
|
106
199
|
/**
|
|
@@ -176,7 +269,18 @@ export class LiveClient {
|
|
|
176
269
|
const readyError = await this.ensureReady();
|
|
177
270
|
if (readyError)
|
|
178
271
|
return readyError;
|
|
179
|
-
|
|
272
|
+
// M23 Plan 3 — compile-verify annotation. For compile-reload tools, capture
|
|
273
|
+
// a before-snapshot (bridge tool count + newest ScriptAssemblies DLL mtime)
|
|
274
|
+
// so the post-call detectCompileVerify() can flag a no-op/stale compile.
|
|
275
|
+
// Non-compile-reload tools skip this entirely (no extra /tools round-trip,
|
|
276
|
+
// no fs scan). The annotation is additive: it never blocks a success.
|
|
277
|
+
const isCompileReload = lifecycleFor(toolName).class === "compile-reload";
|
|
278
|
+
const before = isCompileReload ? await this.captureCompileSnapshot() : null;
|
|
279
|
+
const result = await this.postTool(toolName, args, true);
|
|
280
|
+
if (isCompileReload && !result.isError && before !== null) {
|
|
281
|
+
return this.annotateCompileVerify(toolName, result, before, args);
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
180
284
|
}
|
|
181
285
|
async handleRunTests(args) {
|
|
182
286
|
const readyError = await this.ensureReady();
|
|
@@ -258,11 +362,19 @@ export class LiveClient {
|
|
|
258
362
|
async postTool(toolName, args, retryOn503) {
|
|
259
363
|
try {
|
|
260
364
|
const timeoutMs = typeof args.timeout_ms === "number" ? args.timeout_ms : 60_000;
|
|
365
|
+
// Floor the client fetch timeout at the bridge's own default wait + slack
|
|
366
|
+
// so the client never aborts before the bridge has had a chance to return
|
|
367
|
+
// its timeout envelope. Without this floor, a small/absent timeout_ms
|
|
368
|
+
// (MCP hosts often omit the field → schema default 30000, or smaller)
|
|
369
|
+
// makes the client re-POST while the bridge is still processing →
|
|
370
|
+
// duplicate mutations (specs/feedback.md entry 4B). An explicit large
|
|
371
|
+
// timeout_ms still wins via the Math.max.
|
|
372
|
+
const fetchTimeout = Math.max(timeoutMs + 10_000, BRIDGE_DEFAULT_TIMEOUT_MS + 10_000);
|
|
261
373
|
const res = await this.fetchWithTimeout(`/tools/${toolName}`, {
|
|
262
374
|
method: "POST",
|
|
263
375
|
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
264
376
|
body: JSON.stringify(args),
|
|
265
|
-
},
|
|
377
|
+
}, fetchTimeout);
|
|
266
378
|
if (res.status === 503 && retryOn503) {
|
|
267
379
|
const compileError = await this.waitForCompile();
|
|
268
380
|
if (compileError)
|
|
@@ -284,55 +396,113 @@ export class LiveClient {
|
|
|
284
396
|
},
|
|
285
397
|
});
|
|
286
398
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
399
|
+
// The bridge returns TWO response shapes from /tools/*:
|
|
400
|
+
// 1. A MUTATION envelope: { mutation: { success, ... }, gate: {...} }
|
|
401
|
+
// for gate-tracked mutating tools (gameobject_*, assets_*, etc.).
|
|
402
|
+
// 2. A DIRECT body: the tool's own JSON (e.g. { status, count, tags })
|
|
403
|
+
// for read-only / state-only tools the bridge classifies as
|
|
404
|
+
// DirectResponseTools (editor_get_tags, asmdef_list, shader_list_all,
|
|
405
|
+
// scene_list_opened, material_get_properties, ~80 total).
|
|
406
|
+
//
|
|
407
|
+
// Previously postTool dispatched off a hardcoded DIRECT_RESPONSE_TOOLS set
|
|
408
|
+
// that was ~60 entries behind the bridge's set. Tools missing from it fell
|
|
409
|
+
// through to deriveIsError(body), which dereferences body.mutation.success
|
|
410
|
+
// — undefined for a direct body → TypeError. That thrown TypeError landed
|
|
411
|
+
// in the connection-recovery catch block, surfacing a misleading
|
|
412
|
+
// bridge_offline (and, with the entry-2026-07-02-b retry change, looping).
|
|
413
|
+
//
|
|
414
|
+
// The fix: detect the envelope shape from the BODY itself. A response with
|
|
415
|
+
// a top-level `mutation` object is a mutation envelope; anything else is a
|
|
416
|
+
// direct body. This is reliable because the bridge's DirectResponseTools
|
|
417
|
+
// path (HandleDirectResponseTool) never emits a `mutation` field and the
|
|
418
|
+
// gate path always does (BuildMutationEnvelope). The DIRECT_RESPONSE_TOOLS
|
|
419
|
+
// set is now only the inlineImage-unwrap allowlist (capture_inline), kept
|
|
420
|
+
// for that one special-case transform, not for shape routing.
|
|
421
|
+
const parsed = (await res.json().catch(() => null));
|
|
422
|
+
// capture_inline returns an `inlineImage` base64 PNG field. Unwrap it into
|
|
423
|
+
// an MCP image content block alongside a text metadata block
|
|
424
|
+
// ([{type: image}, {type: text}]). The image carries the same viewable
|
|
425
|
+
// payload a file-path screenshot would; the metadata keeps the view /
|
|
426
|
+
// resolution / byteLength fields without the base64 blob.
|
|
427
|
+
const inlineImage = parsed?.inlineImage;
|
|
428
|
+
if (DIRECT_RESPONSE_TOOLS.has(toolName) &&
|
|
429
|
+
parsed != null &&
|
|
430
|
+
typeof inlineImage === "string" &&
|
|
431
|
+
inlineImage.length > 0 &&
|
|
432
|
+
parsed.error == null) {
|
|
433
|
+
const mimeType = typeof parsed.mimeType === "string" ? parsed.mimeType : "image/png";
|
|
434
|
+
const metadata = { ...parsed };
|
|
435
|
+
delete metadata.inlineImage;
|
|
312
436
|
return {
|
|
313
437
|
content: [
|
|
314
|
-
{
|
|
315
|
-
|
|
316
|
-
text: JSON.stringify(directBody ?? {}),
|
|
317
|
-
},
|
|
438
|
+
{ type: "image", data: inlineImage, mimeType },
|
|
439
|
+
{ type: "text", text: JSON.stringify(metadata) },
|
|
318
440
|
],
|
|
319
|
-
isError:
|
|
441
|
+
isError: false,
|
|
320
442
|
};
|
|
321
443
|
}
|
|
322
|
-
|
|
444
|
+
// Mutation envelope vs direct body, decided from the body itself.
|
|
445
|
+
if (parsed != null && typeof parsed.mutation === "object" && parsed.mutation !== null) {
|
|
446
|
+
const body = parsed;
|
|
447
|
+
return {
|
|
448
|
+
content: [{ type: "text", text: JSON.stringify(body) }],
|
|
449
|
+
isError: deriveIsError(body),
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
// Direct body: return it verbatim. isError when the tool reported a
|
|
453
|
+
// top-level error field (the bridge's direct-response convention).
|
|
323
454
|
return {
|
|
324
|
-
content: [
|
|
325
|
-
|
|
455
|
+
content: [
|
|
456
|
+
{
|
|
457
|
+
type: "text",
|
|
458
|
+
text: JSON.stringify(parsed ?? {}),
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
isError: parsed?.error != null,
|
|
326
462
|
};
|
|
327
463
|
}
|
|
328
|
-
catch {
|
|
464
|
+
catch (err) {
|
|
465
|
+
// M20 Plan 4-5 / T-fix-2 — transient POST failure (ECONNREFUSED /
|
|
466
|
+
// timeout during a domain reload). Classify + recover before declaring
|
|
467
|
+
// offline. handleTransientOffline re-syncs the cached endpoint from the
|
|
468
|
+
// lock first (so a Unity restart self-heals).
|
|
469
|
+
//
|
|
470
|
+
// Re-POST discipline (specs/feedback.md entry 4A — duplicate side-effects):
|
|
471
|
+
// only re-POST when the endpoint CHANGED during recovery (a restart → new
|
|
472
|
+
// bridge that never saw the original POST, so a retry is safe). When the
|
|
473
|
+
// endpoint is unchanged the same live bridge is still processing — or has
|
|
474
|
+
// queued — the original POST's Work; re-POSTing would run the mutation a
|
|
475
|
+
// second time. In that case surface the timeout instead of retrying.
|
|
476
|
+
//
|
|
477
|
+
// shouldRetryPostAfterFailure additionally allows a re-POST on a genuine
|
|
478
|
+
// CONNECTION failure (TypeError — the socket never connected / was reset
|
|
479
|
+
// before the body flushed / ECONNREFUSED), because no bytes reached the
|
|
480
|
+
// server and no side effect is possible. This must NOT fire for a
|
|
481
|
+
// TypeError thrown by response parsing (e.g. deriveIsError on a malformed
|
|
482
|
+
// body) — that path is now guarded by parsing the body defensively above,
|
|
483
|
+
// but as a belt-and-braces measure the response-parsing path is structured
|
|
484
|
+
// so it cannot throw into this catch (every .json() is .catch()-guarded).
|
|
485
|
+
const endpointBefore = this.baseUrl;
|
|
486
|
+
const recovered = await this.handleTransientOffline("post");
|
|
487
|
+
if (recovered !== null)
|
|
488
|
+
return recovered;
|
|
489
|
+
const endpointChanged = this.baseUrl !== endpointBefore;
|
|
490
|
+
if (shouldRetryPostAfterFailure(err, endpointChanged)) {
|
|
491
|
+
return this.postTool(toolName, args, retryOn503);
|
|
492
|
+
}
|
|
329
493
|
return makeErrorResult({
|
|
330
494
|
code: "bridge_offline",
|
|
331
|
-
message: `
|
|
495
|
+
message: `Tool '${toolName}' did not return from the bridge within the client ` +
|
|
496
|
+
`timeout. The bridge endpoint did not change (no restart detected) and ` +
|
|
497
|
+
`the failure was a response timeout (not a connection failure), so ` +
|
|
498
|
+
`the request was NOT retried — re-POSTing would risk running the ` +
|
|
499
|
+
`mutation twice on the same bridge. If the editor was briefly slow ` +
|
|
500
|
+
`(unfocused, GC, a long main-thread op), the original call may still ` +
|
|
501
|
+
`complete there; verify the effect before retrying. Endpoint: ${this.baseUrl}.`,
|
|
332
502
|
detail: {
|
|
333
503
|
error: {
|
|
334
504
|
code: "bridge_offline",
|
|
335
|
-
message: `
|
|
505
|
+
message: `Client timeout against unchanged endpoint ${this.baseUrl}; not retried to avoid duplicate side-effects.`,
|
|
336
506
|
},
|
|
337
507
|
},
|
|
338
508
|
});
|
|
@@ -371,14 +541,42 @@ export class LiveClient {
|
|
|
371
541
|
}
|
|
372
542
|
catch {
|
|
373
543
|
// Bridge is unreachable (ECONNREFUSED / timeout). Before falling back to
|
|
374
|
-
// the generic offline error,
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
544
|
+
// the generic offline error, classify the failure via the instance lock:
|
|
545
|
+
// - dead_bridge → bridge assembly failed to recompile (fail fast)
|
|
546
|
+
// - reloading/compiling → a normal domain reload is in flight; wait it
|
|
547
|
+
// out (the listener socket is torn down for the reload duration) then
|
|
548
|
+
// re-probe once before declaring offline (T-fix-2)
|
|
549
|
+
// - otherwise → retry with backoff a few times, then offline
|
|
550
|
+
// handleTransientOffline refreshes the cached endpoint from the lock
|
|
551
|
+
// first, so a Unity restart (new PID/port) re-points this client before
|
|
552
|
+
// the retry probes (specs/feedback.md entry 2).
|
|
553
|
+
const recovered = await this.handleTransientOffline("ping");
|
|
554
|
+
if (recovered !== null)
|
|
555
|
+
return recovered;
|
|
556
|
+
// Recovery returned null ("recovered" — a /ping probe succeeded during
|
|
557
|
+
// handleTransientOffline, possibly against a freshly-refreshed endpoint
|
|
558
|
+
// after a restart). Re-probe /ping ONCE on the (possibly new) endpoint
|
|
559
|
+
// before declaring offline: without this, a restart that refreshed the
|
|
560
|
+
// endpoint mid-recovery would still surface bridge_offline here, and the
|
|
561
|
+
// caller (handleToolCall) treats any non-null ensureReady result as a
|
|
562
|
+
// terminal error — so the next tool call would fail despite the bridge
|
|
563
|
+
// being back. The single re-probe closes that gap.
|
|
564
|
+
try {
|
|
565
|
+
const retryRes = await this.fetchWithTimeout("/ping", { method: "GET" });
|
|
566
|
+
if (retryRes.status === 503)
|
|
567
|
+
return this.waitForCompile();
|
|
568
|
+
if (retryRes.ok) {
|
|
569
|
+
const retryBody = (await retryRes.json());
|
|
570
|
+
this.pingCache.record(retryBody);
|
|
571
|
+
if (retryBody.compiling)
|
|
572
|
+
return this.waitForCompile();
|
|
573
|
+
if (retryBody.connected)
|
|
574
|
+
return null; // recovered against the new endpoint
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
catch {
|
|
578
|
+
// Re-probe also failed — fall through to the offline error below.
|
|
579
|
+
}
|
|
382
580
|
return makeErrorResult({
|
|
383
581
|
code: "bridge_offline",
|
|
384
582
|
message: `Bridge is not reachable at ${this.baseUrl}. ${buildOfflineHint(this.projectPath)}`,
|
|
@@ -391,6 +589,157 @@ export class LiveClient {
|
|
|
391
589
|
});
|
|
392
590
|
}
|
|
393
591
|
}
|
|
592
|
+
/**
|
|
593
|
+
* M20 Plan 4-5 / T-fix-2 — classify a transient /ping or tool-POST failure
|
|
594
|
+
* (ECONNREFUSED / timeout / socket reset) via the instance lock and either
|
|
595
|
+
* recover or surface the right error.
|
|
596
|
+
*
|
|
597
|
+
* Returns:
|
|
598
|
+
* - a structured `bridge_compile_failed` result when the lock shows a dead
|
|
599
|
+
* bridge assembly (fail fast — preserves the pre-existing fast-path),
|
|
600
|
+
* - `null` to signal "recovered — the caller should retry its operation"
|
|
601
|
+
* after either waiting out a `reloading`/`compiling` lock window or
|
|
602
|
+
* succeeding a bounded backoff retry,
|
|
603
|
+
* - a `bridge_offline` result when the bridge is genuinely unreachable after
|
|
604
|
+
* the recovery attempts (so the caller's outer `bridge_offline` fallback
|
|
605
|
+
* is reached only via this returned result).
|
|
606
|
+
*
|
|
607
|
+
* `operation` is "ping" | "post" and only affects the log line — both paths
|
|
608
|
+
* share the same recovery logic.
|
|
609
|
+
*/
|
|
610
|
+
async handleTransientOffline(operation) {
|
|
611
|
+
// Before any classification/retry, re-sync the cached endpoint to the
|
|
612
|
+
// current live bridge. A Unity restart writes a fresh PID/port/authToken
|
|
613
|
+
// to the lock; without this refresh every retry below would keep hitting
|
|
614
|
+
// the dead cached listener (specs/feedback.md entry 2) and, on the POST
|
|
615
|
+
// path, re-dispatch the Work to the new bridge (duplicate side-effects,
|
|
616
|
+
// entry 4A). refreshEndpointFromLock is a no-op when an env-port override
|
|
617
|
+
// is in force or nothing has changed.
|
|
618
|
+
this.refreshEndpointFromLock();
|
|
619
|
+
// Dead bridge assembly — not recoverable by waiting. Fail fast so the
|
|
620
|
+
// agent can fetch compile errors instead of hanging on /ping.
|
|
621
|
+
const deadBridge = this.deadBridgeResult();
|
|
622
|
+
if (deadBridge)
|
|
623
|
+
return deadBridge;
|
|
624
|
+
const classification = this.classifyLockNow();
|
|
625
|
+
// A normal domain reload in flight: the listener socket is torn down for
|
|
626
|
+
// the reload duration (BridgeHttpServer.OnBeforeAssemblyReload →
|
|
627
|
+
// _listener.Stop()). classifyInstance folds both lock states
|
|
628
|
+
// "reloading" and "compiling" into the "reloading" classification
|
|
629
|
+
// (instance-discovery.ts) — so a single check covers the whole compile +
|
|
630
|
+
// reload window. Wait it out, then signal the caller to retry.
|
|
631
|
+
if (classification === "reloading") {
|
|
632
|
+
const waitError = await this.waitForCompile();
|
|
633
|
+
if (waitError)
|
|
634
|
+
return waitError; // timed out or dead_bridge mid-wait
|
|
635
|
+
return null; // recovered — caller retries the original operation
|
|
636
|
+
}
|
|
637
|
+
// No reload signal but a transient refusal/reset/timeout. Retry a bounded
|
|
638
|
+
// number of times with backoff before declaring offline. Covers brief
|
|
639
|
+
// socket churn that isn't reflected in the lock yet. The bounds come from
|
|
640
|
+
// the env-overridable retry tunables (M23 Plan 3 / retry-policy.ts).
|
|
641
|
+
const maxAttempts = this.retry.transientRetryAttempts;
|
|
642
|
+
const backoffMs = this.retry.transientBackoffMs;
|
|
643
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
644
|
+
await sleep(backoffMs * attempt);
|
|
645
|
+
// Re-classify on each attempt: a reload may have begun between the
|
|
646
|
+
// first failure and now.
|
|
647
|
+
const reclass = this.classifyLockNow();
|
|
648
|
+
if (reclass === "dead_bridge")
|
|
649
|
+
return this.deadBridgeResult();
|
|
650
|
+
if (reclass === "reloading") {
|
|
651
|
+
const waitError = await this.waitForCompile();
|
|
652
|
+
if (waitError)
|
|
653
|
+
return waitError;
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
// Probe whether the listener is back up.
|
|
657
|
+
try {
|
|
658
|
+
const res = await this.fetchWithTimeout("/ping", { method: "GET" });
|
|
659
|
+
if (res.ok || res.status === 503) {
|
|
660
|
+
// Listener responded — recovered. Caller re-runs its readiness /
|
|
661
|
+
// POST. (503 still counts: it means compiling, handled on retry.)
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
catch {
|
|
666
|
+
// Still down — loop and back off again.
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
void operation; // log-only parameter, reserved for future telemetry
|
|
670
|
+
const retryMsg = `Bridge is not reachable at ${this.baseUrl} after ${maxAttempts} retries. ${buildOfflineHint(this.projectPath)}`;
|
|
671
|
+
return makeErrorResult({
|
|
672
|
+
code: "bridge_offline",
|
|
673
|
+
message: retryMsg,
|
|
674
|
+
detail: {
|
|
675
|
+
error: {
|
|
676
|
+
code: "bridge_offline",
|
|
677
|
+
message: retryMsg,
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Re-resolve the bridge endpoint (port + authToken) from the on-disk instance
|
|
684
|
+
* lock and update the cached {@link baseUrl} / {@link authToken} when the
|
|
685
|
+
* live bridge has moved. Returns true when the endpoint changed.
|
|
686
|
+
*
|
|
687
|
+
* This is the single fix for two coupled failure modes (specs/feedback.md
|
|
688
|
+
* entries 2 + 4A): the client cached the port/token once at construction,
|
|
689
|
+
* so a Unity restart (new PID/port/authToken in the lock) left every retry
|
|
690
|
+
* hitting a dead listener — producing minutes of `bridge_offline` *and*
|
|
691
|
+
* (because {@link postTool} re-POSTs on recovery) duplicate side-effects
|
|
692
|
+
* once the editor came back. Refreshing here lets a restart self-heal.
|
|
693
|
+
*
|
|
694
|
+
* No-op (returns false) when there is no projectPath, an env-port override
|
|
695
|
+
* is in force (env is authoritative — no lock to read, matching
|
|
696
|
+
* resolvePort/resolveAuthToken), or the lock is missing/its PID is dead/
|
|
697
|
+
* nothing actually changed. Never throws — readInstanceLock/isPidAlive are
|
|
698
|
+
* already fault-tolerant.
|
|
699
|
+
*/
|
|
700
|
+
refreshEndpointFromLock() {
|
|
701
|
+
if (!this.projectPath)
|
|
702
|
+
return false;
|
|
703
|
+
if (typeof this.envPort === "number")
|
|
704
|
+
return false; // env override wins
|
|
705
|
+
let lock;
|
|
706
|
+
try {
|
|
707
|
+
lock = readInstanceLock(this.projectPath);
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
if (!lock)
|
|
713
|
+
return false;
|
|
714
|
+
if (!isPidAlive(lock.pid))
|
|
715
|
+
return false;
|
|
716
|
+
const portChanged = typeof lock.port === "number" &&
|
|
717
|
+
lock.port > 0 &&
|
|
718
|
+
this.baseUrl !== `http://127.0.0.1:${lock.port}`;
|
|
719
|
+
const tokenChanged = lock.authToken !== this.authToken;
|
|
720
|
+
if (!portChanged && !tokenChanged)
|
|
721
|
+
return false;
|
|
722
|
+
if (portChanged)
|
|
723
|
+
this.baseUrl = `http://127.0.0.1:${lock.port}`;
|
|
724
|
+
if (tokenChanged)
|
|
725
|
+
this.authToken = lock.authToken;
|
|
726
|
+
return true;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Read + classify the instance lock at the current moment, without throwing.
|
|
730
|
+
* Returns "gone" (treated as no-signal) when the lock can't be read or the
|
|
731
|
+
* project path is unknown. T-fix-2 helper.
|
|
732
|
+
*/
|
|
733
|
+
classifyLockNow() {
|
|
734
|
+
if (!this.projectPath)
|
|
735
|
+
return "gone";
|
|
736
|
+
try {
|
|
737
|
+
return classifyInstance(readInstanceLock(this.projectPath));
|
|
738
|
+
}
|
|
739
|
+
catch {
|
|
740
|
+
return "gone";
|
|
741
|
+
}
|
|
742
|
+
}
|
|
394
743
|
// Detect the dead-bridge-assembly signature from the on-disk instance lock:
|
|
395
744
|
// Unity process alive (PID running) but heartbeat stale (the bridge's
|
|
396
745
|
// [InitializeOnLoad] never re-ran after a compile failure, so the heartbeat
|
|
@@ -433,14 +782,15 @@ export class LiveClient {
|
|
|
433
782
|
};
|
|
434
783
|
}
|
|
435
784
|
async waitForCompile() {
|
|
436
|
-
const
|
|
785
|
+
const compileWaitMs = this.retry.compileWaitMs;
|
|
786
|
+
const deadline = Date.now() + compileWaitMs;
|
|
437
787
|
// M13 T4.5 — run the launch-errors / Safe Mode dialog auto-dismiss loop
|
|
438
788
|
// CONCURRENTLY with the compile poll. The dismiss loop ticks on the same
|
|
439
|
-
// stall point this method represents
|
|
440
|
-
//
|
|
441
|
-
//
|
|
442
|
-
//
|
|
443
|
-
//
|
|
789
|
+
// stall point this method represents: if a native modal is blocking the
|
|
790
|
+
// Editor, the compile poll below will spin until timeout with no recovery.
|
|
791
|
+
// The dismiss loop clicks Ignore on that modal; the moment compile
|
|
792
|
+
// resolves (compiling → idle), we abort the loop — there is no launch
|
|
793
|
+
// dialog left to dismiss once Unity is idle.
|
|
444
794
|
//
|
|
445
795
|
// When auto-dismiss is opted out (UNITY_OPEN_MCP_NO_AUTO_DISMISS_LAUNCH_ERRORS=1),
|
|
446
796
|
// `dismissEnabled` is false and this branch is skipped entirely,
|
|
@@ -452,13 +802,15 @@ export class LiveClient {
|
|
|
452
802
|
const dismissOpts = {
|
|
453
803
|
timeoutMs: this.dismissTimeoutMs,
|
|
454
804
|
intervalMs: this.dismissIntervalMs,
|
|
805
|
+
policy: this.dialogPolicy,
|
|
806
|
+
allowProjectUpgrade: this.allowProjectUpgrade,
|
|
455
807
|
abortSignal: dismissAbort.signal,
|
|
456
808
|
};
|
|
457
809
|
dismissDone = this.runDismissLoop(dismissOpts);
|
|
458
810
|
}
|
|
459
811
|
try {
|
|
460
812
|
while (Date.now() < deadline) {
|
|
461
|
-
await sleep(
|
|
813
|
+
await sleep(this.retry.compilePollIntervalMs);
|
|
462
814
|
try {
|
|
463
815
|
const res = await this.fetchWithTimeout("/ping", { method: "GET" });
|
|
464
816
|
if (res.status === 503)
|
|
@@ -489,12 +841,12 @@ export class LiveClient {
|
|
|
489
841
|
return deadBridge;
|
|
490
842
|
return makeErrorResult({
|
|
491
843
|
code: "compile_timeout",
|
|
492
|
-
message: `Unity is still compiling after ${
|
|
844
|
+
message: `Unity is still compiling after ${compileWaitMs / 1000}s. ` +
|
|
493
845
|
"The compile-wait timeout was exceeded.",
|
|
494
846
|
detail: {
|
|
495
847
|
error: {
|
|
496
848
|
code: "compile_timeout",
|
|
497
|
-
message: `Compile-wait exceeded ${
|
|
849
|
+
message: `Compile-wait exceeded ${compileWaitMs / 1000}s`,
|
|
498
850
|
},
|
|
499
851
|
},
|
|
500
852
|
});
|
|
@@ -507,13 +859,13 @@ export class LiveClient {
|
|
|
507
859
|
}
|
|
508
860
|
}
|
|
509
861
|
/**
|
|
510
|
-
* Run the
|
|
862
|
+
* Run the startup-dialog dismiss polling loop. Indirected through a method
|
|
511
863
|
* so unit tests can stub the OS-clicking loop without invoking
|
|
512
864
|
* PowerShell / osascript / xdotool. Production uses the real
|
|
513
|
-
* `
|
|
865
|
+
* `pollAndDismissDialogs`.
|
|
514
866
|
*/
|
|
515
867
|
runDismissLoop(opts) {
|
|
516
|
-
return
|
|
868
|
+
return pollAndDismissDialogs(opts);
|
|
517
869
|
}
|
|
518
870
|
async readResource(route) {
|
|
519
871
|
try {
|
|
@@ -563,6 +915,105 @@ export class LiveClient {
|
|
|
563
915
|
return null;
|
|
564
916
|
}
|
|
565
917
|
}
|
|
918
|
+
// -------------------------------------------------------------------------
|
|
919
|
+
// M23 Plan 3 — compile-verify annotation (compile_noop / dll_stale).
|
|
920
|
+
//
|
|
921
|
+
// For compile-reload tools we capture a before/after snapshot and let the
|
|
922
|
+
// pure detectCompileVerify() flag a no-op or stale compile. The annotation
|
|
923
|
+
// is additive (`_compileVerify` on the result body) — it never blocks a
|
|
924
|
+
// successful response, only surfaces a structured signal so an agent can
|
|
925
|
+
// branch instead of trusting a no-op success. Snapshots degrade gracefully:
|
|
926
|
+
// if the bridge inventory or DLL mtimes can't be read, the fields are
|
|
927
|
+
// undefined and the detector returns null (no false positive).
|
|
928
|
+
// -------------------------------------------------------------------------
|
|
929
|
+
/**
|
|
930
|
+
* Capture a compile-verify snapshot: the bridge tool inventory count + the
|
|
931
|
+
* newest mtime under Library/ScriptAssemblies. Returns undefined fields
|
|
932
|
+
* (never throws) when the bridge or filesystem is unavailable.
|
|
933
|
+
*/
|
|
934
|
+
async captureCompileSnapshot() {
|
|
935
|
+
const snap = {};
|
|
936
|
+
try {
|
|
937
|
+
const inventory = await this.listBridgeTools();
|
|
938
|
+
if (inventory)
|
|
939
|
+
snap.bridgeToolCount = inventory.tools.size;
|
|
940
|
+
}
|
|
941
|
+
catch {
|
|
942
|
+
// best-effort — undefined count is a valid "unknown" snapshot
|
|
943
|
+
}
|
|
944
|
+
const dllMtime = this.newestScriptAssembliesMtime();
|
|
945
|
+
if (dllMtime !== undefined)
|
|
946
|
+
snap.dllMtimeMs = dllMtime;
|
|
947
|
+
return snap;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Newest mtime (epoch ms) of `Library/ScriptAssemblies/*.dll` for this
|
|
951
|
+
* project, or undefined when the directory is missing/unreadable. Used by
|
|
952
|
+
* the compile-verify detector to tell whether a recompile actually advanced
|
|
953
|
+
* the compiled output.
|
|
954
|
+
*/
|
|
955
|
+
newestScriptAssembliesMtime() {
|
|
956
|
+
if (!this.projectPath)
|
|
957
|
+
return undefined;
|
|
958
|
+
const dir = join(this.projectPath, "Library", "ScriptAssemblies");
|
|
959
|
+
let entries;
|
|
960
|
+
try {
|
|
961
|
+
entries = readdirSync(dir);
|
|
962
|
+
}
|
|
963
|
+
catch {
|
|
964
|
+
return undefined;
|
|
965
|
+
}
|
|
966
|
+
let newest;
|
|
967
|
+
for (const name of entries) {
|
|
968
|
+
if (!name.endsWith(".dll"))
|
|
969
|
+
continue;
|
|
970
|
+
try {
|
|
971
|
+
const m = statSync(join(dir, name)).mtimeMs;
|
|
972
|
+
if (newest === undefined || m > newest)
|
|
973
|
+
newest = m;
|
|
974
|
+
}
|
|
975
|
+
catch {
|
|
976
|
+
// best-effort per file
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
return newest;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Apply the compile-verify annotation to a successful compile-reload result.
|
|
983
|
+
* Reads the after-snapshot, runs the pure detector, and — when flagged —
|
|
984
|
+
* injects `_compileVerify: { code, recommendation }` into the result body.
|
|
985
|
+
* The source-edit mtime is inferred from `args._sourceMtimeMs` when the
|
|
986
|
+
* caller (e.g. script_write) supplied it; otherwise only the no-op path
|
|
987
|
+
* (count + dll-mtime delta) is evaluated.
|
|
988
|
+
*/
|
|
989
|
+
async annotateCompileVerify(_toolName, result, before, args) {
|
|
990
|
+
const after = await this.captureCompileSnapshot();
|
|
991
|
+
const sourceMtimeMs = typeof args._sourceMtimeMs === "number"
|
|
992
|
+
? args._sourceMtimeMs
|
|
993
|
+
: undefined;
|
|
994
|
+
const detection = detectCompileVerify({ before, after, sourceMtimeMs });
|
|
995
|
+
const annotation = buildCompileVerifyAnnotation(detection);
|
|
996
|
+
if (annotation === null)
|
|
997
|
+
return result;
|
|
998
|
+
// Inject into the first text content block's JSON body. Mirrors the
|
|
999
|
+
// injectRouteMeta shape in tool-router.ts.
|
|
1000
|
+
const textIndex = result.content.findIndex((c) => c.type === "text");
|
|
1001
|
+
if (textIndex < 0)
|
|
1002
|
+
return result;
|
|
1003
|
+
const block = result.content[textIndex];
|
|
1004
|
+
if (block.type !== "text")
|
|
1005
|
+
return result;
|
|
1006
|
+
try {
|
|
1007
|
+
const body = JSON.parse(block.text);
|
|
1008
|
+
body._compileVerify = annotation;
|
|
1009
|
+
const newContent = result.content.slice();
|
|
1010
|
+
newContent[textIndex] = { type: "text", text: JSON.stringify(body) };
|
|
1011
|
+
return { ...result, content: newContent };
|
|
1012
|
+
}
|
|
1013
|
+
catch {
|
|
1014
|
+
return result;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
566
1017
|
fetchWithTimeout(path, init, timeoutMs = PING_TIMEOUT_MS) {
|
|
567
1018
|
const controller = new AbortController();
|
|
568
1019
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -573,6 +1024,12 @@ export class LiveClient {
|
|
|
573
1024
|
if (this.authToken && !headers.has("Authorization")) {
|
|
574
1025
|
headers.set("Authorization", `Bearer ${this.authToken}`);
|
|
575
1026
|
}
|
|
1027
|
+
// M23 Plan 3 — attach the agent identity so the bridge's fair round-robin
|
|
1028
|
+
// queue can schedule across agents sharing one bridge. Merge so a caller-
|
|
1029
|
+
// supplied header (rare) wins.
|
|
1030
|
+
if (this.agentId && !headers.has("X-Agent-Id")) {
|
|
1031
|
+
headers.set("X-Agent-Id", this.agentId);
|
|
1032
|
+
}
|
|
576
1033
|
return fetch(`${this.baseUrl}${path}`, {
|
|
577
1034
|
...init,
|
|
578
1035
|
headers,
|