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.
Files changed (447) hide show
  1. package/dist/agent-identity.d.ts +52 -0
  2. package/dist/agent-identity.js +115 -0
  3. package/dist/agent-identity.js.map +1 -0
  4. package/dist/batch-spawn.d.ts +7 -0
  5. package/dist/batch-spawn.js +155 -29
  6. package/dist/batch-spawn.js.map +1 -1
  7. package/dist/capabilities/build-capabilities.d.ts +45 -0
  8. package/dist/capabilities/build-capabilities.js +262 -27
  9. package/dist/capabilities/build-capabilities.js.map +1 -1
  10. package/dist/capabilities/cost-hints.d.ts +82 -0
  11. package/dist/capabilities/cost-hints.js +185 -0
  12. package/dist/capabilities/cost-hints.js.map +1 -0
  13. package/dist/capabilities/lifecycle.d.ts +40 -0
  14. package/dist/capabilities/lifecycle.js +353 -0
  15. package/dist/capabilities/lifecycle.js.map +1 -0
  16. package/dist/capabilities/rule-catalog.d.ts +13 -1
  17. package/dist/capabilities/rule-catalog.js +620 -75
  18. package/dist/capabilities/rule-catalog.js.map +1 -1
  19. package/dist/capabilities/tool-groups.d.ts +21 -0
  20. package/dist/capabilities/tool-groups.js +411 -14
  21. package/dist/capabilities/tool-groups.js.map +1 -1
  22. package/dist/cli/args.d.ts +32 -1
  23. package/dist/cli/args.js +201 -4
  24. package/dist/cli/args.js.map +1 -1
  25. package/dist/cli/cli.d.ts +21 -0
  26. package/dist/cli/cli.js +68 -5
  27. package/dist/cli/cli.js.map +1 -1
  28. package/dist/cli/commands.d.ts +33 -0
  29. package/dist/cli/commands.js +400 -0
  30. package/dist/cli/commands.js.map +1 -1
  31. package/dist/cli/exit-codes.d.ts +43 -0
  32. package/dist/cli/exit-codes.js +78 -0
  33. package/dist/cli/exit-codes.js.map +1 -0
  34. package/dist/compile-verify.d.ts +65 -0
  35. package/dist/compile-verify.js +94 -0
  36. package/dist/compile-verify.js.map +1 -0
  37. package/dist/compressible-router.js +64 -11
  38. package/dist/compressible-router.js.map +1 -1
  39. package/dist/compression/asset-model.d.ts +15 -0
  40. package/dist/compression/compact.d.ts +5 -4
  41. package/dist/compression/compact.js +13 -10
  42. package/dist/compression/compact.js.map +1 -1
  43. package/dist/dialog-dismiss.d.ts +127 -74
  44. package/dist/dialog-dismiss.js +427 -170
  45. package/dist/dialog-dismiss.js.map +1 -1
  46. package/dist/dialog-policy.d.ts +141 -0
  47. package/dist/dialog-policy.js +345 -0
  48. package/dist/dialog-policy.js.map +1 -0
  49. package/dist/gate-error.d.ts +6 -0
  50. package/dist/gate-error.js +13 -2
  51. package/dist/gate-error.js.map +1 -1
  52. package/dist/hub-control.d.ts +247 -0
  53. package/dist/hub-control.js +703 -0
  54. package/dist/hub-control.js.map +1 -0
  55. package/dist/index.d.ts +1 -1
  56. package/dist/index.js +46 -12
  57. package/dist/index.js.map +1 -1
  58. package/dist/live-client.d.ts +123 -3
  59. package/dist/live-client.js +526 -69
  60. package/dist/live-client.js.map +1 -1
  61. package/dist/offline.d.ts +105 -0
  62. package/dist/offline.js +816 -23
  63. package/dist/offline.js.map +1 -1
  64. package/dist/output-profile.d.ts +110 -0
  65. package/dist/output-profile.js +255 -0
  66. package/dist/output-profile.js.map +1 -0
  67. package/dist/retry-policy.d.ts +72 -0
  68. package/dist/retry-policy.js +146 -0
  69. package/dist/retry-policy.js.map +1 -0
  70. package/dist/routers.d.ts +4 -0
  71. package/dist/routers.js +2 -2
  72. package/dist/routers.js.map +1 -1
  73. package/dist/server-instructions.d.ts +18 -0
  74. package/dist/server-instructions.js +145 -0
  75. package/dist/server-instructions.js.map +1 -0
  76. package/dist/tool-router.d.ts +38 -1
  77. package/dist/tool-router.js +690 -37
  78. package/dist/tool-router.js.map +1 -1
  79. package/dist/tool-session-state.d.ts +37 -0
  80. package/dist/tool-session-state.js +78 -1
  81. package/dist/tool-session-state.js.map +1 -1
  82. package/dist/tools/apply-fix.js +16 -2
  83. package/dist/tools/apply-fix.js.map +1 -1
  84. package/dist/tools/asmdef-create.d.ts +2 -0
  85. package/dist/tools/asmdef-create.js +114 -0
  86. package/dist/tools/asmdef-create.js.map +1 -0
  87. package/dist/tools/asmdef-get.d.ts +2 -0
  88. package/dist/tools/asmdef-get.js +31 -0
  89. package/dist/tools/asmdef-get.js.map +1 -0
  90. package/dist/tools/asmdef-list.d.ts +2 -0
  91. package/dist/tools/asmdef-list.js +41 -0
  92. package/dist/tools/asmdef-list.js.map +1 -0
  93. package/dist/tools/asmdef-modify.d.ts +2 -0
  94. package/dist/tools/asmdef-modify.js +108 -0
  95. package/dist/tools/asmdef-modify.js.map +1 -0
  96. package/dist/tools/audio-listener-get.d.ts +2 -0
  97. package/dist/tools/audio-listener-get.js +16 -0
  98. package/dist/tools/audio-listener-get.js.map +1 -0
  99. package/dist/tools/audio-mixer-get-parameter.d.ts +2 -0
  100. package/dist/tools/audio-mixer-get-parameter.js +25 -0
  101. package/dist/tools/audio-mixer-get-parameter.js.map +1 -0
  102. package/dist/tools/audio-mixer-set-parameter.d.ts +2 -0
  103. package/dist/tools/audio-mixer-set-parameter.js +44 -0
  104. package/dist/tools/audio-mixer-set-parameter.js.map +1 -0
  105. package/dist/tools/audio-source-add.d.ts +2 -0
  106. package/dist/tools/audio-source-add.js +94 -0
  107. package/dist/tools/audio-source-add.js.map +1 -0
  108. package/dist/tools/audio-source-modify.d.ts +2 -0
  109. package/dist/tools/audio-source-modify.js +52 -0
  110. package/dist/tools/audio-source-modify.js.map +1 -0
  111. package/dist/tools/bridge-status.js +16 -6
  112. package/dist/tools/bridge-status.js.map +1 -1
  113. package/dist/tools/build-get-active-target.js +1 -2
  114. package/dist/tools/build-get-active-target.js.map +1 -1
  115. package/dist/tools/build-get-defines.js +1 -1
  116. package/dist/tools/build-get-defines.js.map +1 -1
  117. package/dist/tools/build-get-scenes.js +1 -2
  118. package/dist/tools/build-get-scenes.js.map +1 -1
  119. package/dist/tools/build-get-targets.js +1 -1
  120. package/dist/tools/build-get-targets.js.map +1 -1
  121. package/dist/tools/build-set-defines.js +1 -2
  122. package/dist/tools/build-set-defines.js.map +1 -1
  123. package/dist/tools/build-set-scenes.js +1 -1
  124. package/dist/tools/build-set-scenes.js.map +1 -1
  125. package/dist/tools/build-set-target.js +1 -1
  126. package/dist/tools/build-set-target.js.map +1 -1
  127. package/dist/tools/build-start.js +0 -1
  128. package/dist/tools/build-start.js.map +1 -1
  129. package/dist/tools/cinemachine-brain-ensure.d.ts +2 -0
  130. package/dist/tools/cinemachine-brain-ensure.js +27 -0
  131. package/dist/tools/cinemachine-brain-ensure.js.map +1 -0
  132. package/dist/tools/cinemachine-camera-list.d.ts +2 -0
  133. package/dist/tools/cinemachine-camera-list.js +15 -0
  134. package/dist/tools/cinemachine-camera-list.js.map +1 -0
  135. package/dist/tools/cinemachine-create-camera.d.ts +2 -0
  136. package/dist/tools/cinemachine-create-camera.js +55 -0
  137. package/dist/tools/cinemachine-create-camera.js.map +1 -0
  138. package/dist/tools/cinemachine-set-body.d.ts +2 -0
  139. package/dist/tools/cinemachine-set-body.js +33 -0
  140. package/dist/tools/cinemachine-set-body.js.map +1 -0
  141. package/dist/tools/cinemachine-set-lens.d.ts +2 -0
  142. package/dist/tools/cinemachine-set-lens.js +41 -0
  143. package/dist/tools/cinemachine-set-lens.js.map +1 -0
  144. package/dist/tools/cinemachine-set-noise.d.ts +2 -0
  145. package/dist/tools/cinemachine-set-noise.js +31 -0
  146. package/dist/tools/cinemachine-set-noise.js.map +1 -0
  147. package/dist/tools/cinemachine-set-targets.d.ts +2 -0
  148. package/dist/tools/cinemachine-set-targets.js +30 -0
  149. package/dist/tools/cinemachine-set-targets.js.map +1 -0
  150. package/dist/tools/console-log.js +2 -2
  151. package/dist/tools/console-log.js.map +1 -1
  152. package/dist/tools/constraint-add.d.ts +2 -0
  153. package/dist/tools/constraint-add.js +85 -0
  154. package/dist/tools/constraint-add.js.map +1 -0
  155. package/dist/tools/dependencies.d.ts +2 -0
  156. package/dist/tools/dependencies.js +76 -0
  157. package/dist/tools/dependencies.js.map +1 -0
  158. package/dist/tools/editor-add-tag.js +2 -3
  159. package/dist/tools/editor-add-tag.js.map +1 -1
  160. package/dist/tools/editor-clear-history.d.ts +2 -0
  161. package/dist/tools/editor-clear-history.js +25 -0
  162. package/dist/tools/editor-clear-history.js.map +1 -0
  163. package/dist/tools/editor-get-layers.js +3 -3
  164. package/dist/tools/editor-get-layers.js.map +1 -1
  165. package/dist/tools/editor-get-tags.js +2 -2
  166. package/dist/tools/editor-get-tags.js.map +1 -1
  167. package/dist/tools/editor-redo.js +2 -3
  168. package/dist/tools/editor-redo.js.map +1 -1
  169. package/dist/tools/editor-undo-history.d.ts +2 -0
  170. package/dist/tools/editor-undo-history.js +21 -0
  171. package/dist/tools/editor-undo-history.js.map +1 -0
  172. package/dist/tools/editor-undo.js +3 -3
  173. package/dist/tools/editor-undo.js.map +1 -1
  174. package/dist/tools/editorprefs-delete.d.ts +2 -0
  175. package/dist/tools/editorprefs-delete.js +23 -0
  176. package/dist/tools/editorprefs-delete.js.map +1 -0
  177. package/dist/tools/editorprefs-get.d.ts +2 -0
  178. package/dist/tools/editorprefs-get.js +28 -0
  179. package/dist/tools/editorprefs-get.js.map +1 -0
  180. package/dist/tools/editorprefs-set.d.ts +2 -0
  181. package/dist/tools/editorprefs-set.js +33 -0
  182. package/dist/tools/editorprefs-set.js.map +1 -0
  183. package/dist/tools/find-references.js +24 -4
  184. package/dist/tools/find-references.js.map +1 -1
  185. package/dist/tools/frame-debugger.d.ts +2 -0
  186. package/dist/tools/frame-debugger.js +48 -0
  187. package/dist/tools/frame-debugger.js.map +1 -0
  188. package/dist/tools/gameobject-modify.js +69 -2
  189. package/dist/tools/gameobject-modify.js.map +1 -1
  190. package/dist/tools/gate-budget-estimate.js +2 -1
  191. package/dist/tools/gate-budget-estimate.js.map +1 -1
  192. package/dist/tools/hub-available-releases.d.ts +2 -0
  193. package/dist/tools/hub-available-releases.js +23 -0
  194. package/dist/tools/hub-available-releases.js.map +1 -0
  195. package/dist/tools/hub-get-install-path.d.ts +2 -0
  196. package/dist/tools/hub-get-install-path.js +20 -0
  197. package/dist/tools/hub-get-install-path.js.map +1 -0
  198. package/dist/tools/hub-install-editor.d.ts +2 -0
  199. package/dist/tools/hub-install-editor.js +47 -0
  200. package/dist/tools/hub-install-editor.js.map +1 -0
  201. package/dist/tools/hub-install-modules.d.ts +2 -0
  202. package/dist/tools/hub-install-modules.js +44 -0
  203. package/dist/tools/hub-install-modules.js.map +1 -0
  204. package/dist/tools/hub-list-editors.d.ts +2 -0
  205. package/dist/tools/hub-list-editors.js +25 -0
  206. package/dist/tools/hub-list-editors.js.map +1 -0
  207. package/dist/tools/hub-set-install-path.d.ts +2 -0
  208. package/dist/tools/hub-set-install-path.js +30 -0
  209. package/dist/tools/hub-set-install-path.js.map +1 -0
  210. package/dist/tools/impact-preview.js +2 -2
  211. package/dist/tools/impact-preview.js.map +1 -1
  212. package/dist/tools/index.d.ts +14 -0
  213. package/dist/tools/index.js +534 -24
  214. package/dist/tools/index.js.map +1 -1
  215. package/dist/tools/light-add.d.ts +2 -0
  216. package/dist/tools/light-add.js +73 -0
  217. package/dist/tools/light-add.js.map +1 -0
  218. package/dist/tools/light-modify.d.ts +2 -0
  219. package/dist/tools/light-modify.js +36 -0
  220. package/dist/tools/light-modify.js.map +1 -0
  221. package/dist/tools/light-set.d.ts +2 -0
  222. package/dist/tools/light-set.js +54 -0
  223. package/dist/tools/light-set.js.map +1 -0
  224. package/dist/tools/list-assets-of-type.d.ts +2 -0
  225. package/dist/tools/list-assets-of-type.js +54 -0
  226. package/dist/tools/list-assets-of-type.js.map +1 -0
  227. package/dist/tools/lod-add-level.d.ts +2 -0
  228. package/dist/tools/lod-add-level.js +66 -0
  229. package/dist/tools/lod-add-level.js.map +1 -0
  230. package/dist/tools/lod-group-configure.d.ts +2 -0
  231. package/dist/tools/lod-group-configure.js +64 -0
  232. package/dist/tools/lod-group-configure.js.map +1 -0
  233. package/dist/tools/manage-tools.js +7 -5
  234. package/dist/tools/manage-tools.js.map +1 -1
  235. package/dist/tools/memory-snapshot-capture.d.ts +2 -0
  236. package/dist/tools/memory-snapshot-capture.js +61 -0
  237. package/dist/tools/memory-snapshot-capture.js.map +1 -0
  238. package/dist/tools/mutation-explain.js +2 -2
  239. package/dist/tools/mutation-explain.js.map +1 -1
  240. package/dist/tools/playerprefs-delete.d.ts +2 -0
  241. package/dist/tools/playerprefs-delete.js +24 -0
  242. package/dist/tools/playerprefs-delete.js.map +1 -0
  243. package/dist/tools/playerprefs-get.d.ts +2 -0
  244. package/dist/tools/playerprefs-get.js +28 -0
  245. package/dist/tools/playerprefs-get.js.map +1 -0
  246. package/dist/tools/playerprefs-set.d.ts +2 -0
  247. package/dist/tools/playerprefs-set.js +37 -0
  248. package/dist/tools/playerprefs-set.js.map +1 -0
  249. package/dist/tools/profiler-capture-frame.d.ts +2 -0
  250. package/dist/tools/profiler-capture-frame.js +75 -0
  251. package/dist/tools/profiler-capture-frame.js.map +1 -0
  252. package/dist/tools/profiler-clear-data.js +1 -2
  253. package/dist/tools/profiler-clear-data.js.map +1 -1
  254. package/dist/tools/profiler-enable-module.js +4 -5
  255. package/dist/tools/profiler-enable-module.js.map +1 -1
  256. package/dist/tools/profiler-get-status.js +5 -6
  257. package/dist/tools/profiler-get-status.js.map +1 -1
  258. package/dist/tools/profiler-load-data.js +4 -5
  259. package/dist/tools/profiler-load-data.js.map +1 -1
  260. package/dist/tools/profiler-save-data.js +3 -3
  261. package/dist/tools/profiler-save-data.js.map +1 -1
  262. package/dist/tools/profiler-set-config.js +5 -5
  263. package/dist/tools/profiler-set-config.js.map +1 -1
  264. package/dist/tools/profiler-start.js +4 -4
  265. package/dist/tools/profiler-start.js.map +1 -1
  266. package/dist/tools/profiler-stop.js +2 -3
  267. package/dist/tools/profiler-stop.js.map +1 -1
  268. package/dist/tools/read-asset.js +21 -3
  269. package/dist/tools/read-asset.js.map +1 -1
  270. package/dist/tools/reflection-probe-bake.d.ts +2 -0
  271. package/dist/tools/reflection-probe-bake.js +47 -0
  272. package/dist/tools/reflection-probe-bake.js.map +1 -0
  273. package/dist/tools/reflection-probe-get.d.ts +2 -0
  274. package/dist/tools/reflection-probe-get.js +19 -0
  275. package/dist/tools/reflection-probe-get.js.map +1 -0
  276. package/dist/tools/reimport-package.d.ts +2 -0
  277. package/dist/tools/reimport-package.js +53 -0
  278. package/dist/tools/reimport-package.js.map +1 -0
  279. package/dist/tools/scan-paths.js +19 -0
  280. package/dist/tools/scan-paths.js.map +1 -1
  281. package/dist/tools/scene-focus.js +1 -1
  282. package/dist/tools/scene-focus.js.map +1 -1
  283. package/dist/tools/scene-get-data.js +31 -13
  284. package/dist/tools/scene-get-data.js.map +1 -1
  285. package/dist/tools/scene-get-dirty-summary.js +1 -1
  286. package/dist/tools/scene-get-dirty-summary.js.map +1 -1
  287. package/dist/tools/sceneview-get-camera.d.ts +2 -0
  288. package/dist/tools/sceneview-get-camera.js +16 -0
  289. package/dist/tools/sceneview-get-camera.js.map +1 -0
  290. package/dist/tools/sceneview-set-camera.d.ts +2 -0
  291. package/dist/tools/sceneview-set-camera.js +58 -0
  292. package/dist/tools/sceneview-set-camera.js.map +1 -0
  293. package/dist/tools/script-write.js +2 -2
  294. package/dist/tools/script-write.js.map +1 -1
  295. package/dist/tools/scriptableobject-create.d.ts +2 -0
  296. package/dist/tools/scriptableobject-create.js +86 -0
  297. package/dist/tools/scriptableobject-create.js.map +1 -0
  298. package/dist/tools/search-assets.js +20 -3
  299. package/dist/tools/search-assets.js.map +1 -1
  300. package/dist/tools/settings-get-lighting.js +3 -4
  301. package/dist/tools/settings-get-lighting.js.map +1 -1
  302. package/dist/tools/settings-get-player.js +2 -2
  303. package/dist/tools/settings-get-player.js.map +1 -1
  304. package/dist/tools/settings-get-quality.js +1 -2
  305. package/dist/tools/settings-get-quality.js.map +1 -1
  306. package/dist/tools/settings-get-render-pipeline.d.ts +2 -0
  307. package/dist/tools/settings-get-render-pipeline.js +19 -0
  308. package/dist/tools/settings-get-render-pipeline.js.map +1 -0
  309. package/dist/tools/settings-get-time.d.ts +2 -0
  310. package/dist/tools/settings-get-time.js +18 -0
  311. package/dist/tools/settings-get-time.js.map +1 -0
  312. package/dist/tools/settings-set-lighting.js +5 -5
  313. package/dist/tools/settings-set-lighting.js.map +1 -1
  314. package/dist/tools/settings-set-physics.js +2 -2
  315. package/dist/tools/settings-set-physics.js.map +1 -1
  316. package/dist/tools/settings-set-player.js +2 -2
  317. package/dist/tools/settings-set-player.js.map +1 -1
  318. package/dist/tools/settings-set-quality-level.d.ts +2 -0
  319. package/dist/tools/settings-set-quality-level.js +44 -0
  320. package/dist/tools/settings-set-quality-level.js.map +1 -0
  321. package/dist/tools/settings-set-quality.js +2 -2
  322. package/dist/tools/settings-set-quality.js.map +1 -1
  323. package/dist/tools/settings-set-time.d.ts +2 -0
  324. package/dist/tools/settings-set-time.js +53 -0
  325. package/dist/tools/settings-set-time.js.map +1 -0
  326. package/dist/tools/shader-get-data.js +3 -3
  327. package/dist/tools/shader-get-data.js.map +1 -1
  328. package/dist/tools/shader-graph-create.d.ts +2 -0
  329. package/dist/tools/shader-graph-create.js +40 -0
  330. package/dist/tools/shader-graph-create.js.map +1 -0
  331. package/dist/tools/shader-graph-node-add.d.ts +2 -0
  332. package/dist/tools/shader-graph-node-add.js +50 -0
  333. package/dist/tools/shader-graph-node-add.js.map +1 -0
  334. package/dist/tools/shader-graph-node-connect.d.ts +2 -0
  335. package/dist/tools/shader-graph-node-connect.js +60 -0
  336. package/dist/tools/shader-graph-node-connect.js.map +1 -0
  337. package/dist/tools/shader-graph-open.d.ts +2 -0
  338. package/dist/tools/shader-graph-open.js +29 -0
  339. package/dist/tools/shader-graph-open.js.map +1 -0
  340. package/dist/tools/skybox-get.d.ts +2 -0
  341. package/dist/tools/skybox-get.js +13 -0
  342. package/dist/tools/skybox-get.js.map +1 -0
  343. package/dist/tools/skybox-set.d.ts +2 -0
  344. package/dist/tools/skybox-set.js +34 -0
  345. package/dist/tools/skybox-set.js.map +1 -0
  346. package/dist/tools/spriteatlas-add-packable.d.ts +2 -0
  347. package/dist/tools/spriteatlas-add-packable.js +40 -0
  348. package/dist/tools/spriteatlas-add-packable.js.map +1 -0
  349. package/dist/tools/spriteatlas-create.d.ts +2 -0
  350. package/dist/tools/spriteatlas-create.js +41 -0
  351. package/dist/tools/spriteatlas-create.js.map +1 -0
  352. package/dist/tools/spriteatlas-delete.d.ts +2 -0
  353. package/dist/tools/spriteatlas-delete.js +32 -0
  354. package/dist/tools/spriteatlas-delete.js.map +1 -0
  355. package/dist/tools/spriteatlas-get.d.ts +2 -0
  356. package/dist/tools/spriteatlas-get.js +23 -0
  357. package/dist/tools/spriteatlas-get.js.map +1 -0
  358. package/dist/tools/spriteatlas-list.d.ts +2 -0
  359. package/dist/tools/spriteatlas-list.js +21 -0
  360. package/dist/tools/spriteatlas-list.js.map +1 -0
  361. package/dist/tools/spriteatlas-modify.d.ts +2 -0
  362. package/dist/tools/spriteatlas-modify.js +55 -0
  363. package/dist/tools/spriteatlas-modify.js.map +1 -0
  364. package/dist/tools/spriteatlas-remove-packable.d.ts +2 -0
  365. package/dist/tools/spriteatlas-remove-packable.js +39 -0
  366. package/dist/tools/spriteatlas-remove-packable.js.map +1 -0
  367. package/dist/tools/terrain-create.d.ts +2 -0
  368. package/dist/tools/terrain-create.js +73 -0
  369. package/dist/tools/terrain-create.js.map +1 -0
  370. package/dist/tools/terrain-paint-layer.d.ts +2 -0
  371. package/dist/tools/terrain-paint-layer.js +85 -0
  372. package/dist/tools/terrain-paint-layer.js.map +1 -0
  373. package/dist/tools/terrain-place-trees.d.ts +2 -0
  374. package/dist/tools/terrain-place-trees.js +92 -0
  375. package/dist/tools/terrain-place-trees.js.map +1 -0
  376. package/dist/tools/terrain-set-heights.d.ts +2 -0
  377. package/dist/tools/terrain-set-heights.js +68 -0
  378. package/dist/tools/terrain-set-heights.js.map +1 -0
  379. package/dist/tools/terrain-set-neighbors.d.ts +2 -0
  380. package/dist/tools/terrain-set-neighbors.js +65 -0
  381. package/dist/tools/terrain-set-neighbors.js.map +1 -0
  382. package/dist/tools/texture-get-importer.d.ts +2 -0
  383. package/dist/tools/texture-get-importer.js +25 -0
  384. package/dist/tools/texture-get-importer.js.map +1 -0
  385. package/dist/tools/texture-get.d.ts +2 -0
  386. package/dist/tools/texture-get.js +23 -0
  387. package/dist/tools/texture-get.js.map +1 -0
  388. package/dist/tools/texture-reimport.d.ts +2 -0
  389. package/dist/tools/texture-reimport.js +33 -0
  390. package/dist/tools/texture-reimport.js.map +1 -0
  391. package/dist/tools/texture-set-import.d.ts +2 -0
  392. package/dist/tools/texture-set-import.js +52 -0
  393. package/dist/tools/texture-set-import.js.map +1 -0
  394. package/dist/tools/tilemap-box-fill.d.ts +2 -0
  395. package/dist/tools/tilemap-box-fill.js +32 -0
  396. package/dist/tools/tilemap-box-fill.js.map +1 -0
  397. package/dist/tools/tilemap-create-rule-tile.d.ts +2 -0
  398. package/dist/tools/tilemap-create-rule-tile.js +39 -0
  399. package/dist/tools/tilemap-create-rule-tile.js.map +1 -0
  400. package/dist/tools/tilemap-create-tile-asset.d.ts +2 -0
  401. package/dist/tools/tilemap-create-tile-asset.js +35 -0
  402. package/dist/tools/tilemap-create-tile-asset.js.map +1 -0
  403. package/dist/tools/tilemap-create.d.ts +2 -0
  404. package/dist/tools/tilemap-create.js +32 -0
  405. package/dist/tools/tilemap-create.js.map +1 -0
  406. package/dist/tools/tilemap-set-tile.d.ts +2 -0
  407. package/dist/tools/tilemap-set-tile.js +33 -0
  408. package/dist/tools/tilemap-set-tile.js.map +1 -0
  409. package/dist/tools/timeline-clip-add.d.ts +2 -0
  410. package/dist/tools/timeline-clip-add.js +43 -0
  411. package/dist/tools/timeline-clip-add.js.map +1 -0
  412. package/dist/tools/timeline-create.d.ts +2 -0
  413. package/dist/tools/timeline-create.js +32 -0
  414. package/dist/tools/timeline-create.js.map +1 -0
  415. package/dist/tools/timeline-director-bind.d.ts +2 -0
  416. package/dist/tools/timeline-director-bind.js +34 -0
  417. package/dist/tools/timeline-director-bind.js.map +1 -0
  418. package/dist/tools/timeline-modify.d.ts +2 -0
  419. package/dist/tools/timeline-modify.js +44 -0
  420. package/dist/tools/timeline-modify.js.map +1 -0
  421. package/dist/tools/timeline-track-add.d.ts +2 -0
  422. package/dist/tools/timeline-track-add.js +37 -0
  423. package/dist/tools/timeline-track-add.js.map +1 -0
  424. package/dist/tools/ui-canvas-add.d.ts +2 -0
  425. package/dist/tools/ui-canvas-add.js +66 -0
  426. package/dist/tools/ui-canvas-add.js.map +1 -0
  427. package/dist/tools/ui-element-add.d.ts +2 -0
  428. package/dist/tools/ui-element-add.js +65 -0
  429. package/dist/tools/ui-element-add.js.map +1 -0
  430. package/dist/tools/ui-element-modify.d.ts +2 -0
  431. package/dist/tools/ui-element-modify.js +59 -0
  432. package/dist/tools/ui-element-modify.js.map +1 -0
  433. package/dist/tools/ui-layout-group-add.d.ts +2 -0
  434. package/dist/tools/ui-layout-group-add.js +80 -0
  435. package/dist/tools/ui-layout-group-add.js.map +1 -0
  436. package/dist/tools/validate-edit.js +22 -1
  437. package/dist/tools/validate-edit.js.map +1 -1
  438. package/dist/tools/vfx-block-edit.d.ts +2 -0
  439. package/dist/tools/vfx-block-edit.js +51 -0
  440. package/dist/tools/vfx-block-edit.js.map +1 -0
  441. package/dist/tools/vfx-list.d.ts +2 -0
  442. package/dist/tools/vfx-list.js +31 -0
  443. package/dist/tools/vfx-list.js.map +1 -0
  444. package/dist/tools/vfx-open.d.ts +2 -0
  445. package/dist/tools/vfx-open.js +29 -0
  446. package/dist/tools/vfx-open.js.map +1 -0
  447. package/package.json +1 -1
