@vgai/editor 0.2.0 → 0.4.0-canary.20260715.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 (430) hide show
  1. package/README.md +24 -0
  2. package/dist/assets/axe-C7YBtOuM.js +30 -0
  3. package/dist/assets/browserAll-DDtGGE48.js +1 -0
  4. package/dist/assets/create-runtime-DbTlBlKG.js +1 -0
  5. package/dist/assets/{game-BI111o5f.js → game-BE0r7DMI.js} +3 -3
  6. package/dist/assets/game-BdTHR2_X.js +1 -0
  7. package/dist/assets/{game-pt_1lQLv.js → game-C3iQGSqo.js} +1 -1
  8. package/dist/assets/game-CcHfYxjP.js +1 -0
  9. package/dist/assets/game-Cqzj_lpD.js +1 -0
  10. package/dist/assets/game-DSZdeCHu.js +1 -0
  11. package/dist/assets/game-Dch9sAPc.js +1 -0
  12. package/dist/assets/{game-xsOho_wg.js → game-SAFn90pA.js} +3 -3
  13. package/dist/assets/game-loop-DMIgSMV-.js +1 -0
  14. package/dist/assets/{game-source-CvidN7AB.js → game-source-Dk83w26t.js} +1 -1
  15. package/dist/assets/{index-Bp5cecD6.js → index-4idIsHHJ.js} +2 -2
  16. package/dist/assets/{index-DL7RcQ0n.js → index-C-LhOTBN.js} +1 -1
  17. package/dist/assets/{index-Cs8xwv3e.js → index-CDkxceqE.js} +1 -1
  18. package/dist/assets/index-CQEfi217.js +15412 -0
  19. package/dist/assets/index-Dvi-V0NE.css +1 -0
  20. package/dist/assets/ingest-siblings-s0P5I1Ub.js +1 -0
  21. package/dist/assets/mount-manifest-DXMOlF79.js +1 -0
  22. package/dist/assets/react-18-C9eHn8x1.js +1 -0
  23. package/dist/assets/webworkerAll-Fn0e-ouF.js +1 -0
  24. package/dist/assets/world-documents-GM0a9Kv7.js +1 -0
  25. package/dist/index.html +15 -13
  26. package/dist/ingest/racing-game/vgai.game.json +5 -4
  27. package/dist/ingest/simcity/vgai.game.json +5 -4
  28. package/dist/ingest/tanks/vgai.game.json +5 -4
  29. package/dist/project-scripts.bundle.js +3917 -0
  30. package/dist/scenes/untitled.vscn.json +2 -0
  31. package/dist-server/packaged.mjs +7697 -3435
  32. package/package.json +31 -4
  33. package/scripts/probe-reporter.ts +349 -0
  34. package/scripts/run-probe.ts +2260 -0
  35. package/server/asset-catalog-audit.ts +268 -0
  36. package/server/asset-catalog-v2.ts +146 -0
  37. package/server/asset-library-routes.ts +276 -58
  38. package/server/canonical-path.ts +1 -1
  39. package/server/dev.ts +148 -44
  40. package/server/editor-server.ts +888 -61
  41. package/server/editor-sse.ts +115 -4
  42. package/server/harness-chat-service.ts +796 -0
  43. package/server/local-asset-catalog.ts +157 -22
  44. package/server/packaged.ts +51 -21
  45. package/server/process-shutdown.ts +136 -0
  46. package/server/prod.ts +9 -6
  47. package/server/project-hmr-files.ts +41 -0
  48. package/server/project-operations.ts +343 -0
  49. package/server/project-optimize-deps-entries.ts +4 -4
  50. package/server/project-output-writer.ts +156 -0
  51. package/server/project-validation.ts +140 -0
  52. package/server/project-view.ts +8 -50
  53. package/server/server-utils.ts +75 -10
  54. package/server/session-registry.ts +12 -0
  55. package/src/EditorContext.tsx +107 -3
  56. package/src/action-registry.ts +115 -17
  57. package/src/adapter-resolver.ts +213 -478
  58. package/src/asset-events.ts +64 -1
  59. package/src/asset-preview.ts +906 -0
  60. package/src/asset-selection.ts +121 -0
  61. package/src/asset-workflow/asset-capabilities.ts +259 -0
  62. package/src/asset-workflow/asset-editor-errors.ts +46 -0
  63. package/src/asset-workflow/asset-import-pipeline.ts +176 -0
  64. package/src/asset-workflow/asset-library-query.ts +107 -0
  65. package/src/asset-workflow/asset-types.ts +192 -0
  66. package/src/asset-workflow/asset-workflow-quality.ts +65 -0
  67. package/src/asset-workflow/import-contract.ts +113 -0
  68. package/src/asset-workflow/model-conversion.ts +126 -0
  69. package/src/asset-workflow/model-inspection.ts +185 -0
  70. package/src/asset-workflow/project-asset-commands.ts +23 -0
  71. package/src/asset-workflow/project-asset-health.ts +153 -0
  72. package/src/asset-workflow/project-asset-operations.ts +119 -0
  73. package/src/asset-workflow/thumbnail-system.ts +172 -0
  74. package/src/authoring/active-systems.ts +15 -0
  75. package/src/authoring/boundary-authoring-adapter.ts +16 -5
  76. package/src/authoring/composite-authoring-adapter.ts +305 -124
  77. package/src/authoring/design-time-layers.ts +300 -117
  78. package/src/authoring/edit-mode-authoring.ts +278 -293
  79. package/src/authoring/embed-only-adapter.ts +4 -2
  80. package/src/authoring/first-party-hierarchy-menu.ts +7 -2
  81. package/src/authoring/first-party-inspector/AddSectionMenu.tsx +5 -12
  82. package/src/authoring/first-party-inspector/EnvironmentSection.tsx +53 -126
  83. package/src/authoring/first-party-inspector/HeaderSection.tsx +54 -65
  84. package/src/authoring/first-party-inspector/is-first-party-selection.ts +30 -11
  85. package/src/authoring/first-party-inspector/register.tsx +13 -2
  86. package/src/authoring/ingest-authoring-adapter.ts +142 -45
  87. package/src/authoring/mount-failure-report.ts +5 -5
  88. package/src/authoring/provenance.ts +41 -0
  89. package/src/authoring/react-dom-authoring-adapter.ts +92 -92
  90. package/src/authoring/react-inspector-section.tsx +489 -51
  91. package/src/authoring/react-world-authoring-adapter.ts +541 -306
  92. package/src/authoring/story-overlay.ts +1 -1
  93. package/src/authoring/story-types.ts +11 -0
  94. package/src/authoring/vgai-scene-authoring-adapter.ts +54 -25
  95. package/src/authoring/viewport-tool-context.ts +78 -0
  96. package/src/authoring/world-canvas-viewport-state.ts +35 -0
  97. package/src/authoring/world-hidden-viewport.ts +186 -0
  98. package/src/authoring/world-pan-state.ts +33 -17
  99. package/src/authoring/world-session-state.ts +1 -1
  100. package/src/authoring/world2d-authoring-adapter.ts +109 -52
  101. package/src/availability-tick.ts +56 -0
  102. package/src/browser-play-entry.ts +5 -5
  103. package/src/browser-project-scripts.ts +1 -0
  104. package/src/build-identity.ts +15 -0
  105. package/src/command-listener.ts +893 -19
  106. package/src/components/AlignToolbar.tsx +74 -63
  107. package/src/components/AppRoot.tsx +193 -26
  108. package/src/components/ApplicationMenus.tsx +272 -0
  109. package/src/components/AssetBrowser.tsx +1450 -293
  110. package/src/components/AssetEditorShell.tsx +141 -0
  111. package/src/components/AssetImportDetails.tsx +218 -0
  112. package/src/components/AssetLibraryPanel.tsx +14 -0
  113. package/src/components/CameraInfo.tsx +11 -20
  114. package/src/components/CenterDocuments.tsx +585 -0
  115. package/src/components/CommandPalette.tsx +75 -30
  116. package/src/components/ConsolePanel.css +55 -0
  117. package/src/components/ConsolePanel.tsx +57 -112
  118. package/src/components/DebugConsolePanel.tsx +461 -0
  119. package/src/components/DebuggerPanel.tsx +165 -0
  120. package/src/components/DefaultEditorLayout.tsx +167 -231
  121. package/src/components/GameHierarchy.tsx +873 -290
  122. package/src/components/GamePerformanceOverlay.tsx +46 -0
  123. package/src/components/HarnessChatPanel.tsx +2352 -0
  124. package/src/components/HistoryPanel.tsx +226 -0
  125. package/src/components/Inspector.tsx +153 -99
  126. package/src/components/InspectorToolSection.tsx +6 -4
  127. package/src/components/MountFailureBanner.tsx +31 -48
  128. package/src/components/NetworkSection.tsx +25 -14
  129. package/src/components/OnlineAssetBrowser.tsx +794 -343
  130. package/src/components/OverlayReportBanner.tsx +38 -64
  131. package/src/components/PerformancePanel.tsx +206 -0
  132. package/src/components/PlayBar.tsx +93 -263
  133. package/src/components/PrefabSaveDialog.tsx +55 -127
  134. package/src/components/ProjectHeader.css +60 -0
  135. package/src/components/ProjectHeader.tsx +170 -105
  136. package/src/components/ProjectScreen.tsx +246 -184
  137. package/src/components/ReactCanvasControls.tsx +185 -0
  138. package/src/components/ResolutionPicker.tsx +85 -0
  139. package/src/components/RootActivateDialog.tsx +39 -68
  140. package/src/components/SaveDialog.tsx +55 -127
  141. package/src/components/SaveStatus.tsx +11 -29
  142. package/src/components/StartupLoadingScreen.tsx +96 -0
  143. package/src/components/StateWatchPanel.tsx +302 -0
  144. package/src/components/StatsOverlay.tsx +12 -5
  145. package/src/components/StatusBar.tsx +64 -0
  146. package/src/components/ThemeManagerDialog.css +89 -0
  147. package/src/components/ThemeManagerDialog.tsx +371 -0
  148. package/src/components/ToolHost.tsx +26 -15
  149. package/src/components/Toolbar.tsx +117 -214
  150. package/src/components/VgaiVoxelLogo.tsx +89 -0
  151. package/src/components/ViewportOverlay.tsx +97 -172
  152. package/src/components/ViewportPanel.tsx +130 -31
  153. package/src/components/WorkspaceDock.tsx +891 -0
  154. package/src/components/WorldSelectionOverlay.tsx +51 -63
  155. package/src/components/WorldTextEditor.tsx +9 -5
  156. package/src/components/asset-documents.tsx +390 -0
  157. package/src/components/asset-editor-persistence.ts +44 -0
  158. package/src/components/asset-selection-section.tsx +338 -0
  159. package/src/components/asset-viewers/AudioViewer.tsx +73 -21
  160. package/src/components/asset-viewers/GenericJsonViewer.tsx +26 -11
  161. package/src/components/asset-viewers/ImageViewer.tsx +43 -11
  162. package/src/components/asset-viewers/InputMapViewer.tsx +512 -134
  163. package/src/components/asset-viewers/MaterialAssetEditor.tsx +540 -0
  164. package/src/components/asset-viewers/ModelViewer.tsx +805 -121
  165. package/src/components/asset-viewers/OnlineAssetDetail.tsx +177 -60
  166. package/src/components/asset-viewers/SourceAssetViewer.tsx +121 -0
  167. package/src/components/asset-workflow.css +506 -0
  168. package/src/components/build-documents.tsx +696 -0
  169. package/src/components/core-utilities.tsx +75 -0
  170. package/src/components/data-documents.tsx +364 -0
  171. package/src/components/{DataPanel.tsx → data-editor.tsx} +138 -288
  172. package/src/components/debugger-panel-state.ts +57 -0
  173. package/src/components/inspector-widgets/AlignmentGrid.tsx +8 -14
  174. package/src/components/inspector-widgets/BorderEditor.tsx +41 -33
  175. package/src/components/inspector-widgets/ColorPicker.tsx +39 -39
  176. package/src/components/inspector-widgets/FontPicker.tsx +13 -21
  177. package/src/components/inspector-widgets/GradientEditor.tsx +11 -19
  178. package/src/components/inspector-widgets/ScrubbableInput.tsx +6 -3
  179. package/src/components/inspector-widgets/ShadowEditor.tsx +20 -33
  180. package/src/components/inspector-widgets/shared.tsx +90 -49
  181. package/src/components/inspectors/AnimationSection.tsx +39 -40
  182. package/src/components/inspectors/AudioSection.tsx +28 -39
  183. package/src/components/inspectors/BoneAttachmentSection.tsx +19 -24
  184. package/src/components/inspectors/CameraSection.tsx +14 -23
  185. package/src/components/inspectors/ComponentsSection.tsx +39 -43
  186. package/src/components/inspectors/JointsSection.tsx +34 -43
  187. package/src/components/inspectors/LightSection.tsx +90 -35
  188. package/src/components/inspectors/MaterialSection.tsx +137 -174
  189. package/src/components/inspectors/MeshSection.tsx +9 -17
  190. package/src/components/inspectors/NavigationSection.tsx +8 -15
  191. package/src/components/inspectors/ParticleSection.tsx +91 -76
  192. package/src/components/inspectors/PhysicsSection.tsx +42 -46
  193. package/src/components/inspectors/PostProcessingSection.tsx +28 -36
  194. package/src/components/inspectors/RenderingSection.tsx +12 -20
  195. package/src/components/inspectors/ShadowSection.tsx +7 -8
  196. package/src/components/inspectors/SplineSection.tsx +31 -42
  197. package/src/components/inspectors/TransformSection.tsx +14 -22
  198. package/src/components/patterns/Dialog.tsx +95 -0
  199. package/src/components/patterns/Fields.tsx +44 -0
  200. package/src/components/patterns/List.tsx +25 -0
  201. package/src/components/patterns/StateSurface.tsx +40 -0
  202. package/src/components/patterns/Surfaces.tsx +115 -0
  203. package/src/components/patterns/Tabs.tsx +80 -0
  204. package/src/components/patterns/Toolbar.tsx +51 -0
  205. package/src/components/patterns/Tree.tsx +72 -0
  206. package/src/components/primitives/Button.tsx +46 -0
  207. package/src/components/primitives/ColorInput.tsx +11 -47
  208. package/src/components/primitives/EditorIcon.tsx +99 -0
  209. package/src/components/primitives/FormControls.tsx +71 -0
  210. package/src/components/primitives/JsonInput.tsx +15 -2
  211. package/src/components/primitives/Layout.tsx +100 -0
  212. package/src/components/primitives/Menu.tsx +105 -0
  213. package/src/components/primitives/NumberInput.tsx +8 -35
  214. package/src/components/primitives/Panel.tsx +49 -44
  215. package/src/components/primitives/SectionHeader.tsx +10 -28
  216. package/src/components/primitives/Text.tsx +46 -0
  217. package/src/components/primitives/Tooltip.tsx +20 -12
  218. package/src/components/primitives/Vec3Input.tsx +3 -0
  219. package/src/components/primitives/banner-tones.ts +33 -0
  220. package/src/components/primitives/editor-icons.ts +102 -0
  221. package/src/components/primitives/panel-header-styles.ts +30 -0
  222. package/src/components/project-operation-documents.tsx +226 -0
  223. package/src/components/status-contributions.tsx +175 -0
  224. package/src/components/story-documents-register.ts +15 -0
  225. package/src/components/story-documents.tsx +1185 -0
  226. package/src/components/tool-documents.tsx +110 -0
  227. package/src/components/workspace-dock.css +293 -0
  228. package/src/components/workspace-history.ts +33 -0
  229. package/src/components/world-documents.tsx +145 -0
  230. package/src/components/xstate-documents.tsx +209 -0
  231. package/src/composite-screenshot.ts +248 -0
  232. package/src/design-system/index.ts +70 -0
  233. package/src/design-system-stories/Foundation.stories.tsx +183 -0
  234. package/src/design-system-stories/Patterns.stories.tsx +251 -0
  235. package/src/design-system-stories/Workspace.stories.tsx +538 -0
  236. package/src/doctor/report.ts +57 -13
  237. package/src/editor-api.ts +295 -31
  238. package/src/editor-compatibility.ts +200 -0
  239. package/src/editor-console.ts +35 -17
  240. package/src/editor-hotkeys.ts +85 -33
  241. package/src/editor-store.ts +538 -233
  242. package/src/editor-viewport.ts +160 -34
  243. package/src/entity-factory.ts +14 -3
  244. package/src/gizmo-registry.ts +21 -5
  245. package/src/harness-chat-editor-context.ts +37 -0
  246. package/src/harness-chat-indicator.ts +68 -0
  247. package/src/harness-chat-layout.ts +53 -0
  248. package/src/harness-chat-lifecycle.ts +170 -0
  249. package/src/harness-chat-reconnect.ts +85 -0
  250. package/src/harness-chat-types.ts +409 -0
  251. package/src/hierarchy-drop.ts +50 -0
  252. package/src/hierarchy-expansion-state.ts +100 -0
  253. package/src/hierarchy-menu-registry.ts +1 -1
  254. package/src/hierarchy-projection.ts +76 -0
  255. package/src/history/editor-session.ts +21 -0
  256. package/src/history/history-commands.ts +147 -0
  257. package/src/history/history-service.ts +904 -0
  258. package/src/history/json-history-resource.ts +165 -0
  259. package/src/history/persistence-coordinator.ts +35 -0
  260. package/src/history/project-file-history.ts +266 -0
  261. package/src/history/project-root-history-backends.ts +53 -0
  262. package/src/history/resource-registry.ts +209 -0
  263. package/src/history/snapshot-store.ts +108 -0
  264. package/src/history/source-history-backend.ts +374 -0
  265. package/src/history/types.ts +100 -0
  266. package/src/hmr-registration-group.ts +67 -0
  267. package/src/hmr-stable-react-context.ts +23 -0
  268. package/src/hosted-example.ts +12 -2
  269. package/src/hotkeys.ts +89 -18
  270. package/src/ingest/discovery-react.ts +9 -10
  271. package/src/ingest/discovery.ts +4 -4
  272. package/src/ingest/discovery2d.ts +5 -5
  273. package/src/ingest/games/bloom-composer/vgai.game.json +3 -3
  274. package/src/ingest/games/embed-bundle/vgai.game.json +3 -3
  275. package/src/ingest/games/games-fps/vgai.game.json +3 -3
  276. package/src/ingest/games/iframe-reachable/vgai.game.json +3 -3
  277. package/src/ingest/games/interactive-cubes/vgai.game.json +3 -3
  278. package/src/ingest/games/mismatched-three/vgai.game.json +3 -3
  279. package/src/ingest/games/own-three-bundled/vgai.game.json +3 -3
  280. package/src/ingest/games/pointerlock-maze/vgai.game.json +7 -4
  281. package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +3 -3
  282. package/src/ingest/games-2d/bunnymark/vgai.game.json +3 -3
  283. package/src/ingest/games-2d/flappy/vgai.game.json +3 -3
  284. package/src/ingest/games-2d/flappy-pixi/vgai.game.json +3 -3
  285. package/src/ingest/games-2d/puzzling-potions/vgai.game.json +3 -3
  286. package/src/ingest/games-2d/tilemap-camera/vgai.game.json +3 -3
  287. package/src/ingest/games-react/react-rpg/vgai.game.json +3 -3
  288. package/src/ingest/types.ts +1 -1
  289. package/src/ingest-mode.ts +47 -52
  290. package/src/ingest-siblings.ts +23 -33
  291. package/src/initial-scene.ts +1 -2
  292. package/src/main.tsx +16 -8
  293. package/src/manifest-project.ts +10 -12
  294. package/src/model-thumbnail.ts +261 -26
  295. package/src/module-mode.ts +44 -50
  296. package/src/play-log-events.ts +26 -0
  297. package/src/play-mode.ts +495 -401
  298. package/src/play-scene-adoption.ts +23 -0
  299. package/src/playground/main.ts +68 -24
  300. package/src/playwright-shim.ts +473 -0
  301. package/src/project-manager.ts +90 -10
  302. package/src/project-operations.ts +112 -0
  303. package/src/project-story-discovery.ts +25 -0
  304. package/src/project-tool-discovery.ts +43 -0
  305. package/src/scene-serializer.ts +0 -6
  306. package/src/scene-sync.ts +7 -2
  307. package/src/script-loader.ts +3 -3
  308. package/src/skeleton-helper.ts +57 -0
  309. package/src/storage/seed.ts +68 -8
  310. package/src/stories/StoryPreviewMount.tsx +265 -0
  311. package/src/stories/action-stories.fixture.tsx +44 -0
  312. package/src/stories/button-stories.fixture.tsx +50 -0
  313. package/src/stories/compose-project-stories.ts +182 -0
  314. package/src/stories/loader-stories.fixture.tsx +53 -0
  315. package/src/stories/story-discovery.ts +130 -0
  316. package/src/stories/story-registry.ts +146 -0
  317. package/src/theme-css.ts +1049 -0
  318. package/src/theme-library.ts +238 -0
  319. package/src/theme-preference.ts +130 -0
  320. package/src/theme.ts +527 -0
  321. package/src/tool-loader.ts +80 -24
  322. package/src/ui-source/inspect.ts +27 -4
  323. package/src/ui-source/source-write-backend.ts +83 -22
  324. package/src/widgets/index.ts +9 -18
  325. package/src/workspace-aux-commands.ts +11 -0
  326. package/src/workspace-dock-controller.ts +64 -0
  327. package/src/workspace-document-ids.ts +5 -0
  328. package/src/workspace-document-registry.ts +367 -0
  329. package/src/workspace-state-persistence.ts +455 -0
  330. package/src/workspace-status-registry.ts +110 -0
  331. package/src/workspace-utility-commands.ts +49 -0
  332. package/src/workspace-utility-registry.ts +161 -0
  333. package/src/world-document-routing.ts +16 -0
  334. package/src/xstate-inspect/XStateMachineInspector.tsx +544 -0
  335. package/src/xstate-inspect/character-animation-machine.fixture.ts +74 -0
  336. package/src/xstate-inspect/use-live-actor-state.ts +39 -0
  337. package/src/xstate-inspect/xstate-graph.ts +265 -0
  338. package/src/xstate-inspect/xstate-layout.ts +76 -0
  339. package/src/z-index.ts +26 -0
  340. package/template/.claude/skills/editor/SKILL.md +129 -90
  341. package/template/AGENTS.md +1110 -0
  342. package/template/CLAUDE.md +1 -512
  343. package/template/IDIOMS.md +50 -0
  344. package/template/check-idioms.ts +706 -0
  345. package/template/manifest-entry-modules-plugin.ts +53 -0
  346. package/template/package.json +13 -4
  347. package/template/public/scenes/default.scn2d.json +6 -0
  348. package/template/public/scenes/default.vscn.json +7 -1
  349. package/template/public/scenes/untitled.vscn.json +10 -0
  350. package/template/scripts/playtest.ts +40 -0
  351. package/template/server/loopback.ts +75 -0
  352. package/template/server/rooms/arena-room.ts +69 -1
  353. package/template/server/rooms/game-room.ts +68 -1
  354. package/template/src/data/README.md +9 -4
  355. package/template/src/data/data-ref.ts +35 -0
  356. package/template/src/data/tuning.schema.ts +1 -1
  357. package/template/src/main.ts +41 -62
  358. package/template/src/manifest-entry-modules.d.ts +3 -0
  359. package/template/src/operations/README.md +88 -0
  360. package/template/src/runtime/pixi-adapter.ts +24 -0
  361. package/template/src/scripts/components/data-spinner.ts +43 -2
  362. package/template/src/scripts/components/spin-lap-math.ts +27 -0
  363. package/template/src/tools/README.md +5 -3
  364. package/template/src/tools/example.tool.tsx +13 -7
  365. package/template/src/ui/Button.stories.tsx +66 -0
  366. package/template/src/ui/RebindPanel.tsx +2 -6
  367. package/template/src/ui/game.tsx +17 -0
  368. package/template/tests/acceptance/example.spec.ts +64 -0
  369. package/template/tests/logic/example.test.ts +31 -0
  370. package/template/tests/logic/room.test.ts +100 -0
  371. package/template/tests/tsconfig.json +30 -0
  372. package/template/validate-manifest.ts +5 -5
  373. package/template/validate-scenes.ts +50 -18
  374. package/template/vgai.game.json +20 -13
  375. package/template/vite.config.ts +77 -16
  376. package/vite-plugin-ui-oid.ts +261 -17
  377. package/dist/assets/CCDIKSolver-Dz-ysxZF.js +0 -1
  378. package/dist/assets/SelectionBox-BXsv_6aH.js +0 -1
  379. package/dist/assets/browser-project-scripts-DOru4L1x.js +0 -2
  380. package/dist/assets/browserAll-CEkWYlFk.js +0 -1
  381. package/dist/assets/create-runtime-DYIWr3va.js +0 -1
  382. package/dist/assets/data-asset-Bb9sIM7i.js +0 -5
  383. package/dist/assets/game-8SDZPPgH.js +0 -1
  384. package/dist/assets/game-CevorxyV.js +0 -1
  385. package/dist/assets/game-Di7XlywD.js +0 -1
  386. package/dist/assets/game-DjnCaw8R.js +0 -1
  387. package/dist/assets/game-Q9A0aXSV.js +0 -1
  388. package/dist/assets/index-CPVV6pjw.js +0 -1
  389. package/dist/assets/index-CpOnYyeD.js +0 -213
  390. package/dist/assets/index-Dr8H_6Rl.js +0 -1
  391. package/dist/assets/index-_bCaipUk.js +0 -13650
  392. package/dist/assets/ingest-siblings-Dz8eEqrV.js +0 -1
  393. package/dist/assets/mount-manifest-Bhk0ohsl.js +0 -1
  394. package/dist/assets/scene-query-BivAHhJu.js +0 -1
  395. package/dist/assets/webworkerAll-BLrH0F6F.js +0 -1
  396. package/dist/assets/xstate-animation-binding-1NGLmw6y.js +0 -1
  397. package/dist/assets/xstate.esm-BgUD33nl.js +0 -3
  398. package/src/authoring/scene-ui-layer.ts +0 -485
  399. package/src/authoring/ui-authoring-adapter.ts +0 -325
  400. package/src/authoring/ui-hierarchy-menu.ts +0 -40
  401. package/src/authoring/ui-inspector-sections.tsx +0 -156
  402. package/src/authoring/undo-timeline.ts +0 -145
  403. package/src/components/BottomPanel.tsx +0 -326
  404. package/src/components/BuildPanel.tsx +0 -455
  405. package/src/components/FolderTree.tsx +0 -179
  406. package/src/components/RightPanel.tsx +0 -166
  407. package/src/components/WorldContextMenu.tsx +0 -257
  408. package/src/components/asset-viewers/AssetViewer.tsx +0 -38
  409. package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +0 -129
  410. package/src/scene-ui-mode/active-scene-ui.ts +0 -58
  411. package/src/scene-ui-mode/scene-ui-palette-panel.tsx +0 -152
  412. package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +0 -29
  413. package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +0 -74
  414. package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +0 -49
  415. package/src/scene-ui-mode/ui-prefab-io.ts +0 -76
  416. package/src/ui-editor/inspector.tsx +0 -563
  417. package/src/ui-editor/node-ops.ts +0 -266
  418. package/src/ui-editor/overlay.tsx +0 -380
  419. package/src/ui-editor/palette.tsx +0 -114
  420. package/src/ui-editor/react-store.ts +0 -78
  421. package/src/ui-editor/timeline-panel.tsx +0 -443
  422. package/src/ui-editor/ui-edit-store.ts +0 -552
  423. package/src/ui-editor/ui-prefab.ts +0 -95
  424. package/template/project.json +0 -9
  425. package/template/src/scripts/main.ts +0 -94
  426. package/template/src/ui/DefaultHud.tsx +0 -40
  427. package/template/src/ui/game-state.tsx +0 -37
  428. package/template/src/ui/mount-default-hud.tsx +0 -19
  429. package/template/src/ui/mount-ui.ts +0 -15
  430. package/vite-plugin-react-world-provider.ts +0 -81
