@alpsckr/unitycli 0.4.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 (1066) hide show
  1. package/README.md +389 -0
  2. package/contracts/command-contract.json +3972 -0
  3. package/contracts/stable-session/admission-acquire-v1.schema.json +16 -0
  4. package/contracts/stable-session/admission-response-v1.schema.json +30 -0
  5. package/contracts/stable-session/admission-revoke-v1.schema.json +15 -0
  6. package/contracts/stable-session/admission-transport-v1.json +36 -0
  7. package/contracts/stable-session/bridge-request-v2.schema.json +26 -0
  8. package/contracts/stable-session/canonical-path-v1-golden.json +23 -0
  9. package/contracts/stable-session/completion-contract.schema.json +46 -0
  10. package/contracts/stable-session/direct-call-semantics.json +102 -0
  11. package/contracts/stable-session/editor-state.schema.json +92 -0
  12. package/contracts/stable-session/health-v3.schema.json +47 -0
  13. package/contracts/stable-session/instance-registry-v3.schema.json +152 -0
  14. package/contracts/stable-session/removal-map.json +27 -0
  15. package/contracts/stable-session/request-digest-v1-golden.json +7 -0
  16. package/contracts/stable-session/runtime-config-v2.schema.json +27 -0
  17. package/contracts/static-capability-catalog.json +48226 -0
  18. package/contracts/tool-completion-contract.json +11322 -0
  19. package/dist/args.d.ts +27 -0
  20. package/dist/args.js +109 -0
  21. package/dist/args.js.map +1 -0
  22. package/dist/bridge/admission.d.ts +28 -0
  23. package/dist/bridge/admission.js +29 -0
  24. package/dist/bridge/admission.js.map +1 -0
  25. package/dist/bridge/args.d.ts +16 -0
  26. package/dist/bridge/args.js +123 -0
  27. package/dist/bridge/args.js.map +1 -0
  28. package/dist/bridge/domains/animation.d.ts +2 -0
  29. package/dist/bridge/domains/animation.js +199 -0
  30. package/dist/bridge/domains/animation.js.map +1 -0
  31. package/dist/bridge/domains/asset.d.ts +2 -0
  32. package/dist/bridge/domains/asset.js +85 -0
  33. package/dist/bridge/domains/asset.js.map +1 -0
  34. package/dist/bridge/domains/build.d.ts +2 -0
  35. package/dist/bridge/domains/build.js +35 -0
  36. package/dist/bridge/domains/build.js.map +1 -0
  37. package/dist/bridge/domains/camera.d.ts +2 -0
  38. package/dist/bridge/domains/camera.js +54 -0
  39. package/dist/bridge/domains/camera.js.map +1 -0
  40. package/dist/bridge/domains/code.d.ts +2 -0
  41. package/dist/bridge/domains/code.js +17 -0
  42. package/dist/bridge/domains/code.js.map +1 -0
  43. package/dist/bridge/domains/console.d.ts +2 -0
  44. package/dist/bridge/domains/console.js +34 -0
  45. package/dist/bridge/domains/console.js.map +1 -0
  46. package/dist/bridge/domains/diagnose.d.ts +2 -0
  47. package/dist/bridge/domains/diagnose.js +11 -0
  48. package/dist/bridge/domains/diagnose.js.map +1 -0
  49. package/dist/bridge/domains/editor.d.ts +2 -0
  50. package/dist/bridge/domains/editor.js +66 -0
  51. package/dist/bridge/domains/editor.js.map +1 -0
  52. package/dist/bridge/domains/extension.d.ts +8 -0
  53. package/dist/bridge/domains/extension.js +20 -0
  54. package/dist/bridge/domains/extension.js.map +1 -0
  55. package/dist/bridge/domains/game-object.d.ts +2 -0
  56. package/dist/bridge/domains/game-object.js +179 -0
  57. package/dist/bridge/domains/game-object.js.map +1 -0
  58. package/dist/bridge/domains/graphics.d.ts +2 -0
  59. package/dist/bridge/domains/graphics.js +185 -0
  60. package/dist/bridge/domains/graphics.js.map +1 -0
  61. package/dist/bridge/domains/material.d.ts +2 -0
  62. package/dist/bridge/domains/material.js +70 -0
  63. package/dist/bridge/domains/material.js.map +1 -0
  64. package/dist/bridge/domains/navmesh.d.ts +2 -0
  65. package/dist/bridge/domains/navmesh.js +34 -0
  66. package/dist/bridge/domains/navmesh.js.map +1 -0
  67. package/dist/bridge/domains/object.d.ts +2 -0
  68. package/dist/bridge/domains/object.js +43 -0
  69. package/dist/bridge/domains/object.js.map +1 -0
  70. package/dist/bridge/domains/occlusion.d.ts +2 -0
  71. package/dist/bridge/domains/occlusion.js +12 -0
  72. package/dist/bridge/domains/occlusion.js.map +1 -0
  73. package/dist/bridge/domains/package.d.ts +2 -0
  74. package/dist/bridge/domains/package.js +2 -0
  75. package/dist/bridge/domains/package.js.map +1 -0
  76. package/dist/bridge/domains/physics.d.ts +2 -0
  77. package/dist/bridge/domains/physics.js +139 -0
  78. package/dist/bridge/domains/physics.js.map +1 -0
  79. package/dist/bridge/domains/prefab.d.ts +2 -0
  80. package/dist/bridge/domains/prefab.js +104 -0
  81. package/dist/bridge/domains/prefab.js.map +1 -0
  82. package/dist/bridge/domains/profiler.d.ts +2 -0
  83. package/dist/bridge/domains/profiler.js +105 -0
  84. package/dist/bridge/domains/profiler.js.map +1 -0
  85. package/dist/bridge/domains/project-tool.d.ts +2 -0
  86. package/dist/bridge/domains/project-tool.js +46 -0
  87. package/dist/bridge/domains/project-tool.js.map +1 -0
  88. package/dist/bridge/domains/reflection.d.ts +2 -0
  89. package/dist/bridge/domains/reflection.js +48 -0
  90. package/dist/bridge/domains/reflection.js.map +1 -0
  91. package/dist/bridge/domains/runtime-tool.d.ts +2 -0
  92. package/dist/bridge/domains/runtime-tool.js +48 -0
  93. package/dist/bridge/domains/runtime-tool.js.map +1 -0
  94. package/dist/bridge/domains/scene.d.ts +2 -0
  95. package/dist/bridge/domains/scene.js +100 -0
  96. package/dist/bridge/domains/scene.js.map +1 -0
  97. package/dist/bridge/domains/screenshot.d.ts +2 -0
  98. package/dist/bridge/domains/screenshot.js +85 -0
  99. package/dist/bridge/domains/screenshot.js.map +1 -0
  100. package/dist/bridge/domains/scriptable-object.d.ts +2 -0
  101. package/dist/bridge/domains/scriptable-object.js +53 -0
  102. package/dist/bridge/domains/scriptable-object.js.map +1 -0
  103. package/dist/bridge/domains/shader.d.ts +2 -0
  104. package/dist/bridge/domains/shader.js +66 -0
  105. package/dist/bridge/domains/shader.js.map +1 -0
  106. package/dist/bridge/domains/texture.d.ts +2 -0
  107. package/dist/bridge/domains/texture.js +78 -0
  108. package/dist/bridge/domains/texture.js.map +1 -0
  109. package/dist/bridge/domains/timeline.d.ts +2 -0
  110. package/dist/bridge/domains/timeline.js +75 -0
  111. package/dist/bridge/domains/timeline.js.map +1 -0
  112. package/dist/bridge/domains/ui.d.ts +2 -0
  113. package/dist/bridge/domains/ui.js +193 -0
  114. package/dist/bridge/domains/ui.js.map +1 -0
  115. package/dist/bridge/index.d.ts +9 -0
  116. package/dist/bridge/index.js +124 -0
  117. package/dist/bridge/index.js.map +1 -0
  118. package/dist/bridge/registry.d.ts +19 -0
  119. package/dist/bridge/registry.js +114 -0
  120. package/dist/bridge/registry.js.map +1 -0
  121. package/dist/bridge/session-supervisor.d.ts +138 -0
  122. package/dist/bridge/session-supervisor.js +873 -0
  123. package/dist/bridge/session-supervisor.js.map +1 -0
  124. package/dist/bridge/types.d.ts +61 -0
  125. package/dist/bridge/types.js +17 -0
  126. package/dist/bridge/types.js.map +1 -0
  127. package/dist/capabilities.d.ts +5 -0
  128. package/dist/capabilities.js +84 -0
  129. package/dist/capabilities.js.map +1 -0
  130. package/dist/cli-commands/command.d.ts +7 -0
  131. package/dist/cli-commands/command.js +513 -0
  132. package/dist/cli-commands/command.js.map +1 -0
  133. package/dist/cli-commands/extension-auto-detect.d.ts +62 -0
  134. package/dist/cli-commands/extension-auto-detect.js +119 -0
  135. package/dist/cli-commands/extension-auto-detect.js.map +1 -0
  136. package/dist/cli-commands/extensions.d.ts +24 -0
  137. package/dist/cli-commands/extensions.js +428 -0
  138. package/dist/cli-commands/extensions.js.map +1 -0
  139. package/dist/cli-commands/package.d.ts +4 -0
  140. package/dist/cli-commands/package.js +151 -0
  141. package/dist/cli-commands/package.js.map +1 -0
  142. package/dist/cli-commands/skill-installer.d.ts +83 -0
  143. package/dist/cli-commands/skill-installer.js +703 -0
  144. package/dist/cli-commands/skill-installer.js.map +1 -0
  145. package/dist/cli-commands/types.d.ts +17 -0
  146. package/dist/cli-commands/types.js +2 -0
  147. package/dist/cli-commands/types.js.map +1 -0
  148. package/dist/cli.d.ts +17 -0
  149. package/dist/cli.js +302 -0
  150. package/dist/cli.js.map +1 -0
  151. package/dist/contracts/command-contract.d.ts +21 -0
  152. package/dist/contracts/command-contract.js +143 -0
  153. package/dist/contracts/command-contract.js.map +1 -0
  154. package/dist/contracts/csharp-command-extractor.d.ts +1 -0
  155. package/dist/contracts/csharp-command-extractor.js +89 -0
  156. package/dist/contracts/csharp-command-extractor.js.map +1 -0
  157. package/dist/contracts/validate-command-contract.d.ts +1 -0
  158. package/dist/contracts/validate-command-contract.js +86 -0
  159. package/dist/contracts/validate-command-contract.js.map +1 -0
  160. package/dist/doctor.d.ts +22 -0
  161. package/dist/doctor.js +498 -0
  162. package/dist/doctor.js.map +1 -0
  163. package/dist/editor-blocker-observer.d.ts +45 -0
  164. package/dist/editor-blocker-observer.js +55 -0
  165. package/dist/editor-blocker-observer.js.map +1 -0
  166. package/dist/editor-dialogs.d.ts +53 -0
  167. package/dist/editor-dialogs.js +79 -0
  168. package/dist/editor-dialogs.js.map +1 -0
  169. package/dist/extension-availability.d.ts +30 -0
  170. package/dist/extension-availability.js +83 -0
  171. package/dist/extension-availability.js.map +1 -0
  172. package/dist/extensions/.generated-bridge-domains/cinemachine.d.ts +2 -0
  173. package/dist/extensions/.generated-bridge-domains/cinemachine.js +104 -0
  174. package/dist/extensions/.generated-bridge-domains/cinemachine.js.map +1 -0
  175. package/dist/extensions/.generated-bridge-domains/hdrp.d.ts +2 -0
  176. package/dist/extensions/.generated-bridge-domains/hdrp.js +35 -0
  177. package/dist/extensions/.generated-bridge-domains/hdrp.js.map +1 -0
  178. package/dist/extensions/.generated-bridge-domains/probuilder.d.ts +13 -0
  179. package/dist/extensions/.generated-bridge-domains/probuilder.js +342 -0
  180. package/dist/extensions/.generated-bridge-domains/probuilder.js.map +1 -0
  181. package/dist/extensions/.generated-bridge-domains/urp.d.ts +2 -0
  182. package/dist/extensions/.generated-bridge-domains/urp.js +52 -0
  183. package/dist/extensions/.generated-bridge-domains/urp.js.map +1 -0
  184. package/dist/extensions/.generated-bridge-domains/vfx.d.ts +2 -0
  185. package/dist/extensions/.generated-bridge-domains/vfx.js +312 -0
  186. package/dist/extensions/.generated-bridge-domains/vfx.js.map +1 -0
  187. package/dist/extensions/.generated-registry-data.d.ts +7 -0
  188. package/dist/extensions/.generated-registry-data.js +895 -0
  189. package/dist/extensions/.generated-registry-data.js.map +1 -0
  190. package/dist/extensions/contribution-loader.d.ts +27 -0
  191. package/dist/extensions/contribution-loader.js +60 -0
  192. package/dist/extensions/contribution-loader.js.map +1 -0
  193. package/dist/extensions/descriptor-loader.d.ts +73 -0
  194. package/dist/extensions/descriptor-loader.js +126 -0
  195. package/dist/extensions/descriptor-loader.js.map +1 -0
  196. package/dist/extensions/index.d.ts +19 -0
  197. package/dist/extensions/index.js +23 -0
  198. package/dist/extensions/index.js.map +1 -0
  199. package/dist/extensions/installer.d.ts +80 -0
  200. package/dist/extensions/installer.js +594 -0
  201. package/dist/extensions/installer.js.map +1 -0
  202. package/dist/extensions/metadata/index.d.ts +15 -0
  203. package/dist/extensions/metadata/index.js +41 -0
  204. package/dist/extensions/metadata/index.js.map +1 -0
  205. package/dist/extensions/metadata/types.d.ts +38 -0
  206. package/dist/extensions/metadata/types.js +11 -0
  207. package/dist/extensions/metadata/types.js.map +1 -0
  208. package/dist/extensions/project-resolver.d.ts +35 -0
  209. package/dist/extensions/project-resolver.js +61 -0
  210. package/dist/extensions/project-resolver.js.map +1 -0
  211. package/dist/extensions/registry.d.ts +86 -0
  212. package/dist/extensions/registry.js +262 -0
  213. package/dist/extensions/registry.js.map +1 -0
  214. package/dist/extensions/tool-manifest.d.ts +29 -0
  215. package/dist/extensions/tool-manifest.js +53 -0
  216. package/dist/extensions/tool-manifest.js.map +1 -0
  217. package/dist/extensions/types.d.ts +115 -0
  218. package/dist/extensions/types.js +12 -0
  219. package/dist/extensions/types.js.map +1 -0
  220. package/dist/instances.d.ts +163 -0
  221. package/dist/instances.js +1137 -0
  222. package/dist/instances.js.map +1 -0
  223. package/dist/output.d.ts +119 -0
  224. package/dist/output.js +204 -0
  225. package/dist/output.js.map +1 -0
  226. package/dist/package-manager.d.ts +280 -0
  227. package/dist/package-manager.js +1832 -0
  228. package/dist/package-manager.js.map +1 -0
  229. package/dist/platform.d.ts +3 -0
  230. package/dist/platform.js +19 -0
  231. package/dist/platform.js.map +1 -0
  232. package/dist/project-identity.d.ts +48 -0
  233. package/dist/project-identity.js +254 -0
  234. package/dist/project-identity.js.map +1 -0
  235. package/dist/project-tool-discovery.d.ts +23 -0
  236. package/dist/project-tool-discovery.js +292 -0
  237. package/dist/project-tool-discovery.js.map +1 -0
  238. package/dist/protocol/envelope.d.ts +64 -0
  239. package/dist/protocol/envelope.js +8 -0
  240. package/dist/protocol/envelope.js.map +1 -0
  241. package/dist/protocol/index.d.ts +7 -0
  242. package/dist/protocol/index.js +2 -0
  243. package/dist/protocol/index.js.map +1 -0
  244. package/dist/runtime-code.d.ts +20 -0
  245. package/dist/runtime-code.js +232 -0
  246. package/dist/runtime-code.js.map +1 -0
  247. package/dist/schema/any-json.d.ts +4 -0
  248. package/dist/schema/any-json.js +2 -0
  249. package/dist/schema/any-json.js.map +1 -0
  250. package/dist/schema/validator.d.ts +7 -0
  251. package/dist/schema/validator.js +135 -0
  252. package/dist/schema/validator.js.map +1 -0
  253. package/dist/services/docs-reflect-cache.d.ts +106 -0
  254. package/dist/services/docs-reflect-cache.js +301 -0
  255. package/dist/services/docs-reflect-cache.js.map +1 -0
  256. package/dist/services/docs-service.d.ts +16 -0
  257. package/dist/services/docs-service.js +208 -0
  258. package/dist/services/docs-service.js.map +1 -0
  259. package/dist/services/reflect-service.d.ts +12 -0
  260. package/dist/services/reflect-service.js +122 -0
  261. package/dist/services/reflect-service.js.map +1 -0
  262. package/dist/static-capability-catalog.d.ts +13 -0
  263. package/dist/static-capability-catalog.js +41 -0
  264. package/dist/static-capability-catalog.js.map +1 -0
  265. package/dist/test-results.d.ts +13 -0
  266. package/dist/test-results.js +44 -0
  267. package/dist/test-results.js.map +1 -0
  268. package/dist/tools/.generated-completion-registry.d.ts +17 -0
  269. package/dist/tools/.generated-completion-registry.js +5862 -0
  270. package/dist/tools/.generated-completion-registry.js.map +1 -0
  271. package/dist/tools/.generated-registry.d.ts +53 -0
  272. package/dist/tools/.generated-registry.js +55 -0
  273. package/dist/tools/.generated-registry.js.map +1 -0
  274. package/dist/tools/.generated-schemas.d.ts +1 -0
  275. package/dist/tools/.generated-schemas.js +56 -0
  276. package/dist/tools/.generated-schemas.js.map +1 -0
  277. package/dist/tools/asset/refresh.d.ts +26 -0
  278. package/dist/tools/asset/refresh.js +97 -0
  279. package/dist/tools/asset/refresh.js.map +1 -0
  280. package/dist/tools/build/run.d.ts +45 -0
  281. package/dist/tools/build/run.js +115 -0
  282. package/dist/tools/build/run.js.map +1 -0
  283. package/dist/tools/catalog.d.ts +33 -0
  284. package/dist/tools/catalog.js +202 -0
  285. package/dist/tools/catalog.js.map +1 -0
  286. package/dist/tools/code/exec.d.ts +53 -0
  287. package/dist/tools/code/exec.js +59 -0
  288. package/dist/tools/code/exec.js.map +1 -0
  289. package/dist/tools/commands.d.ts +5 -0
  290. package/dist/tools/commands.js +45 -0
  291. package/dist/tools/commands.js.map +1 -0
  292. package/dist/tools/completion-probe.d.ts +22 -0
  293. package/dist/tools/completion-probe.js +2 -0
  294. package/dist/tools/completion-probe.js.map +1 -0
  295. package/dist/tools/define.d.ts +77 -0
  296. package/dist/tools/define.js +58 -0
  297. package/dist/tools/define.js.map +1 -0
  298. package/dist/tools/diagnose/capabilities.d.ts +13 -0
  299. package/dist/tools/diagnose/capabilities.js +34 -0
  300. package/dist/tools/diagnose/capabilities.js.map +1 -0
  301. package/dist/tools/dispatcher.d.ts +27 -0
  302. package/dist/tools/dispatcher.js +971 -0
  303. package/dist/tools/dispatcher.js.map +1 -0
  304. package/dist/tools/docs/get.d.ts +22 -0
  305. package/dist/tools/docs/get.js +22 -0
  306. package/dist/tools/docs/get.js.map +1 -0
  307. package/dist/tools/docs/search.d.ts +31 -0
  308. package/dist/tools/docs/search.js +22 -0
  309. package/dist/tools/docs/search.js.map +1 -0
  310. package/dist/tools/editor/compile.d.ts +66 -0
  311. package/dist/tools/editor/compile.js +225 -0
  312. package/dist/tools/editor/compile.js.map +1 -0
  313. package/dist/tools/editor/dialog/click.d.ts +12 -0
  314. package/dist/tools/editor/dialog/click.js +71 -0
  315. package/dist/tools/editor/dialog/click.js.map +1 -0
  316. package/dist/tools/editor/dialogs.d.ts +12 -0
  317. package/dist/tools/editor/dialogs.js +58 -0
  318. package/dist/tools/editor/dialogs.js.map +1 -0
  319. package/dist/tools/editor/readiness.d.ts +13 -0
  320. package/dist/tools/editor/readiness.js +24 -0
  321. package/dist/tools/editor/readiness.js.map +1 -0
  322. package/dist/tools/editor/refresh.d.ts +25 -0
  323. package/dist/tools/editor/refresh.js +154 -0
  324. package/dist/tools/editor/refresh.js.map +1 -0
  325. package/dist/tools/editor/state.d.ts +63 -0
  326. package/dist/tools/editor/state.js +286 -0
  327. package/dist/tools/editor/state.js.map +1 -0
  328. package/dist/tools/errors.d.ts +30 -0
  329. package/dist/tools/errors.js +46 -0
  330. package/dist/tools/errors.js.map +1 -0
  331. package/dist/tools/metadata/derived.d.ts +34 -0
  332. package/dist/tools/metadata/derived.js +136 -0
  333. package/dist/tools/metadata/derived.js.map +1 -0
  334. package/dist/tools/metadata/index.d.ts +34 -0
  335. package/dist/tools/metadata/index.js +130 -0
  336. package/dist/tools/metadata/index.js.map +1 -0
  337. package/dist/tools/metadata/schemas.d.ts +3 -0
  338. package/dist/tools/metadata/schemas.js +19 -0
  339. package/dist/tools/metadata/schemas.js.map +1 -0
  340. package/dist/tools/metadata/status.d.ts +4 -0
  341. package/dist/tools/metadata/status.js +61 -0
  342. package/dist/tools/metadata/status.js.map +1 -0
  343. package/dist/tools/metadata/types.d.ts +79 -0
  344. package/dist/tools/metadata/types.js +2 -0
  345. package/dist/tools/metadata/types.js.map +1 -0
  346. package/dist/tools/navmesh/bake-cancel.d.ts +7 -0
  347. package/dist/tools/navmesh/bake-cancel.js +21 -0
  348. package/dist/tools/navmesh/bake-cancel.js.map +1 -0
  349. package/dist/tools/navmesh/bake-status.d.ts +5 -0
  350. package/dist/tools/navmesh/bake-status.js +18 -0
  351. package/dist/tools/navmesh/bake-status.js.map +1 -0
  352. package/dist/tools/navmesh/bake.d.ts +5 -0
  353. package/dist/tools/navmesh/bake.js +23 -0
  354. package/dist/tools/navmesh/bake.js.map +1 -0
  355. package/dist/tools/navmesh/live.d.ts +86 -0
  356. package/dist/tools/navmesh/live.js +206 -0
  357. package/dist/tools/navmesh/live.js.map +1 -0
  358. package/dist/tools/navmesh/surface-bake.d.ts +5 -0
  359. package/dist/tools/navmesh/surface-bake.js +24 -0
  360. package/dist/tools/navmesh/surface-bake.js.map +1 -0
  361. package/dist/tools/occlusion/bake-cancel.d.ts +7 -0
  362. package/dist/tools/occlusion/bake-cancel.js +21 -0
  363. package/dist/tools/occlusion/bake-cancel.js.map +1 -0
  364. package/dist/tools/occlusion/bake-status.d.ts +5 -0
  365. package/dist/tools/occlusion/bake-status.js +18 -0
  366. package/dist/tools/occlusion/bake-status.js.map +1 -0
  367. package/dist/tools/occlusion/bake.d.ts +11 -0
  368. package/dist/tools/occlusion/bake.js +23 -0
  369. package/dist/tools/occlusion/bake.js.map +1 -0
  370. package/dist/tools/occlusion/live.d.ts +74 -0
  371. package/dist/tools/occlusion/live.js +201 -0
  372. package/dist/tools/occlusion/live.js.map +1 -0
  373. package/dist/tools/package/bridge-remove.d.ts +26 -0
  374. package/dist/tools/package/bridge-remove.js +32 -0
  375. package/dist/tools/package/bridge-remove.js.map +1 -0
  376. package/dist/tools/package/deploy.d.ts +49 -0
  377. package/dist/tools/package/deploy.js +134 -0
  378. package/dist/tools/package/deploy.js.map +1 -0
  379. package/dist/tools/package/embed.d.ts +14 -0
  380. package/dist/tools/package/embed.js +93 -0
  381. package/dist/tools/package/embed.js.map +1 -0
  382. package/dist/tools/package/file-mutation-worker.d.ts +6 -0
  383. package/dist/tools/package/file-mutation-worker.js +154 -0
  384. package/dist/tools/package/file-mutation-worker.js.map +1 -0
  385. package/dist/tools/package/info.d.ts +19 -0
  386. package/dist/tools/package/info.js +27 -0
  387. package/dist/tools/package/info.js.map +1 -0
  388. package/dist/tools/package/install.d.ts +24 -0
  389. package/dist/tools/package/install.js +65 -0
  390. package/dist/tools/package/install.js.map +1 -0
  391. package/dist/tools/package/list.d.ts +27 -0
  392. package/dist/tools/package/list.js +21 -0
  393. package/dist/tools/package/list.js.map +1 -0
  394. package/dist/tools/package/live-resolve.d.ts +46 -0
  395. package/dist/tools/package/live-resolve.js +497 -0
  396. package/dist/tools/package/live-resolve.js.map +1 -0
  397. package/dist/tools/package/public-result.d.ts +4 -0
  398. package/dist/tools/package/public-result.js +25 -0
  399. package/dist/tools/package/public-result.js.map +1 -0
  400. package/dist/tools/package/registry-clear.d.ts +15 -0
  401. package/dist/tools/package/registry-clear.js +27 -0
  402. package/dist/tools/package/registry-clear.js.map +1 -0
  403. package/dist/tools/package/registry-get.d.ts +10 -0
  404. package/dist/tools/package/registry-get.js +22 -0
  405. package/dist/tools/package/registry-get.js.map +1 -0
  406. package/dist/tools/package/registry-set.d.ts +17 -0
  407. package/dist/tools/package/registry-set.js +28 -0
  408. package/dist/tools/package/registry-set.js.map +1 -0
  409. package/dist/tools/package/remove.d.ts +47 -0
  410. package/dist/tools/package/remove.js +67 -0
  411. package/dist/tools/package/remove.js.map +1 -0
  412. package/dist/tools/package/resolve.d.ts +6 -0
  413. package/dist/tools/package/resolve.js +14 -0
  414. package/dist/tools/package/resolve.js.map +1 -0
  415. package/dist/tools/package/restore.d.ts +42 -0
  416. package/dist/tools/package/restore.js +122 -0
  417. package/dist/tools/package/restore.js.map +1 -0
  418. package/dist/tools/package/scoped-registry-add.d.ts +25 -0
  419. package/dist/tools/package/scoped-registry-add.js +30 -0
  420. package/dist/tools/package/scoped-registry-add.js.map +1 -0
  421. package/dist/tools/package/scoped-registry-list.d.ts +14 -0
  422. package/dist/tools/package/scoped-registry-list.js +22 -0
  423. package/dist/tools/package/scoped-registry-list.js.map +1 -0
  424. package/dist/tools/package/scoped-registry-remove.d.ts +29 -0
  425. package/dist/tools/package/scoped-registry-remove.js +31 -0
  426. package/dist/tools/package/scoped-registry-remove.js.map +1 -0
  427. package/dist/tools/package/search.d.ts +17 -0
  428. package/dist/tools/package/search.js +39 -0
  429. package/dist/tools/package/search.js.map +1 -0
  430. package/dist/tools/package/status.d.ts +21 -0
  431. package/dist/tools/package/status.js +37 -0
  432. package/dist/tools/package/status.js.map +1 -0
  433. package/dist/tools/reflect/member.d.ts +22 -0
  434. package/dist/tools/reflect/member.js +22 -0
  435. package/dist/tools/reflect/member.js.map +1 -0
  436. package/dist/tools/reflect/search.d.ts +30 -0
  437. package/dist/tools/reflect/search.js +22 -0
  438. package/dist/tools/reflect/search.js.map +1 -0
  439. package/dist/tools/reflect/type.d.ts +21 -0
  440. package/dist/tools/reflect/type.js +22 -0
  441. package/dist/tools/reflect/type.js.map +1 -0
  442. package/dist/tools/runtime/console.d.ts +24 -0
  443. package/dist/tools/runtime/console.js +17 -0
  444. package/dist/tools/runtime/console.js.map +1 -0
  445. package/dist/tools/runtime/input/key.d.ts +23 -0
  446. package/dist/tools/runtime/input/key.js +18 -0
  447. package/dist/tools/runtime/input/key.js.map +1 -0
  448. package/dist/tools/runtime/input/pointer.d.ts +34 -0
  449. package/dist/tools/runtime/input/pointer.js +18 -0
  450. package/dist/tools/runtime/input/pointer.js.map +1 -0
  451. package/dist/tools/runtime/log.d.ts +20 -0
  452. package/dist/tools/runtime/log.js +17 -0
  453. package/dist/tools/runtime/log.js.map +1 -0
  454. package/dist/tools/runtime/state.d.ts +45 -0
  455. package/dist/tools/runtime/state.js +17 -0
  456. package/dist/tools/runtime/state.js.map +1 -0
  457. package/dist/tools/runtime/target-framerate/set.d.ts +16 -0
  458. package/dist/tools/runtime/target-framerate/set.js +17 -0
  459. package/dist/tools/runtime/target-framerate/set.js.map +1 -0
  460. package/dist/tools/runtime/time-scale/set.d.ts +16 -0
  461. package/dist/tools/runtime/time-scale/set.js +17 -0
  462. package/dist/tools/runtime/time-scale/set.js.map +1 -0
  463. package/dist/tools/runtime/ui/capture-element.d.ts +31 -0
  464. package/dist/tools/runtime/ui/capture-element.js +17 -0
  465. package/dist/tools/runtime/ui/capture-element.js.map +1 -0
  466. package/dist/tools/test/cancel.d.ts +19 -0
  467. package/dist/tools/test/cancel.js +94 -0
  468. package/dist/tools/test/cancel.js.map +1 -0
  469. package/dist/tools/test/run.d.ts +39 -0
  470. package/dist/tools/test/run.js +101 -0
  471. package/dist/tools/test/run.js.map +1 -0
  472. package/dist/tools/tools/describe.d.ts +27 -0
  473. package/dist/tools/tools/describe.js +35 -0
  474. package/dist/tools/tools/describe.js.map +1 -0
  475. package/dist/tools/tools/group-list.d.ts +20 -0
  476. package/dist/tools/tools/group-list.js +39 -0
  477. package/dist/tools/tools/group-list.js.map +1 -0
  478. package/dist/tools/tools/group-summaries.d.ts +3 -0
  479. package/dist/tools/tools/group-summaries.js +52 -0
  480. package/dist/tools/tools/group-summaries.js.map +1 -0
  481. package/dist/tools/tools/group.d.ts +13 -0
  482. package/dist/tools/tools/group.js +34 -0
  483. package/dist/tools/tools/group.js.map +1 -0
  484. package/dist/tools/tools/search.d.ts +17 -0
  485. package/dist/tools/tools/search.js +42 -0
  486. package/dist/tools/tools/search.js.map +1 -0
  487. package/dist/types.d.ts +5 -0
  488. package/dist/types.js +2 -0
  489. package/dist/types.js.map +1 -0
  490. package/dist/unity-project-processes.d.ts +12 -0
  491. package/dist/unity-project-processes.js +59 -0
  492. package/dist/unity-project-processes.js.map +1 -0
  493. package/dist/util.d.ts +11 -0
  494. package/dist/util.js +22 -0
  495. package/dist/util.js.map +1 -0
  496. package/dist/windows-editor-state.d.ts +31 -0
  497. package/dist/windows-editor-state.js +98 -0
  498. package/dist/windows-editor-state.js.map +1 -0
  499. package/docs/README.md +43 -0
  500. package/docs/availability-differences.md +97 -0
  501. package/docs/command-execution.md +89 -0
  502. package/docs/error-codes.md +204 -0
  503. package/docs/extensions.md +132 -0
  504. package/docs/risk-and-side-effects.md +93 -0
  505. package/docs/safety-boundaries.md +103 -0
  506. package/docs/target-and-instance.md +83 -0
  507. package/docs/tool-discovery.md +104 -0
  508. package/docs/tool-execution.md +178 -0
  509. package/extensions/cinemachine/bridge/domains/cinemachine.ts +115 -0
  510. package/extensions/cinemachine/docs/cinemachine.md +50 -0
  511. package/extensions/cinemachine/extension.json +129 -0
  512. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/CinemachineExtensionToolSupport.cs +15 -0
  513. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/CinemachineToolSupport.cs +854 -0
  514. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/aim.cs +96 -0
  515. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/blend.cs +140 -0
  516. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/body.cs +96 -0
  517. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/brain-ensure.cs +16 -0
  518. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/brain-status.cs +12 -0
  519. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/create.cs +147 -0
  520. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/extension-add.cs +13 -0
  521. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/extension-list.cs +13 -0
  522. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/extension-remove.cs +13 -0
  523. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/lens.cs +142 -0
  524. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/noise.cs +126 -0
  525. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/ping.cs +43 -0
  526. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/priority.cs +113 -0
  527. package/extensions/cinemachine/unitypkg/Editor/Cinemachine/target.cs +84 -0
  528. package/extensions/cinemachine/unitypkg/UnityCli.Extensions.Cinemachine.Editor.asmdef +18 -0
  529. package/extensions/hdrp/bridge/domains/hdrp.ts +41 -0
  530. package/extensions/hdrp/docs/hdrp.md +15 -0
  531. package/extensions/hdrp/extension.json +31 -0
  532. package/extensions/hdrp/unitypkg/Editor/Hdrp/HdrpToolSupport.cs +607 -0
  533. package/extensions/hdrp/unitypkg/Editor/Hdrp/settings-get.cs +29 -0
  534. package/extensions/hdrp/unitypkg/Editor/Hdrp/settings-set.cs +47 -0
  535. package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-add-effect.cs +28 -0
  536. package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-create.cs +28 -0
  537. package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-info.cs +25 -0
  538. package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-list.cs +25 -0
  539. package/extensions/hdrp/unitypkg/Editor/Hdrp/volume-set.cs +28 -0
  540. package/extensions/hdrp/unitypkg/UnityCli.Extensions.Hdrp.Editor.asmdef +19 -0
  541. package/extensions/probuilder/bridge/domains/probuilder.ts +386 -0
  542. package/extensions/probuilder/docs/probuilder.md +125 -0
  543. package/extensions/probuilder/extension.json +291 -0
  544. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderAppendCompletionProof.cs +254 -0
  545. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderAtomicReadback.cs +293 -0
  546. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderBevelCompletionProof.cs +299 -0
  547. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderCanonicalMeshSnapshot.cs +951 -0
  548. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderConnectCompletionProof.cs +407 -0
  549. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderEdgeCompletionProof.cs +318 -0
  550. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderErrors.cs +83 -0
  551. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderFaceCompletionProof.cs +152 -0
  552. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderFaceExtrudeCompletionProof.cs +283 -0
  553. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderFlipCompletionProof.cs +170 -0
  554. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderInput.cs +85 -0
  555. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderInsertCompletionProof.cs +674 -0
  556. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderOperationProof.cs +137 -0
  557. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderReflection.cs +1064 -0
  558. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderRefreshUvProof.cs +53 -0
  559. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderSceneTool.cs +184 -0
  560. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderShapeTopologyRecords.g.cs +82 -0
  561. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ProBuilderVertexObjectCompletionProof.cs +346 -0
  562. package/extensions/probuilder/unitypkg/Editor/ProBuilder/append-vertices-to-edge.cs +118 -0
  563. package/extensions/probuilder/unitypkg/Editor/ProBuilder/auto-smooth.cs +104 -0
  564. package/extensions/probuilder/unitypkg/Editor/ProBuilder/bevel-edges.cs +119 -0
  565. package/extensions/probuilder/unitypkg/Editor/ProBuilder/bridge-edges.cs +113 -0
  566. package/extensions/probuilder/unitypkg/Editor/ProBuilder/center-pivot.cs +43 -0
  567. package/extensions/probuilder/unitypkg/Editor/ProBuilder/combine-meshes.cs +58 -0
  568. package/extensions/probuilder/unitypkg/Editor/ProBuilder/connect-elements.cs +128 -0
  569. package/extensions/probuilder/unitypkg/Editor/ProBuilder/convert-to-probuilder.cs +40 -0
  570. package/extensions/probuilder/unitypkg/Editor/ProBuilder/create-poly-shape.cs +55 -0
  571. package/extensions/probuilder/unitypkg/Editor/ProBuilder/create-polygon.cs +28 -0
  572. package/extensions/probuilder/unitypkg/Editor/ProBuilder/create-shape.cs +168 -0
  573. package/extensions/probuilder/unitypkg/Editor/ProBuilder/delete-faces.cs +42 -0
  574. package/extensions/probuilder/unitypkg/Editor/ProBuilder/detach-faces.cs +46 -0
  575. package/extensions/probuilder/unitypkg/Editor/ProBuilder/duplicate-and-flip.cs +100 -0
  576. package/extensions/probuilder/unitypkg/Editor/ProBuilder/extrude-edges.cs +118 -0
  577. package/extensions/probuilder/unitypkg/Editor/ProBuilder/extrude-faces.cs +110 -0
  578. package/extensions/probuilder/unitypkg/Editor/ProBuilder/flip-normals.cs +102 -0
  579. package/extensions/probuilder/unitypkg/Editor/ProBuilder/freeze-transform.cs +45 -0
  580. package/extensions/probuilder/unitypkg/Editor/ProBuilder/insert-vertex.cs +132 -0
  581. package/extensions/probuilder/unitypkg/Editor/ProBuilder/merge-faces.cs +44 -0
  582. package/extensions/probuilder/unitypkg/Editor/ProBuilder/merge-objects.cs +130 -0
  583. package/extensions/probuilder/unitypkg/Editor/ProBuilder/merge-vertices.cs +109 -0
  584. package/extensions/probuilder/unitypkg/Editor/ProBuilder/mesh-info.cs +117 -0
  585. package/extensions/probuilder/unitypkg/Editor/ProBuilder/move-vertices.cs +33 -0
  586. package/extensions/probuilder/unitypkg/Editor/ProBuilder/ping.cs +51 -0
  587. package/extensions/probuilder/unitypkg/Editor/ProBuilder/repair-mesh.cs +40 -0
  588. package/extensions/probuilder/unitypkg/Editor/ProBuilder/select-faces.cs +161 -0
  589. package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-face-color.cs +24 -0
  590. package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-face-material.cs +23 -0
  591. package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-face-uvs.cs +28 -0
  592. package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-pivot.cs +42 -0
  593. package/extensions/probuilder/unitypkg/Editor/ProBuilder/set-smoothing.cs +23 -0
  594. package/extensions/probuilder/unitypkg/Editor/ProBuilder/split-vertices.cs +64 -0
  595. package/extensions/probuilder/unitypkg/Editor/ProBuilder/subdivide.cs +111 -0
  596. package/extensions/probuilder/unitypkg/Editor/ProBuilder/validate-mesh.cs +75 -0
  597. package/extensions/probuilder/unitypkg/Editor/ProBuilder/weld-vertices.cs +112 -0
  598. package/extensions/probuilder/unitypkg/UnityCli.Extensions.ProBuilder.Editor.asmdef +25 -0
  599. package/extensions/urp/bridge/domains/urp.ts +62 -0
  600. package/extensions/urp/docs/urp.md +20 -0
  601. package/extensions/urp/extension.json +41 -0
  602. package/extensions/urp/unitypkg/Editor/Urp/UrpFeatureToolSupport.cs +336 -0
  603. package/extensions/urp/unitypkg/Editor/Urp/UrpToolSupport.cs +801 -0
  604. package/extensions/urp/unitypkg/Editor/Urp/feature-add.cs +12 -0
  605. package/extensions/urp/unitypkg/Editor/Urp/feature-configure.cs +12 -0
  606. package/extensions/urp/unitypkg/Editor/Urp/feature-list.cs +12 -0
  607. package/extensions/urp/unitypkg/Editor/Urp/feature-remove.cs +12 -0
  608. package/extensions/urp/unitypkg/Editor/Urp/feature-toggle.cs +12 -0
  609. package/extensions/urp/unitypkg/Editor/Urp/settings-get.cs +27 -0
  610. package/extensions/urp/unitypkg/Editor/Urp/settings-set.cs +47 -0
  611. package/extensions/urp/unitypkg/Editor/Urp/volume-add-effect.cs +33 -0
  612. package/extensions/urp/unitypkg/Editor/Urp/volume-create.cs +33 -0
  613. package/extensions/urp/unitypkg/Editor/Urp/volume-info.cs +31 -0
  614. package/extensions/urp/unitypkg/Editor/Urp/volume-list.cs +39 -0
  615. package/extensions/urp/unitypkg/Editor/Urp/volume-set.cs +33 -0
  616. package/extensions/urp/unitypkg/UnityCli.Extensions.Urp.Editor.asmdef +19 -0
  617. package/extensions/vfx-graph/bridge/domains/vfx.ts +337 -0
  618. package/extensions/vfx-graph/docs/vfx.md +28 -0
  619. package/extensions/vfx-graph/extension.json +72 -0
  620. package/extensions/vfx-graph/unitypkg/Editor/Vfx/VfxActionToolSupport.cs +248 -0
  621. package/extensions/vfx-graph/unitypkg/Editor/Vfx/VfxToolSupport.cs +1325 -0
  622. package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-pause.cs +37 -0
  623. package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-play.cs +40 -0
  624. package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-reinitialize.cs +37 -0
  625. package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-set-rate.cs +36 -0
  626. package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-set-seed.cs +42 -0
  627. package/extensions/vfx-graph/unitypkg/Editor/Vfx/control-stop.cs +37 -0
  628. package/extensions/vfx-graph/unitypkg/Editor/Vfx/effect-add.cs +55 -0
  629. package/extensions/vfx-graph/unitypkg/Editor/Vfx/effect-assign.cs +44 -0
  630. package/extensions/vfx-graph/unitypkg/Editor/Vfx/effect-info.cs +30 -0
  631. package/extensions/vfx-graph/unitypkg/Editor/Vfx/graph-create.cs +62 -0
  632. package/extensions/vfx-graph/unitypkg/Editor/Vfx/graph-info.cs +32 -0
  633. package/extensions/vfx-graph/unitypkg/Editor/Vfx/line-create.cs +75 -0
  634. package/extensions/vfx-graph/unitypkg/Editor/Vfx/line-positions.cs +100 -0
  635. package/extensions/vfx-graph/unitypkg/Editor/Vfx/parameter.cs +216 -0
  636. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-clear.cs +39 -0
  637. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-create.cs +80 -0
  638. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-emission.cs +126 -0
  639. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-info.cs +55 -0
  640. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-main.cs +75 -0
  641. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-pause.cs +38 -0
  642. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-play.cs +38 -0
  643. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-shape.cs +148 -0
  644. package/extensions/vfx-graph/unitypkg/Editor/Vfx/particle-stop.cs +41 -0
  645. package/extensions/vfx-graph/unitypkg/Editor/Vfx/template-create.cs +58 -0
  646. package/extensions/vfx-graph/unitypkg/Editor/Vfx/template-list.cs +31 -0
  647. package/extensions/vfx-graph/unitypkg/Editor/Vfx/trail-create.cs +41 -0
  648. package/extensions/vfx-graph/unitypkg/Editor/Vfx/trail-set.cs +44 -0
  649. package/extensions/vfx-graph/unitypkg/UnityCli.Extensions.VfxGraph.Editor.asmdef +18 -0
  650. package/package.json +46 -0
  651. package/schemas/error-code.schema.json +70 -0
  652. package/schemas/extension-descriptor.schema.json +88 -0
  653. package/schemas/extension-manifest.schema.json +125 -0
  654. package/schemas/request-envelope.schema.json +34 -0
  655. package/schemas/response-envelope.schema.json +42 -0
  656. package/schemas/tool-metadata.schema.json +111 -0
  657. package/skills/unitycli/SKILL.md +61 -0
  658. package/skills/unitycli/agents/interface.yaml +30 -0
  659. package/skills/unitycli/evals/history/2026-08-05-governed-baseline.json +44 -0
  660. package/skills/unitycli/evals/output/cases.jsonl +5 -0
  661. package/skills/unitycli/evals/output/fixtures/feedback-scenario.json +27 -0
  662. package/skills/unitycli/evals/output/schema.json +32 -0
  663. package/skills/unitycli/evals/semantic_config.json +46 -0
  664. package/skills/unitycli/evals/trigger_cases.json +39 -0
  665. package/skills/unitycli/examples/feedback-event.input.json +8 -0
  666. package/skills/unitycli/examples/feedback-record.output.json +12 -0
  667. package/skills/unitycli/manifest.json +112 -0
  668. package/skills/unitycli/references/custom-tools.md +111 -0
  669. package/skills/unitycli/references/feedback.md +59 -0
  670. package/skills/unitycli/references/operation-protocol.md +83 -0
  671. package/skills/unitycli/reports/output_quality_scorecard.md +56 -0
  672. package/skills/unitycli/reports/trust-report.md +57 -0
  673. package/skills/unitycli/scripts/record-feedback.ps1 +210 -0
  674. package/skills/unitycli/security/network_policy.json +18 -0
  675. package/skills/unitycli/security/permission_policy.json +40 -0
  676. package/unitypkg/Editor/Bridge/BridgeCommandContext.cs +64 -0
  677. package/unitypkg/Editor/Bridge/BridgeCommandException.cs +39 -0
  678. package/unitypkg/Editor/Bridge/BridgeCommandRegistry.cs +172 -0
  679. package/unitypkg/Editor/Bridge/BridgeHttpServer.cs +534 -0
  680. package/unitypkg/Editor/Bridge/BridgeResponse.cs +58 -0
  681. package/unitypkg/Editor/Bridge/BridgeWorkQueue.cs +325 -0
  682. package/unitypkg/Editor/BridgeStartupStateMachine.cs +81 -0
  683. package/unitypkg/Editor/Commands/ProjectToolCommands.cs +43 -0
  684. package/unitypkg/Editor/Commands/RuntimeToolCommands.cs +43 -0
  685. package/unitypkg/Editor/Generated/UnityCliToolCompletionRegistry.g.cs +428 -0
  686. package/unitypkg/Editor/Infrastructure/CodeExecSupport.cs +664 -0
  687. package/unitypkg/Editor/Infrastructure/RegistryOwnershipResolver.cs +148 -0
  688. package/unitypkg/Editor/Infrastructure/UnityCliActiveOperation.cs +411 -0
  689. package/unitypkg/Editor/Infrastructure/UnityCliAdmission.cs +485 -0
  690. package/unitypkg/Editor/Infrastructure/UnityCliArtifacts.cs +110 -0
  691. package/unitypkg/Editor/Infrastructure/UnityCliAssetRefreshCoordinator.cs +106 -0
  692. package/unitypkg/Editor/Infrastructure/UnityCliBridgeHelpers.cs +155 -0
  693. package/unitypkg/Editor/Infrastructure/UnityCliCanonicalProjectPath.cs +325 -0
  694. package/unitypkg/Editor/Infrastructure/UnityCliCompilationEventBroker.cs +192 -0
  695. package/unitypkg/Editor/Infrastructure/UnityCliCompileCoordinator.cs +271 -0
  696. package/unitypkg/Editor/Infrastructure/UnityCliCompletionProbe.cs +515 -0
  697. package/unitypkg/Editor/Infrastructure/UnityCliEditorStateSnapshot.cs +84 -0
  698. package/unitypkg/Editor/Infrastructure/UnityCliInstanceRegistry.cs +558 -0
  699. package/unitypkg/Editor/Infrastructure/UnityCliJson.cs +155 -0
  700. package/unitypkg/Editor/Infrastructure/UnityCliLargeOutput.cs +128 -0
  701. package/unitypkg/Editor/Infrastructure/UnityCliLiveBuildCoordinator.cs +230 -0
  702. package/unitypkg/Editor/Infrastructure/UnityCliLiveRefreshCoordinator.cs +164 -0
  703. package/unitypkg/Editor/Infrastructure/UnityCliLog.cs +127 -0
  704. package/unitypkg/Editor/Infrastructure/UnityCliNavMeshBakeCoordinator.cs +2073 -0
  705. package/unitypkg/Editor/Infrastructure/UnityCliObjectPatch.cs +193 -0
  706. package/unitypkg/Editor/Infrastructure/UnityCliObjectRef.cs +278 -0
  707. package/unitypkg/Editor/Infrastructure/UnityCliObjectSerializer.cs +620 -0
  708. package/unitypkg/Editor/Infrastructure/UnityCliOcclusionBakeCoordinator.cs +1739 -0
  709. package/unitypkg/Editor/Infrastructure/UnityCliOperationCoordinator.cs +828 -0
  710. package/unitypkg/Editor/Infrastructure/UnityCliPackageEmbedCoordinator.cs +147 -0
  711. package/unitypkg/Editor/Infrastructure/UnityCliPackageResolveCoordinator.cs +454 -0
  712. package/unitypkg/Editor/Infrastructure/UnityCliPackageResolveState.cs +561 -0
  713. package/unitypkg/Editor/Infrastructure/UnityCliPipelineHandoff.cs +742 -0
  714. package/unitypkg/Editor/Infrastructure/UnityCliProjectSettings.cs +92 -0
  715. package/unitypkg/Editor/Infrastructure/UnityCliProtocolRequest.cs +116 -0
  716. package/unitypkg/Editor/Infrastructure/UnityCliReflection.cs +477 -0
  717. package/unitypkg/Editor/Infrastructure/UnityCliReloadOperation.cs +664 -0
  718. package/unitypkg/Editor/Infrastructure/UnityCliRequestDigest.cs +34 -0
  719. package/unitypkg/Editor/Infrastructure/UnityCliRuntimeBuildInjection.cs +121 -0
  720. package/unitypkg/Editor/Infrastructure/UnityCliRuntimeExecutor.cs +391 -0
  721. package/unitypkg/Editor/Infrastructure/UnityCliSceneMutation.cs +84 -0
  722. package/unitypkg/Editor/Infrastructure/UnityCliScriptableObjectHelpers.cs +235 -0
  723. package/unitypkg/Editor/Infrastructure/UnityCliSerializer.cs +580 -0
  724. package/unitypkg/Editor/Infrastructure/UnityCliSessionMutationLease.cs +568 -0
  725. package/unitypkg/Editor/Infrastructure/UnityCliStateIO.cs +73 -0
  726. package/unitypkg/Editor/Infrastructure/UnityCliTestRunCoordinator.cs +598 -0
  727. package/unitypkg/Editor/Infrastructure/UnityCliTransactionLog.cs +112 -0
  728. package/unitypkg/Editor/Infrastructure/UnityCliValueConverter.cs +146 -0
  729. package/unitypkg/Editor/Infrastructure/UnityCliWaiter.cs +716 -0
  730. package/unitypkg/Editor/ProjectTools/IUnityCliTool.cs +152 -0
  731. package/unitypkg/Editor/ProjectTools/OfficialAttributeToolRegistry.cs +992 -0
  732. package/unitypkg/Editor/ProjectTools/ProjectToolRegistry.cs +938 -0
  733. package/unitypkg/Editor/ProjectTools/ProjectToolSchemaValidator.cs +258 -0
  734. package/unitypkg/Editor/ProjectTools/RegisteredTool.cs +75 -0
  735. package/unitypkg/Editor/ProjectTools/UnityCliProjectToolContext.cs +68 -0
  736. package/unitypkg/Editor/ProjectTools/UnityCliSchemaDeriver.cs +297 -0
  737. package/unitypkg/Editor/Protocol/ProtocolEnvelopeFactory.cs +92 -0
  738. package/unitypkg/Editor/Protocol/envelope.cs +50 -0
  739. package/unitypkg/Editor/Roslyn/LICENSE.txt +23 -0
  740. package/unitypkg/Editor/Roslyn/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  741. package/unitypkg/Editor/Roslyn/Microsoft.CodeAnalysis.dll +0 -0
  742. package/unitypkg/Editor/Roslyn/System.Collections.Immutable.dll +0 -0
  743. package/unitypkg/Editor/Roslyn/System.Reflection.Metadata.dll +0 -0
  744. package/unitypkg/Editor/Tools/animation/AnimationToolSupport.cs +335 -0
  745. package/unitypkg/Editor/Tools/animation/AnimatorRuntimeToolSupport.cs +24 -0
  746. package/unitypkg/Editor/Tools/animation/animator/crossfade.cs +117 -0
  747. package/unitypkg/Editor/Tools/animation/animator/get-info.cs +24 -0
  748. package/unitypkg/Editor/Tools/animation/animator/get-parameter.cs +36 -0
  749. package/unitypkg/Editor/Tools/animation/animator/set-enabled.cs +33 -0
  750. package/unitypkg/Editor/Tools/animation/animator/set-parameter.cs +71 -0
  751. package/unitypkg/Editor/Tools/animation/animator/set-speed.cs +33 -0
  752. package/unitypkg/Editor/Tools/animation/clip/create.cs +48 -0
  753. package/unitypkg/Editor/Tools/animation/clip/modify.cs +43 -0
  754. package/unitypkg/Editor/Tools/animation/clip.cs +23 -0
  755. package/unitypkg/Editor/Tools/animation/play.cs +41 -0
  756. package/unitypkg/Editor/Tools/animation/state.cs +25 -0
  757. package/unitypkg/Editor/Tools/animation/stop.cs +45 -0
  758. package/unitypkg/Editor/Tools/asset/AssetImportSettingsToolSupport.cs +633 -0
  759. package/unitypkg/Editor/Tools/asset/AssetToolSupport.cs +253 -0
  760. package/unitypkg/Editor/Tools/asset/create.cs +84 -0
  761. package/unitypkg/Editor/Tools/asset/delete.cs +88 -0
  762. package/unitypkg/Editor/Tools/asset/duplicate.cs +68 -0
  763. package/unitypkg/Editor/Tools/asset/get-components.cs +177 -0
  764. package/unitypkg/Editor/Tools/asset/get-data.cs +73 -0
  765. package/unitypkg/Editor/Tools/asset/import-settings/set.cs +127 -0
  766. package/unitypkg/Editor/Tools/asset/import-settings.cs +54 -0
  767. package/unitypkg/Editor/Tools/asset/import.cs +132 -0
  768. package/unitypkg/Editor/Tools/asset/info.cs +48 -0
  769. package/unitypkg/Editor/Tools/asset/modify.cs +69 -0
  770. package/unitypkg/Editor/Tools/asset/move.cs +70 -0
  771. package/unitypkg/Editor/Tools/asset/preview.cs +86 -0
  772. package/unitypkg/Editor/Tools/asset/rename.cs +84 -0
  773. package/unitypkg/Editor/Tools/asset/search-built-in.cs +90 -0
  774. package/unitypkg/Editor/Tools/asset/search.cs +83 -0
  775. package/unitypkg/Editor/Tools/build/BuildSceneStateTool.cs +37 -0
  776. package/unitypkg/Editor/Tools/build/BuildSettingsToolSupport.cs +4 -0
  777. package/unitypkg/Editor/Tools/build/BuildToolDtos.cs +15 -0
  778. package/unitypkg/Editor/Tools/build/BuildToolSupport.cs +111 -0
  779. package/unitypkg/Editor/Tools/build/platform/get.cs +12 -0
  780. package/unitypkg/Editor/Tools/build/platform/set.cs +45 -0
  781. package/unitypkg/Editor/Tools/build/profile/create.cs +44 -0
  782. package/unitypkg/Editor/Tools/build/profile/list.cs +12 -0
  783. package/unitypkg/Editor/Tools/build/profile/modify.cs +42 -0
  784. package/unitypkg/Editor/Tools/build/profile/switch.cs +30 -0
  785. package/unitypkg/Editor/Tools/build/scene/add.cs +45 -0
  786. package/unitypkg/Editor/Tools/build/scene/disable.cs +3 -0
  787. package/unitypkg/Editor/Tools/build/scene/enable.cs +4 -0
  788. package/unitypkg/Editor/Tools/build/scene/list.cs +4 -0
  789. package/unitypkg/Editor/Tools/build/scene/remove.cs +29 -0
  790. package/unitypkg/Editor/Tools/build/settings/get.cs +3 -0
  791. package/unitypkg/Editor/Tools/build/settings/set.cs +74 -0
  792. package/unitypkg/Editor/Tools/camera/CameraToolSupport.cs +292 -0
  793. package/unitypkg/Editor/Tools/camera/create.cs +114 -0
  794. package/unitypkg/Editor/Tools/camera/info.cs +49 -0
  795. package/unitypkg/Editor/Tools/camera/lens.cs +119 -0
  796. package/unitypkg/Editor/Tools/camera/list.cs +52 -0
  797. package/unitypkg/Editor/Tools/camera/multiview.cs +96 -0
  798. package/unitypkg/Editor/Tools/camera/screenshot.cs +65 -0
  799. package/unitypkg/Editor/Tools/camera/target.cs +94 -0
  800. package/unitypkg/Editor/Tools/component/add.cs +125 -0
  801. package/unitypkg/Editor/Tools/component/get.cs +73 -0
  802. package/unitypkg/Editor/Tools/component/list-types.cs +90 -0
  803. package/unitypkg/Editor/Tools/component/modify.cs +103 -0
  804. package/unitypkg/Editor/Tools/component/remove.cs +147 -0
  805. package/unitypkg/Editor/Tools/component/set.cs +93 -0
  806. package/unitypkg/Editor/Tools/console/clear.cs +38 -0
  807. package/unitypkg/Editor/Tools/console/get.cs +28 -0
  808. package/unitypkg/Editor/Tools/controller/add-blend-tree-child.cs +88 -0
  809. package/unitypkg/Editor/Tools/controller/add-layer.cs +43 -0
  810. package/unitypkg/Editor/Tools/controller/add-parameter.cs +51 -0
  811. package/unitypkg/Editor/Tools/controller/add-state.cs +44 -0
  812. package/unitypkg/Editor/Tools/controller/add-transition.cs +71 -0
  813. package/unitypkg/Editor/Tools/controller/assign.cs +38 -0
  814. package/unitypkg/Editor/Tools/controller/create-blend-tree-1d.cs +40 -0
  815. package/unitypkg/Editor/Tools/controller/create-blend-tree-2d.cs +42 -0
  816. package/unitypkg/Editor/Tools/controller/create.cs +31 -0
  817. package/unitypkg/Editor/Tools/controller/get-info.cs +27 -0
  818. package/unitypkg/Editor/Tools/controller/remove-layer.cs +57 -0
  819. package/unitypkg/Editor/Tools/controller/set-layer-weight.cs +42 -0
  820. package/unitypkg/Editor/Tools/diagnose/preflight.cs +269 -0
  821. package/unitypkg/Editor/Tools/editor/EditorActionToolSupport.cs +117 -0
  822. package/unitypkg/Editor/Tools/editor/EditorSearchToolSupport.cs +146 -0
  823. package/unitypkg/Editor/Tools/editor/EditorToolSupport.cs +132 -0
  824. package/unitypkg/Editor/Tools/editor/EditorWindowFocusToolSupport.cs +194 -0
  825. package/unitypkg/Editor/Tools/editor/active-tool/set.cs +33 -0
  826. package/unitypkg/Editor/Tools/editor/active-tool.cs +100 -0
  827. package/unitypkg/Editor/Tools/editor/autotick/set.cs +102 -0
  828. package/unitypkg/Editor/Tools/editor/console.cs +26 -0
  829. package/unitypkg/Editor/Tools/editor/focus.cs +80 -0
  830. package/unitypkg/Editor/Tools/editor/menu-items.cs +50 -0
  831. package/unitypkg/Editor/Tools/editor/menu.cs +53 -0
  832. package/unitypkg/Editor/Tools/editor/pause.cs +38 -0
  833. package/unitypkg/Editor/Tools/editor/play.cs +43 -0
  834. package/unitypkg/Editor/Tools/editor/redo.cs +56 -0
  835. package/unitypkg/Editor/Tools/editor/resolution.cs +34 -0
  836. package/unitypkg/Editor/Tools/editor/search.cs +55 -0
  837. package/unitypkg/Editor/Tools/editor/selection/set.cs +37 -0
  838. package/unitypkg/Editor/Tools/editor/selection.cs +153 -0
  839. package/unitypkg/Editor/Tools/editor/stop.cs +43 -0
  840. package/unitypkg/Editor/Tools/editor/tool-states.cs +140 -0
  841. package/unitypkg/Editor/Tools/editor/undo.cs +56 -0
  842. package/unitypkg/Editor/Tools/editor/windows.cs +87 -0
  843. package/unitypkg/Editor/Tools/extension/status.cs +87 -0
  844. package/unitypkg/Editor/Tools/go/GameObjectToolSupport.cs +524 -0
  845. package/unitypkg/Editor/Tools/go/create.cs +333 -0
  846. package/unitypkg/Editor/Tools/go/delete.cs +87 -0
  847. package/unitypkg/Editor/Tools/go/duplicate.cs +76 -0
  848. package/unitypkg/Editor/Tools/go/find.cs +152 -0
  849. package/unitypkg/Editor/Tools/go/inspect.cs +49 -0
  850. package/unitypkg/Editor/Tools/go/look-at.cs +76 -0
  851. package/unitypkg/Editor/Tools/go/modify.cs +156 -0
  852. package/unitypkg/Editor/Tools/go/move-relative.cs +74 -0
  853. package/unitypkg/Editor/Tools/go/set-parent.cs +84 -0
  854. package/unitypkg/Editor/Tools/graphics/GraphicsToolSupport.cs +396 -0
  855. package/unitypkg/Editor/Tools/graphics/bake-cancel.cs +27 -0
  856. package/unitypkg/Editor/Tools/graphics/bake-clear.cs +31 -0
  857. package/unitypkg/Editor/Tools/graphics/bake-reflection-probe.cs +35 -0
  858. package/unitypkg/Editor/Tools/graphics/bake-settings.cs +131 -0
  859. package/unitypkg/Editor/Tools/graphics/bake-start.cs +27 -0
  860. package/unitypkg/Editor/Tools/graphics/bake-status.cs +31 -0
  861. package/unitypkg/Editor/Tools/graphics/light-probe-group.cs +56 -0
  862. package/unitypkg/Editor/Tools/graphics/pipeline-info.cs +19 -0
  863. package/unitypkg/Editor/Tools/graphics/pipeline-quality.cs +24 -0
  864. package/unitypkg/Editor/Tools/graphics/pipeline-settings.cs +46 -0
  865. package/unitypkg/Editor/Tools/graphics/probe-positions.cs +58 -0
  866. package/unitypkg/Editor/Tools/graphics/quality.cs +85 -0
  867. package/unitypkg/Editor/Tools/graphics/reflection-probe.cs +73 -0
  868. package/unitypkg/Editor/Tools/graphics/scene-debug.cs +37 -0
  869. package/unitypkg/Editor/Tools/graphics/settings.cs +104 -0
  870. package/unitypkg/Editor/Tools/graphics/skybox-get.cs +33 -0
  871. package/unitypkg/Editor/Tools/graphics/skybox-set-ambient.cs +24 -0
  872. package/unitypkg/Editor/Tools/graphics/skybox-set-fog.cs +24 -0
  873. package/unitypkg/Editor/Tools/graphics/skybox-set-material.cs +33 -0
  874. package/unitypkg/Editor/Tools/graphics/skybox-set-properties.cs +51 -0
  875. package/unitypkg/Editor/Tools/graphics/skybox-set-reflection.cs +31 -0
  876. package/unitypkg/Editor/Tools/graphics/skybox-set-sun.cs +35 -0
  877. package/unitypkg/Editor/Tools/graphics/stats-counters.cs +28 -0
  878. package/unitypkg/Editor/Tools/graphics/stats-memory.cs +21 -0
  879. package/unitypkg/Editor/Tools/graphics/stats.cs +17 -0
  880. package/unitypkg/Editor/Tools/graphics/volume.cs +59 -0
  881. package/unitypkg/Editor/Tools/instances/list.cs +71 -0
  882. package/unitypkg/Editor/Tools/material/MaterialToolSupport.cs +401 -0
  883. package/unitypkg/Editor/Tools/material/assign.cs +124 -0
  884. package/unitypkg/Editor/Tools/material/create.cs +82 -0
  885. package/unitypkg/Editor/Tools/material/info.cs +55 -0
  886. package/unitypkg/Editor/Tools/material/set-renderer-color.cs +108 -0
  887. package/unitypkg/Editor/Tools/material/set.cs +114 -0
  888. package/unitypkg/Editor/Tools/navmesh/NavMeshToolSupport.cs +404 -0
  889. package/unitypkg/Editor/Tools/navmesh/bake-clear.cs +70 -0
  890. package/unitypkg/Editor/Tools/navmesh/settings/set.cs +73 -0
  891. package/unitypkg/Editor/Tools/navmesh/settings.cs +37 -0
  892. package/unitypkg/Editor/Tools/object/ObjectToolSupport.cs +99 -0
  893. package/unitypkg/Editor/Tools/object/get-data.cs +69 -0
  894. package/unitypkg/Editor/Tools/object/modify.cs +126 -0
  895. package/unitypkg/Editor/Tools/occlusion/OcclusionToolSupport.cs +422 -0
  896. package/unitypkg/Editor/Tools/occlusion/bake-clear.cs +67 -0
  897. package/unitypkg/Editor/Tools/physics/PhysicsJointToolSupport.cs +13 -0
  898. package/unitypkg/Editor/Tools/physics/PhysicsToolSupport.cs +389 -0
  899. package/unitypkg/Editor/Tools/physics/assign-material.cs +88 -0
  900. package/unitypkg/Editor/Tools/physics/collision-matrix.cs +90 -0
  901. package/unitypkg/Editor/Tools/physics/force.cs +127 -0
  902. package/unitypkg/Editor/Tools/physics/joint/add.cs +99 -0
  903. package/unitypkg/Editor/Tools/physics/joint/remove.cs +4 -0
  904. package/unitypkg/Editor/Tools/physics/linecast.cs +59 -0
  905. package/unitypkg/Editor/Tools/physics/material.cs +73 -0
  906. package/unitypkg/Editor/Tools/physics/overlap.cs +84 -0
  907. package/unitypkg/Editor/Tools/physics/raycast-all.cs +67 -0
  908. package/unitypkg/Editor/Tools/physics/raycast.cs +96 -0
  909. package/unitypkg/Editor/Tools/physics/rigidbody.cs +116 -0
  910. package/unitypkg/Editor/Tools/physics/settings.cs +116 -0
  911. package/unitypkg/Editor/Tools/physics/shapecast.cs +127 -0
  912. package/unitypkg/Editor/Tools/physics/simulation.cs +89 -0
  913. package/unitypkg/Editor/Tools/physics/validate.cs +67 -0
  914. package/unitypkg/Editor/Tools/prefab/PrefabOverrideToolSupport.cs +355 -0
  915. package/unitypkg/Editor/Tools/prefab/PrefabToolSupport.cs +446 -0
  916. package/unitypkg/Editor/Tools/prefab/create.cs +125 -0
  917. package/unitypkg/Editor/Tools/prefab/hierarchy.cs +157 -0
  918. package/unitypkg/Editor/Tools/prefab/info.cs +44 -0
  919. package/unitypkg/Editor/Tools/prefab/instantiate.cs +106 -0
  920. package/unitypkg/Editor/Tools/prefab/modify.cs +112 -0
  921. package/unitypkg/Editor/Tools/prefab/overrides/apply.cs +51 -0
  922. package/unitypkg/Editor/Tools/prefab/overrides/revert.cs +51 -0
  923. package/unitypkg/Editor/Tools/prefab/stage-close.cs +67 -0
  924. package/unitypkg/Editor/Tools/prefab/stage-open.cs +69 -0
  925. package/unitypkg/Editor/Tools/prefab/stage-save.cs +64 -0
  926. package/unitypkg/Editor/Tools/prefab/stage.cs +68 -0
  927. package/unitypkg/Editor/Tools/prefab/unpack.cs +80 -0
  928. package/unitypkg/Editor/Tools/prefab/variant/create.cs +53 -0
  929. package/unitypkg/Editor/Tools/profiler/ProfilerFrameDebuggerSupport.cs +68 -0
  930. package/unitypkg/Editor/Tools/profiler/ProfilerMemorySnapshotSupport.cs +30 -0
  931. package/unitypkg/Editor/Tools/profiler/ProfilerToolSupport.cs +515 -0
  932. package/unitypkg/Editor/Tools/profiler/areas.cs +36 -0
  933. package/unitypkg/Editor/Tools/profiler/capture.cs +188 -0
  934. package/unitypkg/Editor/Tools/profiler/clear.cs +57 -0
  935. package/unitypkg/Editor/Tools/profiler/counter.cs +34 -0
  936. package/unitypkg/Editor/Tools/profiler/deep.cs +48 -0
  937. package/unitypkg/Editor/Tools/profiler/export.cs +85 -0
  938. package/unitypkg/Editor/Tools/profiler/frame-debugger/disable.cs +53 -0
  939. package/unitypkg/Editor/Tools/profiler/frame-debugger/enable.cs +66 -0
  940. package/unitypkg/Editor/Tools/profiler/frame-debugger/events.cs +140 -0
  941. package/unitypkg/Editor/Tools/profiler/frame.cs +38 -0
  942. package/unitypkg/Editor/Tools/profiler/hierarchy.cs +183 -0
  943. package/unitypkg/Editor/Tools/profiler/load.cs +85 -0
  944. package/unitypkg/Editor/Tools/profiler/memory-snapshot/compare.cs +78 -0
  945. package/unitypkg/Editor/Tools/profiler/memory-snapshot/list.cs +73 -0
  946. package/unitypkg/Editor/Tools/profiler/memory-snapshot/take.cs +147 -0
  947. package/unitypkg/Editor/Tools/profiler/memory.cs +34 -0
  948. package/unitypkg/Editor/Tools/profiler/module-enable.cs +52 -0
  949. package/unitypkg/Editor/Tools/profiler/modules.cs +33 -0
  950. package/unitypkg/Editor/Tools/profiler/object-memory.cs +49 -0
  951. package/unitypkg/Editor/Tools/profiler/pause.cs +49 -0
  952. package/unitypkg/Editor/Tools/profiler/rendering-stats.cs +109 -0
  953. package/unitypkg/Editor/Tools/profiler/script-stats.cs +35 -0
  954. package/unitypkg/Editor/Tools/profiler/start.cs +55 -0
  955. package/unitypkg/Editor/Tools/profiler/status.cs +36 -0
  956. package/unitypkg/Editor/Tools/profiler/stop.cs +53 -0
  957. package/unitypkg/Editor/Tools/project/ProjectSettingsReader.cs +31 -0
  958. package/unitypkg/Editor/Tools/project/ProjectSettingsToolSupport.cs +757 -0
  959. package/unitypkg/Editor/Tools/project/ProjectTagLayerToolSupport.cs +23 -0
  960. package/unitypkg/Editor/Tools/project/audio-settings/set.cs +35 -0
  961. package/unitypkg/Editor/Tools/project/audio-settings.cs +31 -0
  962. package/unitypkg/Editor/Tools/project/info.cs +52 -0
  963. package/unitypkg/Editor/Tools/project/input-settings/set.cs +35 -0
  964. package/unitypkg/Editor/Tools/project/input-settings.cs +35 -0
  965. package/unitypkg/Editor/Tools/project/layer/add.cs +3 -0
  966. package/unitypkg/Editor/Tools/project/layer/remove.cs +3 -0
  967. package/unitypkg/Editor/Tools/project/layers.cs +61 -0
  968. package/unitypkg/Editor/Tools/project/player-settings/set.cs +35 -0
  969. package/unitypkg/Editor/Tools/project/player-settings.cs +31 -0
  970. package/unitypkg/Editor/Tools/project/tag/add.cs +3 -0
  971. package/unitypkg/Editor/Tools/project/tag/remove.cs +3 -0
  972. package/unitypkg/Editor/Tools/project/tags.cs +45 -0
  973. package/unitypkg/Editor/Tools/project/time-settings/set.cs +35 -0
  974. package/unitypkg/Editor/Tools/project/time-settings.cs +31 -0
  975. package/unitypkg/Editor/Tools/reflection/method/call.cs +114 -0
  976. package/unitypkg/Editor/Tools/reflection/method/find.cs +72 -0
  977. package/unitypkg/Editor/Tools/reflection/type/schema.cs +56 -0
  978. package/unitypkg/Editor/Tools/scene/SceneToolSupport.cs +181 -0
  979. package/unitypkg/Editor/Tools/scene/active.cs +62 -0
  980. package/unitypkg/Editor/Tools/scene/build-settings.cs +69 -0
  981. package/unitypkg/Editor/Tools/scene/create.cs +78 -0
  982. package/unitypkg/Editor/Tools/scene/data.cs +125 -0
  983. package/unitypkg/Editor/Tools/scene/gameobject/component.cs +63 -0
  984. package/unitypkg/Editor/Tools/scene/gameobject/components.cs +77 -0
  985. package/unitypkg/Editor/Tools/scene/gameobject-support.cs +101 -0
  986. package/unitypkg/Editor/Tools/scene/gameobject.cs +83 -0
  987. package/unitypkg/Editor/Tools/scene/hierarchy.cs +250 -0
  988. package/unitypkg/Editor/Tools/scene/list-opened.cs +77 -0
  989. package/unitypkg/Editor/Tools/scene/load.cs +68 -0
  990. package/unitypkg/Editor/Tools/scene/move-object.cs +122 -0
  991. package/unitypkg/Editor/Tools/scene/save.cs +231 -0
  992. package/unitypkg/Editor/Tools/scene/set-active.cs +76 -0
  993. package/unitypkg/Editor/Tools/scene/unload.cs +109 -0
  994. package/unitypkg/Editor/Tools/scene/validate.cs +115 -0
  995. package/unitypkg/Editor/Tools/scene/view-frame.cs +218 -0
  996. package/unitypkg/Editor/Tools/screenshot/EditorElementCaptureToolSupport.cs +181 -0
  997. package/unitypkg/Editor/Tools/screenshot/ScreenshotToolSupport.cs +196 -0
  998. package/unitypkg/Editor/Tools/screenshot/editor-element.cs +87 -0
  999. package/unitypkg/Editor/Tools/screenshot/game-view.cs +116 -0
  1000. package/unitypkg/Editor/Tools/screenshot/isolated-object.cs +93 -0
  1001. package/unitypkg/Editor/Tools/screenshot/orbit.cs +103 -0
  1002. package/unitypkg/Editor/Tools/screenshot/scene-view.cs +99 -0
  1003. package/unitypkg/Editor/Tools/screenshot/screen.cs +89 -0
  1004. package/unitypkg/Editor/Tools/screenshot/surround.cs +181 -0
  1005. package/unitypkg/Editor/Tools/scriptable-object/ScriptableObjectToolSupport.cs +196 -0
  1006. package/unitypkg/Editor/Tools/scriptable-object/create.cs +79 -0
  1007. package/unitypkg/Editor/Tools/scriptable-object/info.cs +49 -0
  1008. package/unitypkg/Editor/Tools/scriptable-object/list-types.cs +103 -0
  1009. package/unitypkg/Editor/Tools/scriptable-object/modify.cs +208 -0
  1010. package/unitypkg/Editor/Tools/shader/ShaderToolSupport.cs +326 -0
  1011. package/unitypkg/Editor/Tools/shader/create.cs +82 -0
  1012. package/unitypkg/Editor/Tools/shader/delete.cs +90 -0
  1013. package/unitypkg/Editor/Tools/shader/get-data.cs +125 -0
  1014. package/unitypkg/Editor/Tools/shader/info.cs +48 -0
  1015. package/unitypkg/Editor/Tools/shader/list.cs +82 -0
  1016. package/unitypkg/Editor/Tools/shader/read.cs +84 -0
  1017. package/unitypkg/Editor/Tools/shader/update.cs +71 -0
  1018. package/unitypkg/Editor/Tools/tests/list.cs +98 -0
  1019. package/unitypkg/Editor/Tools/texture/TextureToolSupport.cs +327 -0
  1020. package/unitypkg/Editor/Tools/texture/create.cs +182 -0
  1021. package/unitypkg/Editor/Tools/texture/delete.cs +89 -0
  1022. package/unitypkg/Editor/Tools/texture/generate.cs +189 -0
  1023. package/unitypkg/Editor/Tools/texture/import-settings.cs +104 -0
  1024. package/unitypkg/Editor/Tools/texture/info.cs +50 -0
  1025. package/unitypkg/Editor/Tools/texture/modify.cs +154 -0
  1026. package/unitypkg/Editor/Tools/texture/sprite.cs +132 -0
  1027. package/unitypkg/Editor/Tools/timeline/TimelineMutationToolSupport.cs +696 -0
  1028. package/unitypkg/Editor/Tools/timeline/TimelineReadToolSupport.cs +439 -0
  1029. package/unitypkg/Editor/Tools/timeline/TimelineToolSupport.cs +203 -0
  1030. package/unitypkg/Editor/Tools/timeline/clip/add.cs +93 -0
  1031. package/unitypkg/Editor/Tools/timeline/create.cs +88 -0
  1032. package/unitypkg/Editor/Tools/timeline/info.cs +54 -0
  1033. package/unitypkg/Editor/Tools/timeline/track/add.cs +91 -0
  1034. package/unitypkg/Editor/Tools/ui/UiToolSupport.cs +162 -0
  1035. package/unitypkg/Editor/Tools/ui/UiUguiToolSupport.cs +24 -0
  1036. package/unitypkg/Editor/Tools/ui/attach-document.cs +46 -0
  1037. package/unitypkg/Editor/Tools/ui/button/create.cs +58 -0
  1038. package/unitypkg/Editor/Tools/ui/canvas/create.cs +40 -0
  1039. package/unitypkg/Editor/Tools/ui/create-panel-settings.cs +44 -0
  1040. package/unitypkg/Editor/Tools/ui/detach-document.cs +41 -0
  1041. package/unitypkg/Editor/Tools/ui/document/create.cs +49 -0
  1042. package/unitypkg/Editor/Tools/ui/document/delete.cs +33 -0
  1043. package/unitypkg/Editor/Tools/ui/document/info.cs +33 -0
  1044. package/unitypkg/Editor/Tools/ui/document/update.cs +36 -0
  1045. package/unitypkg/Editor/Tools/ui/get-visual-tree.cs +34 -0
  1046. package/unitypkg/Editor/Tools/ui/hierarchy.cs +27 -0
  1047. package/unitypkg/Editor/Tools/ui/image/create.cs +47 -0
  1048. package/unitypkg/Editor/Tools/ui/link-stylesheet.cs +54 -0
  1049. package/unitypkg/Editor/Tools/ui/list-documents.cs +34 -0
  1050. package/unitypkg/Editor/Tools/ui/modify-visual-element.cs +76 -0
  1051. package/unitypkg/Editor/Tools/ui/render-preview.cs +128 -0
  1052. package/unitypkg/Editor/Tools/ui/text/create.cs +45 -0
  1053. package/unitypkg/Editor/Tools/ui/update-panel-settings.cs +49 -0
  1054. package/unitypkg/Editor/UnityCliBridge.cs +431 -0
  1055. package/unitypkg/Editor/UnityCliBridgeBuildInfo.cs +9 -0
  1056. package/unitypkg/Runtime/Infrastructure/UnityCliRuntimeBridge.cs +580 -0
  1057. package/unitypkg/Runtime/Infrastructure/UnityCliRuntimeJson.cs +232 -0
  1058. package/unitypkg/Runtime/Infrastructure/UnityCliRuntimeRegistry.cs +297 -0
  1059. package/unitypkg/Runtime/Tools/UnityCliRuntimeTools.cs +701 -0
  1060. package/unitypkg/Runtime/UnityCli.Runtime.asmdef +14 -0
  1061. package/unitypkg/Shared/UnityCli.Shared.asmdef +14 -0
  1062. package/unitypkg/Shared/UnityCliAdmissionPool.cs +426 -0
  1063. package/unitypkg/Tests/Editor/UnityCli.Editor.Tests.asmdef +15 -0
  1064. package/unitypkg/Tests/Editor/UnityCliAdmissionPoolTests.cs +262 -0
  1065. package/unitypkg/UnityCli.Editor.asmdef +26 -0
  1066. package/unitypkg/package.json +11 -0