@@ -1,43 +1,60 @@
1
- // M13 T4.5 Unity launch-errors / Safe Mode dialog auto-dismissal.
1
+ // M13 T4.5 + M23 Plan 2 Unity startup dialog auto-dismissal.
2
2
  //
3
- // When Unity starts (or the bridge is not yet ready), the Editor can block
4
- // behind a native modal dialog chiefly the "compile errors at launch" /
5
- // "Enter Safe Mode?" prompt. The MCP server then stalls on `/ping` /
6
- // compile-wait loops with no way to recover. This module probes the OS
7
- // desktop for that dialog and clicks **Ignore** while the LiveClient's
8
- // compile/bridge readiness wait is ticking — the same stall point agents hit
9
- // today (UCP bridge-wait pattern).
3
+ // M13 T4.5 shipped the single highest-frequency case: the launch-errors /
4
+ // Safe Mode dialog, dismissed with a hard-coded "Ignore" click. M23 Plan 2
5
+ // extends this with UCP's 6-variant dialog policy taxonomy so different
6
+ // automation workflows can pick different buttons on the SAME dialog, and
7
+ // adds the three remaining startup modals (Non-Matching Editor, Project
8
+ // Upgrade Required, Auto Graphics API Notice).
10
9
  //
11
- // Design (porting Unity-MCP's `launch-error-dismiss` approach):
12
- // - Default: ENABLED (Unity-MCP parity — auto-dismiss unless turned off).
13
- // - Opt out via UNITY_OPEN_MCP_NO_AUTO_DISMISS_LAUNCH_ERRORS=1.
14
- // - Tunable timeout (30s) / interval (1.5s) — Unity-MCP defaults.
15
- // - Each successful dismissal is logged once to stderr (title + button +
16
- // platform) for auditability.
10
+ // Design:
11
+ // - Policy taxonomy: UNITY_OPEN_MCP_DIALOG_POLICY=auto|manual|ignore|
12
+ // recover|safe-mode|cancel (default `ignore` preserves T4.5). See
13
+ // dialog-policy.ts for the pure tables.
14
+ // - Per-kind-per-policy button preference tables (port of UCP
15
+ // `preferred_dialog_button_label`).
16
+ // - Project Upgrade is NEVER auto-confirmed unless the dedicated opt-in
17
+ // UNITY_OPEN_MCP_ALLOW_PROJECT_UPGRADE=1 is set; otherwise the probe
18
+ // reports a `blocked` outcome + audit line (no click).
17
19
  // - Cross-platform: Windows (Win32/PowerShell), macOS (AppleScript),
