@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,796 @@
1
+ /**
2
+ * VGAI host adapter for Supercode's headless client.
3
+ *
4
+ * Supercode owns harness/session/runtime semantics, lifecycle normalization,
5
+ * transcript projection, retries, reconciliation, and concurrency. VGAI owns
6
+ * the trusted YOLO policy, HTTP/SSE boundary, shell-command presentation, and
7
+ * React visuals. Keep this file as a mapping layer; reusable agent logic
8
+ * belongs in @volter-ai-dev/supercode-client.
9
+ */
10
+
11
+ import { existsSync, statSync } from 'node:fs';
12
+ import { createRequire } from 'node:module';
13
+ import { dirname, join, resolve } from 'node:path';
14
+ import { pathToFileURL } from 'node:url';
15
+ import type {
16
+ HarnessChatAction,
17
+ HarnessChatAuthState,
18
+ HarnessChatAvailableActions,
19
+ HarnessChatCapabilities,
20
+ HarnessChatConversationEntry,
21
+ HarnessChatError,
22
+ HarnessChatHarness,
23
+ HarnessChatRuntimeState,
24
+ HarnessChatSession,
25
+ HarnessChatSnapshot,
26
+ JsonValue,
27
+ } from '../src/harness-chat-types';
28
+
29
+ // @volter-ai-dev/supercode-client is an optional runtime peer, so this adapter keeps only
30
+ // the structural projection it consumes. The validator loaded atomically from
31
+ // that same peer module is the runtime authority for every production capture.
32
+ type RuntimeCapabilities = {
33
+ start_session: boolean;
34
+ resume_session: boolean;
35
+ attach_existing_process: boolean;
36
+ send_input: boolean;
37
+ stream_events: boolean;
38
+ interrupt: boolean;
39
+ respond_to_requests?: boolean;
40
+ };
41
+ type StructuredLaunch = {
42
+ program: string;
43
+ arguments: string[];
44
+ cwd: string;
45
+ env?: Record<string, string>;
46
+ };
47
+ type HeadlessHarness = {
48
+ id: string;
49
+ display_name: string;
50
+ installed: boolean;
51
+ auth: HarnessChatAuthState;
52
+ runtime: HarnessChatRuntimeState;
53
+ protocol: string;
54
+ capabilities: RuntimeCapabilities;
55
+ effective_capabilities: RuntimeCapabilities;
56
+ availableActions: {
57
+ start: boolean;
58
+ resume: boolean;
59
+ attach: boolean;
60
+ send: boolean;
61
+ interrupt: boolean;
62
+ respond: boolean;
63
+ };
64
+ reason: string | null;
65
+ repair: string | null;
66
+ };
67
+ type HeadlessSession = {
68
+ key: string;
69
+ identity: string;
70
+ harness: string;
71
+ sessionId: string;
72
+ cwd: string | null;
73
+ title: string | null;
74
+ updatedAt: number | null;
75
+ messageCount: number | null;
76
+ model: string | null;
77
+ storage: 'file' | 'sqlite';
78
+ liveEndpoint: string | null;
79
+ };
80
+ type HeadlessConversationEntry = HarnessChatConversationEntry;
81
+ type HeadlessSnapshot = {
82
+ schema: 'supercode.client-state.v1';
83
+ revision: number;
84
+ workspace: string;
85
+ availability: 'loading' | 'ready' | 'unavailable' | 'error';
86
+ operation: HarnessChatSnapshot['operation'];
87
+ harnesses: HeadlessHarness[];
88
+ sessions: HeadlessSession[];
89
+ activeSessionKey: string | null;
90
+ activeHarness: string | null;
91
+ activeSessionId: string | null;
92
+ connection: {
93
+ mode: 'none' | 'mirror' | 'control';
94
+ strategy: 'start' | 'resume' | 'attach' | 'branch' | null;
95
+ follow: 'inactive' | 'following' | 'retrying';
96
+ ownsRuntime: boolean;
97
+ };
98
+ turn: {
99
+ state: 'idle' | 'running' | 'interrupting' | 'reconciling';
100
+ id: string | null;
101
+ startedAt: number | null;
102
+ };
103
+ conversation: HeadlessConversationEntry[];
104
+ requests: Extract<HeadlessConversationEntry, { kind: 'request' }>[];
105
+ availableActions: HarnessChatAvailableActions;
106
+ error: HarnessChatError | null;
107
+ terminalLaunch: StructuredLaunch | null;
108
+ };
109
+ type HeadlessAction =
110
+ | { type: 'refresh'; autoObserve?: boolean }
111
+ | { type: 'observe'; sessionKey: string }
112
+ | { type: 'start'; harness: string }
113
+ | { type: 'resume'; sessionKey: string }
114
+ | { type: 'attach'; sessionKey: string; baseUrl?: string }
115
+ | { type: 'branch'; sessionKey: string }
116
+ | { type: 'restore'; identity: string; connection: 'observe' | 'attach' }
117
+ | { type: 'detach' }
118
+ | { type: 'openTerminal' }
119
+ | { type: 'send'; text: string; context?: Array<{ label: string; detail: string }> }
120
+ | { type: 'interrupt' }
121
+ | { type: 'respond'; requestId: JsonValue; optionId: string | null }
122
+ | { type: 'respond'; requestId: JsonValue; response: JsonValue };
123
+ type HeadlessController = {
124
+ getSnapshot(): HeadlessSnapshot;
125
+ subscribe(listener: () => void): () => void;
126
+ initialize(): Promise<HeadlessSnapshot>;
127
+ dispatch(action: HeadlessAction): Promise<HeadlessSnapshot>;
128
+ setWorkspace(workspace: string, options?: { autoObserve?: boolean }): Promise<HeadlessSnapshot>;
129
+ close(): Promise<void>;
130
+ };
131
+ type HeadlessControllerConstructor = new (options: {
132
+ client: SupercodeClient;
133
+ workspace: string;
134
+ policy: 'yolo';
135
+ ownsClient: true;
136
+ autoObserve: boolean;
137
+ }) => HeadlessController;
138
+ type HeadlessSnapshotAssertion = (value: unknown) => HeadlessSnapshot;
139
+ type SupercodeClient = {
140
+ close(): Promise<void>;
141
+ [key: string]: unknown;
142
+ };
143
+ type SupercodeClientConstructor = new (options?: {
144
+ command?: string;
145
+ cwd?: string;
146
+ }) => SupercodeClient;
147
+
148
+ export interface HarnessChatServiceOptions {
149
+ engineRoot: string;
150
+ getProjectRoot: () => string;
151
+ onChange: (snapshot: HarnessChatSnapshot) => void;
152
+ initializeTimeoutMs?: number;
153
+ /** Test seam. Production loads the real zero-dependency Supercode SDK. */
154
+ createClient?: ((workspace: string) => Promise<SupercodeClient>) | undefined;
155
+ /** Test seam for the framework-neutral controller. */
156
+ createController?:
157
+ | ((
158
+ client: SupercodeClient,
159
+ workspace: string,
160
+ options: { autoObserve: boolean },
161
+ ) => HeadlessController)
162
+ | undefined;
163
+ }
164
+
165
+ const EMPTY_CAPABILITIES: HarnessChatCapabilities = {
166
+ startSession: false,
167
+ resumeSession: false,
168
+ attachExistingProcess: false,
169
+ sendInput: false,
170
+ streamEvents: false,
171
+ interrupt: false,
172
+ respondToRequests: false,
173
+ };
174
+
175
+ function capabilities(value: RuntimeCapabilities | undefined): HarnessChatCapabilities {
176
+ if (!value) return { ...EMPTY_CAPABILITIES };
177
+ return {
178
+ startSession: value.start_session,
179
+ resumeSession: value.resume_session,
180
+ attachExistingProcess: value.attach_existing_process,
181
+ sendInput: value.send_input,
182
+ streamEvents: value.stream_events,
183
+ interrupt: value.interrupt,
184
+ respondToRequests: value.respond_to_requests ?? false,
185
+ };
186
+ }
187
+
188
+ function defaultSessionTitle(harness: string): string {
189
+ const label = harness
190
+ .split('-')
191
+ .filter(Boolean)
192
+ .map((part) => `${part[0]?.toUpperCase() ?? ''}${part.slice(1)}`)
193
+ .join(' ');
194
+ return `${label || 'Agent'} chat`;
195
+ }
196
+
197
+ function errorMessage(error: unknown): string {
198
+ return error instanceof Error ? error.message : String(error);
199
+ }
200
+
201
+ async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {
202
+ let timer: ReturnType<typeof setTimeout> | undefined;
203
+ try {
204
+ return await Promise.race([
205
+ promise,
206
+ new Promise<never>((_, reject) => {
207
+ timer = setTimeout(
208
+ () => reject(new Error(`${label} timed out after ${timeoutMs}ms.`)),
209
+ timeoutMs,
210
+ );
211
+ }),
212
+ ]);
213
+ } finally {
214
+ if (timer) clearTimeout(timer);
215
+ }
216
+ }
217
+
218
+ // A cold editor boot can be optimizing Vite dependencies while Supercode scans
219
+ // several local harness stores. Real five-harness startup can cross ten seconds
220
+ // without being stuck, so keep the failure bounded but leave enough cold-start
221
+ // grace for the first inventory pass to finish.
222
+ const DEFAULT_INITIALIZE_TIMEOUT_MS = 30_000;
223
+
224
+ function moduleCandidate(explicit: string | undefined, defaultFile: string): string[] {
225
+ if (!explicit) return [];
226
+ const absolute = resolve(explicit);
227
+ try {
228
+ return [
229
+ existsSync(absolute) && statSync(absolute).isDirectory()
230
+ ? join(absolute, defaultFile)
231
+ : absolute,
232
+ ];
233
+ } catch {
234
+ return [absolute];
235
+ }
236
+ }
237
+
238
+ async function importOptional<T>(
239
+ label: string,
240
+ exportName: string,
241
+ candidates: string[],
242
+ ): Promise<T> {
243
+ const failures: string[] = [];
244
+ for (const candidate of candidates) {
245
+ try {
246
+ const specifier = candidate.startsWith('/') ? pathToFileURL(candidate).href : candidate;
247
+ const module = (await import(specifier)) as Record<string, unknown>;
248
+ const value = module[exportName];
249
+ if (value) return value as T;
250
+ failures.push(`${candidate}: missing ${exportName} export`);
251
+ } catch (error) {
252
+ failures.push(`${candidate}: ${errorMessage(error)}`);
253
+ }
254
+ }
255
+ throw new Error(`${label} is unavailable. Tried: ${failures.join('; ')}`);
256
+ }
257
+
258
+ async function importOptionalModule<const Names extends readonly string[]>(
259
+ label: string,
260
+ exportNames: Names,
261
+ candidates: string[],
262
+ ): Promise<{ [Key in Names[number]]: unknown }> {
263
+ const failures: string[] = [];
264
+ for (const candidate of candidates) {
265
+ try {
266
+ const specifier = candidate.startsWith('/') ? pathToFileURL(candidate).href : candidate;
267
+ const module = (await import(specifier)) as Record<string, unknown>;
268
+ const missing = exportNames.filter((name) => !module[name]);
269
+ if (missing.length === 0) return module as { [Key in Names[number]]: unknown };
270
+ failures.push(`${candidate}: missing ${missing.join(', ')} export`);
271
+ } catch (error) {
272
+ failures.push(`${candidate}: ${errorMessage(error)}`);
273
+ }
274
+ }
275
+ throw new Error(`${label} is unavailable. Tried: ${failures.join('; ')}`);
276
+ }
277
+
278
+ async function importSupercodePackages(engineRoot: string): Promise<{
279
+ SupercodeHarnessClient: SupercodeClientConstructor;
280
+ SupercodeController: HeadlessControllerConstructor;
281
+ assertSupercodeClientSnapshot: HeadlessSnapshotAssertion;
282
+ }> {
283
+ const sibling = join(dirname(engineRoot), 'supercode', 'sdk');
284
+ const cwdSibling = join(dirname(resolve(process.cwd())), 'supercode', 'sdk');
285
+ const controllerCandidates = [
286
+ ...moduleCandidate(process.env['SUPERCODE_CLIENT_PATH'], 'client.mjs'),
287
+ '@volter-ai-dev/supercode-client',
288
+ join(sibling, 'client', 'client.mjs'),
289
+ join(cwdSibling, 'client', 'client.mjs'),
290
+ ];
291
+ const [SupercodeHarnessClient, clientModule] = await Promise.all([
292
+ importOptional<SupercodeClientConstructor>('Supercode harness SDK', 'SupercodeHarnessClient', [
293
+ ...moduleCandidate(process.env['SUPERCODE_SDK_PATH'], 'client.mjs'),
294
+ '@volter-ai-dev/supercode-harness-sdk',
295
+ join(sibling, 'typescript', 'client.mjs'),
296
+ join(cwdSibling, 'typescript', 'client.mjs'),
297
+ ]),
298
+ importOptionalModule(
299
+ 'Supercode headless client',
300
+ ['SupercodeController', 'assertSupercodeClientSnapshot'] as const,
301
+ controllerCandidates,
302
+ ),
303
+ ]);
304
+ const SupercodeController = clientModule.SupercodeController as HeadlessControllerConstructor;
305
+ const assertSupercodeClientSnapshot =
306
+ clientModule.assertSupercodeClientSnapshot as HeadlessSnapshotAssertion;
307
+ return { SupercodeHarnessClient, SupercodeController, assertSupercodeClientSnapshot };
308
+ }
309
+
310
+ async function importSupercodeController(engineRoot: string): Promise<{
311
+ SupercodeController: HeadlessControllerConstructor;
312
+ assertSupercodeClientSnapshot: HeadlessSnapshotAssertion;
313
+ }> {
314
+ const sibling = join(dirname(engineRoot), 'supercode', 'sdk', 'client', 'client.mjs');
315
+ const cwdSibling = join(
316
+ dirname(resolve(process.cwd())),
317
+ 'supercode',
318
+ 'sdk',
319
+ 'client',
320
+ 'client.mjs',
321
+ );
322
+ const candidates = [
323
+ ...moduleCandidate(process.env['SUPERCODE_CLIENT_PATH'], 'client.mjs'),
324
+ '@volter-ai-dev/supercode-client',
325
+ sibling,
326
+ cwdSibling,
327
+ ];
328
+ const clientModule = await importOptionalModule(
329
+ 'Supercode headless client',
330
+ ['SupercodeController', 'assertSupercodeClientSnapshot'] as const,
331
+ candidates,
332
+ );
333
+ const SupercodeController = clientModule.SupercodeController as HeadlessControllerConstructor;
334
+ const assertSupercodeClientSnapshot =
335
+ clientModule.assertSupercodeClientSnapshot as HeadlessSnapshotAssertion;
336
+ return { SupercodeController, assertSupercodeClientSnapshot };
337
+ }
338
+
339
+ export function findSupercodeCommand(engineRoot: string): string | undefined {
340
+ if (process.env['SUPERCODE_BIN']) return process.env['SUPERCODE_BIN'];
341
+ try {
342
+ const require = createRequire(import.meta.url);
343
+ const packageRoot = dirname(require.resolve('@volter-ai-dev/supercode/package.json'));
344
+ const installedCommand = join(packageRoot, 'bin', 'supercode.js');
345
+ if (existsSync(installedCommand) && statSync(installedCommand).isFile())
346
+ return installedCommand;
347
+ } catch {
348
+ // Optional dependency omitted: retain source-checkout fallbacks below.
349
+ }
350
+ for (const candidate of [
351
+ join(dirname(engineRoot), 'supercode', 'target', 'release', 'supercode'),
352
+ join(dirname(engineRoot), 'supercode', 'target', 'debug', 'supercode'),
353
+ ]) {
354
+ if (existsSync(candidate)) return candidate;
355
+ }
356
+ return undefined;
357
+ }
358
+
359
+ function shellQuote(value: string): string {
360
+ if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
361
+ return `'${value.replaceAll("'", "'\\''")}'`;
362
+ }
363
+
364
+ function launchCommand(launch: StructuredLaunch): string {
365
+ return `cd ${shellQuote(launch.cwd)} && ${[launch.program, ...launch.arguments].map(shellQuote).join(' ')}`;
366
+ }
367
+
368
+ function visibleEntry(entry: HeadlessConversationEntry): HarnessChatConversationEntry | null {
369
+ if (entry.kind === 'message') {
370
+ // Supercode preserves context; VGAI chooses not to render harness
371
+ // envelopes as chat messages. This is a presentation policy, not loss.
372
+ return entry.visibility === 'context'
373
+ ? null
374
+ : {
375
+ id: entry.id,
376
+ kind: 'message',
377
+ role: entry.role,
378
+ text: entry.text,
379
+ ...(entry.content === undefined ? {} : { content: entry.content }),
380
+ metadata: { ...entry.metadata },
381
+ visibility: entry.visibility,
382
+ };
383
+ }
384
+ if (entry.kind === 'tool') {
385
+ return {
386
+ id: entry.id,
387
+ kind: 'tool',
388
+ callId: entry.callId,
389
+ name: entry.name,
390
+ arguments: entry.arguments,
391
+ resultText: entry.resultText,
392
+ ...(entry.resultContent === undefined ? {} : { resultContent: entry.resultContent }),
393
+ status: entry.status,
394
+ metadata: { ...entry.metadata },
395
+ };
396
+ }
397
+ if (entry.kind === 'reasoning') return { ...entry };
398
+ if (entry.kind === 'request') {
399
+ return { ...entry, options: entry.options.map((option) => ({ ...option })) };
400
+ }
401
+ return { ...entry };
402
+ }
403
+
404
+ function conversation(entries: HeadlessConversationEntry[]): HarnessChatConversationEntry[] {
405
+ return entries.map(visibleEntry).filter((entry) => entry !== null);
406
+ }
407
+
408
+ function toSnapshot(snapshot: HeadlessSnapshot, workspaceGeneration: number): HarnessChatSnapshot {
409
+ const harnesses: HarnessChatHarness[] = snapshot.harnesses.map((item) => ({
410
+ id: item.id,
411
+ label: item.display_name,
412
+ installed: item.installed,
413
+ auth: item.auth,
414
+ runtime: item.runtime,
415
+ protocol: item.protocol,
416
+ capabilities: capabilities(item.effective_capabilities),
417
+ availableActions: { ...item.availableActions },
418
+ reason: item.reason,
419
+ repair: item.repair,
420
+ }));
421
+ const sessions: HarnessChatSession[] = snapshot.sessions.map((item) => ({
422
+ // The controller key is intentionally opaque and collision-free. Native
423
+ // IDs can collide between harnesses and stores.
424
+ id: item.key,
425
+ identity: item.identity,
426
+ nativeId: item.sessionId,
427
+ harness: item.harness,
428
+ title: item.title ?? defaultSessionTitle(item.harness),
429
+ cwd: item.cwd,
430
+ updatedAt: item.updatedAt,
431
+ messageCount: item.messageCount,
432
+ model: item.model,
433
+ storage: item.storage,
434
+ attachable: Boolean(item.liveEndpoint),
435
+ }));
436
+ const working = snapshot.turn.state !== 'idle';
437
+ return {
438
+ workspace: snapshot.workspace,
439
+ status:
440
+ snapshot.availability === 'unavailable'
441
+ ? 'unavailable'
442
+ : snapshot.availability === 'error'
443
+ ? 'error'
444
+ : snapshot.availability === 'loading'
445
+ ? 'loading'
446
+ : working
447
+ ? 'working'
448
+ : 'ready',
449
+ mode:
450
+ snapshot.connection.mode === 'mirror'
451
+ ? 'observe'
452
+ : snapshot.connection.mode === 'control'
453
+ ? 'control'
454
+ : 'none',
455
+ schema: 'vgai.harness-chat.v3',
456
+ revision: snapshot.revision,
457
+ workspaceGeneration,
458
+ operation: snapshot.operation,
459
+ harnesses,
460
+ sessions,
461
+ activeHarness: snapshot.activeHarness,
462
+ activeSessionId: snapshot.activeSessionKey,
463
+ connection: {
464
+ strategy: snapshot.connection.strategy,
465
+ follow: snapshot.connection.follow,
466
+ ownsRuntime: snapshot.connection.ownsRuntime,
467
+ },
468
+ turn: { ...snapshot.turn },
469
+ conversation: conversation(snapshot.conversation),
470
+ requests: snapshot.requests.map((request) => ({
471
+ ...request,
472
+ options: request.options.map((option) => ({ ...option })),
473
+ })),
474
+ availableActions: { ...snapshot.availableActions },
475
+ error: snapshot.error ? { ...snapshot.error } : null,
476
+ terminalCommand: snapshot.terminalLaunch ? launchCommand(snapshot.terminalLaunch) : null,
477
+ };
478
+ }
479
+
480
+ function unavailable(
481
+ workspace: string | null,
482
+ error: string,
483
+ workspaceGeneration = 0,
484
+ ): HarnessChatSnapshot {
485
+ return {
486
+ schema: 'vgai.harness-chat.v3',
487
+ revision: 0,
488
+ workspaceGeneration,
489
+ workspace,
490
+ status: 'unavailable',
491
+ mode: 'none',
492
+ operation: null,
493
+ harnesses: [],
494
+ sessions: [],
495
+ activeHarness: null,
496
+ activeSessionId: null,
497
+ connection: { strategy: null, follow: 'inactive', ownsRuntime: false },
498
+ turn: { state: 'idle', id: null, startedAt: null },
499
+ conversation: [],
500
+ requests: [],
501
+ availableActions: {
502
+ refresh: false,
503
+ observe: false,
504
+ start: false,
505
+ resume: false,
506
+ attach: false,
507
+ branch: false,
508
+ detach: false,
509
+ openTerminal: false,
510
+ send: false,
511
+ interrupt: false,
512
+ respond: false,
513
+ },
514
+ error: { code: 'unavailable', message: error, operation: 'load', recoverable: true },
515
+ terminalCommand: null,
516
+ };
517
+ }
518
+
519
+ function loading(workspace: string, workspaceGeneration: number): HarnessChatSnapshot {
520
+ return {
521
+ ...unavailable(workspace, 'Supercode is loading.', workspaceGeneration),
522
+ status: 'loading',
523
+ error: null,
524
+ };
525
+ }
526
+
527
+ export class HarnessChatService {
528
+ private workspace: string | null = null;
529
+ private controller: HeadlessController | null = null;
530
+ private unsubscribe: (() => void) | null = null;
531
+ private starting: Promise<void> | null = null;
532
+ private closing: Promise<void> | null = null;
533
+ private workspaceGeneration = 0;
534
+ private closed = false;
535
+ private assertSnapshot: HeadlessSnapshotAssertion = (value) => value as HeadlessSnapshot;
536
+ private lastSnapshot: HarnessChatSnapshot = unavailable(null, 'Supercode is loading.');
537
+
538
+ constructor(private readonly options: HarnessChatServiceOptions) {}
539
+
540
+ snapshot(): HarnessChatSnapshot {
541
+ return {
542
+ ...this.lastSnapshot,
543
+ harnesses: this.lastSnapshot.harnesses.map((item) => ({
544
+ ...item,
545
+ capabilities: { ...item.capabilities },
546
+ availableActions: { ...item.availableActions },
547
+ })),
548
+ sessions: this.lastSnapshot.sessions.map((item) => ({ ...item })),
549
+ connection: { ...this.lastSnapshot.connection },
550
+ turn: { ...this.lastSnapshot.turn },
551
+ conversation: this.lastSnapshot.conversation.map((item) =>
552
+ item.kind === 'request'
553
+ ? { ...item, options: item.options.map((option) => ({ ...option })) }
554
+ : { ...item },
555
+ ),
556
+ requests: this.lastSnapshot.requests.map((item) => ({
557
+ ...item,
558
+ options: item.options.map((option) => ({ ...option })),
559
+ })),
560
+ availableActions: { ...this.lastSnapshot.availableActions },
561
+ error: this.lastSnapshot.error ? { ...this.lastSnapshot.error } : null,
562
+ };
563
+ }
564
+
565
+ async refresh(autoObserve = true): Promise<HarnessChatSnapshot> {
566
+ const created = !this.controller;
567
+ await this.ensureController(autoObserve);
568
+ if (!this.controller) return this.snapshot();
569
+ if (!created) await this.controller.dispatch({ type: 'refresh', autoObserve });
570
+ this.capture();
571
+ return this.snapshot();
572
+ }
573
+
574
+ /** Ensure the live controller exists without re-running full harness/session
575
+ * discovery when an editor tab mounts or reloads. The controller already
576
+ * follows external transcript changes; discovery is reserved for the
577
+ * explicit Refresh action. */
578
+ async load(autoObserve = true): Promise<HarnessChatSnapshot> {
579
+ const next = resolve(this.options.getProjectRoot());
580
+ if (!this.controller && !this.starting) {
581
+ this.beginWorkspace(next);
582
+ void this.ensureController(autoObserve);
583
+ } else if (this.controller && this.workspace === next) {
584
+ this.capture();
585
+ } else if (this.workspace !== next) {
586
+ this.beginWorkspace(next);
587
+ void this.ensureController(autoObserve);
588
+ }
589
+ return this.snapshot();
590
+ }
591
+
592
+ async act(action: HarnessChatAction): Promise<HarnessChatSnapshot> {
593
+ await this.ensureController();
594
+ const controller = this.controller;
595
+ if (!controller) {
596
+ throw new Error(this.lastSnapshot.error?.message ?? 'Supercode is unavailable.');
597
+ }
598
+ switch (action.type) {
599
+ case 'refresh':
600
+ await controller.dispatch({ type: 'refresh', autoObserve: true });
601
+ break;
602
+ case 'create':
603
+ await controller.dispatch({ type: 'start', harness: action.harness });
604
+ break;
605
+ case 'observe':
606
+ await controller.dispatch({ type: 'observe', sessionKey: action.sessionId });
607
+ break;
608
+ case 'resume':
609
+ await controller.dispatch({ type: 'resume', sessionKey: action.sessionId });
610
+ break;
611
+ case 'attach':
612
+ await controller.dispatch({ type: 'attach', sessionKey: action.sessionId });
613
+ break;
614
+ case 'branch':
615
+ await controller.dispatch({ type: 'branch', sessionKey: action.sessionId });
616
+ break;
617
+ case 'restore':
618
+ await controller.dispatch({
619
+ type: 'restore',
620
+ identity: action.sessionIdentity,
621
+ connection: action.connection,
622
+ });
623
+ break;
624
+ case 'detach':
625
+ await controller.dispatch({ type: 'detach' });
626
+ break;
627
+ case 'openTerminal':
628
+ await controller.dispatch({ type: 'openTerminal' });
629
+ break;
630
+ case 'prompt':
631
+ await controller.dispatch({
632
+ type: 'send',
633
+ text: action.text,
634
+ ...(action.context
635
+ ? { context: action.context.map(({ label, detail }) => ({ label, detail })) }
636
+ : {}),
637
+ });
638
+ break;
639
+ case 'interrupt':
640
+ await controller.dispatch({ type: 'interrupt' });
641
+ break;
642
+ case 'respond':
643
+ await controller.dispatch(
644
+ 'optionId' in action
645
+ ? { type: 'respond', requestId: action.requestId, optionId: action.optionId }
646
+ : { type: 'respond', requestId: action.requestId, response: action.response },
647
+ );
648
+ break;
649
+ default:
650
+ throw new Error('Unknown harness Chat action.');
651
+ }
652
+ this.capture();
653
+ return this.snapshot();
654
+ }
655
+
656
+ async setWorkspace(): Promise<void> {
657
+ const next = resolve(this.options.getProjectRoot());
658
+ if (this.workspace === next) return;
659
+ this.beginWorkspace(next);
660
+ if (this.controller) {
661
+ await this.controller.setWorkspace(next, { autoObserve: true });
662
+ this.capture();
663
+ return;
664
+ }
665
+ await this.ensureController(true);
666
+ }
667
+
668
+ async close(): Promise<void> {
669
+ if (this.closing) return this.closing;
670
+ this.closed = true;
671
+ this.unsubscribe?.();
672
+ this.unsubscribe = null;
673
+ const controller = this.controller;
674
+ this.controller = null;
675
+ this.closing = controller
676
+ ? withTimeout(controller.close(), 2_500, 'Supercode shutdown').then(
677
+ () => undefined,
678
+ () => undefined,
679
+ )
680
+ : Promise.resolve();
681
+ return this.closing;
682
+ }
683
+
684
+ private async ensureController(autoObserve = true): Promise<void> {
685
+ if (this.closed) throw new Error('Harness Chat service is closed.');
686
+ const next = resolve(this.options.getProjectRoot());
687
+ if (this.controller) {
688
+ if (this.workspace !== next) {
689
+ this.beginWorkspace(next);
690
+ await this.controller.setWorkspace(next, { autoObserve: true });
691
+ this.capture();
692
+ }
693
+ return;
694
+ }
695
+ if (this.starting) {
696
+ await this.starting;
697
+ return;
698
+ }
699
+ this.beginWorkspace(next);
700
+ const generation = this.workspaceGeneration;
701
+ this.starting = this.startController(next, autoObserve, generation);
702
+ try {
703
+ await this.starting;
704
+ } finally {
705
+ this.starting = null;
706
+ }
707
+ }
708
+
709
+ private async startController(
710
+ workspace: string,
711
+ autoObserve: boolean,
712
+ generation: number,
713
+ ): Promise<void> {
714
+ let controller: HeadlessController | null = null;
715
+ try {
716
+ controller = await this.createHeadlessController(workspace, autoObserve);
717
+ if (this.closed || generation !== this.workspaceGeneration) {
718
+ await withTimeout(controller.close(), 2_500, 'Supercode shutdown').catch(() => undefined);
719
+ return;
720
+ }
721
+ this.workspace = workspace;
722
+ this.controller = controller;
723
+ this.unsubscribe = controller.subscribe(() => this.capture());
724
+ this.capture();
725
+ const timeoutMs = this.options.initializeTimeoutMs ?? DEFAULT_INITIALIZE_TIMEOUT_MS;
726
+ await withTimeout(controller.initialize(), timeoutMs, 'Supercode initialization');
727
+ this.capture();
728
+ } catch (error) {
729
+ if (controller && controller === this.controller) {
730
+ this.unsubscribe?.();
731
+ this.unsubscribe = null;
732
+ this.controller = null;
733
+ }
734
+ if (generation === this.workspaceGeneration && !this.closed) {
735
+ this.lastSnapshot = unavailable(workspace, errorMessage(error), generation);
736
+ this.options.onChange(this.snapshot());
737
+ }
738
+ if (controller) {
739
+ await withTimeout(controller.close(), 2_500, 'Supercode shutdown').catch(() => undefined);
740
+ }
741
+ }
742
+ }
743
+
744
+ private async createHeadlessController(
745
+ workspace: string,
746
+ autoObserve: boolean,
747
+ ): Promise<HeadlessController> {
748
+ if (this.options.createClient) {
749
+ const client = await this.options.createClient(workspace);
750
+ if (this.options.createController) {
751
+ return this.options.createController(client, workspace, { autoObserve });
752
+ }
753
+ const { SupercodeController, assertSupercodeClientSnapshot } =
754
+ await importSupercodeController(this.options.engineRoot);
755
+ this.assertSnapshot = assertSupercodeClientSnapshot;
756
+ return new SupercodeController({
757
+ client,
758
+ workspace,
759
+ policy: 'yolo',
760
+ ownsClient: true,
761
+ autoObserve,
762
+ });
763
+ }
764
+ const { SupercodeHarnessClient, SupercodeController, assertSupercodeClientSnapshot } =
765
+ await importSupercodePackages(this.options.engineRoot);
766
+ this.assertSnapshot = assertSupercodeClientSnapshot;
767
+ const command = findSupercodeCommand(this.options.engineRoot);
768
+ const client = new SupercodeHarnessClient({
769
+ cwd: workspace,
770
+ ...(command ? { command } : {}),
771
+ });
772
+ return new SupercodeController({
773
+ client,
774
+ workspace,
775
+ policy: 'yolo',
776
+ ownsClient: true,
777
+ autoObserve,
778
+ });
779
+ }
780
+
781
+ private capture(): void {
782
+ if (!this.controller) return;
783
+ const snapshot = this.assertSnapshot(this.controller.getSnapshot());
784
+ if (snapshot.workspace !== this.workspace) return;
785
+ this.lastSnapshot = toSnapshot(snapshot, this.workspaceGeneration);
786
+ this.options.onChange(this.snapshot());
787
+ }
788
+
789
+ private beginWorkspace(workspace: string): void {
790
+ if (this.workspace === workspace) return;
791
+ this.workspace = workspace;
792
+ this.workspaceGeneration++;
793
+ this.lastSnapshot = loading(workspace, this.workspaceGeneration);
794
+ this.options.onChange(this.snapshot());
795
+ }
796
+ }