@@ -0,0 +1,2073 @@
1
+ #if UNITY_EDITOR
2
+ #pragma warning disable 0618
3
+ using System;
4
+ using System.Collections;
5
+ using System.Collections.Generic;
6
+ using System.Globalization;
7
+ using System.IO;
8
+ using System.Linq;
9
+ using System.Reflection;
10
+ using System.Security.Cryptography;
11
+ using System.Text;
12
+ using System.Threading;
13
+ using System.Threading.Tasks;
14
+ using UnityCli.Protocol;
15
+ using UnityEditor;
16
+ using UnityEditor.AI;
17
+ using UnityEngine;
18
+ using UnityEngine.AI;
19
+ using UnityEngine.SceneManagement;
20
+ using Object = UnityEngine.Object;
21
+ using NavMeshBuilder = UnityEditor.AI.NavMeshBuilder;
22
+
23
+ namespace UnityCli
24
+ {
25
+ public static partial class UnityCliBridge
26
+ {
27
+ /// <summary>legacy 与 AI Navigation surface bake 共用的单 root durable coordinator。</summary>
28
+ internal static class UnityCliNavMeshBakeCoordinator
29
+ {
30
+ internal const string StartCommand = "internal.navmesh.bake.start";
31
+ internal const string StatusCommand = "internal.navmesh.bake.status";
32
+ internal const string PlanCommand = "internal.navmesh.bake.plan";
33
+ internal const string SnapshotCommand = "internal.navmesh.bake.snapshot";
34
+ internal const string CancelCommand = "internal.navmesh.bake.cancel";
35
+ private const string ActiveScope = "navmesh-bake";
36
+ private const string StatusScope = "navmesh-bake-status";
37
+ private const string TerminalLedgerScope = "navmesh-bake-terminal-ledger";
38
+ private const int TerminalLedgerCapacity = 128;
39
+ private const string StatusSessionInitializedKey = "UnityCli.NavMeshBake.StatusSessionInitialized";
40
+ private const string StatusWrittenThisSessionKey = "UnityCli.NavMeshBake.StatusWrittenThisSession";
41
+ private const string TerminalLedgerWrittenThisSessionKey = "UnityCli.NavMeshBake.TerminalLedgerWrittenThisSession";
42
+ private const string StatusProofKeySessionKey = "UnityCli.NavMeshBake.StatusProofKey";
43
+ private const string OwnerRequestIdSessionKey = "UnityCli.NavMeshBake.OwnerRequestId";
44
+ private const string OwnerRequestDigestSessionKey = "UnityCli.NavMeshBake.OwnerRequestDigest";
45
+ private const string OwnerKindSessionKey = "UnityCli.NavMeshBake.OwnerKind";
46
+ private const string PreEffectAbortRequestIdSessionKey = "UnityCli.NavMeshBake.PreEffectAbortRequestId";
47
+ private const string PreEffectAbortRequestDigestSessionKey = "UnityCli.NavMeshBake.PreEffectAbortRequestDigest";
48
+ private static readonly Coordinator Operation = new Coordinator();
49
+ private enum StatusLoadOutcome { Missing, Loaded, TransientFailure, Corrupt }
50
+ private static StatusLoadOutcome LastStatusLoadOutcome;
51
+ private static bool StatusJournalCorrupt;
52
+
53
+ private sealed class StartArgs
54
+ {
55
+ public string ExpectedEditorSessionId { get; set; }
56
+ public string Kind { get; set; }
57
+ }
58
+
59
+ private sealed class CancelArgs
60
+ {
61
+ public string OperationId { get; set; }
62
+ }
63
+
64
+ private sealed class RootStatusArgs { public string RootRequestId { get; set; } public string RootRequestDigest { get; set; } }
65
+
66
+ private sealed class Coordinator : UnityCliReloadOperation
67
+ {
68
+ private Dictionary<string, object> rejectedRoot;
69
+ private SurfaceApi.SurfaceDispatchObservation surfaceObservation;
70
+ private string surfaceObservationRequestId;
71
+ private string surfaceObservationRequestDigest;
72
+ private string reloadUncertainRequestId;
73
+ private string reloadUncertainRequestDigest;
74
+ protected override string Scope { get { return ActiveScope; } }
75
+ protected override string StartCommand { get { return UnityCliNavMeshBakeCoordinator.StartCommand; } }
76
+ protected override string StatusCommand { get { return UnityCliNavMeshBakeCoordinator.StatusCommand; } }
77
+ protected override string OperationDisplayName { get { return "NavMesh bake"; } }
78
+ protected override int CommandTimeoutMs { get { return 600000; } }
79
+ protected override bool IncludeAcceptedInStatus { get { return true; } }
80
+ protected override IEnumerable<(string name, bool destructive)> AdditionalCommands
81
+ {
82
+ get
83
+ {
84
+ yield return (PlanCommand, false);
85
+ yield return (SnapshotCommand, false);
86
+ yield return (CancelCommand, true);
87
+ }
88
+ }
89
+
90
+ protected override object DeserializeStartArgs(BridgeRequest request) { return request.DeserializeArgs<StartArgs>(); }
91
+ protected override void ValidateStart(object value)
92
+ {
93
+ var args = value as StartArgs;
94
+ ValidateStartShape(args);
95
+ if (!string.Equals(args.ExpectedEditorSessionId, EditorSessionId(), StringComparison.Ordinal))
96
+ throw new BridgeCommandException("E_EDITOR_SESSION_REPLACED", "The selected Editor session changed after NavMesh bake preflight.");
97
+ NavMeshToolSupport.EnsureEditorReady(args.Kind == "legacy" ? "navmesh.bake" : "navmesh.surface.bake", true);
98
+ }
99
+
100
+ private static void ValidateStartShape(StartArgs args)
101
+ {
102
+ if (args == null || string.IsNullOrWhiteSpace(args.ExpectedEditorSessionId) || (args.Kind != "legacy" && args.Kind != "surfaces"))
103
+ throw new BridgeCommandException("E_SCHEMA_INVALID", "NavMesh bake requires expectedEditorSessionId and kind=legacy|surfaces.");
104
+ }
105
+
106
+ /// <summary>语法可读但identity、phase、status或terminal proof不一致的root必须进入保守恢复。</summary>
107
+ protected override void ValidateLoadedRoot(Dictionary<string, object> root)
108
+ {
109
+ if (!ValidateNavMeshRoot(root))
110
+ {
111
+ rejectedRoot = root;
112
+ throw new InvalidOperationException("NavMesh active root semantic validation failed.");
113
+ }
114
+ rejectedRoot = null;
115
+ }
116
+
117
+ protected override TerminalCleanupDecision EvaluateTerminalCleanupRoot(Dictionary<string, object> root, string requestId, string requestDigest)
118
+ {
119
+ if (Text(root, "phase") != "queued" && NativeGateRunning(root)) return TerminalCleanupDecision.Retry;
120
+ var hasTerminalState = !string.IsNullOrEmpty(Text(root, "terminalState"));
121
+ if (hasTerminalState)
122
+ {
123
+ try { if (TryRestoreTerminalStatus(root)) return TerminalCleanupDecision.Cleanup; }
124
+ catch (Exception exception) { WriteLog("navmesh_terminal_restore_retry " + exception.Message); }
125
+ }
126
+ var status = LoadStatus();
127
+ if (status == null) return hasTerminalState || LastStatusLoadOutcome == StatusLoadOutcome.TransientFailure
128
+ ? TerminalCleanupDecision.Retry : TerminalCleanupDecision.Reject;
129
+ return TerminalStatusMatchesIdentity(status, requestId, requestDigest)
130
+ ? TerminalCleanupDecision.Cleanup : TerminalCleanupDecision.Reject;
131
+ }
132
+
133
+ protected override TerminalCleanupDecision EvaluateUnreadableTerminalCleanup(string requestId, string requestDigest)
134
+ {
135
+ var status = LoadStatus();
136
+ if (status == null) return TerminalCleanupDecision.Retry;
137
+ return TerminalStatusMatchesIdentity(status, requestId, requestDigest)
138
+ ? TerminalCleanupDecision.Cleanup : TerminalCleanupDecision.Retry;
139
+ }
140
+
141
+ protected override bool CanCleanupAbandonedPreEffectRoot(Dictionary<string, object> root, string requestId, string requestDigest)
142
+ {
143
+ if (root == null || !IsBoundPreEffectAbort(requestId, requestDigest)
144
+ || !string.Equals(Text(root, "requestId"), requestId, StringComparison.Ordinal)
145
+ || !string.Equals(Text(root, "requestDigest"), requestDigest, StringComparison.Ordinal)
146
+ || Bool(root, "dispatchConfirmed") || !string.IsNullOrEmpty(Text(root, "terminalState"))) return false;
147
+ var phase = Text(root, "phase");
148
+ var nonce = Text(root, "dispatchNonce");
149
+ return (phase == "queued" && string.IsNullOrEmpty(nonce))
150
+ || (phase == "dispatching" && !string.IsNullOrEmpty(nonce));
151
+ }
152
+
153
+ /// <summary>effect前root删除后只清理同identity且明确未dispatch的active status。</summary>
154
+ protected override bool FinalizeAbandonedPreEffectState(string requestId, string requestDigest)
155
+ {
156
+ var status = LoadStatus();
157
+ if (status == null)
158
+ return LastStatusLoadOutcome == StatusLoadOutcome.Missing && !StatusJournalCorrupt
159
+ && !SessionState.GetBool(StatusWrittenThisSessionKey, false)
160
+ && !UnityCliActiveOperation.Exists(StatusScope);
161
+ object confirmed;
162
+ if (!string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal)
163
+ || !string.Equals(Text(status, "rootRequestDigest"), requestDigest, StringComparison.Ordinal)
164
+ || !Bool(status, "active") || !status.TryGetValue("dispatchConfirmed", out confirmed)
165
+ || !(confirmed is bool) || (bool)confirmed) return false;
166
+ try { UnityCliActiveOperation.Clear(StatusScope); }
167
+ catch (Exception exception) { WriteLog("navmesh_pre_effect_status_cleanup_failed " + exception.Message); return false; }
168
+ if (UnityCliActiveOperation.Exists(StatusScope)) return false;
169
+ StatusJournalCorrupt = false;
170
+ LastStatusLoadOutcome = StatusLoadOutcome.Missing;
171
+ SessionState.SetBool(StatusWrittenThisSessionKey, false);
172
+ return true;
173
+ }
174
+
175
+ protected override bool TryResumePublishedTerminal(Dictionary<string, object> root)
176
+ {
177
+ if (!string.IsNullOrEmpty(Text(root, "terminalState")))
178
+ {
179
+ if (TryRestoreTerminalStatus(root)) ResumeTerminalCleanup(root);
180
+ return true;
181
+ }
182
+ var terminal = RecoveredStoredTerminal(Text(root, "requestId"), Text(root, "requestDigest"));
183
+ if (terminal == null) return false;
184
+ ResumeTerminalCleanup(root);
185
+ return true;
186
+ }
187
+
188
+ protected override bool OnActiveStateCorrupt(Exception exception) { return RecoverUnavailableActiveState(exception); }
189
+ protected override bool OnActiveStateMissing() { return RecoverUnavailableActiveState(new InvalidOperationException("NavMesh active state is missing.")); }
190
+
191
+ protected override object Planned(object value) { return DryRun((StartArgs)value, BuildPlan((StartArgs)value)); }
192
+
193
+ protected override Dictionary<string, object> CreateRoot(BridgeRequest request, string digest, object value)
194
+ {
195
+ return CreateRoot(request, digest, (StartArgs)value, BuildPlan((StartArgs)value));
196
+ }
197
+
198
+ /// <summary>Start先解析durable identity,再在accept barrier后创建唯一root;历史不确定时禁止新effect。</summary>
199
+ internal override async Task<string> Execute(BridgeRequest request, byte[] raw, Func<bool> accept, CancellationToken cancellationToken)
200
+ {
201
+ if (request.Command != StartCommand) return await base.Execute(request, raw, accept, cancellationToken);
202
+ var args = (StartArgs)DeserializeStartArgs(request);
203
+ ValidateStartShape(args);
204
+ if (request.Execution != null && request.Execution.DryRun == true)
205
+ {
206
+ ValidateStart(args);
207
+ return UnityCliSerializer.Serialize(ProtocolEnvelopeFactory.Success(request.RequestId, DryRun(args, BuildPlan(args))));
208
+ }
209
+ var digest = UnityCliRequestDigest.Compute(raw, request.InstanceId);
210
+ var inspectedRoot = Load();
211
+ var recovered = RecoveredLedgerTerminal(request.RequestId, digest)
212
+ ?? RecoveredStoredTerminal(request.RequestId, digest) ?? RecoveredTerminal(request.RequestId, digest);
213
+ if (recovered != null) return UnityCliSerializer.Serialize(ProtocolEnvelopeFactory.Success(request.RequestId, recovered));
214
+
215
+ var replay = inspectedRoot != null;
216
+ if (inspectedRoot != null)
217
+ {
218
+ if (!string.Equals(Text(inspectedRoot, "requestId"), request.RequestId, StringComparison.Ordinal)) return Busy(request.RequestId);
219
+ if (!string.Equals(Text(inspectedRoot, "requestDigest"), digest, StringComparison.Ordinal))
220
+ throw new BridgeCommandException("E_REQUEST_ID_REUSED", "NavMesh bake requestId was reused with different input.");
221
+ if (!UnityCliSessionMutationLease.IsOwnedBy(request.RequestId, digest))
222
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh active root is not owned by the current session lease; outcome is unknown.");
223
+ BindSessionOwner(request.RequestId, digest, Text(inspectedRoot, "kind"));
224
+ }
225
+ else
226
+ {
227
+ if (LastLoadWasCorrupt || LastLoadRecoveredTerminal || StatusHistoryUnavailable())
228
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh active history is unavailable; outcome is unknown.");
229
+ string ownerId;
230
+ string ownerDigest;
231
+ if (!UnityCliSessionMutationLease.TryGetCurrentOwner(out ownerId, out ownerDigest))
232
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh lease ownership cannot be read; outcome is unknown.");
233
+ if (!string.IsNullOrEmpty(ownerId))
234
+ {
235
+ if (!string.Equals(ownerId, request.RequestId, StringComparison.Ordinal)) return Busy(request.RequestId);
236
+ if (!string.Equals(ownerDigest, digest, StringComparison.Ordinal))
237
+ throw new BridgeCommandException("E_REQUEST_ID_REUSED", "NavMesh bake requestId was reused with different input.");
238
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh lease exists without a readable root; outcome is unknown.");
239
+ }
240
+ ValidateStart(args);
241
+ EnsureTerminalLedgerCapacity();
242
+ CreateFirst(request, digest, args, BuildPlan(args), accept);
243
+ }
244
+ var acceptedRoot = Load();
245
+ if (acceptedRoot == null || !string.Equals(Text(acceptedRoot, "requestId"), request.RequestId, StringComparison.Ordinal)
246
+ || !string.Equals(Text(acceptedRoot, "requestDigest"), digest, StringComparison.Ordinal))
247
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh acceptance root could not be confirmed; outcome is unknown.");
248
+ await Task.FromResult(0);
249
+ return UnityCliSerializer.Serialize(ProtocolEnvelopeFactory.Success(request.RequestId, new Dictionary<string, object>
250
+ {
251
+ { "terminal", false }, { "accepted", true }, { "operationId", Text(acceptedRoot, "operationId") }, { "rootRequestDigest", digest }, { "replayed", replay }
252
+ }));
253
+ }
254
+
255
+ /// <summary>仅在确认无root、无status缺口且无owner后跨accept barrier创建首次root。</summary>
256
+ private void CreateFirst(BridgeRequest request, string digest, StartArgs args, Dictionary<string, object> plan, Func<bool> accept)
257
+ {
258
+ bool acquired;
259
+ if (!UnityCliSessionMutationLease.TryAcquire(request.RequestId, digest, out acquired))
260
+ throw new BridgeCommandException("E_UNITY_BUSY", "Another Unity mutation owns the active session.");
261
+ var saved = false;
262
+ try
263
+ {
264
+ var candidate = CreateRoot(request, digest, args, plan);
265
+ if (accept == null || !accept()) throw new OperationCanceledException("HTTP timeout cancelled NavMesh bake before acceptance.");
266
+ BindSessionOwner(request.RequestId, digest, args.Kind);
267
+ Save(candidate);
268
+ saved = true;
269
+ try { WriteRunningStatus(candidate); }
270
+ catch (Exception exception)
271
+ {
272
+ AbortBeforeNativeEffect(candidate, "initial running status could not be stored: " + exception.GetType().Name);
273
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh bake was aborted before the native effect because its initial status could not be stored.");
274
+ }
275
+ }
276
+ catch
277
+ {
278
+ if (acquired && !saved) UnityCliSessionMutationLease.Release(request.RequestId, digest);
279
+ throw;
280
+ }
281
+ }
282
+
283
+ /// <summary>status优先返回same-session durable terminal,不从active root重造结果。</summary>
284
+ protected override object Status(BridgeRequest request)
285
+ {
286
+ var args = request.DeserializeArgs<RootStatusArgs>();
287
+ if (args == null || string.IsNullOrWhiteSpace(args.RootRequestId) || !IsSha256Text(args.RootRequestDigest))
288
+ throw new BridgeCommandException("E_SCHEMA_INVALID", "NavMesh bake status requires rootRequestId and rootRequestDigest.");
289
+ var ledgerTerminal = RecoveredLedgerTerminal(args.RootRequestId, args.RootRequestDigest);
290
+ if (ledgerTerminal != null) return ProtocolEnvelopeFactory.Success(request.RequestId, ledgerTerminal);
291
+ var root = Load();
292
+ if (root != null && string.Equals(Text(root, "requestId"), args.RootRequestId, StringComparison.Ordinal))
293
+ {
294
+ if (!string.Equals(Text(root, "requestDigest"), args.RootRequestDigest, StringComparison.Ordinal))
295
+ throw new BridgeCommandException("E_REQUEST_ID_REUSED", "NavMesh bake requestId was reused with different input.");
296
+ var terminal = RecoveredStoredTerminal(args.RootRequestId, args.RootRequestDigest);
297
+ if (terminal != null) return ProtocolEnvelopeFactory.Success(request.RequestId, terminal);
298
+ return ProtocolEnvelopeFactory.Success(request.RequestId, new Dictionary<string, object>
299
+ {
300
+ { "terminal", false }, { "accepted", true }, { "phase", Text(root, "phase") }, { "rootRequestDigest", args.RootRequestDigest }
301
+ });
302
+ }
303
+ var stored = RecoveredStoredTerminal(args.RootRequestId, args.RootRequestDigest) ?? RecoveredTerminal(args.RootRequestId, args.RootRequestDigest);
304
+ if (stored != null) return ProtocolEnvelopeFactory.Success(request.RequestId, stored);
305
+ throw new BridgeCommandException("E_REQUEST_NOT_FOUND", "NavMesh bake request was not found.");
306
+ }
307
+
308
+ protected override object ExecuteAdditionalCommand(BridgeRequest request)
309
+ {
310
+ if (request.Command == PlanCommand)
311
+ {
312
+ var args = request.DeserializeArgs<StartArgs>();
313
+ ValidateStart(args);
314
+ return ProtocolEnvelopeFactory.Success(request.RequestId, DryRun(args, BuildPlan(args)));
315
+ }
316
+ if (request.Command == SnapshotCommand) return ProtocolEnvelopeFactory.Success(request.RequestId, PublicStatus());
317
+ if (request.Command == CancelCommand) return ProtocolEnvelopeFactory.Success(request.RequestId, Cancel(request.DeserializeArgs<CancelArgs>()));
318
+ return base.ExecuteAdditionalCommand(request);
319
+ }
320
+
321
+ protected override void OnTick(Dictionary<string, object> root)
322
+ {
323
+ if (!string.IsNullOrEmpty(Text(root, "terminalState"))) return;
324
+ if (IsBoundPreEffectAbort(Text(root, "requestId"), Text(root, "requestDigest")))
325
+ {
326
+ PublishPreEffectAbortTerminal(root);
327
+ return;
328
+ }
329
+ var phase = Text(root, "phase");
330
+ if (Bool(root, "reloadObserved") || IsReloadUncertain(root)) { RecoverReload(root); return; }
331
+ if (Bool(root, "cancelRequested") && phase == "queued") { FinishCancelled(root); return; }
332
+ if (phase == "queued" && (EditorApplication.isCompiling || EditorApplication.isUpdating)) { WriteRunningStatus(root); return; }
333
+ if (phase == "queued" && DateTimeOffset.UtcNow < Time(root, "dispatchNotBefore")) { WriteRunningStatus(root); return; }
334
+ var kind = Text(root, "kind");
335
+ if (kind == "legacy") TickLegacy(root); else if (kind == "surfaces") TickSurfaces(root);
336
+ else Fail(root, "E_EXECUTION_TIMEOUT", "NavMesh bake journal kind is invalid; outcome is unknown.");
337
+ }
338
+
339
+ protected override object BuildSuccessData(Dictionary<string, object> root)
340
+ {
341
+ var finishedAt = Text(root, "finishedAt");
342
+ var startedAt = Text(root, "startedAt");
343
+ return new Dictionary<string, object>
344
+ {
345
+ { "operationId", Text(root, "operationId") },
346
+ { "kind", Text(root, "kind") },
347
+ { "status", "completed" },
348
+ { "startedAt", startedAt },
349
+ { "finishedAt", finishedAt },
350
+ { "durationMs", DurationMs(startedAt, finishedAt) },
351
+ { "topology", root.ContainsKey("topologyAfter") ? root["topologyAfter"] : NavMeshToolSupport.TopologyDictionary() },
352
+ { "scenes", root.ContainsKey("scenesResult") ? root["scenesResult"] : root["scenes"] },
353
+ { "surfaces", root.ContainsKey("surfacesResult") ? root["surfacesResult"] : new List<object>() },
354
+ { "plannedEffects", new List<object> { PlannedEffect(Text(root, "kind"), Long(root, "targetCount")) } }
355
+ };
356
+ }
357
+
358
+ /// <summary>先冻结root exact envelope,再首次发布带session HMAC的status;任一失败均由后续Tick原样恢复。</summary>
359
+ protected override void OnTerminal(Dictionary<string, object> root, object envelope)
360
+ {
361
+ try
362
+ {
363
+ StoreTerminalEnvelope(root, envelope);
364
+ if (!TerminalAlreadyPublished(root, envelope))
365
+ {
366
+ if (Text(root, "terminalState") != "completed" && Text(root, "phase") != "queued" && NativeGateRunning(root))
367
+ throw new InvalidOperationException("NavMesh native work must stop before terminal publication.");
368
+ WriteTerminalStatus(root, Text(root, "terminalState"), envelope);
369
+ }
370
+ StoreTerminalLedger(root, envelope);
371
+ }
372
+ catch (Exception exception)
373
+ {
374
+ WriteLog("navmesh_bake_status_write_failed " + exception.GetType().Name + ": " + exception.Message);
375
+ throw;
376
+ }
377
+ }
378
+
379
+ /// <summary>Editor重启清空机制状态;domain reload只恢复exact cleanup或保守观察,绝不重放effect。</summary>
380
+ internal void InitializeState()
381
+ {
382
+ ClearSurfaceObservation();
383
+ if (!SessionState.GetBool(StatusSessionInitializedKey, false))
384
+ {
385
+ UnityCliActiveOperation.Clear(StatusScope);
386
+ UnityCliActiveOperation.Clear(TerminalLedgerScope);
387
+ UnityCliActiveOperation.Clear(ActiveScope);
388
+ UnityCliActiveOperation.Clear(ActiveScope + "-terminal-cleanup");
389
+ UnityCliActiveOperation.Clear(ActiveScope + "-pre-effect-cleanup");
390
+ SessionState.SetBool(StatusSessionInitializedKey, true);
391
+ SessionState.SetBool(StatusWrittenThisSessionKey, false);
392
+ SessionState.SetBool(TerminalLedgerWrittenThisSessionKey, false);
393
+ SessionState.SetString(StatusProofKeySessionKey, NewStatusProofKey());
394
+ ClearSessionBindings();
395
+ StatusJournalCorrupt = false;
396
+ LastStatusLoadOutcome = StatusLoadOutcome.Missing;
397
+ }
398
+ else if (string.IsNullOrEmpty(SessionState.GetString(StatusProofKeySessionKey, string.Empty)))
399
+ {
400
+ SessionState.SetString(StatusProofKeySessionKey, NewStatusProofKey());
401
+ StatusJournalCorrupt = UnityCliActiveOperation.Exists(StatusScope);
402
+ }
403
+ if (ResumeBoundPreEffectAbort()) return;
404
+ if (ResumePreEffectCleanup()) return;
405
+ ClearReloadUncertain();
406
+ var active = Load();
407
+ if (active == null) return;
408
+ if (TryResumePublishedTerminal(active)) return;
409
+ TryRecoverActiveLease(active);
410
+ var activePhase = Text(active, "phase");
411
+ if (!Bool(active, "reloadObserved") && (activePhase == "dispatching" || activePhase == "running"))
412
+ BindReloadUncertain(active);
413
+ WriteRunningStatus(active);
414
+ }
415
+
416
+ /// <summary>domain reload 后只按 SessionState 与已验证 root 的 exact identity 恢复 lease;竞争失败保持 binding 并在后续 tick 重试。</summary>
417
+ internal void RecoverActiveLease()
418
+ {
419
+ Dictionary<string, object> active;
420
+ try { active = Load(); }
421
+ catch { return; }
422
+ if (active == null || !string.IsNullOrEmpty(Text(active, "terminalState"))) return;
423
+ TryRecoverActiveLease(active);
424
+ }
425
+
426
+ private static bool TryRecoverActiveLease(Dictionary<string, object> active)
427
+ {
428
+ var requestId = Text(active, "requestId");
429
+ var digest = Text(active, "requestDigest");
430
+ var kind = Text(active, "kind");
431
+ if (!SessionOwnerIdentityMatches(requestId, digest, kind)
432
+ || !string.Equals(Text(active, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal)) return false;
433
+ try
434
+ {
435
+ if (UnityCliSessionMutationLease.IsOwnedBy(requestId, digest)) return true;
436
+ bool newlyAcquired;
437
+ return UnityCliSessionMutationLease.TryRecoverExact(requestId, digest, out newlyAcquired)
438
+ && UnityCliSessionMutationLease.IsOwnedBy(requestId, digest);
439
+ }
440
+ catch (Exception exception)
441
+ {
442
+ WriteLog("navmesh_bake_lease_recovery_failed " + exception.GetType().Name + ": " + exception.Message);
443
+ return false;
444
+ }
445
+ }
446
+
447
+ internal void BeforeReload()
448
+ {
449
+ ClearSurfaceObservation();
450
+ Update(root =>
451
+ {
452
+ if (!string.IsNullOrEmpty(Text(root, "terminalState"))) return;
453
+ root["reloadObserved"] = true;
454
+ root["phaseBeforeReload"] = Text(root, "phase");
455
+ });
456
+ }
457
+
458
+ private Dictionary<string, object> CreateRoot(BridgeRequest request, string digest, StartArgs args, Dictionary<string, object> plan)
459
+ {
460
+ var operationId = "navmesh-" + digest.Substring(0, 32);
461
+ return new Dictionary<string, object>
462
+ {
463
+ { "requestId", request.RequestId }, { "requestDigest", digest }, { "editorSessionId", EditorSessionId() },
464
+ { "deadline", request.Execution.AbsoluteDeadlineUtc }, { "operationId", operationId }, { "kind", args.Kind },
465
+ { "phase", "queued" }, { "startedAt", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) },
466
+ { "dispatchNotBefore", DateTime.UtcNow.AddMilliseconds(3000).ToString("O", CultureInfo.InvariantCulture) },
467
+ { "targetCount", plan["targetCount"] }, { "targets", plan["targets"] }, { "scenes", plan["scenes"] }, { "topologyBefore", plan["topology"] },
468
+ { "reloadObserved", false }, { "cancelRequested", false }, { "dispatchConfirmed", false },
469
+ { "foreignNativeOperationObserved", false }
470
+ };
471
+ }
472
+
473
+ private Dictionary<string, object> BuildPlan(StartArgs args)
474
+ {
475
+ if (NavMeshBuilder.isRunning)
476
+ throw new BridgeCommandException("E_NAVMESH_BAKE_ACTIVE", "An unowned legacy NavMesh bake is already running.", "Wait for it to finish before starting a tracked bake.");
477
+ if (SurfaceApi.Available && SurfaceApi.ActiveOperationCount() > 0)
478
+ throw new BridgeCommandException("E_NAVMESH_BAKE_ACTIVE", "An unowned NavMeshSurface bake is already running.", "Wait for it to finish before starting a tracked bake.");
479
+ List<object> targets;
480
+ List<NavMeshSceneFileData> scenes;
481
+ if (args.Kind == "legacy")
482
+ {
483
+ scenes = NavMeshToolSupport.PrepareLoadedSceneFiles("navmesh.bake");
484
+ targets = LegacyTargets(scenes);
485
+ }
486
+ else
487
+ {
488
+ targets = SurfaceApi.PlanTargets();
489
+ scenes = NavMeshToolSupport.PrepareSceneFiles(targets.Cast<Dictionary<string, object>>().Select(target => Text(target, "scenePath")), "navmesh.surface.bake");
490
+ }
491
+ return new Dictionary<string, object>
492
+ {
493
+ { "kind", args.Kind }, { "targetCount", (long)targets.Count }, { "targets", targets },
494
+ { "scenes", SceneObjects(scenes) }, { "topology", NavMeshToolSupport.TopologyDictionary() },
495
+ { "plannedEffects", new List<object> { PlannedEffect(args.Kind, targets.Count) } }
496
+ };
497
+ }
498
+
499
+ private object DryRun(StartArgs args, Dictionary<string, object> plan)
500
+ {
501
+ return new Dictionary<string, object>
502
+ {
503
+ { "dryRun", true }, { "kind", args.Kind }, { "targetCount", plan["targetCount"] },
504
+ { "targets", plan["targets"] }, { "scenes", plan["scenes"] }, { "topology", plan["topology"] }, { "plannedEffects", plan["plannedEffects"] }
505
+ };
506
+ }
507
+
508
+ private void TickLegacy(Dictionary<string, object> root)
509
+ {
510
+ var phase = Text(root, "phase");
511
+ if (phase == "queued") { DispatchLegacy(root); return; }
512
+ if (phase == "dispatching") { RecoverDispatch(root); return; }
513
+ if (phase == "verifying")
514
+ {
515
+ if (Bool(root, "cancelRequested")) FinishCancelled(root); else CompleteLegacy(root);
516
+ return;
517
+ }
518
+ if (phase != "running") { Fail(root, "E_EXECUTION_TIMEOUT", "Legacy NavMesh bake phase is invalid; outcome is unknown."); return; }
519
+ WriteRunningStatus(root);
520
+ if (SurfaceApi.ActiveOperationCount() > 0)
521
+ {
522
+ if (!Bool(root, "foreignNativeOperationObserved")) MarkForeignNativeOperation(root);
523
+ return;
524
+ }
525
+ if (NavMeshBuilder.isRunning || DateTimeOffset.UtcNow < Time(root, "observeNotBefore")) return;
526
+ if (Bool(root, "foreignNativeOperationObserved"))
527
+ {
528
+ Fail(root, "E_EXECUTION_TIMEOUT", "Another native NavMesh bake overlapped the observed legacy operation; outcome is unknown.");
529
+ return;
530
+ }
531
+ if (Bool(root, "cancelRequested")) FinishCancelled(root); else CompleteLegacy(root);
532
+ }
533
+
534
+ /// <summary>nonce/status均在Unity调用前durable;调用后只确认一次,失败只能same-stack rollback或observe。</summary>
535
+ private void DispatchLegacy(Dictionary<string, object> root)
536
+ {
537
+ var preEffectRoot = root;
538
+ try
539
+ {
540
+ if (!TryUpdateAndReload(value => { value["phase"] = "dispatching"; value["dispatchNonce"] = Guid.NewGuid().ToString("N"); }, out root))
541
+ {
542
+ AbortBeforeNativeEffect(preEffectRoot, "legacy dispatch transition could not be confirmed");
543
+ return;
544
+ }
545
+ }
546
+ catch (Exception exception) { AbortBeforeNativeEffect(preEffectRoot, "legacy dispatch transition failed: " + exception.GetType().Name); return; }
547
+ var nativeDispatched = false;
548
+ try
549
+ {
550
+ NavMeshToolSupport.EnsureEditorReady("navmesh.bake", true);
551
+ if (AnyNativeOperationRunning())
552
+ throw new BridgeCommandException("E_NAVMESH_BAKE_ACTIVE", "An unowned native NavMesh bake started before legacy dispatch.");
553
+ NavMeshToolSupport.ValidateFrozenSceneFiles(SceneFiles(root), "navmesh.bake");
554
+ NavMeshBuilder.BuildNavMeshAsync();
555
+ nativeDispatched = true;
556
+ if (ConfirmDispatch(root)) return;
557
+ if (NavMeshBuilder.isRunning) { CancelOwnedLegacyBake(); if (NavMeshBuilder.isRunning) return; }
558
+ Fail(root, "E_EXECUTION_TIMEOUT", "Legacy NavMesh dispatch journal disappeared after the native effect; outcome is unknown.");
559
+ }
560
+ catch (Exception exception)
561
+ {
562
+ if (nativeDispatched && NavMeshBuilder.isRunning) { CancelOwnedLegacyBake(); if (NavMeshBuilder.isRunning) return; }
563
+ var bridge = exception as BridgeCommandException;
564
+ Fail(root, bridge == null ? "E_EXECUTION_TIMEOUT" : bridge.Code,
565
+ bridge == null ? "Legacy NavMesh dispatch could not be confirmed: " + exception.GetType().Name + "." : bridge.Message);
566
+ }
567
+ }
568
+
569
+ /// <summary>save与fresh readback跨至少一个Editor update;verifying恢复只继续proof。</summary>
570
+ private void CompleteLegacy(Dictionary<string, object> root)
571
+ {
572
+ root = Load();
573
+ if (root == null) return;
574
+ try
575
+ {
576
+ if (AnyNativeOperationRunning())
577
+ {
578
+ if (!Bool(root, "foreignNativeOperationObserved")) MarkForeignNativeOperation(root);
579
+ return;
580
+ }
581
+ if (Bool(root, "foreignNativeOperationObserved"))
582
+ {
583
+ Fail(root, "E_EXECUTION_TIMEOUT", "Another native NavMesh bake overlapped legacy terminal proof; outcome is unknown.");
584
+ return;
585
+ }
586
+ if (Text(root, "phase") != "verifying")
587
+ {
588
+ if (NavMeshBuilder.isRunning) return;
589
+ root["topologyAfter"] = NavMeshToolSupport.TopologyDictionary();
590
+ root["scenesResult"] = SceneObjects(NavMeshToolSupport.SaveAndProveSceneFiles(SceneFiles(root), false));
591
+ root["phase"] = "verifying";
592
+ root["verifyNotBefore"] = DateTime.UtcNow.AddMilliseconds(250).ToString("O", CultureInfo.InvariantCulture);
593
+ Save(root);
594
+ WriteRunningStatus(root);
595
+ return;
596
+ }
597
+ if (DateTimeOffset.UtcNow < Time(root, "verifyNotBefore")) return;
598
+ NavMeshToolSupport.ValidateFrozenSceneFiles(SceneFilesFrom(root, "scenesResult"), "navmesh.bake terminal proof");
599
+ if (!SameTopology(root["topologyAfter"] as IDictionary<string, object>, NavMeshToolSupport.TopologyDictionary()))
600
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Legacy NavMesh topology changed during terminal proof.");
601
+ root["finishedAt"] = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
602
+ root["terminalState"] = "completed";
603
+ Succeed(root);
604
+ }
605
+ catch (BridgeCommandException exception) { if (Text(root, "terminalState") != "completed") Fail(root, exception.Code, exception.Message); }
606
+ catch (Exception exception) { if (Text(root, "terminalState") != "completed") Fail(root, "E_EXECUTION_TIMEOUT", "Legacy NavMesh scene persistence proof failed: " + exception.GetType().Name + "."); }
607
+ }
608
+
609
+ private void TickSurfaces(Dictionary<string, object> root)
610
+ {
611
+ var phase = Text(root, "phase");
612
+ if (phase == "queued") { DispatchSurfaces(root); return; }
613
+ if (phase == "dispatching") { RecoverDispatch(root); return; }
614
+ if (phase == "verifying")
615
+ {
616
+ if (Bool(root, "cancelRequested")) FinishCancelled(root); else CompleteSurfaces(root, null);
617
+ return;
618
+ }
619
+ if (phase != "running") { Fail(root, "E_EXECUTION_TIMEOUT", "NavMeshSurface bake phase is invalid; outcome is unknown."); return; }
620
+ WriteRunningStatus(root);
621
+ var observation = SurfaceObservationFor(root);
622
+ if (NavMeshBuilder.isRunning || (observation != null && SurfaceApi.HasForeignOperations(observation)))
623
+ {
624
+ if (!Bool(root, "foreignNativeOperationObserved")) MarkForeignNativeOperation(root);
625
+ return;
626
+ }
627
+ if (observation != null && (SurfaceApi.IsRunning(observation) || SurfaceApi.IsPresent(observation))) return;
628
+ if (SurfaceApi.AnyBaking(TargetIds(root)))
629
+ {
630
+ if (observation != null && !Bool(root, "foreignNativeOperationObserved")) MarkForeignNativeOperation(root);
631
+ return;
632
+ }
633
+ if (Bool(root, "foreignNativeOperationObserved") || (observation != null && !SurfaceApi.PublishedDataMatches(observation)))
634
+ {
635
+ Fail(root, "E_EXECUTION_TIMEOUT", "Another native NavMesh bake replaced or overlapped the observed Surface operation; outcome is unknown.");
636
+ return;
637
+ }
638
+ if (DateTimeOffset.UtcNow < Time(root, "observeNotBefore")) return;
639
+ if (Bool(root, "cancelRequested")) { FinishCancelled(root); return; }
640
+ List<object> result;
641
+ if (!SurfaceApi.TryProve(TargetIds(root), Targets(root), false, out result))
642
+ {
643
+ Fail(root, "E_EXECUTION_TIMEOUT", "NavMeshSurface terminal assets could not be proven; outcome is unknown.");
644
+ return;
645
+ }
646
+ CompleteSurfaces(root, result);
647
+ }
648
+
649
+ private void MarkForeignNativeOperation(Dictionary<string, object> root)
650
+ {
651
+ Dictionary<string, object> fresh;
652
+ if (TryUpdateAndReload(value => value["foreignNativeOperationObserved"] = true, out fresh)) return;
653
+ Fail(root, "E_EXECUTION_TIMEOUT", "Foreign native NavMesh operation observation could not be stored; outcome is unknown.");
654
+ }
655
+
656
+ private void DispatchSurfaces(Dictionary<string, object> root)
657
+ {
658
+ var preEffectRoot = root;
659
+ try
660
+ {
661
+ if (!TryUpdateAndReload(value => { value["phase"] = "dispatching"; value["dispatchNonce"] = Guid.NewGuid().ToString("N"); }, out root))
662
+ {
663
+ AbortBeforeNativeEffect(preEffectRoot, "surface dispatch transition could not be confirmed");
664
+ return;
665
+ }
666
+ }
667
+ catch (Exception exception) { AbortBeforeNativeEffect(preEffectRoot, "surface dispatch transition failed: " + exception.GetType().Name); return; }
668
+ var nativeDispatched = false;
669
+ try
670
+ {
671
+ NavMeshToolSupport.EnsureEditorReady("navmesh.surface.bake", true);
672
+ if (AnyNativeOperationRunning())
673
+ throw new BridgeCommandException("E_NAVMESH_BAKE_ACTIVE", "An unowned native NavMesh bake started before Surface dispatch.");
674
+ NavMeshToolSupport.ValidateFrozenSceneFiles(SceneFiles(root), "navmesh.surface.bake");
675
+ SurfaceApi.Start(TargetIds(root));
676
+ nativeDispatched = true;
677
+ var observation = SurfaceApi.CaptureObservation(TargetIds(root));
678
+ SetSurfaceObservation(root, observation);
679
+ if (ConfirmDispatch(root)) return;
680
+ if (SurfaceObservationRunning(root)) { CancelOwnedSurfaceBake(root); if (SurfaceObservationRunning(root)) return; }
681
+ Fail(root, "E_EXECUTION_TIMEOUT", "NavMeshSurface dispatch journal disappeared after the native effect; outcome is unknown.");
682
+ }
683
+ catch (Exception exception)
684
+ {
685
+ if (nativeDispatched && SurfaceObservationRunning(root)) { CancelOwnedSurfaceBake(root); if (SurfaceObservationRunning(root)) return; }
686
+ var bridge = exception as BridgeCommandException;
687
+ Fail(root, bridge == null ? "E_EXECUTION_TIMEOUT" : bridge.Code,
688
+ bridge == null ? "NavMeshSurface dispatch could not be confirmed: " + exception.GetType().Name + "." : bridge.Message);
689
+ }
690
+ }
691
+
692
+ private void CompleteSurfaces(Dictionary<string, object> root, List<object> result)
693
+ {
694
+ root = Load();
695
+ if (root == null) return;
696
+ try
697
+ {
698
+ if (AnyNativeOperationRunning())
699
+ {
700
+ if (!Bool(root, "foreignNativeOperationObserved")) MarkForeignNativeOperation(root);
701
+ return;
702
+ }
703
+ if (Bool(root, "foreignNativeOperationObserved"))
704
+ {
705
+ Fail(root, "E_EXECUTION_TIMEOUT", "Another native NavMesh bake overlapped Surface terminal proof; outcome is unknown.");
706
+ return;
707
+ }
708
+ if (Text(root, "phase") != "verifying")
709
+ {
710
+ if (SurfaceObservationRunning(root) || SurfaceApi.AnyBaking(TargetIds(root))) return;
711
+ if (result == null && !SurfaceApi.TryProve(TargetIds(root), Targets(root), false, out result))
712
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMeshSurface terminal assets could not be proven; outcome is unknown.");
713
+ root["scenesResult"] = SceneObjects(NavMeshToolSupport.SaveAndProveSceneFiles(SceneFiles(root), false));
714
+ List<object> persisted;
715
+ if (!SurfaceApi.TryProve(TargetIds(root), Targets(root), false, out persisted))
716
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMeshSurface persisted assets could not be proven; outcome is unknown.");
717
+ root["surfacesResult"] = persisted;
718
+ root["topologyAfter"] = NavMeshToolSupport.TopologyDictionary();
719
+ root["phase"] = "verifying";
720
+ root["verifyNotBefore"] = DateTime.UtcNow.AddMilliseconds(250).ToString("O", CultureInfo.InvariantCulture);
721
+ Save(root);
722
+ WriteRunningStatus(root);
723
+ return;
724
+ }
725
+ if (DateTimeOffset.UtcNow < Time(root, "verifyNotBefore")) return;
726
+ NavMeshToolSupport.ValidateFrozenSceneFiles(SceneFilesFrom(root, "scenesResult"), "navmesh.surface.bake terminal proof");
727
+ List<object> observed;
728
+ if (!SurfaceApi.TryProve(TargetIds(root), Targets(root), false, out observed)
729
+ || !SameJsonList(RootList(root, "surfacesResult"), observed))
730
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMeshSurface asset changed during terminal proof.");
731
+ if (!SameTopology(root["topologyAfter"] as IDictionary<string, object>, NavMeshToolSupport.TopologyDictionary()))
732
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMeshSurface topology changed during terminal proof.");
733
+ root["finishedAt"] = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
734
+ root["terminalState"] = "completed";
735
+ Succeed(root);
736
+ }
737
+ catch (BridgeCommandException exception) { if (Text(root, "terminalState") != "completed") Fail(root, exception.Code, exception.Message); }
738
+ catch (Exception exception) { if (Text(root, "terminalState") != "completed") Fail(root, "E_EXECUTION_TIMEOUT", "NavMeshSurface scene persistence proof failed: " + exception.GetType().Name + "."); }
739
+ }
740
+
741
+ private object Cancel(CancelArgs args)
742
+ {
743
+ if (args == null || string.IsNullOrWhiteSpace(args.OperationId))
744
+ throw new BridgeCommandException("E_SCHEMA_INVALID", "navmesh.bake-cancel requires the frozen operationId.");
745
+ var root = Load();
746
+ if (root == null)
747
+ {
748
+ var storedStatus = PublicStatus();
749
+ var exactCancelled = string.Equals(Text(storedStatus, "operationId"), args.OperationId, StringComparison.Ordinal)
750
+ && string.Equals(Text(storedStatus, "state"), "cancelled", StringComparison.Ordinal);
751
+ return new Dictionary<string, object>
752
+ {
753
+ { "cancelled", exactCancelled }, { "operationId", exactCancelled ? args.OperationId : null }, { "status", storedStatus }
754
+ };
755
+ }
756
+ if (!string.Equals(Text(root, "operationId"), args.OperationId, StringComparison.Ordinal))
757
+ throw new BridgeCommandException("E_NAVMESH_OPERATION_MISMATCH", "The active NavMesh bake does not match operationId.", "Read navmesh.bake-status and retry only for that exact operation.");
758
+ if (!string.IsNullOrEmpty(Text(root, "terminalState")))
759
+ {
760
+ var terminalStatus = PublicStatus();
761
+ return new Dictionary<string, object>
762
+ {
763
+ { "cancelled", Text(terminalStatus, "state") == "cancelled" }, { "operationId", args.OperationId }, { "status", terminalStatus }
764
+ };
765
+ }
766
+ if (!UnityCliSessionMutationLease.IsOwnedBy(Text(root, "requestId"), Text(root, "requestDigest")))
767
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh cancel cannot prove current root ownership; outcome is unknown.");
768
+ Dictionary<string, object> fresh;
769
+ if (Text(root, "phase") == "queued")
770
+ {
771
+ if (!TryUpdateAndReload(value => value["cancelRequested"] = true, out fresh))
772
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh queued cancel could not confirm its root; outcome is unknown.");
773
+ FinishCancelled(fresh);
774
+ }
775
+ else if (!TryUpdateAndReload(value => value["cancelRequested"] = true, out fresh))
776
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh cooperative cancel could not confirm its root; outcome is unknown.");
777
+ return new Dictionary<string, object> { { "cancelled", true }, { "operationId", args.OperationId }, { "status", PublicStatus() } };
778
+ }
779
+
780
+ private void FinishCancelled(Dictionary<string, object> root)
781
+ {
782
+ root = Load();
783
+ if (root == null || (Text(root, "phase") != "queued" && NativeGateRunning(root))) return;
784
+ root["finishedAt"] = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
785
+ root["terminalState"] = "cancelled";
786
+ root["topologyAfter"] = NavMeshToolSupport.TopologyDictionary();
787
+ Fail(root, "E_NAVMESH_BAKE_CANCELLED", "NavMesh bake was cancelled for operation " + Text(root, "operationId") + ".");
788
+ }
789
+
790
+ /// <summary>只在fresh root durable更新、exact lease与running status均确认后返回true。</summary>
791
+ private bool TryUpdateAndReload(Action<Dictionary<string, object>> action, out Dictionary<string, object> root)
792
+ {
793
+ root = null;
794
+ var before = Load();
795
+ if (before == null || !string.IsNullOrEmpty(Text(before, "terminalState"))) return false;
796
+ if (!Update(action)) return false;
797
+ Dictionary<string, object> updated;
798
+ try { updated = UnityCliActiveOperation.Load(ActiveScope); }
799
+ catch (Exception exception) { WriteLog("navmesh_updated_root_reload_failed " + exception.Message); return false; }
800
+ if (!UnityCliSessionMutationLease.IsOwnedBy(Text(updated, "requestId"), Text(updated, "requestDigest"))) return false;
801
+ WriteRunningStatus(updated);
802
+ root = Load();
803
+ return root != null;
804
+ }
805
+
806
+ /// <summary>effect前dispatch无法durable确认时冻结identity并发布唯一终态;写入失败由后续tick/reload原样重试。</summary>
807
+ private void AbortBeforeNativeEffect(Dictionary<string, object> root, string reason)
808
+ {
809
+ BindPreEffectAbort(root);
810
+ WriteLog("navmesh_pre_effect_abort requestId=" + Text(root, "requestId") + " reason=" + reason);
811
+ PublishPreEffectAbortTerminal(root);
812
+ }
813
+
814
+ private bool PublishPreEffectAbortTerminal(Dictionary<string, object> root)
815
+ {
816
+ try
817
+ {
818
+ Fail(root, "E_EXECUTION_TIMEOUT", "NavMesh bake was aborted before the native effect because the dispatch transition could not be durably confirmed.");
819
+ return true;
820
+ }
821
+ catch (Exception exception)
822
+ {
823
+ WriteLog("navmesh_pre_effect_terminal_retry " + exception.GetType().Name + ": " + exception.Message);
824
+ return false;
825
+ }
826
+ }
827
+
828
+ /// <summary>Unity调用返回后只允许一次running确认;失败由调用方same-stack rollback。</summary>
829
+ private bool ConfirmDispatch(Dictionary<string, object> root)
830
+ {
831
+ var fresh = Load();
832
+ if (fresh == null) return false;
833
+ fresh["phase"] = "running";
834
+ fresh["dispatchConfirmed"] = true;
835
+ fresh["observeNotBefore"] = DateTime.UtcNow.AddMilliseconds(100).ToString("O", CultureInfo.InvariantCulture);
836
+ Save(fresh);
837
+ WriteRunningStatus(fresh);
838
+ return true;
839
+ }
840
+
841
+ /// <summary>dispatching表示Unity可能已被调用;global状态与产物变化只用于等待安全idle,不能归因成功。</summary>
842
+ private void RecoverDispatch(Dictionary<string, object> root)
843
+ {
844
+ WriteRunningStatus(root);
845
+ if (NativeGateRunning(root)) return;
846
+ Fail(root, "E_EXECUTION_TIMEOUT", "NavMesh dispatch could not be attributed after the effect boundary; outcome is unknown.");
847
+ }
848
+
849
+ /// <summary>reload后queued可继续、verifying可继续冻结proof;更早phase丢失native identity后只能等待idle并发布unknown。</summary>
850
+ private void RecoverReload(Dictionary<string, object> root)
851
+ {
852
+ var phase = Text(root, "phase");
853
+ var beforeReload = Text(root, "phaseBeforeReload");
854
+ Dictionary<string, object> fresh;
855
+ if (phase == "queued" && beforeReload == "queued")
856
+ {
857
+ try
858
+ {
859
+ if (!TryUpdateAndReload(value => { value["reloadObserved"] = false; value.Remove("phaseBeforeReload"); }, out fresh))
860
+ AbortBeforeNativeEffect(root, "queued reload recovery could not be confirmed");
861
+ }
862
+ catch (Exception exception) { AbortBeforeNativeEffect(root, "queued reload recovery failed: " + exception.GetType().Name); }
863
+ return;
864
+ }
865
+ if (phase == "verifying" && beforeReload == "verifying")
866
+ {
867
+ if (Bool(root, "cancelRequested"))
868
+ {
869
+ WriteRunningStatus(root);
870
+ if (NativeGateRunning(root)) return;
871
+ Fail(root, "E_EXECUTION_TIMEOUT", "NavMesh bake crossed a domain reload without request-specific completion proof; outcome is unknown.");
872
+ return;
873
+ }
874
+ if (!TryUpdateAndReload(value => { value["reloadObserved"] = false; value.Remove("phaseBeforeReload"); }, out fresh)) return;
875
+ if (Text(fresh, "kind") == "legacy") CompleteLegacy(fresh); else CompleteSurfaces(fresh, null);
876
+ return;
877
+ }
878
+ WriteRunningStatus(root);
879
+ if (NativeGateRunning(root)) return;
880
+ Fail(root, "E_EXECUTION_TIMEOUT", "NavMesh bake crossed a domain reload without request-specific completion proof; outcome is unknown.");
881
+ }
882
+
883
+ private void BindReloadUncertain(Dictionary<string, object> root)
884
+ {
885
+ reloadUncertainRequestId = Text(root, "requestId");
886
+ reloadUncertainRequestDigest = Text(root, "requestDigest");
887
+ }
888
+
889
+ private bool IsReloadUncertain(Dictionary<string, object> root)
890
+ {
891
+ return !string.IsNullOrEmpty(reloadUncertainRequestId)
892
+ && string.Equals(reloadUncertainRequestId, Text(root, "requestId"), StringComparison.Ordinal)
893
+ && string.Equals(reloadUncertainRequestDigest, Text(root, "requestDigest"), StringComparison.Ordinal);
894
+ }
895
+
896
+ private void ClearReloadUncertain()
897
+ {
898
+ reloadUncertainRequestId = null;
899
+ reloadUncertainRequestDigest = null;
900
+ }
901
+
902
+ private void SetSurfaceObservation(Dictionary<string, object> root, SurfaceApi.SurfaceDispatchObservation observation)
903
+ {
904
+ surfaceObservation = observation;
905
+ surfaceObservationRequestId = Text(root, "requestId");
906
+ surfaceObservationRequestDigest = Text(root, "requestDigest");
907
+ }
908
+
909
+ private SurfaceApi.SurfaceDispatchObservation SurfaceObservationFor(Dictionary<string, object> root)
910
+ {
911
+ return surfaceObservation != null
912
+ && string.Equals(surfaceObservationRequestId, Text(root, "requestId"), StringComparison.Ordinal)
913
+ && string.Equals(surfaceObservationRequestDigest, Text(root, "requestDigest"), StringComparison.Ordinal)
914
+ ? surfaceObservation : null;
915
+ }
916
+
917
+ private bool SurfaceObservationRunning(Dictionary<string, object> root)
918
+ {
919
+ var observation = SurfaceObservationFor(root);
920
+ return observation != null && SurfaceApi.IsRunning(observation);
921
+ }
922
+
923
+ private double? SurfaceObservationProgress(Dictionary<string, object> root)
924
+ {
925
+ var observation = SurfaceObservationFor(root);
926
+ return observation == null ? (double?)null : SurfaceApi.Progress(observation);
927
+ }
928
+
929
+ private void ClearSurfaceObservation()
930
+ {
931
+ surfaceObservation = null;
932
+ surfaceObservationRequestId = null;
933
+ surfaceObservationRequestDigest = null;
934
+ }
935
+
936
+ private static bool AnyNativeOperationRunning()
937
+ {
938
+ try { return NavMeshBuilder.isRunning || SurfaceApi.ActiveOperationCount() > 0; }
939
+ catch { return true; }
940
+ }
941
+
942
+ private bool NativeGateRunning(Dictionary<string, object> root)
943
+ {
944
+ return root != null && AnyNativeOperationRunning();
945
+ }
946
+
947
+ private static void CancelOwnedLegacyBake() { NavMeshBuilder.Cancel(); }
948
+ private void CancelOwnedSurfaceBake(Dictionary<string, object> root)
949
+ {
950
+ var observation = SurfaceObservationFor(root);
951
+ if (observation != null) SurfaceApi.Cancel(observation);
952
+ }
953
+
954
+ /// <summary>公开snapshot只直接读取并验证文件,不触发corrupt recovery或cleanup。</summary>
955
+ internal Dictionary<string, object> LoadActiveForStatus()
956
+ {
957
+ if (!UnityCliActiveOperation.Exists(ActiveScope)) return null;
958
+ try
959
+ {
960
+ var root = UnityCliActiveOperation.Load(ActiveScope);
961
+ if (ValidateNavMeshRoot(root)) return root;
962
+ }
963
+ catch (BridgeCommandException) { throw; }
964
+ catch { }
965
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh active snapshot cannot be verified; outcome is unknown.");
966
+ }
967
+
968
+ internal void WriteRunningStatus(Dictionary<string, object> root)
969
+ {
970
+ if (!string.IsNullOrEmpty(Text(root, "terminalState")))
971
+ throw new InvalidOperationException("NavMesh terminal root cannot be projected as running.");
972
+ SaveStatus(BuildRunningStatus(root));
973
+ }
974
+
975
+ internal Dictionary<string, object> BuildRunningStatus(Dictionary<string, object> root)
976
+ {
977
+ return new Dictionary<string, object>
978
+ {
979
+ { "editorSessionId", EditorSessionId() }, { "state", "running" }, { "active", true },
980
+ { "operationId", Text(root, "operationId") }, { "kind", Text(root, "kind") }, { "phase", PublicPhase(Text(root, "phase")) },
981
+ { "startedAt", Text(root, "startedAt") }, { "finishedAt", null },
982
+ { "durationMs", DurationMs(Text(root, "startedAt"), DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)) },
983
+ { "progress", Text(root, "kind") == "legacy" ? (NavMeshBuilder.isRunning ? (object)null : 1d) : SurfaceObservationProgress(root) },
984
+ { "targetCount", Long(root, "targetCount") }, { "topology", NavMeshToolSupport.TopologyDictionary() },
985
+ { "dispatchConfirmed", Bool(root, "dispatchConfirmed") },
986
+ { "rootRequestId", Text(root, "requestId") }, { "rootRequestDigest", Text(root, "requestDigest") }
987
+ };
988
+ }
989
+
990
+ private void WriteTerminalStatus(Dictionary<string, object> root, string state, object envelope)
991
+ {
992
+ var finishedAt = string.IsNullOrEmpty(Text(root, "finishedAt")) ? DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) : Text(root, "finishedAt");
993
+ SaveStatus(new Dictionary<string, object>
994
+ {
995
+ { "editorSessionId", EditorSessionId() }, { "state", state }, { "active", false },
996
+ { "operationId", Text(root, "operationId") }, { "kind", Text(root, "kind") }, { "phase", "terminal" },
997
+ { "startedAt", Text(root, "startedAt") }, { "finishedAt", finishedAt },
998
+ { "durationMs", DurationMs(Text(root, "startedAt"), finishedAt) }, { "progress", state == "completed" ? 1d : (object)null },
999
+ { "targetCount", Long(root, "targetCount") },
1000
+ { "topology", root.ContainsKey("topologyAfter") ? root["topologyAfter"] : NavMeshToolSupport.TopologyDictionary() },
1001
+ { "dispatchConfirmed", Bool(root, "dispatchConfirmed") },
1002
+ { "rootRequestId", Text(root, "requestId") }, { "rootRequestDigest", Text(root, "requestDigest") },
1003
+ { "rootEnvelope", envelope }
1004
+ });
1005
+ }
1006
+
1007
+ private void StoreTerminalEnvelope(Dictionary<string, object> root, object envelope)
1008
+ {
1009
+ var serialized = UnityCliSerializer.Serialize(envelope);
1010
+ object existing;
1011
+ if (root.TryGetValue("terminalEnvelope", out existing) && existing != null)
1012
+ {
1013
+ if (!string.Equals(UnityCliSerializer.Serialize(existing), serialized, StringComparison.Ordinal) || !RootTerminalProofMatches(root))
1014
+ throw new InvalidOperationException("NavMesh root terminal cannot be overwritten by a different envelope.");
1015
+ return;
1016
+ }
1017
+ var parsed = UnityCliSerializer.Parse(serialized) as IDictionary<string, object>;
1018
+ object ok;
1019
+ if (parsed == null || !parsed.TryGetValue("ok", out ok) || !(ok is bool)) throw new InvalidOperationException("NavMesh terminal envelope is invalid.");
1020
+ var state = (bool)ok ? "completed" : Text(root, "terminalState") == "cancelled" ? "cancelled" : "failed";
1021
+ var existingState = Text(root, "terminalState");
1022
+ if (!string.IsNullOrEmpty(existingState) && !string.Equals(existingState, state, StringComparison.Ordinal))
1023
+ throw new InvalidOperationException("NavMesh terminal state conflicts with the proposed envelope.");
1024
+ root["terminalState"] = state;
1025
+ if (string.IsNullOrEmpty(Text(root, "finishedAt"))) root["finishedAt"] = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
1026
+ if (!root.ContainsKey("topologyAfter")) root["topologyAfter"] = NavMeshToolSupport.TopologyDictionary();
1027
+ root["terminalEnvelope"] = parsed;
1028
+ root["terminalProofSha256"] = RootTerminalProofSha256(root);
1029
+ if (!RootTerminalProofMatches(root)) throw new InvalidOperationException("NavMesh terminal proof key is unavailable.");
1030
+ Save(root);
1031
+ }
1032
+
1033
+ private static bool TerminalAlreadyPublished(Dictionary<string, object> root, object envelope)
1034
+ {
1035
+ var status = LoadStatus();
1036
+ if (status == null)
1037
+ {
1038
+ if (LastStatusLoadOutcome == StatusLoadOutcome.TransientFailure)
1039
+ throw new InvalidOperationException("NavMesh durable terminal cannot be verified while status I/O is unavailable.");
1040
+ return false;
1041
+ }
1042
+ if (!string.Equals(Text(status, "rootRequestId"), Text(root, "requestId"), StringComparison.Ordinal)
1043
+ || !string.Equals(Text(status, "rootRequestDigest"), Text(root, "requestDigest"), StringComparison.Ordinal))
1044
+ throw new InvalidOperationException("NavMesh status belongs to a different request identity.");
1045
+ if (Bool(status, "active")) return false;
1046
+ object stored;
1047
+ if (!status.TryGetValue("rootEnvelope", out stored) || stored == null) return false;
1048
+ if (!string.Equals(UnityCliSerializer.Serialize(stored), UnityCliSerializer.Serialize(envelope), StringComparison.Ordinal))
1049
+ throw new InvalidOperationException("NavMesh durable terminal cannot be overwritten by a different envelope.");
1050
+ return true;
1051
+ }
1052
+
1053
+ private bool TryRestoreTerminalStatus(Dictionary<string, object> root)
1054
+ {
1055
+ if (!ValidateStoredTerminalEnvelope(root)) return false;
1056
+ var requestId = Text(root, "requestId");
1057
+ var digest = Text(root, "requestDigest");
1058
+ object envelope;
1059
+ root.TryGetValue("terminalEnvelope", out envelope);
1060
+ var status = LoadStatus();
1061
+ if (status != null)
1062
+ {
1063
+ if (!string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal)
1064
+ || !string.Equals(Text(status, "rootRequestDigest"), digest, StringComparison.Ordinal)) return false;
1065
+ if (!Bool(status, "active"))
1066
+ {
1067
+ object stored;
1068
+ if (!status.TryGetValue("rootEnvelope", out stored) || stored == null
1069
+ || !string.Equals(UnityCliSerializer.Serialize(stored), UnityCliSerializer.Serialize(envelope), StringComparison.Ordinal))
1070
+ throw new InvalidOperationException("NavMesh stored terminal conflicts with the root envelope.");
1071
+ StoreTerminalLedger(root, envelope);
1072
+ return true;
1073
+ }
1074
+ }
1075
+ else if (LastStatusLoadOutcome == StatusLoadOutcome.TransientFailure) return false;
1076
+ if (Text(root, "terminalState") != "completed" && Text(root, "phase") != "queued" && NativeGateRunning(root)) return false;
1077
+ WriteTerminalStatus(root, Text(root, "terminalState"), envelope);
1078
+ if (!TerminalStatusMatchesRoot(root, LoadStatus(), requestId, digest)) return false;
1079
+ StoreTerminalLedger(root, envelope);
1080
+ return true;
1081
+ }
1082
+
1083
+ private bool ValidateStoredTerminalEnvelope(Dictionary<string, object> root)
1084
+ {
1085
+ if (!RootTerminalProofMatches(root)) return false;
1086
+ var state = Text(root, "terminalState");
1087
+ object value;
1088
+ var envelope = root.TryGetValue("terminalEnvelope", out value) ? value as IDictionary<string, object> : null;
1089
+ object ok;
1090
+ string requestId;
1091
+ if ((state != "completed" && state != "cancelled" && state != "failed") || envelope == null
1092
+ || !envelope.TryGetValue("ok", out ok) || !(ok is bool)
1093
+ || !TryNonEmptyString(envelope, "requestId", out requestId)
1094
+ || !string.Equals(requestId, Text(root, "requestId"), StringComparison.Ordinal)) return false;
1095
+ if (state == "completed")
1096
+ {
1097
+ object dataValue;
1098
+ var data = envelope.TryGetValue("data", out dataValue) ? dataValue as IDictionary<string, object> : null;
1099
+ return (bool)ok && data != null
1100
+ && string.Equals(UnityCliSerializer.Serialize(data), UnityCliSerializer.Serialize(BuildSuccessData(root)), StringComparison.Ordinal);
1101
+ }
1102
+ object errorValue;
1103
+ var error = envelope.TryGetValue("error", out errorValue) ? errorValue as IDictionary<string, object> : null;
1104
+ string code;
1105
+ return !(bool)ok && error != null && TryNonEmptyString(error, "code", out code)
1106
+ && (state != "cancelled" || code == "E_NAVMESH_BAKE_CANCELLED");
1107
+ }
1108
+
1109
+ internal Dictionary<string, object> RecoveredStoredTerminal(string requestId, string requestDigest)
1110
+ {
1111
+ var status = LoadStatus();
1112
+ if (status == null)
1113
+ {
1114
+ if (StatusHistoryUnavailable()) throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh terminal status is unavailable; outcome is unknown.");
1115
+ return null;
1116
+ }
1117
+ if (!string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal) || !status.ContainsKey("rootEnvelope")) return null;
1118
+ if (!string.IsNullOrEmpty(requestDigest) && !string.Equals(Text(status, "rootRequestDigest"), requestDigest, StringComparison.Ordinal))
1119
+ throw new BridgeCommandException("E_REQUEST_ID_REUSED", "NavMesh bake requestId was reused with different input.");
1120
+ StoreTerminalLedgerIdentity(requestId, Text(status, "rootRequestDigest"), status["rootEnvelope"]);
1121
+ if (UnityCliActiveOperation.Exists(ActiveScope))
1122
+ {
1123
+ var active = UnityCliActiveOperation.Load(ActiveScope);
1124
+ if (!TerminalStatusMatchesRoot(active, status, requestId, Text(status, "rootRequestDigest")))
1125
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh terminal status does not match the active root.");
1126
+ }
1127
+ else if (IsBoundSessionOwner(requestId, Text(status, "rootRequestDigest")))
1128
+ {
1129
+ ResumeTerminalCleanup(new Dictionary<string, object> { { "requestId", requestId }, { "requestDigest", Text(status, "rootRequestDigest") } });
1130
+ }
1131
+ return new Dictionary<string, object> { { "terminal", true }, { "rootEnvelope", status["rootEnvelope"] } };
1132
+ }
1133
+
1134
+ private bool RecoverUnavailableActiveState(Exception exception)
1135
+ {
1136
+ string requestId;
1137
+ string digest;
1138
+ if (!UnityCliSessionMutationLease.TryGetCurrentOwner(out requestId, out digest)
1139
+ || !IsBoundSessionOwner(requestId, digest)) return false;
1140
+ var observed = rejectedRoot;
1141
+ rejectedRoot = null;
1142
+ if (observed != null && !string.IsNullOrEmpty(Text(observed, "terminalState")))
1143
+ {
1144
+ if (TryRestoreTerminalStatus(observed)) return true;
1145
+ return TerminalStatusMatchesIdentity(LoadStatus(), requestId, digest);
1146
+ }
1147
+ var status = LoadStatus();
1148
+ if (observed == null && status == null
1149
+ && (LastStatusLoadOutcome == StatusLoadOutcome.TransientFailure || LastStatusLoadOutcome == StatusLoadOutcome.Corrupt)) return false;
1150
+ if (status != null && (!string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal)
1151
+ || !string.Equals(Text(status, "rootRequestDigest"), digest, StringComparison.Ordinal))) return false;
1152
+ object terminalEnvelope;
1153
+ if (status != null && !Bool(status, "active") && status.TryGetValue("rootEnvelope", out terminalEnvelope) && terminalEnvelope != null)
1154
+ {
1155
+ StoreTerminalLedgerIdentity(requestId, digest, terminalEnvelope);
1156
+ return true;
1157
+ }
1158
+ var kind = SessionState.GetString(OwnerKindSessionKey, string.Empty);
1159
+ if (OwnerNativeRunning(kind)) return false;
1160
+ var finishedAt = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
1161
+ var startedAt = Text(status, "startedAt");
1162
+ if (!ValidateTimestamp(startedAt)) startedAt = finishedAt;
1163
+ var terminalRoot = new Dictionary<string, object>
1164
+ {
1165
+ { "requestId", requestId }, { "requestDigest", digest }, { "operationId", "navmesh-" + digest.Substring(0, 32) },
1166
+ { "kind", kind }, { "startedAt", startedAt }, { "finishedAt", finishedAt }, { "targetCount", Long(status, "targetCount") },
1167
+ { "topologyAfter", NavMeshToolSupport.TopologyDictionary() }, { "dispatchConfirmed", Bool(status, "dispatchConfirmed") }
1168
+ };
1169
+ var envelope = ProtocolEnvelopeFactory.Error(requestId, "E_EXECUTION_TIMEOUT", "NavMesh active state is unavailable; outcome is unknown.", null,
1170
+ new[] { "Inspect navmesh.bake-status and the target scenes before submitting a new request." });
1171
+ WriteTerminalStatus(terminalRoot, "failed", envelope);
1172
+ StoreTerminalLedgerIdentity(requestId, digest, envelope);
1173
+ WriteLog("navmesh_active_state_recovered requestId=" + requestId + " " + exception.Message);
1174
+ return true;
1175
+ }
1176
+
1177
+ private bool ValidateNavMeshRoot(Dictionary<string, object> root)
1178
+ {
1179
+ if (root == null || string.IsNullOrWhiteSpace(Text(root, "requestId")) || !IsSha256Text(Text(root, "requestDigest"))
1180
+ || !string.Equals(Text(root, "operationId"), "navmesh-" + Text(root, "requestDigest").Substring(0, 32), StringComparison.Ordinal)
1181
+ || (Text(root, "kind") != "legacy" && Text(root, "kind") != "surfaces")
1182
+ || !string.Equals(Text(root, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal)
1183
+ || !ValidateTimestamp(Text(root, "startedAt")) || !ValidateTimestamp(Text(root, "deadline"))
1184
+ || !ValidateTimestamp(Text(root, "dispatchNotBefore")) || !TryPositiveLong(root, "targetCount")
1185
+ || !ValidateTopology(root["topologyBefore"] as IDictionary<string, object>)
1186
+ || !TryBoolean(root, "reloadObserved") || !TryBoolean(root, "cancelRequested") || !TryBoolean(root, "dispatchConfirmed")
1187
+ || !TryBoolean(root, "foreignNativeOperationObserved")) return false;
1188
+ var targets = RootList(root, "targets");
1189
+ var scenes = RootList(root, "scenes");
1190
+ if (!ValidateSceneList(scenes) || !ValidateTargetList(Text(root, "kind"), targets, scenes)
1191
+ || targets.Count != Long(root, "targetCount")) return false;
1192
+ var phase = Text(root, "phase");
1193
+ var nonce = Text(root, "dispatchNonce");
1194
+ var confirmed = Bool(root, "dispatchConfirmed");
1195
+ if (phase != "queued" && phase != "dispatching" && phase != "running" && phase != "verifying") return false;
1196
+ if (phase == "queued" && (!string.IsNullOrEmpty(nonce) || confirmed)) return false;
1197
+ if (phase == "dispatching" && (string.IsNullOrEmpty(nonce) || confirmed)) return false;
1198
+ if ((phase == "running" || phase == "verifying") && (string.IsNullOrEmpty(nonce) || !confirmed)) return false;
1199
+ if ((phase == "running" || phase == "verifying") && !ValidateTimestamp(Text(root, "observeNotBefore"))) return false;
1200
+ if (Bool(root, "reloadObserved"))
1201
+ {
1202
+ var beforeReload = Text(root, "phaseBeforeReload");
1203
+ if (beforeReload != "queued" && beforeReload != "dispatching" && beforeReload != "running" && beforeReload != "verifying") return false;
1204
+ }
1205
+ if (phase == "verifying")
1206
+ {
1207
+ if (!ValidateTimestamp(Text(root, "verifyNotBefore")) || !ValidateSceneList(RootList(root, "scenesResult"))
1208
+ || !ValidateTopology(root["topologyAfter"] as IDictionary<string, object>)) return false;
1209
+ if (Text(root, "kind") == "surfaces" && !ValidateSurfaceResultList(RootList(root, "surfacesResult"), Long(root, "targetCount"))) return false;
1210
+ }
1211
+ var terminalState = Text(root, "terminalState");
1212
+ if (!string.IsNullOrEmpty(terminalState))
1213
+ {
1214
+ if ((terminalState != "completed" && terminalState != "cancelled" && terminalState != "failed")
1215
+ || !ValidateTimestamp(Text(root, "finishedAt")) || !ValidateTopology(root["topologyAfter"] as IDictionary<string, object>)
1216
+ || !RootTerminalProofMatches(root)) return false;
1217
+ if (terminalState == "completed" && (phase != "verifying" || !confirmed)) return false;
1218
+ if (terminalState == "cancelled" && !Bool(root, "cancelRequested")) return false;
1219
+ }
1220
+ var status = LoadStatus();
1221
+ if (status == null || !string.Equals(Text(status, "rootRequestId"), Text(root, "requestId"), StringComparison.Ordinal)
1222
+ || !string.Equals(Text(status, "rootRequestDigest"), Text(root, "requestDigest"), StringComparison.Ordinal)
1223
+ || !string.Equals(Text(status, "startedAt"), Text(root, "startedAt"), StringComparison.Ordinal)
1224
+ || Long(status, "targetCount") != Long(root, "targetCount")) return false;
1225
+ if (Bool(status, "active"))
1226
+ return Bool(status, "dispatchConfirmed") == confirmed && Text(status, "phase") == PublicPhase(phase);
1227
+ return TerminalStatusMatchesRoot(root, status, Text(root, "requestId"), Text(root, "requestDigest"));
1228
+ }
1229
+
1230
+ private static bool TerminalStatusMatchesIdentity(IDictionary<string, object> status, string requestId, string digest)
1231
+ {
1232
+ return status != null && !Bool(status, "active") && status.ContainsKey("rootEnvelope")
1233
+ && string.Equals(Text(status, "rootRequestId"), requestId, StringComparison.Ordinal)
1234
+ && string.Equals(Text(status, "rootRequestDigest"), digest, StringComparison.Ordinal);
1235
+ }
1236
+
1237
+ internal static bool TerminalStatusMatchesRoot(Dictionary<string, object> root, IDictionary<string, object> status, string requestId, string digest)
1238
+ {
1239
+ if (root == null || !TerminalStatusMatchesIdentity(status, requestId, digest)
1240
+ || !string.Equals(Text(root, "requestId"), requestId, StringComparison.Ordinal)
1241
+ || !string.Equals(Text(root, "requestDigest"), digest, StringComparison.Ordinal)
1242
+ || !string.Equals(Text(root, "operationId"), Text(status, "operationId"), StringComparison.Ordinal)
1243
+ || !string.Equals(Text(root, "kind"), Text(status, "kind"), StringComparison.Ordinal)
1244
+ || !string.Equals(Text(root, "startedAt"), Text(status, "startedAt"), StringComparison.Ordinal)
1245
+ || !string.Equals(Text(root, "finishedAt"), Text(status, "finishedAt"), StringComparison.Ordinal)
1246
+ || Long(root, "targetCount") != Long(status, "targetCount")
1247
+ || !string.Equals(Text(root, "terminalState"), Text(status, "state"), StringComparison.Ordinal)) return false;
1248
+ object rootEnvelope;
1249
+ object statusEnvelope;
1250
+ return RootTerminalProofMatches(root)
1251
+ && root.TryGetValue("terminalEnvelope", out rootEnvelope) && rootEnvelope != null
1252
+ && status.TryGetValue("rootEnvelope", out statusEnvelope) && statusEnvelope != null
1253
+ && string.Equals(UnityCliSerializer.Serialize(rootEnvelope), UnityCliSerializer.Serialize(statusEnvelope), StringComparison.Ordinal);
1254
+ }
1255
+
1256
+ /// <summary>domain reload从exact binding恢复effect前abort,只发布同一terminal或保守恢复identity,绝不再次dispatch。</summary>
1257
+ private bool ResumeBoundPreEffectAbort()
1258
+ {
1259
+ var requestId = SessionState.GetString(PreEffectAbortRequestIdSessionKey, string.Empty);
1260
+ var digest = SessionState.GetString(PreEffectAbortRequestDigestSessionKey, string.Empty);
1261
+ if (!IsBoundPreEffectAbort(requestId, digest)) return false;
1262
+ if (!UnityCliSessionMutationLease.IsOwnedBy(requestId, digest)) { ClearPreEffectAbortBinding(); return false; }
1263
+ if (!UnityCliActiveOperation.Exists(ActiveScope))
1264
+ {
1265
+ if (RecoverUnavailableActiveState(new InvalidOperationException("NavMesh pre-effect abort root is missing.")))
1266
+ ResumeTerminalCleanup(new Dictionary<string, object> { { "requestId", requestId }, { "requestDigest", digest } });
1267
+ return true;
1268
+ }
1269
+ Dictionary<string, object> root;
1270
+ try { root = UnityCliActiveOperation.Load(ActiveScope); }
1271
+ catch { return true; }
1272
+ if (!string.Equals(Text(root, "requestId"), requestId, StringComparison.Ordinal)
1273
+ || !string.Equals(Text(root, "requestDigest"), digest, StringComparison.Ordinal)) return true;
1274
+ PublishPreEffectAbortTerminal(root);
1275
+ return true;
1276
+ }
1277
+ }
1278
+
1279
+ internal static bool IsCommand(string command) { return Operation.IsCommand(command); }
1280
+ internal static void Register(BridgeCommandRegistry registry) { Operation.Register(registry); }
1281
+ internal static Task<string> Execute(BridgeRequest request, byte[] raw, Func<bool> accept, CancellationToken ct) { return Operation.Execute(request, raw, accept, ct); }
1282
+ internal static void Initialize() { Operation.InitializeState(); }
1283
+ internal static void Shutdown() { }
1284
+ internal static void BeforeReload() { Operation.BeforeReload(); }
1285
+ internal static void Tick()
1286
+ {
1287
+ Operation.RecoverActiveLease();
1288
+ Operation.Tick();
1289
+ ClearSessionBindingsIfReleased();
1290
+ }
1291
+ internal static bool HasActiveSurfaceData() { return SurfaceApi.HasActiveData(); }
1292
+ /// <summary>mutation effect前同时观察legacy全局gate与AI Navigation当前operation,禁止与未跟踪native bake并发。</summary>
1293
+ internal static bool HasActiveNativeBake()
1294
+ {
1295
+ return NavMeshBuilder.isRunning || (SurfaceApi.Available && SurfaceApi.ActiveOperationCount() > 0);
1296
+ }
1297
+
1298
+ /// <summary>公开status只构造有界snapshot,不写status、journal或lease。</summary>
1299
+ internal static Dictionary<string, object> PublicStatus()
1300
+ {
1301
+ var active = Operation.LoadActiveForStatus();
1302
+ if (active != null && string.Equals(Text(active, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal))
1303
+ {
1304
+ if (string.IsNullOrEmpty(Text(active, "terminalState"))) return PublicStatusFromStored(Operation.BuildRunningStatus(active));
1305
+ var terminalStatus = LoadStatus();
1306
+ if (!Coordinator.TerminalStatusMatchesRoot(active, terminalStatus, Text(active, "requestId"), Text(active, "requestDigest")))
1307
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh terminal snapshot cannot be verified; outcome is unknown.");
1308
+ return PublicStatusFromStored(terminalStatus);
1309
+ }
1310
+ var stored = LoadStatus();
1311
+ if (stored == null && StatusHistoryUnavailable())
1312
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh status snapshot cannot be verified; outcome is unknown.");
1313
+ return PublicStatusFromStored(stored);
1314
+ }
1315
+
1316
+ private static Dictionary<string, object> PublicStatusFromStored(Dictionary<string, object> stored)
1317
+ {
1318
+ if (stored == null || !string.Equals(Text(stored, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal))
1319
+ return new Dictionary<string, object> { { "state", "idle" }, { "active", false }, { "operationId", null }, { "kind", null }, { "phase", null }, { "startedAt", null }, { "finishedAt", null }, { "durationMs", 0L }, { "progress", null }, { "targetCount", 0L }, { "topology", NavMeshToolSupport.TopologyDictionary() } };
1320
+ var result = new Dictionary<string, object>(stored, StringComparer.Ordinal);
1321
+ result.Remove("editorSessionId");
1322
+ result.Remove("dispatchConfirmed");
1323
+ result.Remove("rootRequestId");
1324
+ result.Remove("rootRequestDigest");
1325
+ result.Remove("rootEnvelope");
1326
+ result.Remove("statusProofSha256");
1327
+ return result;
1328
+ }
1329
+
1330
+ private static Dictionary<string, object> LoadStatus()
1331
+ {
1332
+ try
1333
+ {
1334
+ if (!UnityCliActiveOperation.Exists(StatusScope))
1335
+ {
1336
+ LastStatusLoadOutcome = StatusLoadOutcome.Missing;
1337
+ if (StatusHistoryUnavailable()) StatusJournalCorrupt = true;
1338
+ return null;
1339
+ }
1340
+ var status = UnityCliActiveOperation.Load(StatusScope);
1341
+ if (!ValidateStatus(status))
1342
+ {
1343
+ LastStatusLoadOutcome = StatusLoadOutcome.Corrupt;
1344
+ StatusJournalCorrupt = true;
1345
+ return null;
1346
+ }
1347
+ LastStatusLoadOutcome = StatusLoadOutcome.Loaded;
1348
+ return status;
1349
+ }
1350
+ catch (IOException) { LastStatusLoadOutcome = StatusLoadOutcome.TransientFailure; return null; }
1351
+ catch (UnauthorizedAccessException) { LastStatusLoadOutcome = StatusLoadOutcome.TransientFailure; return null; }
1352
+ catch { LastStatusLoadOutcome = StatusLoadOutcome.Corrupt; StatusJournalCorrupt = true; return null; }
1353
+ }
1354
+
1355
+ private static void SaveStatus(Dictionary<string, object> status)
1356
+ {
1357
+ status.Remove("statusProofSha256");
1358
+ status["statusProofSha256"] = StatusProofSha256(status);
1359
+ UnityCliActiveOperation.Save(StatusScope, status);
1360
+ SessionState.SetBool(StatusWrittenThisSessionKey, true);
1361
+ StatusJournalCorrupt = false;
1362
+ LastStatusLoadOutcome = StatusLoadOutcome.Loaded;
1363
+ }
1364
+
1365
+ /// <summary>terminal ledger是单文件、固定容量、session-scoped;不淘汰已记录identity。</summary>
1366
+ private static Dictionary<string, object> LoadTerminalLedger()
1367
+ {
1368
+ try
1369
+ {
1370
+ if (!UnityCliActiveOperation.Exists(TerminalLedgerScope))
1371
+ {
1372
+ if (SessionState.GetBool(TerminalLedgerWrittenThisSessionKey, false))
1373
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh terminal ledger is missing; outcome is unknown.");
1374
+ return new Dictionary<string, object>
1375
+ {
1376
+ { "editorSessionId", EditorSessionId() }, { "entries", new List<object>() }
1377
+ };
1378
+ }
1379
+ var ledger = UnityCliActiveOperation.Load(TerminalLedgerScope);
1380
+ if (!ValidateTerminalLedger(ledger))
1381
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh terminal ledger is corrupt; outcome is unknown.");
1382
+ return ledger;
1383
+ }
1384
+ catch (BridgeCommandException) { throw; }
1385
+ catch (Exception exception)
1386
+ {
1387
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh terminal ledger cannot be read; outcome is unknown.", exception.GetType().Name);
1388
+ }
1389
+ }
1390
+
1391
+ private static void SaveTerminalLedger(Dictionary<string, object> ledger)
1392
+ {
1393
+ ledger.Remove("ledgerProofSha256");
1394
+ ledger["ledgerProofSha256"] = TerminalLedgerProofSha256(ledger);
1395
+ UnityCliActiveOperation.Save(TerminalLedgerScope, ledger);
1396
+ SessionState.SetBool(TerminalLedgerWrittenThisSessionKey, true);
1397
+ }
1398
+
1399
+ private static bool ValidateTerminalLedger(Dictionary<string, object> ledger)
1400
+ {
1401
+ if (ledger == null || !string.Equals(Text(ledger, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal)) return false;
1402
+ object entriesValue;
1403
+ var entries = ledger.TryGetValue("entries", out entriesValue) ? entriesValue as IList : null;
1404
+ string proof;
1405
+ if (entries == null || entries.Count > TerminalLedgerCapacity || !TryNonEmptyString(ledger, "ledgerProofSha256", out proof)
1406
+ || !IsSha256Text(proof) || !string.Equals(proof, TerminalLedgerProofSha256(ledger), StringComparison.Ordinal)) return false;
1407
+ var requestIds = new HashSet<string>(StringComparer.Ordinal);
1408
+ foreach (var value in entries)
1409
+ {
1410
+ var entry = value as IDictionary<string, object>;
1411
+ object envelopeValue;
1412
+ var envelope = entry != null && entry.TryGetValue("rootEnvelope", out envelopeValue) ? envelopeValue as IDictionary<string, object> : null;
1413
+ object ok;
1414
+ string requestId;
1415
+ if (entry == null || !TryNonEmptyString(entry, "requestId", out requestId) || !requestIds.Add(requestId)
1416
+ || !IsSha256Text(Text(entry, "requestDigest")) || !ValidateTimestamp(Text(entry, "storedAt"))
1417
+ || envelope == null || !envelope.TryGetValue("ok", out ok) || !(ok is bool)
1418
+ || !string.Equals(Text(envelope, "requestId"), requestId, StringComparison.Ordinal)) return false;
1419
+ }
1420
+ return true;
1421
+ }
1422
+
1423
+ private static string TerminalLedgerProofSha256(IDictionary<string, object> ledger)
1424
+ {
1425
+ var key = SessionState.GetString(StatusProofKeySessionKey, string.Empty);
1426
+ if (string.IsNullOrEmpty(key) || ledger == null) return string.Empty;
1427
+ object entries;
1428
+ ledger.TryGetValue("entries", out entries);
1429
+ return HmacSha256(key, UnityCliSerializer.Serialize(new Dictionary<string, object>
1430
+ {
1431
+ { "editorSessionId", Text(ledger, "editorSessionId") }, { "entries", entries }
1432
+ }));
1433
+ }
1434
+
1435
+ private static Dictionary<string, object> RecoveredLedgerTerminal(string requestId, string requestDigest)
1436
+ {
1437
+ var ledger = LoadTerminalLedger();
1438
+ var entries = (ledger["entries"] as IList).Cast<object>();
1439
+ foreach (var value in entries)
1440
+ {
1441
+ var entry = value as IDictionary<string, object>;
1442
+ if (entry == null || !string.Equals(Text(entry, "requestId"), requestId, StringComparison.Ordinal)) continue;
1443
+ if (!string.IsNullOrEmpty(requestDigest) && !string.Equals(Text(entry, "requestDigest"), requestDigest, StringComparison.Ordinal))
1444
+ throw new BridgeCommandException("E_REQUEST_ID_REUSED", "NavMesh bake requestId was reused with different input.");
1445
+ return new Dictionary<string, object> { { "terminal", true }, { "rootEnvelope", entry["rootEnvelope"] } };
1446
+ }
1447
+ return null;
1448
+ }
1449
+
1450
+ private static void EnsureTerminalLedgerCapacity()
1451
+ {
1452
+ var ledger = LoadTerminalLedger();
1453
+ var entries = ledger["entries"] as IList;
1454
+ if (entries != null && entries.Count >= TerminalLedgerCapacity)
1455
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMesh terminal ledger reached its session capacity; restart the Editor before starting another bake.");
1456
+ }
1457
+
1458
+ private static void StoreTerminalLedger(Dictionary<string, object> root, object envelope)
1459
+ {
1460
+ if (root == null || !RootTerminalProofMatches(root))
1461
+ throw new InvalidOperationException("NavMesh terminal ledger requires an exact root proof.");
1462
+ StoreTerminalLedgerIdentity(Text(root, "requestId"), Text(root, "requestDigest"), envelope);
1463
+ }
1464
+
1465
+ private static void StoreTerminalLedgerIdentity(string requestId, string requestDigest, object envelope)
1466
+ {
1467
+ if (string.IsNullOrWhiteSpace(requestId) || !IsSha256Text(requestDigest))
1468
+ throw new InvalidOperationException("NavMesh terminal ledger identity is invalid.");
1469
+ var parsedEnvelope = UnityCliSerializer.Parse(UnityCliSerializer.Serialize(envelope)) as IDictionary<string, object>;
1470
+ if (parsedEnvelope == null || !string.Equals(Text(parsedEnvelope, "requestId"), requestId, StringComparison.Ordinal))
1471
+ throw new InvalidOperationException("NavMesh terminal ledger envelope identity is invalid.");
1472
+ var ledger = LoadTerminalLedger();
1473
+ var entries = (ledger["entries"] as IList).Cast<object>().ToList();
1474
+ foreach (var value in entries)
1475
+ {
1476
+ var entry = value as IDictionary<string, object>;
1477
+ if (entry == null || !string.Equals(Text(entry, "requestId"), requestId, StringComparison.Ordinal)) continue;
1478
+ if (!string.Equals(Text(entry, "requestDigest"), requestDigest, StringComparison.Ordinal)
1479
+ || !string.Equals(UnityCliSerializer.Serialize(entry["rootEnvelope"]), UnityCliSerializer.Serialize(parsedEnvelope), StringComparison.Ordinal))
1480
+ throw new InvalidOperationException("NavMesh terminal ledger entry cannot be overwritten.");
1481
+ return;
1482
+ }
1483
+ if (entries.Count >= TerminalLedgerCapacity)
1484
+ throw new InvalidOperationException("NavMesh terminal ledger capacity was exhausted before publication.");
1485
+ entries.Add(new Dictionary<string, object>
1486
+ {
1487
+ { "requestId", requestId }, { "requestDigest", requestDigest },
1488
+ { "storedAt", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture) }, { "rootEnvelope", parsedEnvelope }
1489
+ });
1490
+ ledger["entries"] = entries;
1491
+ SaveTerminalLedger(ledger);
1492
+ }
1493
+
1494
+ private static bool ValidateStatus(Dictionary<string, object> status)
1495
+ {
1496
+ if (status == null || !string.Equals(Text(status, "editorSessionId"), EditorSessionId(), StringComparison.Ordinal)) return false;
1497
+ string state; string operationId; string kind; string phase; string startedAt; string requestId; string digest; string proof;
1498
+ object active; object confirmed;
1499
+ if (!TryNonEmptyString(status, "state", out state) || !TryNonEmptyString(status, "operationId", out operationId)
1500
+ || !TryNonEmptyString(status, "kind", out kind) || !TryNonEmptyString(status, "phase", out phase)
1501
+ || !TryNonEmptyString(status, "startedAt", out startedAt) || !TryNonEmptyString(status, "rootRequestId", out requestId)
1502
+ || !TryNonEmptyString(status, "rootRequestDigest", out digest) || !IsSha256Text(digest)
1503
+ || !TryNonEmptyString(status, "statusProofSha256", out proof) || !IsSha256Text(proof)
1504
+ || !string.Equals(proof, StatusProofSha256(status), StringComparison.Ordinal)
1505
+ || !status.TryGetValue("active", out active) || !(active is bool)
1506
+ || !status.TryGetValue("dispatchConfirmed", out confirmed) || !(confirmed is bool)
1507
+ || (kind != "legacy" && kind != "surfaces") || !ValidateTimestamp(startedAt)
1508
+ || !string.Equals(operationId, "navmesh-" + digest.Substring(0, 32), StringComparison.Ordinal)
1509
+ || !TryNonNegativeLong(status, "durationMs") || !TryNonNegativeLong(status, "targetCount")
1510
+ || !ValidateTopology(status["topology"] as IDictionary<string, object>)) return false;
1511
+ var isActive = (bool)active;
1512
+ if (isActive) return state == "running" && (phase == "queued" || phase == "starting" || phase == "baking")
1513
+ && !status.ContainsKey("rootEnvelope") && string.IsNullOrEmpty(Text(status, "finishedAt"));
1514
+ if ((state != "completed" && state != "cancelled" && state != "failed") || phase != "terminal"
1515
+ || !ValidateTimestamp(Text(status, "finishedAt"))) return false;
1516
+ object envelopeValue;
1517
+ var envelope = status.TryGetValue("rootEnvelope", out envelopeValue) ? envelopeValue as IDictionary<string, object> : null;
1518
+ object ok;
1519
+ string envelopeRequestId;
1520
+ if (envelope == null || !envelope.TryGetValue("ok", out ok) || !(ok is bool)
1521
+ || !TryNonEmptyString(envelope, "requestId", out envelopeRequestId)
1522
+ || !string.Equals(envelopeRequestId, requestId, StringComparison.Ordinal)) return false;
1523
+ if (state == "completed") return (bool)ok && envelope.ContainsKey("data");
1524
+ object errorValue;
1525
+ var error = envelope.TryGetValue("error", out errorValue) ? errorValue as IDictionary<string, object> : null;
1526
+ string code;
1527
+ return !(bool)ok && error != null && TryNonEmptyString(error, "code", out code)
1528
+ && (state != "cancelled" || code == "E_NAVMESH_BAKE_CANCELLED");
1529
+ }
1530
+
1531
+ private static bool StatusHistoryUnavailable()
1532
+ {
1533
+ return StatusJournalCorrupt || (SessionState.GetBool(StatusWrittenThisSessionKey, false) && !UnityCliActiveOperation.Exists(StatusScope));
1534
+ }
1535
+
1536
+ private static string StatusProofSha256(IDictionary<string, object> status)
1537
+ {
1538
+ var key = SessionState.GetString(StatusProofKeySessionKey, string.Empty);
1539
+ if (string.IsNullOrEmpty(key) || status == null) return string.Empty;
1540
+ var material = new Dictionary<string, object>(StringComparer.Ordinal);
1541
+ foreach (var field in new[] { "editorSessionId", "state", "active", "operationId", "kind", "phase", "startedAt", "finishedAt", "durationMs", "progress", "targetCount", "topology", "dispatchConfirmed", "rootRequestId", "rootRequestDigest", "rootEnvelope" })
1542
+ {
1543
+ object value;
1544
+ if (status.TryGetValue(field, out value)) material[field] = value;
1545
+ }
1546
+ return HmacSha256(key, UnityCliSerializer.Serialize(material));
1547
+ }
1548
+
1549
+ private static string RootTerminalProofSha256(IDictionary<string, object> root)
1550
+ {
1551
+ var key = SessionState.GetString(StatusProofKeySessionKey, string.Empty);
1552
+ if (string.IsNullOrEmpty(key) || root == null) return string.Empty;
1553
+ var material = new Dictionary<string, object>(StringComparer.Ordinal);
1554
+ foreach (var field in new[] { "editorSessionId", "requestId", "requestDigest", "operationId", "kind", "phase", "startedAt", "finishedAt", "targetCount", "topologyAfter", "dispatchConfirmed", "terminalState", "terminalEnvelope" })
1555
+ {
1556
+ object value;
1557
+ if (root.TryGetValue(field, out value)) material[field] = value;
1558
+ }
1559
+ return HmacSha256(key, UnityCliSerializer.Serialize(material));
1560
+ }
1561
+
1562
+ private static bool RootTerminalProofMatches(IDictionary<string, object> root)
1563
+ {
1564
+ string proof;
1565
+ return TryNonEmptyString(root, "terminalProofSha256", out proof) && IsSha256Text(proof)
1566
+ && string.Equals(proof, RootTerminalProofSha256(root), StringComparison.Ordinal);
1567
+ }
1568
+
1569
+ private static string HmacSha256(string key, string text)
1570
+ {
1571
+ using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
1572
+ {
1573
+ var bytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(text));
1574
+ var builder = new StringBuilder(bytes.Length * 2);
1575
+ foreach (var value in bytes) builder.Append(value.ToString("x2", CultureInfo.InvariantCulture));
1576
+ return builder.ToString();
1577
+ }
1578
+ }
1579
+
1580
+ private static string NewStatusProofKey() { return Guid.NewGuid().ToString("N") + Guid.NewGuid().ToString("N"); }
1581
+
1582
+ private static void BindSessionOwner(string requestId, string digest, string kind)
1583
+ {
1584
+ if (string.IsNullOrWhiteSpace(requestId) || !IsSha256Text(digest) || (kind != "legacy" && kind != "surfaces"))
1585
+ throw new InvalidOperationException("NavMesh session owner identity is invalid.");
1586
+ SessionState.SetString(OwnerRequestIdSessionKey, requestId);
1587
+ SessionState.SetString(OwnerRequestDigestSessionKey, digest);
1588
+ SessionState.SetString(OwnerKindSessionKey, kind);
1589
+ }
1590
+
1591
+ private static bool SessionOwnerIdentityMatches(string requestId, string digest, string kind)
1592
+ {
1593
+ return !string.IsNullOrWhiteSpace(requestId) && IsSha256Text(digest)
1594
+ && (kind == "legacy" || kind == "surfaces")
1595
+ && string.Equals(SessionState.GetString(OwnerRequestIdSessionKey, string.Empty), requestId, StringComparison.Ordinal)
1596
+ && string.Equals(SessionState.GetString(OwnerRequestDigestSessionKey, string.Empty), digest, StringComparison.Ordinal)
1597
+ && string.Equals(SessionState.GetString(OwnerKindSessionKey, string.Empty), kind, StringComparison.Ordinal);
1598
+ }
1599
+
1600
+ private static bool IsBoundSessionOwner(string requestId, string digest)
1601
+ {
1602
+ var kind = SessionState.GetString(OwnerKindSessionKey, string.Empty);
1603
+ return SessionOwnerIdentityMatches(requestId, digest, kind)
1604
+ && UnityCliSessionMutationLease.IsOwnedBy(requestId, digest);
1605
+ }
1606
+
1607
+ private static void BindPreEffectAbort(Dictionary<string, object> root)
1608
+ {
1609
+ SessionState.SetString(PreEffectAbortRequestIdSessionKey, Text(root, "requestId"));
1610
+ SessionState.SetString(PreEffectAbortRequestDigestSessionKey, Text(root, "requestDigest"));
1611
+ }
1612
+
1613
+ private static bool IsBoundPreEffectAbort(string requestId, string digest)
1614
+ {
1615
+ return !string.IsNullOrWhiteSpace(requestId) && IsSha256Text(digest)
1616
+ && string.Equals(SessionState.GetString(PreEffectAbortRequestIdSessionKey, string.Empty), requestId, StringComparison.Ordinal)
1617
+ && string.Equals(SessionState.GetString(PreEffectAbortRequestDigestSessionKey, string.Empty), digest, StringComparison.Ordinal);
1618
+ }
1619
+
1620
+ private static void ClearPreEffectAbortBinding()
1621
+ {
1622
+ SessionState.SetString(PreEffectAbortRequestIdSessionKey, string.Empty);
1623
+ SessionState.SetString(PreEffectAbortRequestDigestSessionKey, string.Empty);
1624
+ }
1625
+
1626
+ private static void ClearSessionBindings()
1627
+ {
1628
+ SessionState.SetString(OwnerRequestIdSessionKey, string.Empty);
1629
+ SessionState.SetString(OwnerRequestDigestSessionKey, string.Empty);
1630
+ SessionState.SetString(OwnerKindSessionKey, string.Empty);
1631
+ ClearPreEffectAbortBinding();
1632
+ }
1633
+
1634
+ private static void ClearSessionBindingsIfReleased()
1635
+ {
1636
+ var ownerId = SessionState.GetString(OwnerRequestIdSessionKey, string.Empty);
1637
+ var ownerDigest = SessionState.GetString(OwnerRequestDigestSessionKey, string.Empty);
1638
+ var abortId = SessionState.GetString(PreEffectAbortRequestIdSessionKey, string.Empty);
1639
+ var abortDigest = SessionState.GetString(PreEffectAbortRequestDigestSessionKey, string.Empty);
1640
+ if ((!string.IsNullOrEmpty(ownerId) || !string.IsNullOrEmpty(ownerDigest)) && UnityCliSessionMutationLease.IsOwnedBy(ownerId, ownerDigest)) return;
1641
+ if ((!string.IsNullOrEmpty(abortId) || !string.IsNullOrEmpty(abortDigest)) && UnityCliSessionMutationLease.IsOwnedBy(abortId, abortDigest)) return;
1642
+ try
1643
+ {
1644
+ var active = UnityCliActiveOperation.Load(ActiveScope);
1645
+ var kind = SessionState.GetString(OwnerKindSessionKey, string.Empty);
1646
+ if (active != null && string.IsNullOrEmpty(Text(active, "terminalState"))
1647
+ && SessionOwnerIdentityMatches(Text(active, "requestId"), Text(active, "requestDigest"), kind)) return;
1648
+ }
1649
+ catch { return; }
1650
+ ClearSessionBindings();
1651
+ }
1652
+
1653
+ private static bool OwnerNativeRunning(string kind)
1654
+ {
1655
+ if (kind != "legacy" && kind != "surfaces") return false;
1656
+ try { return NavMeshBuilder.isRunning || SurfaceApi.ActiveOperationCount() > 0; }
1657
+ catch { return true; }
1658
+ }
1659
+
1660
+ private static bool ValidateTimestamp(string value) { DateTimeOffset parsed; return !string.IsNullOrWhiteSpace(value) && DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out parsed); }
1661
+ private static bool IsSha256Text(string value) { return value != null && value.Length == 64 && value.All(character => (character >= '0' && character <= '9') || (character >= 'a' && character <= 'f')); }
1662
+ private static bool TryNonEmptyString(IDictionary<string, object> value, string key, out string result) { result = Text(value, key); return !string.IsNullOrWhiteSpace(result); }
1663
+ private static bool TryNonNegativeLong(IDictionary<string, object> value, string key) { object item; return value != null && value.TryGetValue(key, out item) && item is long && (long)item >= 0L; }
1664
+ private static bool TryPositiveLong(IDictionary<string, object> value, string key) { object item; return value != null && value.TryGetValue(key, out item) && item is long && (long)item > 0L; }
1665
+ private static bool TryBoolean(IDictionary<string, object> value, string key) { object item; return value != null && value.TryGetValue(key, out item) && item is bool; }
1666
+ private static bool ValidateTopology(IDictionary<string, object> value) { return value != null && TryNonNegativeLong(value, "vertexCount") && TryNonNegativeLong(value, "indexCount") && IsSha256Text(Text(value, "sha256")); }
1667
+ private static bool SameTopology(IDictionary<string, object> left, IDictionary<string, object> right) { return ValidateTopology(left) && ValidateTopology(right) && Long(left, "vertexCount") == Long(right, "vertexCount") && Long(left, "indexCount") == Long(right, "indexCount") && string.Equals(Text(left, "sha256"), Text(right, "sha256"), StringComparison.Ordinal); }
1668
+ private static List<object> RootList(Dictionary<string, object> root, string key) { object value; var list = root != null && root.TryGetValue(key, out value) ? value as IList : null; return list == null ? null : list.Cast<object>().ToList(); }
1669
+ private static bool SameJsonList(List<object> left, List<object> right) { return left != null && right != null && string.Equals(UnityCliSerializer.Serialize(left), UnityCliSerializer.Serialize(right), StringComparison.Ordinal); }
1670
+
1671
+ private static bool ValidateSceneList(List<object> scenes)
1672
+ {
1673
+ if (scenes == null || scenes.Count == 0) return false;
1674
+ var paths = new HashSet<string>(StringComparer.Ordinal);
1675
+ foreach (var value in scenes)
1676
+ {
1677
+ var scene = value as IDictionary<string, object>;
1678
+ object dirty;
1679
+ if (scene == null || !Text(scene, "path").StartsWith("Assets/", StringComparison.Ordinal)
1680
+ || !Text(scene, "path").EndsWith(".unity", StringComparison.OrdinalIgnoreCase)
1681
+ || Text(scene, "guid").Length != 32 || !Text(scene, "guid").All(character => (character >= '0' && character <= '9') || (character >= 'a' && character <= 'f'))
1682
+ || !IsSha256Text(Text(scene, "sha256")) || !TryPositiveLong(scene, "sizeBytes")
1683
+ || !scene.TryGetValue("dirty", out dirty) || !(dirty is bool) || (bool)dirty
1684
+ || !paths.Add(Text(scene, "path"))) return false;
1685
+ }
1686
+ return true;
1687
+ }
1688
+
1689
+ private static bool ValidateTargetList(string kind, List<object> targets, List<object> scenes)
1690
+ {
1691
+ if (targets == null || targets.Count == 0 || scenes == null) return false;
1692
+ var sceneByPath = scenes.Cast<IDictionary<string, object>>().ToDictionary(scene => Text(scene, "path"), StringComparer.Ordinal);
1693
+ var identities = new HashSet<string>(StringComparer.Ordinal);
1694
+ foreach (var value in targets)
1695
+ {
1696
+ var target = value as IDictionary<string, object>;
1697
+ if (target == null || !sceneByPath.ContainsKey(Text(target, "scenePath"))) return false;
1698
+ if (kind == "legacy")
1699
+ {
1700
+ if (!string.Equals(Text(target, "sceneGuid"), Text(sceneByPath[Text(target, "scenePath")], "guid"), StringComparison.Ordinal)
1701
+ || !identities.Add(Text(target, "sceneGuid"))) return false;
1702
+ }
1703
+ else
1704
+ {
1705
+ object nameValue; object beforeIdentityValue; object beforeHashValue;
1706
+ if (string.IsNullOrWhiteSpace(Text(target, "globalId")) || !identities.Add(Text(target, "globalId"))
1707
+ || !target.TryGetValue("name", out nameValue) || !(nameValue is string)
1708
+ || !target.TryGetValue("beforeDataIdentity", out beforeIdentityValue) || !(beforeIdentityValue is string)
1709
+ || !target.TryGetValue("beforeDataHash", out beforeHashValue) || !(beforeHashValue is string)) return false;
1710
+ var beforeIdentity = (string)beforeIdentityValue;
1711
+ var beforeHash = (string)beforeHashValue;
1712
+ if ((string.IsNullOrEmpty(beforeIdentity) && !string.IsNullOrEmpty(beforeHash))
1713
+ || (!string.IsNullOrEmpty(beforeIdentity) && !IsSha256Text(beforeHash))) return false;
1714
+ }
1715
+ }
1716
+ return kind != "legacy" || targets.Count == scenes.Count;
1717
+ }
1718
+
1719
+ private static bool ValidateSurfaceResultList(List<object> values, long targetCount)
1720
+ {
1721
+ if (values == null || values.Count != targetCount) return false;
1722
+ var ids = new HashSet<string>(StringComparer.Ordinal);
1723
+ foreach (var value in values)
1724
+ {
1725
+ var item = value as IDictionary<string, object>;
1726
+ object dirty;
1727
+ if (item == null || string.IsNullOrWhiteSpace(Text(item, "globalId")) || !ids.Add(Text(item, "globalId"))
1728
+ || string.IsNullOrWhiteSpace(Text(item, "scenePath")) || string.IsNullOrWhiteSpace(Text(item, "dataPath"))
1729
+ || Text(item, "dataGuid").Length != 32 || !IsSha256Text(Text(item, "dataHash")) || !TryPositiveLong(item, "dataSizeBytes")
1730
+ || !item.TryGetValue("sceneDirty", out dirty) || !(dirty is bool) || (bool)dirty) return false;
1731
+ }
1732
+ return true;
1733
+ }
1734
+
1735
+ private static List<object> LegacyTargets(IEnumerable<NavMeshSceneFileData> scenes)
1736
+ {
1737
+ return scenes.Select(scene => (object)new Dictionary<string, object> { { "scenePath", scene.Path }, { "sceneGuid", scene.Guid } }).ToList();
1738
+ }
1739
+
1740
+ private static List<object> SceneObjects(IEnumerable<NavMeshSceneFileData> scenes)
1741
+ {
1742
+ return scenes.Select(scene => (object)NavMeshToolSupport.SceneDictionary(scene)).ToList();
1743
+ }
1744
+
1745
+ private static List<NavMeshSceneFileData> SceneFiles(Dictionary<string, object> root) { return SceneFilesFrom(root, "scenes"); }
1746
+
1747
+ private static List<NavMeshSceneFileData> SceneFilesFrom(Dictionary<string, object> root, string key)
1748
+ {
1749
+ var result = new List<NavMeshSceneFileData>();
1750
+ object source;
1751
+ var values = root != null && root.TryGetValue(key, out source) ? source as IEnumerable : null;
1752
+ if (values != null) foreach (var value in values)
1753
+ {
1754
+ var item = value as IDictionary<string, object>;
1755
+ if (item == null) continue;
1756
+ result.Add(new NavMeshSceneFileData { Path = Text(item, "path"), Guid = Text(item, "guid"), Sha256 = Text(item, "sha256"), SizeBytes = Long(item, "sizeBytes"), Dirty = Bool(item, "dirty") });
1757
+ }
1758
+ return result;
1759
+ }
1760
+
1761
+ private static List<Dictionary<string, object>> Targets(Dictionary<string, object> root)
1762
+ {
1763
+ var result = new List<Dictionary<string, object>>();
1764
+ var values = root["targets"] as IEnumerable;
1765
+ if (values != null) foreach (var value in values) if (value is Dictionary<string, object>) result.Add((Dictionary<string, object>)value);
1766
+ return result;
1767
+ }
1768
+ private static List<string> TargetIds(Dictionary<string, object> root) { return Targets(root).Select(value => Text(value, "globalId")).Where(value => !string.IsNullOrEmpty(value)).ToList(); }
1769
+ private static Dictionary<string, object> PlannedEffect(string kind, long count) { return new Dictionary<string, object> { { "type", kind == "legacy" ? "navmesh.bake" : "navmesh.surface.bake" }, { "target", kind == "legacy" ? "open-scenes" : "all-active-surfaces" }, { "targetCount", count } }; }
1770
+ private static string PublicPhase(string phase) { return phase == "queued" ? "queued" : phase == "dispatching" ? "starting" : phase == "running" || phase == "verifying" ? "baking" : "unknown"; }
1771
+ private static long DurationMs(string start, string finish) { DateTimeOffset a; DateTimeOffset b; return DateTimeOffset.TryParse(start, out a) && DateTimeOffset.TryParse(finish, out b) ? (long)Math.Max(0, (b - a).TotalMilliseconds) : 0L; }
1772
+ private static string Text(IDictionary<string, object> value, string key) { object item; return value != null && value.TryGetValue(key, out item) && item != null ? Convert.ToString(item, CultureInfo.InvariantCulture) : string.Empty; }
1773
+ private static bool Bool(IDictionary<string, object> value, string key) { object item; return value != null && value.TryGetValue(key, out item) && item is bool && (bool)item; }
1774
+ private static long Long(IDictionary<string, object> value, string key) { long result; return long.TryParse(Text(value, key), out result) ? result : 0L; }
1775
+
1776
+ /// <summary>AI Navigation editor API 的唯一 package-optional reflection boundary。</summary>
1777
+ private static class SurfaceApi
1778
+ {
1779
+ private static readonly Type SurfaceType = Type.GetType("Unity.AI.Navigation.NavMeshSurface, Unity.AI.Navigation", false);
1780
+ private static readonly Type ManagerType = Type.GetType("Unity.AI.Navigation.Editor.NavMeshAssetManager, Unity.AI.Navigation.Editor", false);
1781
+ internal static bool Available { get { return SurfaceType != null && ManagerType != null; } }
1782
+
1783
+ internal sealed class SurfaceDispatchObservation
1784
+ {
1785
+ internal readonly List<SurfaceDispatchEntry> Entries;
1786
+ internal SurfaceDispatchObservation(List<SurfaceDispatchEntry> entries) { Entries = entries; }
1787
+ }
1788
+
1789
+ internal sealed class SurfaceDispatchEntry
1790
+ {
1791
+ internal string GlobalId;
1792
+ internal Object Surface;
1793
+ internal NavMeshData Data;
1794
+ internal AsyncOperation Operation;
1795
+ }
1796
+
1797
+ internal static List<object> PlanTargets()
1798
+ {
1799
+ EnsureAvailable();
1800
+ EnsureShape();
1801
+ if (ActiveOperationCount() > 0) throw new BridgeCommandException("E_NAVMESH_BAKE_ACTIVE", "An unowned NavMeshSurface bake is already running.");
1802
+ var result = new List<Dictionary<string, object>>();
1803
+ foreach (var raw in Resources.FindObjectsOfTypeAll(SurfaceType))
1804
+ {
1805
+ var component = raw as Component;
1806
+ var behaviour = raw as Behaviour;
1807
+ if (component == null || behaviour == null || !behaviour.isActiveAndEnabled || EditorUtility.IsPersistent(component)) continue;
1808
+ var scene = component.gameObject.scene;
1809
+ if (!scene.IsValid() || !scene.isLoaded || string.IsNullOrEmpty(scene.path)) continue;
1810
+ var globalId = GlobalObjectId.GetGlobalObjectIdSlow(component).ToString();
1811
+ if (string.IsNullOrEmpty(globalId)) continue;
1812
+ var beforeIdentity = DataIdentity(component);
1813
+ var beforeHash = string.Empty;
1814
+ if (!string.IsNullOrEmpty(beforeIdentity))
1815
+ {
1816
+ try { beforeHash = Text(DataProof(component), "dataHash"); }
1817
+ catch { throw new BridgeCommandException("E_NAVMESH_SURFACE_PROOF_UNAVAILABLE", "Existing NavMeshSurface data could not be read before baking.", "Inspect or clear the surface data asset, then retry."); }
1818
+ }
1819
+ result.Add(new Dictionary<string, object>
1820
+ {
1821
+ { "globalId", globalId }, { "name", component.gameObject.name }, { "scenePath", scene.path },
1822
+ { "beforeDataIdentity", beforeIdentity }, { "beforeDataHash", beforeHash }
1823
+ });
1824
+ }
1825
+ result.Sort((left, right) => string.CompareOrdinal(Text(left, "globalId"), Text(right, "globalId")));
1826
+ if (result.Count == 0)
1827
+ throw new BridgeCommandException("E_NAVMESH_SURFACE_NOT_FOUND", "No active NavMeshSurface component exists in a loaded, saved scene.", "Add and enable a NavMeshSurface in a saved scene, then retry.");
1828
+ return result.Cast<object>().ToList();
1829
+ }
1830
+
1831
+ internal static void Start(List<string> ids)
1832
+ {
1833
+ var surfaces = Resolve(ids);
1834
+ Method("StartBakingSurfaces", BindingFlags.Instance | BindingFlags.Public, new[] { typeof(Object[]) }).Invoke(Manager(), new object[] { surfaces });
1835
+ }
1836
+
1837
+ /// <summary>Start返回后的同一调用栈捕获exact AsyncOperation引用;该对象绝不写入journal。</summary>
1838
+ internal static SurfaceDispatchObservation CaptureObservation(List<string> ids)
1839
+ {
1840
+ var wanted = new HashSet<string>(ids, StringComparer.Ordinal);
1841
+ if (wanted.Count != ids.Count)
1842
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMeshSurface target identities are not unique; outcome is unknown.");
1843
+ var byId = new Dictionary<string, SurfaceDispatchEntry>(StringComparer.Ordinal);
1844
+ foreach (var operation in Operations())
1845
+ {
1846
+ var surface = Field(operation, "surface") as Object;
1847
+ var data = Field(operation, "bakeData") as NavMeshData;
1848
+ var async = Field(operation, "bakeOperation") as AsyncOperation;
1849
+ var globalId = surface == null ? string.Empty : GlobalObjectId.GetGlobalObjectIdSlow(surface).ToString();
1850
+ if (surface == null || data == null || async == null || !wanted.Contains(globalId)) continue;
1851
+ if (byId.ContainsKey(globalId))
1852
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMeshSurface exact AsyncOperation observation is duplicated; outcome is unknown.");
1853
+ byId.Add(globalId, new SurfaceDispatchEntry { GlobalId = globalId, Surface = surface, Data = data, Operation = async });
1854
+ }
1855
+ if (byId.Count != wanted.Count || wanted.Any(globalId => !byId.ContainsKey(globalId)))
1856
+ throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "NavMeshSurface exact AsyncOperation observation could not be captured; outcome is unknown.");
1857
+ return new SurfaceDispatchObservation(ids.Select(globalId => byId[globalId]).ToList());
1858
+ }
1859
+
1860
+ internal static bool IsRunning(SurfaceDispatchObservation observation)
1861
+ {
1862
+ if (observation == null) return false;
1863
+ var current = Operations();
1864
+ foreach (var entry in observation.Entries)
1865
+ {
1866
+ if (entry.Operation == null || entry.Operation.isDone) continue;
1867
+ var exact = false;
1868
+ foreach (var operation in current)
1869
+ {
1870
+ var async = Field(operation, "bakeOperation") as AsyncOperation;
1871
+ var data = Field(operation, "bakeData") as NavMeshData;
1872
+ if (ReferenceEquals(async, entry.Operation) && ReferenceEquals(data, entry.Data)) { exact = true; break; }
1873
+ }
1874
+ if (!exact) return true;
1875
+ return true;
1876
+ }
1877
+ return false;
1878
+ }
1879
+
1880
+ internal static bool IsPresent(SurfaceDispatchObservation observation)
1881
+ {
1882
+ if (observation == null) return false;
1883
+ foreach (var operation in Operations())
1884
+ {
1885
+ var async = Field(operation, "bakeOperation") as AsyncOperation;
1886
+ var data = Field(operation, "bakeData") as NavMeshData;
1887
+ if (observation.Entries.Any(entry => ReferenceEquals(async, entry.Operation) && ReferenceEquals(data, entry.Data))) return true;
1888
+ }
1889
+ return false;
1890
+ }
1891
+
1892
+ internal static bool HasForeignOperations(SurfaceDispatchObservation observation)
1893
+ {
1894
+ if (observation == null) return false;
1895
+ foreach (var operation in Operations())
1896
+ {
1897
+ var async = Field(operation, "bakeOperation") as AsyncOperation;
1898
+ var data = Field(operation, "bakeData") as NavMeshData;
1899
+ if (!observation.Entries.Any(entry => ReferenceEquals(async, entry.Operation) && ReferenceEquals(data, entry.Data))) return true;
1900
+ }
1901
+ return false;
1902
+ }
1903
+
1904
+ internal static bool PublishedDataMatches(SurfaceDispatchObservation observation)
1905
+ {
1906
+ if (observation == null || observation.Entries.Count == 0) return false;
1907
+ var property = SurfaceType.GetProperty("navMeshData", BindingFlags.Instance | BindingFlags.Public);
1908
+ if (property == null) return false;
1909
+ foreach (var entry in observation.Entries)
1910
+ {
1911
+ if (entry.Surface == null || !SurfaceType.IsInstanceOfType(entry.Surface)
1912
+ || !ReferenceEquals(property.GetValue(entry.Surface, null), entry.Data)) return false;
1913
+ }
1914
+ return true;
1915
+ }
1916
+
1917
+ internal static bool AnyBaking(List<string> ids)
1918
+ {
1919
+ var manager = Manager();
1920
+ var method = Method("IsSurfaceBaking", BindingFlags.Instance | BindingFlags.Public, new[] { SurfaceType });
1921
+ foreach (var surface in Resolve(ids)) if ((bool)method.Invoke(manager, new object[] { surface })) return true;
1922
+ return false;
1923
+ }
1924
+
1925
+ internal static double? Progress(SurfaceDispatchObservation observation)
1926
+ {
1927
+ if (observation == null || observation.Entries.Count == 0) return null;
1928
+ double total = 0d;
1929
+ foreach (var entry in observation.Entries)
1930
+ total += entry.Operation == null ? 0d : Math.Max(0d, Math.Min(1d, entry.Operation.progress));
1931
+ return total / observation.Entries.Count;
1932
+ }
1933
+
1934
+ /// <summary>仅供Start返回后的同一主线程调用栈rollback exact bakeData。</summary>
1935
+ internal static void Cancel(SurfaceDispatchObservation observation)
1936
+ {
1937
+ if (observation == null) return;
1938
+ foreach (var entry in observation.Entries)
1939
+ if (entry.Data != null && entry.Operation != null && !entry.Operation.isDone)
1940
+ UnityEngine.AI.NavMeshBuilder.Cancel(entry.Data);
1941
+ }
1942
+
1943
+ internal static int ActiveOperationCount() { return Available ? Operations().Count : 0; }
1944
+ internal static bool HasActiveData()
1945
+ {
1946
+ if (!Available) return false;
1947
+ var property = SurfaceType.GetProperty("navMeshData", BindingFlags.Instance | BindingFlags.Public);
1948
+ if (property == null) throw new BridgeCommandException("E_NAVMESH_SURFACE_API_UNAVAILABLE", "AI Navigation navMeshData API is unavailable.");
1949
+ foreach (var raw in Resources.FindObjectsOfTypeAll(SurfaceType))
1950
+ {
1951
+ var behaviour = raw as Behaviour;
1952
+ var component = raw as Component;
1953
+ if (behaviour == null || component == null || !behaviour.isActiveAndEnabled || EditorUtility.IsPersistent(component)) continue;
1954
+ if (property.GetValue(component, null) != null) return true;
1955
+ }
1956
+ return false;
1957
+ }
1958
+
1959
+ internal static bool TryProve(List<string> ids, List<Dictionary<string, object>> plan, bool requireChangedArtifact, out List<object> result)
1960
+ {
1961
+ result = new List<object>();
1962
+ var planned = plan.ToDictionary(value => Text(value, "globalId"), StringComparer.Ordinal);
1963
+ try
1964
+ {
1965
+ foreach (var surface in Resolve(ids))
1966
+ {
1967
+ var globalId = GlobalObjectId.GetGlobalObjectIdSlow(surface).ToString();
1968
+ Dictionary<string, object> expected;
1969
+ if (!planned.TryGetValue(globalId, out expected)) return false;
1970
+ var proof = DataProof((Component)surface);
1971
+ var beforeIdentity = Text(expected, "beforeDataIdentity");
1972
+ if (string.IsNullOrEmpty(Text(proof, "dataIdentity"))) return false;
1973
+ if (requireChangedArtifact && !string.IsNullOrEmpty(beforeIdentity)
1974
+ && string.Equals(Text(proof, "dataIdentity"), beforeIdentity, StringComparison.Ordinal)
1975
+ && string.Equals(Text(proof, "dataHash"), Text(expected, "beforeDataHash"), StringComparison.Ordinal)) return false;
1976
+ result.Add(proof);
1977
+ }
1978
+ return result.Count == ids.Count;
1979
+ }
1980
+ catch { result = new List<object>(); return false; }
1981
+ }
1982
+
1983
+ private static Object[] Resolve(List<string> ids)
1984
+ {
1985
+ EnsureAvailable();
1986
+ var result = new List<Object>();
1987
+ foreach (var id in ids)
1988
+ {
1989
+ GlobalObjectId parsed;
1990
+ if (!GlobalObjectId.TryParse(id, out parsed)) throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Stored NavMeshSurface identity is invalid; outcome is unknown.");
1991
+ var value = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(parsed);
1992
+ if (value == null || !SurfaceType.IsInstanceOfType(value)) throw new BridgeCommandException("E_EXECUTION_TIMEOUT", "Stored NavMeshSurface no longer resolves; outcome is unknown.");
1993
+ result.Add(value);
1994
+ }
1995
+ return result.ToArray();
1996
+ }
1997
+
1998
+ private static Dictionary<string, object> DataProof(Component surface)
1999
+ {
2000
+ var property = SurfaceType.GetProperty("navMeshData", BindingFlags.Instance | BindingFlags.Public);
2001
+ var data = property == null ? null : property.GetValue(surface, null) as Object;
2002
+ if (data == null) throw new InvalidOperationException("NavMeshSurface data is null.");
2003
+ long localId; var guid = string.Empty;
2004
+ if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(data, out guid, out localId) || string.IsNullOrEmpty(guid)) throw new InvalidOperationException("NavMeshSurface data identity is unavailable.");
2005
+ var path = AssetDatabase.GetAssetPath(data);
2006
+ if (string.IsNullOrEmpty(path) || !path.StartsWith("Assets/", StringComparison.Ordinal) || !path.EndsWith(".asset", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("NavMeshSurface data path is invalid.");
2007
+ var full = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Application.dataPath), path));
2008
+ if (!File.Exists(full)) throw new InvalidOperationException("NavMeshSurface data file is missing.");
2009
+ var bytes = File.ReadAllBytes(full);
2010
+ return new Dictionary<string, object>
2011
+ {
2012
+ { "globalId", GlobalObjectId.GetGlobalObjectIdSlow(surface).ToString() }, { "name", surface.gameObject.name }, { "scenePath", surface.gameObject.scene.path },
2013
+ { "sceneDirty", surface.gameObject.scene.isDirty }, { "dataPath", path }, { "dataGuid", guid },
2014
+ { "dataLocalId", localId.ToString(CultureInfo.InvariantCulture) }, { "dataIdentity", guid + ":" + localId.ToString(CultureInfo.InvariantCulture) },
2015
+ { "dataHash", NavMeshToolSupport.Sha256(bytes) }, { "dataSizeBytes", bytes.LongLength }
2016
+ };
2017
+ }
2018
+
2019
+ private static string DataIdentity(Component surface)
2020
+ {
2021
+ var property = SurfaceType.GetProperty("navMeshData", BindingFlags.Instance | BindingFlags.Public);
2022
+ var data = property == null ? null : property.GetValue(surface, null) as Object;
2023
+ long localId; string guid;
2024
+ return data != null && AssetDatabase.TryGetGUIDAndLocalFileIdentifier(data, out guid, out localId) && !string.IsNullOrEmpty(guid) ? guid + ":" + localId.ToString(CultureInfo.InvariantCulture) : string.Empty;
2025
+ }
2026
+
2027
+ private static object Manager()
2028
+ {
2029
+ EnsureAvailable();
2030
+ var property = ManagerType.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
2031
+ var value = property == null ? null : property.GetValue(null, null);
2032
+ if (value == null) throw new BridgeCommandException("E_NAVMESH_SURFACE_API_UNAVAILABLE", "AI Navigation NavMeshAssetManager.instance is unavailable.");
2033
+ return value;
2034
+ }
2035
+
2036
+ private static IList Operations()
2037
+ {
2038
+ EnsureAvailable();
2039
+ var method = ManagerType.GetMethod("GetBakeOperations", BindingFlags.Instance | BindingFlags.NonPublic);
2040
+ var value = method == null ? null : method.Invoke(Manager(), null) as IList;
2041
+ if (value == null) throw new BridgeCommandException("E_NAVMESH_SURFACE_API_UNAVAILABLE", "AI Navigation bake operation API is unavailable.", "Use a supported com.unity.ai.navigation version.");
2042
+ return value;
2043
+ }
2044
+
2045
+ private static void EnsureShape()
2046
+ {
2047
+ Method("StartBakingSurfaces", BindingFlags.Instance | BindingFlags.Public, new[] { typeof(Object[]) });
2048
+ Method("IsSurfaceBaking", BindingFlags.Instance | BindingFlags.Public, new[] { SurfaceType });
2049
+ Operations();
2050
+ }
2051
+ private static MethodInfo Method(string name, BindingFlags flags, Type[] parameters)
2052
+ {
2053
+ EnsureAvailable();
2054
+ var method = ManagerType.GetMethod(name, flags, null, parameters, null);
2055
+ if (method == null) throw new BridgeCommandException("E_NAVMESH_SURFACE_API_UNAVAILABLE", "AI Navigation API is unavailable: " + name + ".", "Use a supported com.unity.ai.navigation version.");
2056
+ return method;
2057
+ }
2058
+ private static object Field(object value, string name)
2059
+ {
2060
+ var field = value == null ? null : value.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
2061
+ if (field == null) throw new BridgeCommandException("E_NAVMESH_SURFACE_API_UNAVAILABLE", "AI Navigation bake operation field is unavailable: " + name + ".");
2062
+ return field.GetValue(value);
2063
+ }
2064
+ private static void EnsureAvailable()
2065
+ {
2066
+ if (!Available) throw new BridgeCommandException("E_PACKAGE_NOT_AVAILABLE", "navmesh.surface.bake requires com.unity.ai.navigation.", "Install com.unity.ai.navigation, then retry.");
2067
+ }
2068
+ }
2069
+ }
2070
+ }
2071
+ }
2072
+ #pragma warning restore 0618
2073
+ #endif