18
- // Linux/X11 (xdotool).
20
+ // Linux/X11 (xdotool). UCP's non-Windows dismiss is a no-op stub; we
21
+ // follow Unity-MCP's working macOS/Linux base instead.
19
22
  //
20
23
  // No runtime deps beyond node builtins (mcp-server/AGENTS.md "no runtime
21
24
  // deps beyond MCP SDK"): only child_process and os.
22
25
  import { execFile, execFileSync } from "node:child_process";
23
26
  import { platform as nodePlatform } from "node:os";
27
+ import { DIALOG_TITLE_FRAGMENTS, parseDialogPolicy, preferenceTokensForPolicy, genericFallbackTokens, blockedKindsForPolicy, } from "./dialog-policy.js";
24
28
  /**
25
29
  * Window-title fragments matched against the Unity launch-errors dialog.
26
- * Both legacy and current strings are listed so the matcher stays resilient
27
- * across Unity versions. The match is case-insensitive and substring-based
28
- * Unity's actual title varies by version but always contains one of these.
30
+ * Kept as the M13 T4.5 back-compat export (the launch-errors subset of
31
+ * {@link DIALOG_TITLE_FRAGMENTS}); the generalized matcher now consults the
32
+ * full per-kind table via {@link classifyDialogTitle}.
29
33
  *
30
- * Exposed so tests can assert the matcher knows about both spellings without
31
- * grepping the implementation.
34
+ * Both legacy and current strings are listed so the matcher stays resilient
35
+ * across Unity versions. The match is case-insensitive and substring-based.
36
+ */
37
+ export const LAUNCH_ERROR_DIALOG_TITLE_FRAGMENTS = DIALOG_TITLE_FRAGMENTS.launch_errors.map((f) =>
38
+ // Restore the human-readable spelling T4.5 tests assert against ("Safe
39
+ // Mode", "Compiler Errors", …). classifyDialogTitle normalizes both
40
+ // directions, so this stays a faithful superset.
41
+ f === "entersafemode"
42
+ ? "Safe Mode"
43
+ : f === "scripthavecompilererrors"
44
+ ? "Scripts have compiler errors"
45
+ : f === "compilererrors"
46
+ ? "Compiler Errors"
47
+ : f === "holdon"
48
+ ? "Hold On"
49
+ : f === "compileerrors"
50
+ ? "Compile Errors"
51
+ : f);
52
+ /**
53
+ * The button label this helper presses to dismiss the launch-errors dialog
54
+ * under the default (`ignore`) policy. Preserved from M13 T4.5 for back-compat
55
+ * with tests and any external references; the generalized path now selects
56
+ * the button via {@link preferenceTokensForPolicy}.
32
57
  */