@@ -0,0 +1,170 @@
1
+ import type { HarnessChatSession, HarnessChatSnapshot } from './harness-chat-types';
2
+
3
+ export type HarnessChatLifecycleKind =
4
+ | 'idle'
5
+ | 'offline'
6
+ | 'mirror'
7
+ | 'mirror-reconnecting'
8
+ | 'live-terminal'
9
+ | 'editor'
10
+ | 'resumed'
11
+ | 'branched';
12
+
13
+ export interface HarnessChatLifecycle {
14
+ kind: HarnessChatLifecycleKind;
15
+ label: string;
16
+ detail: string;
17
+ canCompose: boolean;
18
+ controlAction: 'detach' | 'openTerminal' | null;
19
+ controlActionLabel: string | null;
20
+ }
21
+
22
+ /**
23
+ * One presentation vocabulary over Supercode's authoritative connection
24
+ * state. This is deliberately derived: VGAI does not own a second session
25
+ * state machine and cannot drift from the runtime semantics.
26
+ */
27
+ export function harnessChatLifecycle(snapshot: HarnessChatSnapshot): HarnessChatLifecycle {
28
+ if (snapshot.status === 'unavailable' || snapshot.status === 'error') {
29
+ return {
30
+ kind: 'offline',
31
+ label: 'OFFLINE',
32
+ detail: 'The local coding runtime is unavailable.',
33
+ canCompose: false,
34
+ controlAction: null,
35
+ controlActionLabel: null,
36
+ };
37
+ }
38
+ if (snapshot.mode === 'observe') {
39
+ const retrying = snapshot.connection.follow === 'retrying';
40
+ return {
41
+ kind: retrying ? 'mirror-reconnecting' : 'mirror',
42
+ label: retrying ? 'MIRROR · RECONNECTING' : 'MIRROR · READ ONLY',
43
+ detail: retrying
44
+ ? 'The saved transcript is temporarily unavailable; Supercode is retrying.'
45
+ : 'Following the saved transcript without controlling its process.',
46
+ canCompose: false,
47
+ controlAction: null,
48
+ controlActionLabel: null,
49
+ };
50
+ }
51
+ if (snapshot.mode !== 'control') {
52
+ return {
53
+ kind: 'idle',
54
+ label: 'READY',
55
+ detail: 'Start a new chat or open an existing session.',
56
+ canCompose: true,
57
+ controlAction: null,
58
+ controlActionLabel: null,
59
+ };
60
+ }
61
+ switch (snapshot.connection.strategy) {
62
+ case 'attach':
63
+ return {
64
+ kind: 'live-terminal',
65
+ label: 'LIVE · TERMINAL',
66
+ detail: 'The editor and another frontend share one live runtime.',
67
+ canCompose: snapshot.availableActions.send || snapshot.turn.state !== 'idle',
68
+ controlAction: 'detach',
69
+ controlActionLabel: 'Disconnect live',
70
+ };
71
+ case 'resume':
72
+ return {
73
+ kind: 'resumed',
74
+ label: 'RESUMED HERE',
75
+ detail: 'The editor controls a continuation of the saved conversation.',
76
+ canCompose: snapshot.availableActions.send || snapshot.turn.state !== 'idle',
77
+ controlAction: 'openTerminal',
78
+ controlActionLabel: 'Open in terminal',
79
+ };
80
+ case 'branch':
81
+ return {
82
+ kind: 'branched',
83
+ label: 'BRANCHED',
84
+ detail: 'This is an independent continuation of the source conversation.',
85
+ canCompose: snapshot.availableActions.send || snapshot.turn.state !== 'idle',
86
+ controlAction: 'openTerminal',
87
+ controlActionLabel: 'Open in terminal',
88
+ };
89
+ default:
90
+ return {
91
+ kind: 'editor',
92
+ label: 'EDITOR · LIVE',
93
+ detail: 'This live runtime was started and is controlled by the editor.',
94
+ canCompose: snapshot.availableActions.send || snapshot.turn.state !== 'idle',
95
+ controlAction: 'openTerminal',
96
+ controlActionLabel: 'Open in terminal',
97
+ };
98
+ }
99
+ }
100
+
101
+ export interface HarnessChatReconnectPreference {
102
+ workspace: string;
103
+ sessionIdentity: string;
104
+ connection: 'observe' | 'attach';
105
+ }
106
+
107
+ // v2 stores Supercode's opaque identity. Do not read the former v1 values:
108
+ // they contained reversible locator fingerprints from the pre-v3 wire contract.
109
+ const RECONNECT_KEY = 'vgai:harness-chat:reconnect:v2';
110
+
111
+ function preferenceRecord(value: unknown): Record<string, HarnessChatReconnectPreference> {
112
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
113
+ const result: Record<string, HarnessChatReconnectPreference> = {};
114
+ for (const [workspace, candidate] of Object.entries(value)) {
115
+ if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) continue;
116
+ const item = candidate as Partial<HarnessChatReconnectPreference>;
117
+ if (
118
+ item.workspace === workspace &&
119
+ typeof item.sessionIdentity === 'string' &&
120
+ /^scs_[a-f0-9]{64}$/.test(item.sessionIdentity) &&
121
+ (item.connection === 'observe' || item.connection === 'attach')
122
+ ) {
123
+ result[workspace] = item as HarnessChatReconnectPreference;
124
+ }
125
+ }
126
+ return result;
127
+ }
128
+
129
+ export function readHarnessChatReconnectPreference(
130
+ storage: Pick<Storage, 'getItem'>,
131
+ workspace: string,
132
+ ): HarnessChatReconnectPreference | null {
133
+ try {
134
+ const raw = storage.getItem(RECONNECT_KEY);
135
+ if (!raw) return null;
136
+ return preferenceRecord(JSON.parse(raw))[workspace] ?? null;
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+
142
+ export function writeHarnessChatReconnectPreference(
143
+ storage: Pick<Storage, 'getItem' | 'setItem'>,
144
+ preference: HarnessChatReconnectPreference,
145
+ ): void {
146
+ try {
147
+ const raw = storage.getItem(RECONNECT_KEY);
148
+ const current = raw ? preferenceRecord(JSON.parse(raw)) : {};
149
+ current[preference.workspace] = preference;
150
+ storage.setItem(RECONNECT_KEY, JSON.stringify(current));
151
+ } catch {
152
+ // Browser persistence is an enhancement. A denied/quota-limited storage
153
+ // backend must not disable the live chat itself.
154
+ }
155
+ }
156
+
157
+ export function reconnectPreferenceForSnapshot(
158
+ snapshot: HarnessChatSnapshot,
159
+ session: HarnessChatSession | null,
160
+ ): HarnessChatReconnectPreference | null {
161
+ if (!snapshot.workspace || !session || snapshot.activeSessionId !== session.id) return null;
162
+ return {
163
+ workspace: snapshot.workspace,
164
+ sessionIdentity: session.identity,
165
+ connection:
166
+ snapshot.mode === 'control' && snapshot.connection.strategy === 'attach'
167
+ ? 'attach'
168
+ : 'observe',
169
+ };
170
+ }
@@ -0,0 +1,85 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import {
3
+ type HarnessChatReconnectPreference,
4
+ readHarnessChatReconnectPreference,
5
+ reconnectPreferenceForSnapshot,
6
+ writeHarnessChatReconnectPreference,
7
+ } from './harness-chat-lifecycle';
8
+ import type {
9
+ HarnessChatAction,
10
+ HarnessChatSession,
11
+ HarnessChatSnapshot,
12
+ } from './harness-chat-types';
13
+
14
+ interface ReconnectTracker {
15
+ generation: number;
16
+ preference: HarnessChatReconnectPreference | null;
17
+ attempted: boolean;
18
+ }
19
+
20
+ type RunAction = (action: HarnessChatAction) => Promise<HarnessChatSnapshot | null>;
21
+
22
+ function readyForReconnect(snapshot: HarnessChatSnapshot): snapshot is HarnessChatSnapshot & {
23
+ workspace: string;
24
+ } {
25
+ return (
26
+ Boolean(snapshot.workspace) &&
27
+ (snapshot.status === 'ready' || snapshot.status === 'working') &&
28
+ snapshot.operation === null
29
+ );
30
+ }
31
+
32
+ function resetTracker(
33
+ tracker: ReconnectTracker,
34
+ snapshot: HarnessChatSnapshot & { workspace: string },
35
+ ): void {
36
+ tracker.generation = snapshot.workspaceGeneration;
37
+ tracker.preference = readHarnessChatReconnectPreference(localStorage, snapshot.workspace);
38
+ tracker.attempted = false;
39
+ }
40
+
41
+ function synchronizeReconnect(
42
+ tracker: ReconnectTracker,
43
+ snapshot: HarnessChatSnapshot,
44
+ session: HarnessChatSession | null,
45
+ actionInFlight: boolean,
46
+ runAction: RunAction,
47
+ ): void {
48
+ if (!readyForReconnect(snapshot)) return;
49
+ if (tracker.generation !== snapshot.workspaceGeneration) resetTracker(tracker, snapshot);
50
+
51
+ if (!tracker.attempted && tracker.preference) {
52
+ if (!actionInFlight) {
53
+ tracker.attempted = true;
54
+ void runAction({
55
+ type: 'restore',
56
+ sessionIdentity: tracker.preference.sessionIdentity,
57
+ connection: tracker.preference.connection,
58
+ });
59
+ }
60
+ return;
61
+ }
62
+ if (actionInFlight) return;
63
+
64
+ const preference = reconnectPreferenceForSnapshot(snapshot, session);
65
+ if (preference) writeHarnessChatReconnectPreference(localStorage, preference);
66
+ }
67
+
68
+ /** Restore one exact session after browser/server reload, then remember later
69
+ * explicit selection changes. Runtime ownership remains in Supercode. */
70
+ export function useHarnessChatReconnect(
71
+ snapshot: HarnessChatSnapshot,
72
+ session: HarnessChatSession | null,
73
+ actionInFlight: boolean,
74
+ runAction: RunAction,
75
+ ): void {
76
+ const tracker = useRef<ReconnectTracker>({
77
+ generation: -1,
78
+ preference: null,
79
+ attempted: false,
80
+ });
81
+ useEffect(
82
+ () => synchronizeReconnect(tracker.current, snapshot, session, actionInFlight, runAction),
83
+ [actionInFlight, runAction, session, snapshot],
84
+ );
85
+ }
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Browser/server contract for the permanent coding-agent surface.
3
+ *
4
+ * Supercode owns harness, session, transcript, and runtime semantics. This
5
+ * wire mirrors its frontend state instead of inventing a second state
6
+ * machine; VGAI adds only editor-specific presentation and context.
7
+ */
8
+
9
+ export type JsonValue =
10
+ | string
11
+ | number
12
+ | boolean
13
+ | null
14
+ | JsonValue[]
15
+ | { [key: string]: JsonValue };
16
+
17
+ export type HarnessChatMode = 'none' | 'observe' | 'control';
18
+ export type HarnessChatStatus = 'loading' | 'ready' | 'working' | 'error' | 'unavailable';
19
+ /** Mirrors Supercode's passive/handshake authentication posture exactly. */
20
+ export type HarnessChatAuthState = 'ready' | 'configured' | 'required' | 'unknown';
21
+ export type HarnessChatRuntimeState = 'ready' | 'degraded' | 'unavailable';
22
+ export type HarnessChatOperation =
23
+ | 'refresh'
24
+ | 'observe'
25
+ | 'start'
26
+ | 'resume'
27
+ | 'attach'
28
+ | 'branch'
29
+ | 'restore'
30
+ | 'detach'
31
+ | 'openTerminal'
32
+ | 'send'
33
+ | 'interrupt'
34
+ | 'respond'
35
+ | 'load'
36
+ | 'import'
37
+ | 'export'
38
+ | 'translate'
39
+ | 'handoff'
40
+ | 'workspace'
41
+ | 'close'
42
+ | null;
43
+
44
+ export interface HarnessChatCapabilities {
45
+ startSession: boolean;
46
+ resumeSession: boolean;
47
+ attachExistingProcess: boolean;
48
+ sendInput: boolean;
49
+ streamEvents: boolean;
50
+ interrupt: boolean;
51
+ respondToRequests: boolean;
52
+ }
53
+
54
+ export interface HarnessChatHarness {
55
+ id: string;
56
+ label: string;
57
+ installed: boolean;
58
+ auth: HarnessChatAuthState;
59
+ runtime: HarnessChatRuntimeState;
60
+ protocol: string;
61
+ capabilities: HarnessChatCapabilities;
62
+ availableActions: {
63
+ start: boolean;
64
+ resume: boolean;
65
+ attach: boolean;
66
+ send: boolean;
67
+ interrupt: boolean;
68
+ respond: boolean;
69
+ };
70
+ reason: string | null;
71
+ repair: string | null;
72
+ }
73
+
74
+ export interface HarnessChatSession {
75
+ /** Opaque, controller-scoped key used by actions. */
76
+ id: string;
77
+ /** Stable, non-display identity used only to restore this exact selection. */
78
+ identity: string;
79
+ /** Harness-native identity, display/diagnostics only. */
80
+ nativeId?: string;
81
+ harness: string;
82
+ title: string;
83
+ cwd: string | null;
84
+ updatedAt: number | null;
85
+ messageCount: number | null;
86
+ model: string | null;
87
+ storage: 'file' | 'sqlite';
88
+ /** The trusted host has a reachable endpoint receipt for this session. */
89
+ attachable: boolean;
90
+ }
91
+
92
+ export interface HarnessChatMessageEntry {
93
+ id: string;
94
+ kind: 'message';
95
+ role: 'user' | 'assistant' | 'system';
96
+ text: string;
97
+ content?: JsonValue;
98
+ metadata: Record<string, string>;
99
+ visibility: 'conversation' | 'context';
100
+ }
101
+
102
+ export interface HarnessChatToolEntry {
103
+ id: string;
104
+ kind: 'tool';
105
+ callId: string | null;
106
+ name: string | null;
107
+ arguments: string | null;
108
+ resultText: string;
109
+ resultContent?: JsonValue;
110
+ status: 'pending' | 'completed' | 'error';
111
+ metadata: Record<string, string>;
112
+ }
113
+
114
+ export interface HarnessChatReasoningEntry {
115
+ id: string;
116
+ kind: 'reasoning';
117
+ text: string;
118
+ streaming: boolean;
119
+ }
120
+
121
+ export interface HarnessChatRequestOption {
122
+ optionId: string;
123
+ name: string;
124
+ kind: 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always' | 'other';
125
+ }
126
+
127
+ export interface HarnessChatRequestEntry {
128
+ id: string;
129
+ kind: 'request';
130
+ requestId: JsonValue;
131
+ requestKind: string;
132
+ payload: JsonValue;
133
+ options: HarnessChatRequestOption[];
134
+ cancellable: boolean;
135
+ status: 'pending' | 'responded';
136
+ resolution: { optionId: string | null; name: string; kind: string } | null;
137
+ }
138
+
139
+ export interface HarnessChatNoticeEntry {
140
+ id: string;
141
+ kind: 'notice';
142
+ code: string;
143
+ text: string;
144
+ }
145
+
146
+ export type HarnessChatConversationEntry =
147
+ | HarnessChatMessageEntry
148
+ | HarnessChatToolEntry
149
+ | HarnessChatReasoningEntry
150
+ | HarnessChatRequestEntry
151
+ | HarnessChatNoticeEntry;
152
+
153
+ export interface HarnessChatAvailableActions {
154
+ refresh: boolean;
155
+ observe: boolean;
156
+ start: boolean;
157
+ resume: boolean;
158
+ attach: boolean;
159
+ branch: boolean;
160
+ detach: boolean;
161
+ openTerminal: boolean;
162
+ send: boolean;
163
+ interrupt: boolean;
164
+ respond: boolean;
165
+ }
166
+
167
+ export interface HarnessChatPromptContext {
168
+ id: string;
169
+ label: string;
170
+ detail: string;
171
+ }
172
+
173
+ export interface HarnessChatError {
174
+ code: string;
175
+ message: string;
176
+ operation: Exclude<HarnessChatOperation, null> | 'runtime' | 'follow';
177
+ recoverable: boolean;
178
+ }
179
+
180
+ export interface HarnessChatSnapshot {
181
+ schema: 'vgai.harness-chat.v3';
182
+ revision: number;
183
+ /** Monotonic host generation. Revisions are only comparable within it. */
184
+ workspaceGeneration: number;
185
+ workspace: string | null;
186
+ status: HarnessChatStatus;
187
+ mode: HarnessChatMode;
188
+ operation: HarnessChatOperation;
189
+ harnesses: HarnessChatHarness[];
190
+ sessions: HarnessChatSession[];
191
+ activeHarness: string | null;
192
+ activeSessionId: string | null;
193
+ connection: {
194
+ strategy: 'start' | 'resume' | 'attach' | 'branch' | null;
195
+ follow: 'inactive' | 'following' | 'retrying';
196
+ ownsRuntime: boolean;
197
+ };
198
+ turn: {
199
+ state: 'idle' | 'running' | 'interrupting' | 'reconciling';
200
+ id: string | null;
201
+ startedAt: number | null;
202
+ };
203
+ conversation: HarnessChatConversationEntry[];
204
+ requests: HarnessChatRequestEntry[];
205
+ availableActions: HarnessChatAvailableActions;
206
+ error: HarnessChatError | null;
207
+ terminalCommand: string | null;
208
+ }
209
+
210
+ export type HarnessChatAction =
211
+ | { type: 'refresh' }
212
+ | { type: 'create'; harness: string }
213
+ | { type: 'observe'; sessionId: string }
214
+ | { type: 'resume'; sessionId: string }
215
+ | { type: 'attach'; sessionId: string }
216
+ | { type: 'branch'; sessionId: string }
217
+ | { type: 'restore'; sessionIdentity: string; connection: 'observe' | 'attach' }
218
+ | { type: 'detach' }
219
+ | { type: 'openTerminal' }
220
+ | { type: 'prompt'; text: string; context?: HarnessChatPromptContext[] }
221
+ | { type: 'interrupt' }
222
+ | { type: 'respond'; requestId: JsonValue; optionId: string | null }
223
+ | { type: 'respond'; requestId: JsonValue; response: JsonValue };
224
+
225
+ function actionRecord(value: unknown): Record<string, unknown> {
226
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
227
+ throw new Error('A harness Chat action is required.');
228
+ }
229
+ return value as Record<string, unknown>;
230
+ }
231
+
232
+ function actionString(record: Record<string, unknown>, key: string, max: number): string {
233
+ const value = record[key];
234
+ if (typeof value !== 'string' || !value.trim() || value.length > max) {
235
+ throw new Error(`Harness Chat action field \`${key}\` is invalid.`);
236
+ }
237
+ return value;
238
+ }
239
+
240
+ function parsePromptAction(
241
+ record: Record<string, unknown>,
242
+ ): Extract<HarnessChatAction, { type: 'prompt' }> {
243
+ const context = record['context'];
244
+ if (context !== undefined && !Array.isArray(context)) {
245
+ throw new Error('Harness Chat prompt context is invalid.');
246
+ }
247
+ const parsedContext = (context ?? []).map((item) => {
248
+ const entry = actionRecord(item);
249
+ return {
250
+ id: actionString(entry, 'id', 128),
251
+ label: actionString(entry, 'label', 200),
252
+ detail: actionString(entry, 'detail', 20_000),
253
+ };
254
+ });
255
+ if (parsedContext.length > 20) throw new Error('Harness Chat prompt context is too large.');
256
+ return {
257
+ type: 'prompt',
258
+ text: actionString(record, 'text', 200_000),
259
+ ...(parsedContext.length > 0 ? { context: parsedContext } : {}),
260
+ };
261
+ }
262
+
263
+ function parseRespondAction(
264
+ record: Record<string, unknown>,
265
+ ): Extract<HarnessChatAction, { type: 'respond' }> {
266
+ if (!Object.hasOwn(record, 'requestId')) throw new Error('Harness Chat requestId is required.');
267
+ if (Object.hasOwn(record, 'optionId')) {
268
+ const optionId = record['optionId'];
269
+ if (optionId !== null && (typeof optionId !== 'string' || optionId.length > 4_096)) {
270
+ throw new Error('Harness Chat optionId is invalid.');
271
+ }
272
+ return { type: 'respond', requestId: record['requestId'] as JsonValue, optionId };
273
+ }
274
+ if (Object.hasOwn(record, 'response')) {
275
+ return {
276
+ type: 'respond',
277
+ requestId: record['requestId'] as JsonValue,
278
+ response: record['response'] as JsonValue,
279
+ };
280
+ }
281
+ throw new Error('Harness Chat response is required.');
282
+ }
283
+
284
+ /** Runtime validation for the local HTTP trust boundary. */
285
+ export function parseHarnessChatAction(value: unknown): HarnessChatAction {
286
+ const record = actionRecord(value);
287
+ const type = actionString(record, 'type', 32);
288
+ if (type === 'refresh' || type === 'detach' || type === 'openTerminal' || type === 'interrupt')
289
+ return { type };
290
+ if (type === 'create') return { type, harness: actionString(record, 'harness', 128) };
291
+ if (type === 'prompt') return parsePromptAction(record);
292
+ if (type === 'observe' || type === 'resume' || type === 'attach' || type === 'branch') {
293
+ return { type, sessionId: actionString(record, 'sessionId', 4_096) };
294
+ }
295
+ if (type === 'restore') {
296
+ const connection = actionString(record, 'connection', 16);
297
+ const sessionIdentity = actionString(record, 'sessionIdentity', 256);
298
+ if (connection !== 'observe' && connection !== 'attach') {
299
+ throw new Error('Harness Chat restore connection is invalid.');
300
+ }
301
+ if (!/^scs_[a-f0-9]{64}$/.test(sessionIdentity)) {
302
+ throw new Error('Harness Chat session identity is invalid.');
303
+ }
304
+ return {
305
+ type,
306
+ sessionIdentity,
307
+ connection,
308
+ };
309
+ }
310
+ if (type === 'respond') return parseRespondAction(record);
311
+ throw new Error(`Unknown harness Chat action: ${type}`);
312
+ }
313
+
314
+ /** Reject malformed/stale-version wire payloads before they enter React state. */
315
+ export function parseHarnessChatSnapshot(value: unknown): HarnessChatSnapshot {
316
+ const record = actionRecord(value);
317
+ const connection = actionRecord(record['connection']);
318
+ const turn = actionRecord(record['turn']);
319
+ const actions = actionRecord(record['availableActions']);
320
+ const actionNames: Array<keyof HarnessChatAvailableActions> = [
321
+ 'refresh',
322
+ 'observe',
323
+ 'start',
324
+ 'resume',
325
+ 'attach',
326
+ 'branch',
327
+ 'detach',
328
+ 'openTerminal',
329
+ 'send',
330
+ 'interrupt',
331
+ 'respond',
332
+ ];
333
+ if (
334
+ record['schema'] !== 'vgai.harness-chat.v3' ||
335
+ typeof record['revision'] !== 'number' ||
336
+ typeof record['workspaceGeneration'] !== 'number' ||
337
+ !['loading', 'ready', 'working', 'error', 'unavailable'].includes(String(record['status'])) ||
338
+ !['none', 'observe', 'control'].includes(String(record['mode'])) ||
339
+ !Array.isArray(record['harnesses']) ||
340
+ !Array.isArray(record['sessions']) ||
341
+ !record['sessions'].every((session) => {
342
+ if (!session || typeof session !== 'object' || Array.isArray(session)) return false;
343
+ const item = session as Record<string, unknown>;
344
+ return (
345
+ typeof item['id'] === 'string' &&
346
+ typeof item['identity'] === 'string' &&
347
+ /^scs_[a-f0-9]{64}$/.test(item['identity'])
348
+ );
349
+ }) ||
350
+ !Array.isArray(record['conversation']) ||
351
+ !['inactive', 'following', 'retrying'].includes(String(connection['follow'])) ||
352
+ typeof connection['ownsRuntime'] !== 'boolean' ||
353
+ !['idle', 'running', 'interrupting', 'reconciling'].includes(String(turn['state'])) ||
354
+ !actionNames.every((name) => typeof actions[name] === 'boolean') ||
355
+ (record['terminalCommand'] !== null && typeof record['terminalCommand'] !== 'string')
356
+ ) {
357
+ throw new Error('Malformed Harness Chat snapshot.');
358
+ }
359
+ return value as HarnessChatSnapshot;
360
+ }
361
+
362
+ export function unavailableHarnessChatSnapshot(error: string): HarnessChatSnapshot {
363
+ return {
364
+ schema: 'vgai.harness-chat.v3',
365
+ revision: 0,
366
+ workspaceGeneration: 0,
367
+ workspace: null,
368
+ status: 'unavailable',
369
+ mode: 'none',
370
+ operation: null,
371
+ harnesses: [],
372
+ sessions: [],
373
+ activeHarness: null,
374
+ activeSessionId: null,
375
+ connection: { strategy: null, follow: 'inactive', ownsRuntime: false },
376
+ turn: { state: 'idle', id: null, startedAt: null },
377
+ conversation: [],
378
+ requests: [],
379
+ availableActions: {
380
+ refresh: false,
381
+ observe: false,
382
+ start: false,
383
+ resume: false,
384
+ attach: false,
385
+ branch: false,
386
+ detach: false,
387
+ openTerminal: false,
388
+ send: false,
389
+ interrupt: false,
390
+ respond: false,
391
+ },
392
+ error: error
393
+ ? { code: 'unavailable', message: error, operation: 'load', recoverable: true }
394
+ : null,
395
+ terminalCommand: null,
396
+ };
397
+ }
398
+
399
+ /** Accept only authoritative forward progress for the same workspace. */
400
+ export function shouldAcceptHarnessChatSnapshot(
401
+ current: HarnessChatSnapshot,
402
+ next: HarnessChatSnapshot,
403
+ ): boolean {
404
+ if (next.workspaceGeneration !== current.workspaceGeneration) {
405
+ return next.workspaceGeneration > current.workspaceGeneration;
406
+ }
407
+ if (current.workspace !== next.workspace) return false;
408
+ return next.revision >= current.revision;
409
+ }