@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.
- package/README.md +24 -0
- package/dist/assets/axe-C7YBtOuM.js +30 -0
- package/dist/assets/browserAll-DDtGGE48.js +1 -0
- package/dist/assets/create-runtime-DbTlBlKG.js +1 -0
- package/dist/assets/{game-BI111o5f.js → game-BE0r7DMI.js} +3 -3
- package/dist/assets/game-BdTHR2_X.js +1 -0
- package/dist/assets/{game-pt_1lQLv.js → game-C3iQGSqo.js} +1 -1
- package/dist/assets/game-CcHfYxjP.js +1 -0
- package/dist/assets/game-Cqzj_lpD.js +1 -0
- package/dist/assets/game-DSZdeCHu.js +1 -0
- package/dist/assets/game-Dch9sAPc.js +1 -0
- package/dist/assets/{game-xsOho_wg.js → game-SAFn90pA.js} +3 -3
- package/dist/assets/game-loop-DMIgSMV-.js +1 -0
- package/dist/assets/{game-source-CvidN7AB.js → game-source-Dk83w26t.js} +1 -1
- package/dist/assets/{index-Bp5cecD6.js → index-4idIsHHJ.js} +2 -2
- package/dist/assets/{index-DL7RcQ0n.js → index-C-LhOTBN.js} +1 -1
- package/dist/assets/{index-Cs8xwv3e.js → index-CDkxceqE.js} +1 -1
- package/dist/assets/index-CQEfi217.js +15412 -0
- package/dist/assets/index-Dvi-V0NE.css +1 -0
- package/dist/assets/ingest-siblings-s0P5I1Ub.js +1 -0
- package/dist/assets/mount-manifest-DXMOlF79.js +1 -0
- package/dist/assets/react-18-C9eHn8x1.js +1 -0
- package/dist/assets/webworkerAll-Fn0e-ouF.js +1 -0
- package/dist/assets/world-documents-GM0a9Kv7.js +1 -0
- package/dist/index.html +15 -13
- package/dist/ingest/racing-game/vgai.game.json +5 -4
- package/dist/ingest/simcity/vgai.game.json +5 -4
- package/dist/ingest/tanks/vgai.game.json +5 -4
- package/dist/project-scripts.bundle.js +3917 -0
- package/dist/scenes/untitled.vscn.json +2 -0
- package/dist-server/packaged.mjs +7697 -3435
- package/package.json +31 -4
- package/scripts/probe-reporter.ts +349 -0
- package/scripts/run-probe.ts +2260 -0
- package/server/asset-catalog-audit.ts +268 -0
- package/server/asset-catalog-v2.ts +146 -0
- package/server/asset-library-routes.ts +276 -58
- package/server/canonical-path.ts +1 -1
- package/server/dev.ts +148 -44
- package/server/editor-server.ts +888 -61
- package/server/editor-sse.ts +115 -4
- package/server/harness-chat-service.ts +796 -0
- package/server/local-asset-catalog.ts +157 -22
- package/server/packaged.ts +51 -21
- package/server/process-shutdown.ts +136 -0
- package/server/prod.ts +9 -6
- package/server/project-hmr-files.ts +41 -0
- package/server/project-operations.ts +343 -0
- package/server/project-optimize-deps-entries.ts +4 -4
- package/server/project-output-writer.ts +156 -0
- package/server/project-validation.ts +140 -0
- package/server/project-view.ts +8 -50
- package/server/server-utils.ts +75 -10
- package/server/session-registry.ts +12 -0
- package/src/EditorContext.tsx +107 -3
- package/src/action-registry.ts +115 -17
- package/src/adapter-resolver.ts +213 -478
- package/src/asset-events.ts +64 -1
- package/src/asset-preview.ts +906 -0
- package/src/asset-selection.ts +121 -0
- package/src/asset-workflow/asset-capabilities.ts +259 -0
- package/src/asset-workflow/asset-editor-errors.ts +46 -0
- package/src/asset-workflow/asset-import-pipeline.ts +176 -0
- package/src/asset-workflow/asset-library-query.ts +107 -0
- package/src/asset-workflow/asset-types.ts +192 -0
- package/src/asset-workflow/asset-workflow-quality.ts +65 -0
- package/src/asset-workflow/import-contract.ts +113 -0
- package/src/asset-workflow/model-conversion.ts +126 -0
- package/src/asset-workflow/model-inspection.ts +185 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +153 -0
- package/src/asset-workflow/project-asset-operations.ts +119 -0
- package/src/asset-workflow/thumbnail-system.ts +172 -0
- package/src/authoring/active-systems.ts +15 -0
- package/src/authoring/boundary-authoring-adapter.ts +16 -5
- package/src/authoring/composite-authoring-adapter.ts +305 -124
- package/src/authoring/design-time-layers.ts +300 -117
- package/src/authoring/edit-mode-authoring.ts +278 -293
- package/src/authoring/embed-only-adapter.ts +4 -2
- package/src/authoring/first-party-hierarchy-menu.ts +7 -2
- package/src/authoring/first-party-inspector/AddSectionMenu.tsx +5 -12
- package/src/authoring/first-party-inspector/EnvironmentSection.tsx +53 -126
- package/src/authoring/first-party-inspector/HeaderSection.tsx +54 -65
- package/src/authoring/first-party-inspector/is-first-party-selection.ts +30 -11
- package/src/authoring/first-party-inspector/register.tsx +13 -2
- package/src/authoring/ingest-authoring-adapter.ts +142 -45
- package/src/authoring/mount-failure-report.ts +5 -5
- package/src/authoring/provenance.ts +41 -0
- package/src/authoring/react-dom-authoring-adapter.ts +92 -92
- package/src/authoring/react-inspector-section.tsx +489 -51
- package/src/authoring/react-world-authoring-adapter.ts +541 -306
- package/src/authoring/story-overlay.ts +1 -1
- package/src/authoring/story-types.ts +11 -0
- package/src/authoring/vgai-scene-authoring-adapter.ts +54 -25
- package/src/authoring/viewport-tool-context.ts +78 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +186 -0
- package/src/authoring/world-pan-state.ts +33 -17
- package/src/authoring/world-session-state.ts +1 -1
- package/src/authoring/world2d-authoring-adapter.ts +109 -52
- package/src/availability-tick.ts +56 -0
- package/src/browser-play-entry.ts +5 -5
- package/src/browser-project-scripts.ts +1 -0
- package/src/build-identity.ts +15 -0
- package/src/command-listener.ts +893 -19
- package/src/components/AlignToolbar.tsx +74 -63
- package/src/components/AppRoot.tsx +193 -26
- package/src/components/ApplicationMenus.tsx +272 -0
- package/src/components/AssetBrowser.tsx +1450 -293
- package/src/components/AssetEditorShell.tsx +141 -0
- package/src/components/AssetImportDetails.tsx +218 -0
- package/src/components/AssetLibraryPanel.tsx +14 -0
- package/src/components/CameraInfo.tsx +11 -20
- package/src/components/CenterDocuments.tsx +585 -0
- package/src/components/CommandPalette.tsx +75 -30
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +57 -112
- package/src/components/DebugConsolePanel.tsx +461 -0
- package/src/components/DebuggerPanel.tsx +165 -0
- package/src/components/DefaultEditorLayout.tsx +167 -231
- package/src/components/GameHierarchy.tsx +873 -290
- package/src/components/GamePerformanceOverlay.tsx +46 -0
- package/src/components/HarnessChatPanel.tsx +2352 -0
- package/src/components/HistoryPanel.tsx +226 -0
- package/src/components/Inspector.tsx +153 -99
- package/src/components/InspectorToolSection.tsx +6 -4
- package/src/components/MountFailureBanner.tsx +31 -48
- package/src/components/NetworkSection.tsx +25 -14
- package/src/components/OnlineAssetBrowser.tsx +794 -343
- package/src/components/OverlayReportBanner.tsx +38 -64
- package/src/components/PerformancePanel.tsx +206 -0
- package/src/components/PlayBar.tsx +93 -263
- package/src/components/PrefabSaveDialog.tsx +55 -127
- package/src/components/ProjectHeader.css +60 -0
- package/src/components/ProjectHeader.tsx +170 -105
- package/src/components/ProjectScreen.tsx +246 -184
- package/src/components/ReactCanvasControls.tsx +185 -0
- package/src/components/ResolutionPicker.tsx +85 -0
- package/src/components/RootActivateDialog.tsx +39 -68
- package/src/components/SaveDialog.tsx +55 -127
- package/src/components/SaveStatus.tsx +11 -29
- package/src/components/StartupLoadingScreen.tsx +96 -0
- package/src/components/StateWatchPanel.tsx +302 -0
- package/src/components/StatsOverlay.tsx +12 -5
- package/src/components/StatusBar.tsx +64 -0
- package/src/components/ThemeManagerDialog.css +89 -0
- package/src/components/ThemeManagerDialog.tsx +371 -0
- package/src/components/ToolHost.tsx +26 -15
- package/src/components/Toolbar.tsx +117 -214
- package/src/components/VgaiVoxelLogo.tsx +89 -0
- package/src/components/ViewportOverlay.tsx +97 -172
- package/src/components/ViewportPanel.tsx +130 -31
- package/src/components/WorkspaceDock.tsx +891 -0
- package/src/components/WorldSelectionOverlay.tsx +51 -63
- package/src/components/WorldTextEditor.tsx +9 -5
- package/src/components/asset-documents.tsx +390 -0
- package/src/components/asset-editor-persistence.ts +44 -0
- package/src/components/asset-selection-section.tsx +338 -0
- package/src/components/asset-viewers/AudioViewer.tsx +73 -21
- package/src/components/asset-viewers/GenericJsonViewer.tsx +26 -11
- package/src/components/asset-viewers/ImageViewer.tsx +43 -11
- package/src/components/asset-viewers/InputMapViewer.tsx +512 -134
- package/src/components/asset-viewers/MaterialAssetEditor.tsx +540 -0
- package/src/components/asset-viewers/ModelViewer.tsx +805 -121
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +177 -60
- package/src/components/asset-viewers/SourceAssetViewer.tsx +121 -0
- package/src/components/asset-workflow.css +506 -0
- package/src/components/build-documents.tsx +696 -0
- package/src/components/core-utilities.tsx +75 -0
- package/src/components/data-documents.tsx +364 -0
- package/src/components/{DataPanel.tsx → data-editor.tsx} +138 -288
- package/src/components/debugger-panel-state.ts +57 -0
- package/src/components/inspector-widgets/AlignmentGrid.tsx +8 -14
- package/src/components/inspector-widgets/BorderEditor.tsx +41 -33
- package/src/components/inspector-widgets/ColorPicker.tsx +39 -39
- package/src/components/inspector-widgets/FontPicker.tsx +13 -21
- package/src/components/inspector-widgets/GradientEditor.tsx +11 -19
- package/src/components/inspector-widgets/ScrubbableInput.tsx +6 -3
- package/src/components/inspector-widgets/ShadowEditor.tsx +20 -33
- package/src/components/inspector-widgets/shared.tsx +90 -49
- package/src/components/inspectors/AnimationSection.tsx +39 -40
- package/src/components/inspectors/AudioSection.tsx +28 -39
- package/src/components/inspectors/BoneAttachmentSection.tsx +19 -24
- package/src/components/inspectors/CameraSection.tsx +14 -23
- package/src/components/inspectors/ComponentsSection.tsx +39 -43
- package/src/components/inspectors/JointsSection.tsx +34 -43
- package/src/components/inspectors/LightSection.tsx +90 -35
- package/src/components/inspectors/MaterialSection.tsx +137 -174
- package/src/components/inspectors/MeshSection.tsx +9 -17
- package/src/components/inspectors/NavigationSection.tsx +8 -15
- package/src/components/inspectors/ParticleSection.tsx +91 -76
- package/src/components/inspectors/PhysicsSection.tsx +42 -46
- package/src/components/inspectors/PostProcessingSection.tsx +28 -36
- package/src/components/inspectors/RenderingSection.tsx +12 -20
- package/src/components/inspectors/ShadowSection.tsx +7 -8
- package/src/components/inspectors/SplineSection.tsx +31 -42
- package/src/components/inspectors/TransformSection.tsx +14 -22
- package/src/components/patterns/Dialog.tsx +95 -0
- package/src/components/patterns/Fields.tsx +44 -0
- package/src/components/patterns/List.tsx +25 -0
- package/src/components/patterns/StateSurface.tsx +40 -0
- package/src/components/patterns/Surfaces.tsx +115 -0
- package/src/components/patterns/Tabs.tsx +80 -0
- package/src/components/patterns/Toolbar.tsx +51 -0
- package/src/components/patterns/Tree.tsx +72 -0
- package/src/components/primitives/Button.tsx +46 -0
- package/src/components/primitives/ColorInput.tsx +11 -47
- package/src/components/primitives/EditorIcon.tsx +99 -0
- package/src/components/primitives/FormControls.tsx +71 -0
- package/src/components/primitives/JsonInput.tsx +15 -2
- package/src/components/primitives/Layout.tsx +100 -0
- package/src/components/primitives/Menu.tsx +105 -0
- package/src/components/primitives/NumberInput.tsx +8 -35
- package/src/components/primitives/Panel.tsx +49 -44
- package/src/components/primitives/SectionHeader.tsx +10 -28
- package/src/components/primitives/Text.tsx +46 -0
- package/src/components/primitives/Tooltip.tsx +20 -12
- package/src/components/primitives/Vec3Input.tsx +3 -0
- package/src/components/primitives/banner-tones.ts +33 -0
- package/src/components/primitives/editor-icons.ts +102 -0
- package/src/components/primitives/panel-header-styles.ts +30 -0
- package/src/components/project-operation-documents.tsx +226 -0
- package/src/components/status-contributions.tsx +175 -0
- package/src/components/story-documents-register.ts +15 -0
- package/src/components/story-documents.tsx +1185 -0
- package/src/components/tool-documents.tsx +110 -0
- package/src/components/workspace-dock.css +293 -0
- package/src/components/workspace-history.ts +33 -0
- package/src/components/world-documents.tsx +145 -0
- package/src/components/xstate-documents.tsx +209 -0
- package/src/composite-screenshot.ts +248 -0
- package/src/design-system/index.ts +70 -0
- package/src/design-system-stories/Foundation.stories.tsx +183 -0
- package/src/design-system-stories/Patterns.stories.tsx +251 -0
- package/src/design-system-stories/Workspace.stories.tsx +538 -0
- package/src/doctor/report.ts +57 -13
- package/src/editor-api.ts +295 -31
- package/src/editor-compatibility.ts +200 -0
- package/src/editor-console.ts +35 -17
- package/src/editor-hotkeys.ts +85 -33
- package/src/editor-store.ts +538 -233
- package/src/editor-viewport.ts +160 -34
- package/src/entity-factory.ts +14 -3
- package/src/gizmo-registry.ts +21 -5
- package/src/harness-chat-editor-context.ts +37 -0
- package/src/harness-chat-indicator.ts +68 -0
- package/src/harness-chat-layout.ts +53 -0
- package/src/harness-chat-lifecycle.ts +170 -0
- package/src/harness-chat-reconnect.ts +85 -0
- package/src/harness-chat-types.ts +409 -0
- package/src/hierarchy-drop.ts +50 -0
- package/src/hierarchy-expansion-state.ts +100 -0
- package/src/hierarchy-menu-registry.ts +1 -1
- package/src/hierarchy-projection.ts +76 -0
- package/src/history/editor-session.ts +21 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-service.ts +904 -0
- package/src/history/json-history-resource.ts +165 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +266 -0
- package/src/history/project-root-history-backends.ts +53 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +108 -0
- package/src/history/source-history-backend.ts +374 -0
- package/src/history/types.ts +100 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hosted-example.ts +12 -2
- package/src/hotkeys.ts +89 -18
- package/src/ingest/discovery-react.ts +9 -10
- package/src/ingest/discovery.ts +4 -4
- package/src/ingest/discovery2d.ts +5 -5
- package/src/ingest/games/bloom-composer/vgai.game.json +3 -3
- package/src/ingest/games/embed-bundle/vgai.game.json +3 -3
- package/src/ingest/games/games-fps/vgai.game.json +3 -3
- package/src/ingest/games/iframe-reachable/vgai.game.json +3 -3
- package/src/ingest/games/interactive-cubes/vgai.game.json +3 -3
- package/src/ingest/games/mismatched-three/vgai.game.json +3 -3
- package/src/ingest/games/own-three-bundled/vgai.game.json +3 -3
- package/src/ingest/games/pointerlock-maze/vgai.game.json +7 -4
- package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +3 -3
- package/src/ingest/games-2d/bunnymark/vgai.game.json +3 -3
- package/src/ingest/games-2d/flappy/vgai.game.json +3 -3
- package/src/ingest/games-2d/flappy-pixi/vgai.game.json +3 -3
- package/src/ingest/games-2d/puzzling-potions/vgai.game.json +3 -3
- package/src/ingest/games-2d/tilemap-camera/vgai.game.json +3 -3
- package/src/ingest/games-react/react-rpg/vgai.game.json +3 -3
- package/src/ingest/types.ts +1 -1
- package/src/ingest-mode.ts +47 -52
- package/src/ingest-siblings.ts +23 -33
- package/src/initial-scene.ts +1 -2
- package/src/main.tsx +16 -8
- package/src/manifest-project.ts +10 -12
- package/src/model-thumbnail.ts +261 -26
- package/src/module-mode.ts +44 -50
- package/src/play-log-events.ts +26 -0
- package/src/play-mode.ts +495 -401
- package/src/play-scene-adoption.ts +23 -0
- package/src/playground/main.ts +68 -24
- package/src/playwright-shim.ts +473 -0
- package/src/project-manager.ts +90 -10
- package/src/project-operations.ts +112 -0
- package/src/project-story-discovery.ts +25 -0
- package/src/project-tool-discovery.ts +43 -0
- package/src/scene-serializer.ts +0 -6
- package/src/scene-sync.ts +7 -2
- package/src/script-loader.ts +3 -3
- package/src/skeleton-helper.ts +57 -0
- package/src/storage/seed.ts +68 -8
- package/src/stories/StoryPreviewMount.tsx +265 -0
- package/src/stories/action-stories.fixture.tsx +44 -0
- package/src/stories/button-stories.fixture.tsx +50 -0
- package/src/stories/compose-project-stories.ts +182 -0
- package/src/stories/loader-stories.fixture.tsx +53 -0
- package/src/stories/story-discovery.ts +130 -0
- package/src/stories/story-registry.ts +146 -0
- package/src/theme-css.ts +1049 -0
- package/src/theme-library.ts +238 -0
- package/src/theme-preference.ts +130 -0
- package/src/theme.ts +527 -0
- package/src/tool-loader.ts +80 -24
- package/src/ui-source/inspect.ts +27 -4
- package/src/ui-source/source-write-backend.ts +83 -22
- package/src/widgets/index.ts +9 -18
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-dock-controller.ts +64 -0
- package/src/workspace-document-ids.ts +5 -0
- package/src/workspace-document-registry.ts +367 -0
- package/src/workspace-state-persistence.ts +455 -0
- package/src/workspace-status-registry.ts +110 -0
- package/src/workspace-utility-commands.ts +49 -0
- package/src/workspace-utility-registry.ts +161 -0
- package/src/world-document-routing.ts +16 -0
- package/src/xstate-inspect/XStateMachineInspector.tsx +544 -0
- package/src/xstate-inspect/character-animation-machine.fixture.ts +74 -0
- package/src/xstate-inspect/use-live-actor-state.ts +39 -0
- package/src/xstate-inspect/xstate-graph.ts +265 -0
- package/src/xstate-inspect/xstate-layout.ts +76 -0
- package/src/z-index.ts +26 -0
- package/template/.claude/skills/editor/SKILL.md +129 -90
- package/template/AGENTS.md +1110 -0
- package/template/CLAUDE.md +1 -512
- package/template/IDIOMS.md +50 -0
- package/template/check-idioms.ts +706 -0
- package/template/manifest-entry-modules-plugin.ts +53 -0
- package/template/package.json +13 -4
- package/template/public/scenes/default.scn2d.json +6 -0
- package/template/public/scenes/default.vscn.json +7 -1
- package/template/public/scenes/untitled.vscn.json +10 -0
- package/template/scripts/playtest.ts +40 -0
- package/template/server/loopback.ts +75 -0
- package/template/server/rooms/arena-room.ts +69 -1
- package/template/server/rooms/game-room.ts +68 -1
- package/template/src/data/README.md +9 -4
- package/template/src/data/data-ref.ts +35 -0
- package/template/src/data/tuning.schema.ts +1 -1
- package/template/src/main.ts +41 -62
- package/template/src/manifest-entry-modules.d.ts +3 -0
- package/template/src/operations/README.md +88 -0
- package/template/src/runtime/pixi-adapter.ts +24 -0
- package/template/src/scripts/components/data-spinner.ts +43 -2
- package/template/src/scripts/components/spin-lap-math.ts +27 -0
- package/template/src/tools/README.md +5 -3
- package/template/src/tools/example.tool.tsx +13 -7
- package/template/src/ui/Button.stories.tsx +66 -0
- package/template/src/ui/RebindPanel.tsx +2 -6
- package/template/src/ui/game.tsx +17 -0
- package/template/tests/acceptance/example.spec.ts +64 -0
- package/template/tests/logic/example.test.ts +31 -0
- package/template/tests/logic/room.test.ts +100 -0
- package/template/tests/tsconfig.json +30 -0
- package/template/validate-manifest.ts +5 -5
- package/template/validate-scenes.ts +50 -18
- package/template/vgai.game.json +20 -13
- package/template/vite.config.ts +77 -16
- package/vite-plugin-ui-oid.ts +261 -17
- package/dist/assets/CCDIKSolver-Dz-ysxZF.js +0 -1
- package/dist/assets/SelectionBox-BXsv_6aH.js +0 -1
- package/dist/assets/browser-project-scripts-DOru4L1x.js +0 -2
- package/dist/assets/browserAll-CEkWYlFk.js +0 -1
- package/dist/assets/create-runtime-DYIWr3va.js +0 -1
- package/dist/assets/data-asset-Bb9sIM7i.js +0 -5
- package/dist/assets/game-8SDZPPgH.js +0 -1
- package/dist/assets/game-CevorxyV.js +0 -1
- package/dist/assets/game-Di7XlywD.js +0 -1
- package/dist/assets/game-DjnCaw8R.js +0 -1
- package/dist/assets/game-Q9A0aXSV.js +0 -1
- package/dist/assets/index-CPVV6pjw.js +0 -1
- package/dist/assets/index-CpOnYyeD.js +0 -213
- package/dist/assets/index-Dr8H_6Rl.js +0 -1
- package/dist/assets/index-_bCaipUk.js +0 -13650
- package/dist/assets/ingest-siblings-Dz8eEqrV.js +0 -1
- package/dist/assets/mount-manifest-Bhk0ohsl.js +0 -1
- package/dist/assets/scene-query-BivAHhJu.js +0 -1
- package/dist/assets/webworkerAll-BLrH0F6F.js +0 -1
- package/dist/assets/xstate-animation-binding-1NGLmw6y.js +0 -1
- package/dist/assets/xstate.esm-BgUD33nl.js +0 -3
- package/src/authoring/scene-ui-layer.ts +0 -485
- package/src/authoring/ui-authoring-adapter.ts +0 -325
- package/src/authoring/ui-hierarchy-menu.ts +0 -40
- package/src/authoring/ui-inspector-sections.tsx +0 -156
- package/src/authoring/undo-timeline.ts +0 -145
- package/src/components/BottomPanel.tsx +0 -326
- package/src/components/BuildPanel.tsx +0 -455
- package/src/components/FolderTree.tsx +0 -179
- package/src/components/RightPanel.tsx +0 -166
- package/src/components/WorldContextMenu.tsx +0 -257
- package/src/components/asset-viewers/AssetViewer.tsx +0 -38
- package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +0 -129
- package/src/scene-ui-mode/active-scene-ui.ts +0 -58
- package/src/scene-ui-mode/scene-ui-palette-panel.tsx +0 -152
- package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +0 -29
- package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +0 -74
- package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +0 -49
- package/src/scene-ui-mode/ui-prefab-io.ts +0 -76
- package/src/ui-editor/inspector.tsx +0 -563
- package/src/ui-editor/node-ops.ts +0 -266
- package/src/ui-editor/overlay.tsx +0 -380
- package/src/ui-editor/palette.tsx +0 -114
- package/src/ui-editor/react-store.ts +0 -78
- package/src/ui-editor/timeline-panel.tsx +0 -443
- package/src/ui-editor/ui-edit-store.ts +0 -552
- package/src/ui-editor/ui-prefab.ts +0 -95
- package/template/project.json +0 -9
- package/template/src/scripts/main.ts +0 -94
- package/template/src/ui/DefaultHud.tsx +0 -40
- package/template/src/ui/game-state.tsx +0 -37
- package/template/src/ui/mount-default-hud.tsx +0 -19
- package/template/src/ui/mount-ui.ts +0 -15
- 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
|
+
}
|