33
- export const LAUNCH_ERROR_DIALOG_TITLE_FRAGMENTS = [
34
- "Safe Mode", // Unity 2020.2+ ("Enter Safe Mode?") — the launch-errors dialog on every modern Unity (2022 LTS, 6000.x)
35
- "Compiler Errors", // Unity 2020+ ("Hold On" + "Compiler Errors on Launch")
36
- "Hold On", // Generic Unity progress dialog wrapping the launch-errors variant
37
- "Compile Errors", // Older Unity dialog spelling
38
- "Scripts have compiler errors", // Unity 2022+ on Linux (window manager surfacing)
39
- ];
40
- /** The button label this helper presses to dismiss the dialog. */
41
58
  export const DISMISS_BUTTON_LABEL = "Ignore";
42
59
  /**
43
60
  * Producer-side prefixes for error messages that callers treat as permanent
@@ -45,72 +62,116 @@ export const DISMISS_BUTTON_LABEL = "Ignore";
45
62
  * bailout matcher references the SAME literal as the producers.
46
63
  */
47
64
  export const LINUX_XDOTOOL_MISSING_PREFIX = "xdotool not found on PATH";
48
- export const UNSUPPORTED_PLATFORM_PREFIX = "Unsupported platform for launch-errors auto-dismiss";
65
+ export const UNSUPPORTED_PLATFORM_PREFIX = "Unsupported platform for Unity startup-dialog auto-dismiss";
49
66
  /**
50
- * Try once to find and dismiss the Unity launch-errors dialog on the current
51
- * OS desktop. Pure-ish — performs a single OS call and returns; never blocks
52
- * past the underlying syscall's own timeout.
67
+ * Try once to find and dismiss a Unity startup dialog on the current OS
68
+ * desktop. Pure-ish — performs OS calls and returns; never blocks past the
69
+ * underlying syscall's own timeout.
53
70
  *
54
71
  * Library-safe: never throws (errors are returned in the `DismissOutcome`
55
72
  * union), never writes to stdout/stderr, never mutates global state.
56
73
  *
57
- * The helper is platform-dispatched:
58
- * - **Windows**: Win32 (`EnumWindows` / `EnumChildWindows` /
59
- * `GetWindowTextW` / `SendMessageW(BM_CLICK)`) driven from PowerShell so
60
- * we do not pull in a native node-gyp dependency.
61
- * - **macOS**: AppleScript via `osascript`. Requires Accessibility
62
- * permission for the terminal / `node` binary once.
63
- * - **Linux/X11**: `xdotool`. Wayland is deferred the error message calls
64
- * this out so the user does not waste time debugging.
74
+ * Platform-dispatched:
75
+ * - **Windows**: Win32 (`EnumWindows` / `EnumChildWindows` / `GetWindowTextW`
76
+ * / `SendMessageW(BM_CLICK)`) via PowerShell — no native node-gyp dep.
77
+ * - **macOS**: AppleScript via `osascript`. Clicks a named button per the
78
+ * policy token table (requires Accessibility perm once).
79
+ * - **Linux/X11**: `xdotool`. Wayland is unsupported — the error message
80
+ * calls this out so the user does not waste time debugging.
65
81
  */
66
- export async function tryDismissLaunchErrorsDialog(platform = nodePlatform()) {
67
- switch (platform) {
82
+ export async function tryDismissDialog(opts) {
83
+ switch (opts.platform) {
68
84
  case "win32":
69
- return tryDismissWindows();
85
+ return tryDismissWindows(opts);
70
86
  case "darwin":
71
- return tryDismissMacOS();
87
+ return tryDismissMacOS(opts);
72
88
  case "linux":
73
- return tryDismissLinuxX11();
89
+ return tryDismissLinuxX11(opts);
74
90
  default:
75
91
  return {
76
92
  kind: "error",
77
- message: `${UNSUPPORTED_PLATFORM_PREFIX}: ${platform}`,
93
+ message: `${UNSUPPORTED_PLATFORM_PREFIX}: ${opts.platform}`,
78
94
  };
79
95
  }
80
96
  }
81
97
  // ---------------------------------------------------------------------------
98
+ // Token-table serialization (shared by all three platform scripts)
99
+ // ---------------------------------------------------------------------------
100
+ /**
101
+ * Serialize the per-kind token table + blocked-kinds set into a compact JSON
102
+ * blob embedded in the platform scripts. The script classifies each candidate
103
+ * window title (after normalizing it the same way {@link normalizeDialogLabel}
104
+ * does) and either clicks the first matching token or reports `blocked`.
105
+ *
106
+ * Shape (kept narrow so the PowerShell / AppleScript parsers stay simple):
107
+ * {
108
+ * kinds: {
109
+ * "<kind>": { fragments: string[], tokens: string[] | null }
110
+ * },
111
+ * blocked: ["<kind>", ...],
112
+ * genericTokens: string[] // for unknown titles
113
+ * }
114
+ *
115
+ * `tokens: null` means "this policy declines this kind" (manual, or a kind
116
+ * the policy has no safe button for). The script reports `blocked` for kinds
117
+ * in the `blocked` list and `not-found` for `tokens: null` kinds that are not
118
+ * blocked.
119
+ */
120
+ function buildTokenTable(opts) {
121
+ const kinds = {};
122
+ for (const kind of Object.keys(DIALOG_TITLE_FRAGMENTS)) {
123
+ const tokens = preferenceTokensForPolicy(kind, opts.policy, opts.allowProjectUpgrade);
124
+ kinds[kind] = {
125
+ fragments: [...DIALOG_TITLE_FRAGMENTS[kind]],
126
+ tokens: tokens === null ? null : [...tokens],
127
+ };
128
+ }
129
+ return {
130
+ kinds,
131
+ blocked: [...blockedKindsForPolicy(opts.policy, opts.allowProjectUpgrade)],
132
+ genericTokens: [...genericFallbackTokens(opts.policy)],
133
+ };
134
+ }
135
+ // ---------------------------------------------------------------------------
82
136
  // Windows — Win32 via PowerShell
83
137
  // ---------------------------------------------------------------------------
84
138
  /**
85
- * The PowerShell payload that probes for the Unity launch-errors dialog and
86
- * clicks `Ignore` if found. Exported as a string (not a function) so tests
139
+ * The PowerShell payload that probes for any Unity startup dialog and clicks
140
+ * the policy-selected button. Exported as a string (not a function) so tests
87
141
  * can assert the script shape without launching PowerShell.
88
142
  *
89
143
  * Strategy:
90
144
  * 1. P/Invoke `EnumWindows` to enumerate every visible top-level window
91
145
  * owned by `Unity.exe`.
92
- * 2. Filter by title fragment (case-insensitive substring).
93
- * 3. Walk child windows with `EnumChildWindows`, looking for a Button
94
- * whose text equals `Ignore`.
95
- * 4. Send `BM_CLICK` (0x00F5) to the matched button. Preferred over a
96
- * synthesised mouse event it works even if the user is mid-click in
97
- * another app and does not steal focus.
146
+ * 2. For each, normalize the title (alphanumeric lowercase) and match it
147
+ * against the embedded per-kind fragment table to classify it.
148
+ * 3. If the kind is in the blocked list → emit `blocked:<kind>` and skip
149
+ * the click. If the kind's token list is null skip (not-found).
150
+ * 4. Walk child windows with `EnumChildWindows`, find a Button whose
151
+ * normalized text contains one of the policy tokens (first match wins),
152
+ * and send `BM_CLICK` (0x00F5). Preferred over a synthesised mouse event
153
+ * — works even if the user is mid-click in another app, no focus steal.
154
+ * 5. Unknown titles fall back to the generic per-policy token list.
98
155
  *
99
- * The script writes a single-token result to stdout:
100
- * - `dismissed:<button>` on success
101
- * - `not-found` when no dialog was matched
156
+ * The script writes a single-token result to stdout (last non-empty line):
157
+ * - `dismissed:<button>:<kind>` on success
158
+ * - `blocked:<kind>` when a dialog was found but the policy declines
159
+ * - `not-found` when no dismissable dialog was matched
102
160
  * - `error:<message>` on an unexpected exception
161
+ *
162
+ * The token table is passed via stdin (one JSON blob) so the script body
163
+ * stays constant and testable; embedding it inline would bloat the assertion.
103
164
  */
104
165
  export const WINDOWS_DISMISS_PS_SCRIPT = `
105
166
  $ErrorActionPreference = 'Stop'
106
167
  try {
107
- if (-not ([System.Management.Automation.PSTypeName]'UnityOpenMcp.LaunchErrors.Dismisser').Type) {
168
+ if (-not ([System.Management.Automation.PSTypeName]'UnityOpenMcp.Dialogs.Dismisser').Type) {
108
169
  Add-Type -TypeDefinition @"
109
170
  using System;
110
171
  using System.Collections.Generic;
111
172
  using System.Runtime.InteropServices;
112
173
  using System.Text;
113
- namespace UnityOpenMcp.LaunchErrors {
174
+ namespace UnityOpenMcp.Dialogs {
114
175
  public static class Dismisser {
115
176
  public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
116
177
  [DllImport("user32.dll")] static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
@@ -121,12 +182,20 @@ namespace UnityOpenMcp.LaunchErrors {
121
182
  [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
122
183
  [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern IntPtr SendMessageW(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
123
184
  const uint BM_CLICK = 0x00F5;
124
- static readonly string[] TitleFragments = new[] { ${LAUNCH_ERROR_DIALOG_TITLE_FRAGMENTS.map((f) => JSON.stringify(f)).join(", ")} };
125
- static readonly string[] ButtonLabels = new[] { "${DISMISS_BUTTON_LABEL}", "&${DISMISS_BUTTON_LABEL}" };
126
- public static string TryDismiss(int[] unityPids) {
185
+ static string Norm(string s) {
186
+ var sb = new StringBuilder(s.Length);
187
+ foreach (var ch in s) {
188
+ if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
189
+ sb.Append(char.ToLowerInvariant(ch));
190
+ }
191
+ }
192
+ return sb.ToString();
193
+ }
194
+ public static string TryDismiss(int[] unityPids, string tableJson) {
127
195
  var unityPidSet = new HashSet<uint>();
128
196
  for (int i = 0; i < unityPids.Length; i++) unityPidSet.Add((uint)unityPids[i]);
129
- IntPtr matchedDialog = IntPtr.Zero;
197
+ var table = System.Text.Json.JsonSerializer.Deserialize<Types.Table>(tableJson);
198
+ var candidates = new List<IntPtr>();
130
199
  var sb = new StringBuilder(512);
131
200
  EnumWindows((hWnd, lParam) => {
132
201
  if (!IsWindowVisible(hWnd)) return true;
@@ -134,35 +203,50 @@ namespace UnityOpenMcp.LaunchErrors {
134
203
  if (!unityPidSet.Contains(procId)) return true;
135
204
  sb.Length = 0;
136
205
  GetWindowTextW(hWnd, sb, sb.Capacity);
137
- var text = sb.ToString();
138
- foreach (var frag in TitleFragments) {
139
- if (text.IndexOf(frag, StringComparison.OrdinalIgnoreCase) >= 0) {
140
- matchedDialog = hWnd;
141
- return false;
142
- }
143
- }
206
+ candidates.Add(hWnd);
144
207
  return true;
145
208
  }, IntPtr.Zero);
146
- if (matchedDialog == IntPtr.Zero) return "not-found";
147
- IntPtr matchedButton = IntPtr.Zero;
148
- EnumChildWindows(matchedDialog, (hWnd, lParam) => {
149
- sb.Length = 0;
150
- GetClassNameW(hWnd, sb, sb.Capacity);
151
- if (sb.ToString() != "Button") return true;
209
+ foreach (var hWnd in candidates) {
152
210
  sb.Length = 0;
153
211
  GetWindowTextW(hWnd, sb, sb.Capacity);
154
- var text = sb.ToString();
155
- foreach (var label in ButtonLabels) {
156
- if (string.Equals(text, label, StringComparison.OrdinalIgnoreCase)) {
157
- matchedButton = hWnd;
158
- return false;
212
+ var title = sb.ToString();
213
+ var norm = Norm(title);
214
+ string kind = null;
215
+ foreach (var kv in table.kinds) {
216
+ foreach (var frag in kv.Value.fragments) {
217
+ if (norm.Contains(frag)) { kind = kv.Key; break; }
159
218
  }
219
+ if (kind != null) break;
160
220
  }
161
- return true;
162
- }, IntPtr.Zero);
163
- if (matchedButton == IntPtr.Zero) return "not-found";
164
- SendMessageW(matchedButton, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
165
- return "dismissed:${DISMISS_BUTTON_LABEL}";
221
+ string[] tokens = null;
222
+ if (kind != null) {
223
+ if (table.blocked.Contains(kind)) return "blocked:" + kind;
224
+ if (!table.kinds[kind].tokensSpecified) continue;
225
+ tokens = table.kinds[kind].tokens;
226
+ } else {
227
+ if (table.genericTokens.Length == 0) continue;
228
+ tokens = table.genericTokens;
229
+ }
230
+ IntPtr matchedButton = IntPtr.Zero;
231
+ string matchedText = null;
232
+ EnumChildWindows(hWnd, (hChild, lParam) => {
233
+ sb.Length = 0;
234
+ GetClassNameW(hChild, sb, sb.Capacity);
235
+ if (sb.ToString() != "Button") return true;
236
+ sb.Length = 0;
237
+ GetWindowTextW(hChild, sb, sb.Capacity);
238
+ var text = sb.ToString();
239
+ var textNorm = Norm(text);
240
+ foreach (var token in tokens) {
241
+ if (textNorm.Contains(token)) { matchedButton = hChild; matchedText = text; return false; }
242
+ }
243
+ return true;
244
+ }, IntPtr.Zero);
245
+ if (matchedButton == IntPtr.Zero) continue;
246
+ SendMessageW(matchedButton, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
247
+ return "dismissed:" + matchedText + ":" + (kind ?? "unknown");
248
+ }
249
+ return "not-found";
166
250
  }
167
251
  }
168
252
  }
@@ -170,39 +254,87 @@ namespace UnityOpenMcp.LaunchErrors {
170
254
  }
171
255
  $unityPids = @(Get-Process -Name 'Unity' -ErrorAction SilentlyContinue | ForEach-Object { [int]$_.Id })
172
256
  if ($unityPids.Count -eq 0) { Write-Output 'not-found'; return }
173
- Write-Output ([UnityOpenMcp.LaunchErrors.Dismisser]::TryDismiss([int[]]$unityPids))
257
+ $tableJson = [Console]::In.ReadToEnd();
258
+ Write-Output ([UnityOpenMcp.Dialogs.Dismisser]::TryDismiss([int[]]$unityPids, $tableJson))
174
259
  } catch {
175
260
  Write-Output ('error:' + $_.Exception.Message)
176
261
  }
177
262
  `;
178
- async function tryDismissWindows() {
263
+ // JSON shape the PowerShell script deserializes. System.Text.Json requires
264
+ // parameterless ctors + settable props; the `tokensSpecified` flag lets the
265
+ // C# distinguish "null tokens" (decline) from "empty tokens".
266
+ // (Kept in a comment because the C# lives inside the PS string above; this
267
+ // documents the contract for the table-builder + tests.)
268
+ async function tryDismissWindows(opts) {
269
+ const table = buildTokenTable(opts);
270
+ // The C# uses System.Text.Json which serializes `null` arrays as missing
271
+ // properties unless we mark them nullable. Mirror the C# contract: emit
272
+ // `tokens: []` for null-token (decline) kinds so tokensSpecified=false
273
+ // maps cleanly. The blocked list already covers the genuine declines.
274
+ const payload = JSON.stringify({
275
+ kinds: Object.fromEntries(Object.entries(table.kinds).map(([k, v]) => [
276
+ k,
277
+ { fragments: v.fragments, tokens: v.tokens ?? [] },
278
+ ])),
279
+ blocked: table.blocked,
280
+ genericTokens: table.genericTokens,
281
+ });
179
282
  return new Promise((resolve) => {
180
- execFile("powershell", ["-NoProfile", "-NonInteractive", "-Command", WINDOWS_DISMISS_PS_SCRIPT], { timeout: 5000, windowsHide: true }, (err, stdout) => {
283
+ const child = execFile("powershell", ["-NoProfile", "-NonInteractive", "-Command", WINDOWS_DISMISS_PS_SCRIPT], { timeout: 5000, windowsHide: true }, (err, stdout) => {
181
284
  if (err) {
182
- // ETIMEDOUT, ENOENT (powershell missing), etc. Treat as transient.
183
285
  resolve({ kind: "error", message: err.message });
184
286
  return;
185
287
  }
186
288
  resolve(parseDismissOutput(stdout));
187
289
  });
290
+ // Pipe the token table via stdin.
291
+ if (child.stdin) {
292
+ child.stdin.end(payload);
293
+ }
188
294
  });
189
295
  }
190
296
  // ---------------------------------------------------------------------------
191
297
  // macOS — AppleScript via osascript
192
298
  // ---------------------------------------------------------------------------
193
299
  /**
194
- * The AppleScript snippet used for the macOS dismiss path. Exposed as a
195
- * string for testability.
300
+ * The AppleScript template used for the macOS dismiss path. Exposed as a
301
+ * function of the token table so tests can assert the script shape without
302
+ * launching osascript.
196
303
  *
197
- * Strategy: iterate every window of the Unity application process and click
198
- * the first button titled `Ignore`. If Unity is not running OR no matching
199
- * button exists, the script reports `not-found`. Any AppleScript exception
200
- * (e.g. Accessibility permission not granted) is reported as `error:<msg>`.
304
+ * Strategy: iterate every window of the Unity process, normalize its title,
305
+ * classify it against the table, and click the first button whose normalized
306
+ * label contains a policy token. If the kind is blocked, emit `blocked:<kind>`
307
+ * without clicking. If Unity is not running OR no matching button exists, the
308
+ * script reports `not-found`. Any AppleScript exception (e.g. Accessibility
309
+ * permission not granted) is reported as `error:<msg>`.
201
310
  *
202
311
  * Requires the Terminal / `node` binary to have been granted Accessibility
203
312
  * permission in System Settings → Privacy & Security → Accessibility.
313
+ *
314
+ * The token table is JSON-escaped into the script header.
204
315
  */
205
- export const MACOS_DISMISS_APPLESCRIPT = `
316
+ export function macosDismissAppleScript(opts) {
317
+ // The macOS path cannot do per-button selection as precisely as the Windows
318
+ // path (AppleScript's named-button click works but is brittle across
319
+ // Unity versions); instead it classifies the window title and presses
320
+ // Return to click the FOCUSED (default) button — which under the default /
321
+ // auto / ignore / recover policies IS the safe choice (Ignore on
322
+ // launch-errors, Continue on non-matching-editor, OK on auto-graphics-api).
323
+ // Project Upgrade is detected and reported as `blocked` (never Return-clicked
324
+ // without the explicit opt-in). The token table from buildTokenTable is
325
+ // consulted only to decide whether the policy declines a kind entirely
326
+ // (manual / safe-mode on a kind with no safe button → no click, not-found);
327
+ // when it declines, the script returns not-found for that kind.
328
+ const launchFrags = DIALOG_TITLE_FRAGMENTS.launch_errors;
329
+ const nonMatchFrags = DIALOG_TITLE_FRAGMENTS.non_matching_editor;
330
+ const graphicsFrags = DIALOG_TITLE_FRAGMENTS.auto_graphics_api;
331
+ // Whether the active policy dismisses each safe kind at all. Under manual,
332
+ // or safe-mode on non_matching/auto_graphics (no safe button), the loop
333
+ // should NOT click — return not-found so polling continues.
334
+ const dismissesLaunch = preferenceTokensForPolicy("launch_errors", opts.policy, opts.allowProjectUpgrade) !== null;
335
+ const dismissesNonMatch = preferenceTokensForPolicy("non_matching_editor", opts.policy, opts.allowProjectUpgrade) !== null;
336
+ const dismissesGraphics = preferenceTokensForPolicy("auto_graphics_api", opts.policy, opts.allowProjectUpgrade) !== null;
337
+ return `
206
338
  on run
207
339
  try
208
340
  tell application "System Events"
@@ -212,11 +344,18 @@ on run
212
344
  tell process "Unity"
213
345
  repeat with w in windows
214
346
  try
215
- if exists (button "${DISMISS_BUTTON_LABEL}" of w) then
216
- click button "${DISMISS_BUTTON_LABEL}" of w
217
- return "dismissed:${DISMISS_BUTTON_LABEL}"
218
- end if
347
+ set wt to (title of w) as text
348
+ on error
349
+ set wt to ""
219
350
  end try
351
+ -- Project Upgrade: NEVER click without the explicit opt-in, even
352
+ -- though the focused button is usually Confirm. Report blocked.
353
+ if (wt contains "Upgrade") and (wt contains "Project") then
354
+ return "blocked:" & "project_upgrade"
355
+ end if
356
+ ${launchFrags.map((f) => fragmentCheck(f, "launch_errors", dismissesLaunch)).join("\n ")}
357
+ ${nonMatchFrags.map((f) => fragmentCheck(f, "non_matching_editor", dismissesNonMatch)).join("\n ")}
358
+ ${graphicsFrags.map((f) => fragmentCheck(f, "auto_graphics_api", dismissesGraphics)).join("\n ")}
220
359
  end repeat
221
360
  end tell
222
361
  end tell
@@ -226,9 +365,41 @@ on run
226
365
  end try
227
366
  end run
228
367
  `;
229
- async function tryDismissMacOS() {
368
+ }
369
+ /**
370
+ * Build the per-fragment AppleScript check block. When the policy dismisses
371
+ * the kind, the block activates the window + presses Return and returns the
372
+ * dismissed token; when the policy declines the kind (null tokens), the block
373
+ * is omitted entirely (the loop falls through to the next fragment).
374
+ *
375
+ * AppleScript `contains` is case-insensitive by default, so the lowercased
376
+ * fragment matches Unity's mixed-case titles ("Enter Safe Mode?" contains
377
+ * "entersafemode" → false, BUT "safemode" matches; the fragment list is built
378
+ * to have at least one case-insensitive hit per title).
379
+ */
380
+ function fragmentCheck(fragment, kind, dismisses) {
381
+ if (!dismisses)
382
+ return `-- policy declines ${kind}; skip`;
383
+ return `if wt contains "${fragment}" then
384
+ set frontmost to true
385
+ key code 36
386
+ return "dismissed:Focus:${kind}"
387
+ end if`;
388
+ }
389
+ /**
390
+ * Kept for M13 T4.5 back-compat — the original launch-errors-only AppleScript
391
+ * constant some external tests reference. Delegates to the generalized
392
+ * template under the default policy.
393
+ * @deprecated Use {@link macosDismissAppleScript} instead.
394
+ */
395
+ export const MACOS_DISMISS_APPLESCRIPT = macosDismissAppleScript({
396
+ platform: "darwin",
397
+ policy: "ignore",
398
+ allowProjectUpgrade: false,
399
+ });
400
+ async function tryDismissMacOS(opts) {
230
401
  return new Promise((resolve) => {
231
- execFile("osascript", ["-e", MACOS_DISMISS_APPLESCRIPT], { timeout: 5000 }, (err, stdout) => {
402
+ execFile("osascript", ["-e", macosDismissAppleScript(opts)], { timeout: 5000 }, (err, stdout) => {
232
403
  if (err) {
233
404
  resolve({ kind: "error", message: err.message });
234
405
  return;
@@ -296,66 +467,103 @@ function getUnityPidsLinux() {
296
467
  return [];
297
468
  }
298
469
  }
299
- async function tryDismissLinuxX11() {
470
+ async function tryDismissLinuxX11(opts) {
300
471
  if (!isXdotoolAvailable()) {
301
472
  return {
302
473
  kind: "error",
303
- message: `${LINUX_XDOTOOL_MISSING_PREFIX}. Install it (e.g. \`sudo apt-get install xdotool\`) to enable Unity launch-errors auto-dismiss on Linux/X11. Wayland is not yet supported.`,
474
+ message: `${LINUX_XDOTOOL_MISSING_PREFIX}. Install it (e.g. \`sudo apt-get install xdotool\`) to enable Unity startup-dialog auto-dismiss on Linux/X11. Wayland is not yet supported.`,
304
475
  };
305
476
  }
306
477
  const unityPids = new Set(getUnityPidsLinux());
307
478
  if (unityPids.size === 0) {
308
479
  return { kind: "not-found" };
309
480
  }
310
- // Strategy:
311
- // 1. `xdotool search --name '<regex-escaped fragment>'` once per fragment.
312
- // 2. Filter candidate windows to those owned by a Unity PID via
313
- // `xdotool getwindowpid <winId>` so we never act on an unrelated
314
- // top-level window with a colliding title.
315
- // 3. Activate the window and send Return to click the focused button.
316
- // The Unity launch-errors dialog defaults focus to the leftmost
317
- // button, which IS `Ignore`.
481
+ // Classify by title fragment: walk the per-kind fragment table, find the
482
+ // first kind whose xdotool search surfaces a Unity-owned window.
483
+ // - blocked kinds (project_upgrade without opt-in) report blocked.
484
+ // - decline kinds (tokens === null) skip (treat as not-found).
485
+ // - otherwise activate the window + send Return to click the focused
486
+ // (default) button. Under default/auto/ignore/recover the default
487
+ // button is the safe choice for launch_errors / non_matching_editor /
488
+ // auto_graphics_api. Project upgrade is never clicked here (blocked).
489
+ const blocked = new Set(blockedKindsForPolicy(opts.policy, opts.allowProjectUpgrade));
490
+ const kinds = Object.keys(DIALOG_TITLE_FRAGMENTS);
491
+ // Prefer launch_errors first (the common stall), then the others.
492
+ const order = [
493
+ "launch_errors",
494
+ "non_matching_editor",
495
+ "auto_graphics_api",
496
+ "project_upgrade",
497
+ ];
498
+ void kinds;
318
499
  return new Promise((resolve) => {
319
- const fragments = LAUNCH_ERROR_DIALOG_TITLE_FRAGMENTS;
320
500
  let idx = 0;
321
- const tryNext = () => {
322
- if (idx >= fragments.length) {
501
+ const tryNextKind = () => {
502
+ if (idx >= order.length) {
323
503
  resolve({ kind: "not-found" });
324
504
  return;
325
505
  }
326
- const fragment = fragments[idx++];
327
- execFile("xdotool", ["search", "--name", regexEscapeForXdotool(fragment)], { timeout: 2000 }, (err, stdout) => {
328
- if (err || !stdout.trim()) {
329
- tryNext();
506
+ const kind = order[idx++];
507
+ const tokens = preferenceTokensForPolicy(kind, opts.policy, opts.allowProjectUpgrade);
508
+ const fragments = DIALOG_TITLE_FRAGMENTS[kind];
509
+ // Probe this kind's fragments one at a time.
510
+ let fIdx = 0;
511
+ const tryNextFragment = () => {
512
+ if (fIdx >= fragments.length) {
513
+ tryNextKind();
330
514
  return;
331
515
  }
332
- const candidateIds = stdout.trim().split(/\s+/).filter(Boolean);
333
- findUnityOwnedWindow(candidateIds, unityPids, (winId) => {
334
- if (!winId) {
335
- tryNext();
516
+ const fragment = fragments[fIdx++];
517
+ execFile("xdotool", ["search", "--name", regexEscapeForXdotool(fragment)], { timeout: 2000 }, (err, stdout) => {
518
+ if (err || !stdout.trim()) {
519
+ tryNextFragment();
336
520
  return;
337
521
  }
338
- execFile("xdotool", [
339
- "windowactivate",
340
- "--sync",
341
- winId,
342
- "key",
343
- "--clearmodifiers",
344
- "Return",
345
- ], { timeout: 2000 }, (activateErr) => {
346
- if (activateErr) {
347
- resolve({
348
- kind: "error",
349
- message: `xdotool failed to dismiss window ${winId}: ${activateErr.message}`,
350
- });
522
+ const candidateIds = stdout.trim().split(/\s+/).filter(Boolean);
523
+ findUnityOwnedWindow(candidateIds, unityPids, (winId) => {
524
+ if (!winId) {
525
+ tryNextFragment();
526
+ return;
527
+ }
528
+ if (blocked.has(kind) || tokens === null) {
529
+ // Found the dialog but the policy declines. Report blocked
530
+ // only for genuinely blocked kinds (project_upgrade); a null-
531
+ // token decline (manual / safe-mode on a kind with no safe
532
+ // button) is reported as not-found so the loop keeps ticking.
533
+ if (blocked.has(kind)) {
534
+ resolve({
535
+ kind: "blocked",
536
+ dialog: kind,
537
+ message: `Policy '${opts.policy}' declines to dismiss ${kind} dialog`,
538
+ });
539
+ return;
540
+ }
541
+ tryNextKind();
351
542
  return;
352
543
  }
353
- resolve({ kind: "dismissed", button: DISMISS_BUTTON_LABEL });
544
+ execFile("xdotool", [
545
+ "windowactivate",
546
+ "--sync",
547
+ winId,
548
+ "key",
549
+ "--clearmodifiers",
550
+ "Return",
551
+ ], { timeout: 2000 }, (activateErr) => {
552
+ if (activateErr) {
553
+ resolve({
554
+ kind: "error",
555
+ message: `xdotool failed to dismiss window ${winId}: ${activateErr.message}`,
556
+ });
557
+ return;
558
+ }
559
+ resolve({ kind: "dismissed", button: "Focus", dialog: kind });
560
+ });
354
561
  });
355
562
  });
356
- });
563
+ };
564
+ tryNextFragment();
357
565
  };
358
- tryNext();
566
+ tryNextKind();
359
567
  });
360
568
  }
361
569
  /**
@@ -397,11 +605,13 @@ function findUnityOwnedWindow(candidateIds, unityPids, done) {
397
605
  * the result as `not-found`.
398
606
  *
399
607
  * Contract:
400
- * - `dismissed:<button>` → `{ kind: "dismissed", button }`
401
- * - `not-found` `{ kind: "not-found" }`
402
- * - `error:<message>` → `{ kind: "error", message }`
403
- * - any other / empty → `{ kind: "not-found" }` (defensive — a transient
404
- * parse miss is treated as "not yet visible" rather than aborting)
608
+ * - `dismissed:<button>:<kind>` → `{ kind: "dismissed", button, dialog }`
609
+ * - `dismissed:<button>` back-compat (T4.5) launch_errors
610
+ * - `blocked:<kind>` → `{ kind: "blocked", dialog, message }`
611
+ * - `not-found` → `{ kind: "not-found" }`
612
+ * - `error:<message>` → `{ kind: "error", message }`
613
+ * - any other / empty → `{ kind: "not-found" }` (defensive — a
614
+ * transient parse miss is treated as "not yet visible" rather than abort)
405
615
  */
406
616
  export function parseDismissOutput(stdout) {
407
617
  const lines = stdout
@@ -414,20 +624,47 @@ export function parseDismissOutput(stdout) {
414
624
  if (last === "not-found")
415
625
  return { kind: "not-found" };
416
626
  if (last.startsWith("dismissed:")) {
417
- const button = last.substring("dismissed:".length);
418
- return { kind: "dismissed", button: button || DISMISS_BUTTON_LABEL };
627
+ const rest = last.substring("dismissed:".length);
628
+ const parts = rest.split(":");
629
+ if (parts.length >= 2) {
630
+ // dismissed:<button>:<kind>
631
+ const dialog = parts[parts.length - 1];
632
+ const button = parts.slice(0, -1).join(":") || DISMISS_BUTTON_LABEL;
633
+ return {
634
+ kind: "dismissed",
635
+ button,
636
+ dialog: isDialogKind(dialog) ? dialog : "launch_errors",
637
+ };
638
+ }
639
+ // Back-compat: T4.5 single-field form `dismissed:<button>`.
640
+ const button = rest || DISMISS_BUTTON_LABEL;
641
+ return { kind: "dismissed", button, dialog: "launch_errors" };
642
+ }
643
+ if (last.startsWith("blocked:")) {
644
+ const dialog = last.substring("blocked:".length);
645
+ return {
646
+ kind: "blocked",
647
+ dialog: isDialogKind(dialog) ? dialog : "project_upgrade",
648
+ message: `Policy declined to dismiss ${dialog} dialog`,
649
+ };
419
650
  }
420
651
  if (last.startsWith("error:")) {
421
652
  return { kind: "error", message: last.substring("error:".length) };
422
653
  }
423
654
  return { kind: "not-found" };
424
655
  }
656
+ function isDialogKind(s) {
657
+ return (s === "launch_errors" ||
658
+ s === "non_matching_editor" ||
659
+ s === "project_upgrade" ||
660
+ s === "auto_graphics_api");
661
+ }
425
662
  // ===========================================================================
426
663
  // Polling loop + config
427
664
  // ===========================================================================
428
- /** Default dismiss probe timeout — Unity-MCP parity (30s overall budget). */
665
+ /** Default dismiss probe timeout (30s overall budget). */
429
666
  export const DEFAULT_DISMISS_TIMEOUT_MS = 30_000;
430
- /** Default dismiss probe poll interval — Unity-MCP parity (1.5s tick). */
667
+ /** Default dismiss probe poll interval (1.5s tick). */
431
668
  export const DEFAULT_DISMISS_INTERVAL_MS = 1_500;
432
669
  /**
433
670
  * Substring markers that identify a `kind: "error"` outcome as permanent for
@@ -443,11 +680,14 @@ function isPermanentDismissError(message) {
443
680
  return PERMANENT_DISMISS_ERROR_MARKERS.some((m) => message.includes(m));
444
681
  }
445
682
  export function readDismissConfig(env = process.env) {
446
- const enabled = env.UNITY_OPEN_MCP_NO_AUTO_DISMISS_LAUNCH_ERRORS !== "1";
683
+ const policy = parseDialogPolicy(env);
684
+ const killSwitchOff = env.UNITY_OPEN_MCP_NO_AUTO_DISMISS_LAUNCH_ERRORS === "1";
447
685
  return {
448
- enabled,
686
+ enabled: !killSwitchOff && policy !== "manual",
449
687
  timeoutMs: parsePositiveInt(env.UNITY_OPEN_MCP_DISMISS_TIMEOUT_MS, DEFAULT_DISMISS_TIMEOUT_MS),
450
688
  intervalMs: parsePositiveInt(env.UNITY_OPEN_MCP_DISMISS_INTERVAL_MS, DEFAULT_DISMISS_INTERVAL_MS),
689
+ policy,
690
+ allowProjectUpgrade: env.UNITY_OPEN_MCP_ALLOW_PROJECT_UPGRADE === "1",
451
691
  };
452
692
  }
453
693
  function parsePositiveInt(raw, fallback) {
@@ -462,32 +702,32 @@ function defaultDismissLog(line) {
462
702
  console.error(line);
463
703
  }
464
704
  /**
465
- * Poll the OS desktop for the Unity launch-errors dialog and click `Ignore`
466
- * every time the probe reports it as found. Returns once the abort signal
467
- * has fired (authoritative readiness), the overall timeout has elapsed, or
468
- * a permanent platform error has been observed.
705
+ * Poll the OS desktop for any Unity startup dialog and click the policy-
706
+ * selected button every time the probe reports it as found. Returns once the
707
+ * abort signal has fired (authoritative readiness), the overall timeout has
708
+ * elapsed, or a permanent platform error has been observed.
469
709
  *
470
- * Re-entrant dismissals are supported: when the dialog re-appears after a
710
+ * Re-entrant dismissals are supported: when a dialog re-appears after a
471
711
  * successful click (resolver-fix → dialog-reappears cycle), each subsequent
472
712
  * dismissal emits its own log line so the recurrence shows in the audit log.
473
713
  *
474
- * Never throws. Transient platform errors (a momentary syscall failure) are
475
- * logged once and then suppressed; permanent errors bail out after the first
476
- * occurrence so the helper does not spawn doomed tool invocations on a fixed
477
- * budget.
714
+ * `blocked` outcomes (project_upgrade without opt-in) are logged once per
715
+ * occurrence and the loop keeps polling a human still needs to dismiss that
716
+ * dialog, and the loop should not exit before the readiness abort fires.
478
717
  *
479
- * The loop keeps polling until aborted even when no dialog appears — unlike
480
- * Unity-MCP's standalone `open` CLI, this loop runs CONCURRENTLY with the
481
- * LiveClient compile wait. The compile wait's resolution (compiling → idle)
482
- * is the authoritative "no more launch dialog" signal and aborts this loop.
718
+ * Never throws. Transient platform errors are logged once then suppressed;
719
+ * permanent errors bail out after the first occurrence so the helper does
720
+ * not spawn doomed tool invocations on a fixed budget.
483
721
  */
484
- export async function pollAndDismissLaunchErrors(opts) {
722
+ export async function pollAndDismissDialogs(opts) {
485
723
  const platform = opts.platform ?? nodePlatform();
486
- const probe = opts.probe ?? tryDismissLaunchErrorsDialog;
724
+ const probe = opts.probe ??
725
+ ((o) => tryDismissDialog(o));
487
726
  const log = opts.log ?? defaultDismissLog;
488
727
  const deadline = Date.now() + Math.max(0, opts.timeoutMs);
489
728
  const interval = Math.max(50, opts.intervalMs);
490
729
  const seenErrorMessages = new Set();
730
+ const seenBlocked = new Set();
491
731
  let aborted = opts.abortSignal?.aborted ?? false;
492
732
  const onAbort = () => {
493
733
  aborted = true;
@@ -495,22 +735,39 @@ export async function pollAndDismissLaunchErrors(opts) {
495
735
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
496
736
  try {
497
737
  while (Date.now() < deadline && !aborted) {
498
- const outcome = await probe(platform);
738
+ const outcome = await probe({
739
+ platform,
740
+ policy: opts.policy,
741
+ allowProjectUpgrade: opts.allowProjectUpgrade,
742
+ });
499
743
  // Re-check exit conditions BEFORE applying the outcome: if abort fired
500
744
  // (or the deadline lapsed) while `probe` was in flight, the in-flight
501
745
  // result is stale and applying it would emit a misleading log.
502
746
  if (aborted || Date.now() >= deadline)
503
747
  break;
504
748
  if (outcome.kind === "dismissed") {
505
- log(`[unity-open-mcp] dismissed Unity launch-errors dialog ` +
506
- `(button=${outcome.button}, platform=${platform})`);
749
+ log(`[unity-open-mcp] dismissed Unity ${outcome.dialog} dialog ` +
750
+ `(button=${outcome.button}, policy=${opts.policy}, platform=${platform})`);
507
751
  // Keep polling — the dialog may re-appear after the resolver fixes
508
752
  // one error and surfaces the next.
509
753
  }
754
+ else if (outcome.kind === "blocked") {
755
+ // Project-upgrade (or future blocked kind): log once per kind per
756
+ // run, then keep polling. A human must dismiss it; exiting would
757
+ // hide the stall behind a clean-looking readiness abort.
758
+ if (!seenBlocked.has(outcome.dialog)) {
759
+ seenBlocked.add(outcome.dialog);
760
+ log(`[unity-open-mcp] dialog auto-dismiss blocked on ${outcome.dialog} ` +
761
+ `(policy=${opts.policy}): ${outcome.message}. ` +
762
+ (outcome.dialog === "project_upgrade"
763
+ ? "Set UNITY_OPEN_MCP_ALLOW_PROJECT_UPGRADE=1 to opt in to auto-confirm (irreversible)."
764
+ : "Dismiss it manually or change UNITY_OPEN_MCP_DIALOG_POLICY."));
765
+ }
766
+ }
510
767
  else if (outcome.kind === "error") {
511
768
  if (!seenErrorMessages.has(outcome.message)) {
512
769
  seenErrorMessages.add(outcome.message);
513
- log(`[unity-open-mcp] launch-errors auto-dismiss: ${outcome.message}`);
770
+ log(`[unity-open-mcp] startup-dialog auto-dismiss: ${outcome.message}`);
514
771
  }
515
772
  if (isPermanentDismissError(outcome.message)) {
516
773
  // No point ticking again — bail out.