@volter/editor-core 0.5.57
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/BUNDLED_NOTICES +1527 -0
- package/LICENSE +686 -0
- package/LICENSE-APACHE +202 -0
- package/NOTICE +18 -0
- package/README.md +24 -0
- package/dist/build/chunk-JS4D4XZ3.js +175 -0
- package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
- package/dist/build/vite-plugin-product-contributions.js +100 -0
- package/dist/build/vite-plugin-product-contributions.js.map +7 -0
- package/dist/build/vite-plugin-shared-react.js +25 -0
- package/dist/build/vite-plugin-shared-react.js.map +7 -0
- package/dist/build/vite-plugin-shared-three.js +86 -0
- package/dist/build/vite-plugin-shared-three.js.map +7 -0
- package/dist/server/chunk-KMJC7AGO.js +98 -0
- package/dist/server/chunk-KMJC7AGO.js.map +7 -0
- package/dist/server/chunk-PSILUMGS.js +46 -0
- package/dist/server/chunk-PSILUMGS.js.map +7 -0
- package/dist/server/frame-proxy.js +255 -0
- package/dist/server/frame-proxy.js.map +7 -0
- package/dist/server/open-browser.js +53 -0
- package/dist/server/open-browser.js.map +7 -0
- package/dist/server/process-shutdown.js +127 -0
- package/dist/server/process-shutdown.js.map +7 -0
- package/dist/server/session-registry.js +141 -0
- package/dist/server/session-registry.js.map +7 -0
- package/dist/server/spawn-opener.js +11 -0
- package/dist/server/spawn-opener.js.map +7 -0
- package/dist/server/worktree-identity.js +11 -0
- package/dist/server/worktree-identity.js.map +7 -0
- package/dist-server/packaged.mjs +88816 -0
- package/package.json +118 -0
- package/scripts/build-plugins.mjs +14 -0
- package/scripts/build-server.mjs +26 -0
- package/scripts/build-session.mjs +9 -0
- package/server/account-credentials.ts +60 -0
- package/server/account-service.ts +1434 -0
- package/server/adapter-region-includes.ts +47 -0
- package/server/asset-catalog-v2.ts +175 -0
- package/server/asset-history-snapshots.ts +196 -0
- package/server/asset-ledger-store.ts +195 -0
- package/server/asset-library-routes.ts +1595 -0
- package/server/blender-wasm-artifact.ts +430 -0
- package/server/boot-timings.ts +106 -0
- package/server/canonical-path.ts +50 -0
- package/server/checkout-workspace-preflight.ts +349 -0
- package/server/cloud-asset-catalog.ts +158 -0
- package/server/coding-inference-launch.ts +237 -0
- package/server/collaboration-account-client.ts +202 -0
- package/server/collaboration-attribution.ts +122 -0
- package/server/collaboration-session.ts +934 -0
- package/server/comfyui-bridge.ts +248 -0
- package/server/console-ledger.ts +499 -0
- package/server/creation-site-transform.ts +304 -0
- package/server/creation-site-write.ts +648 -0
- package/server/data-file-serialize.ts +158 -0
- package/server/editor-brand-html.ts +72 -0
- package/server/editor-control-socket.ts +366 -0
- package/server/editor-server-options.ts +190 -0
- package/server/editor-server.ts +1483 -0
- package/server/editor-sse.ts +352 -0
- package/server/engine-provenance.ts +169 -0
- package/server/engine-source-restart.ts +117 -0
- package/server/frame-bridge.ts +115 -0
- package/server/frame-proxy.ts +490 -0
- package/server/frame-workbench.ts +259 -0
- package/server/frontend-handoff.ts +229 -0
- package/server/game-globals-shadow.ts +113 -0
- package/server/gameplay-session-retention.ts +149 -0
- package/server/gameplay-sessions.ts +123 -0
- package/server/generation-jobs.ts +201 -0
- package/server/generation-reconciler.ts +137 -0
- package/server/generative-execution-context.ts +59 -0
- package/server/git-workflow.ts +612 -0
- package/server/harness-chat-caller.ts +36 -0
- package/server/harness-chat-service.ts +1681 -0
- package/server/idle-shutdown.ts +170 -0
- package/server/js-profiling-policy.ts +52 -0
- package/server/launcher-settings.ts +84 -0
- package/server/local-asset-catalog.ts +410 -0
- package/server/managed-account-defaults.ts +22 -0
- package/server/mock-control-plane.ts +540 -0
- package/server/model-import-conversion.ts +345 -0
- package/server/native-credential-store.ts +162 -0
- package/server/open-browser.ts +118 -0
- package/server/packaged.ts +1192 -0
- package/server/play-stall.ts +126 -0
- package/server/process-shutdown.ts +218 -0
- package/server/product-presets.ts +55 -0
- package/server/project-build-artifact.ts +95 -0
- package/server/project-components.ts +100 -0
- package/server/project-dependency-invalidation.ts +114 -0
- package/server/project-file-scan.ts +235 -0
- package/server/project-hmr-files.ts +215 -0
- package/server/project-install-roots.ts +210 -0
- package/server/project-kinds.ts +71 -0
- package/server/project-mcp-servers.ts +82 -0
- package/server/project-module-freshness.ts +109 -0
- package/server/project-module-instance.ts +375 -0
- package/server/project-optimize-deps-entries.ts +827 -0
- package/server/project-output-writer.ts +679 -0
- package/server/project-package-origin.ts +63 -0
- package/server/project-root-surface.ts +831 -0
- package/server/project-scratch-path.ts +47 -0
- package/server/project-script-hmr.ts +619 -0
- package/server/project-serving-plugins.ts +236 -0
- package/server/project-tools.ts +653 -0
- package/server/project-validation.ts +250 -0
- package/server/project-verbs.ts +127 -0
- package/server/project-view.ts +52 -0
- package/server/project-watch.ts +1134 -0
- package/server/project-work-coordinator.ts +288 -0
- package/server/provider-credentials.ts +331 -0
- package/server/recent-projects-store.ts +98 -0
- package/server/redact-secrets.ts +71 -0
- package/server/repository-presence.ts +267 -0
- package/server/routes/account.ts +357 -0
- package/server/routes/agents.ts +60 -0
- package/server/routes/assets.ts +154 -0
- package/server/routes/build.ts +13 -0
- package/server/routes/collaboration.ts +595 -0
- package/server/routes/configurations.ts +419 -0
- package/server/routes/context.ts +264 -0
- package/server/routes/control-plane.ts +1811 -0
- package/server/routes/logs.ts +272 -0
- package/server/routes/project-identity.ts +302 -0
- package/server/routes/project-open.ts +429 -0
- package/server/routes/project-source.ts +686 -0
- package/server/routes/project-state.ts +383 -0
- package/server/routes/relay.ts +1029 -0
- package/server/routes/route-helpers.ts +29 -0
- package/server/routes/served-modules.ts +138 -0
- package/server/routes/session-tabs.ts +196 -0
- package/server/routes/settings.ts +121 -0
- package/server/routes/share-control.ts +486 -0
- package/server/routes/themes.ts +116 -0
- package/server/routes/tools.ts +230 -0
- package/server/routes/worktrees.ts +595 -0
- package/server/scoped-game-css.ts +461 -0
- package/server/server-utils.ts +1194 -0
- package/server/session-product.ts +112 -0
- package/server/session-registry.ts +230 -0
- package/server/share-claims.ts +138 -0
- package/server/share-host.ts +526 -0
- package/server/share-session-gateway.ts +1362 -0
- package/server/share-tunnel.ts +123 -0
- package/server/spawn-opener.ts +74 -0
- package/server/support/play/log-format.ts +76 -0
- package/server/support/play/session-record.ts +199 -0
- package/server/support/project/build-discipline.ts +718 -0
- package/server/support/project/inspection-node.ts +119 -0
- package/server/support/project/log-naming.ts +54 -0
- package/server/support/project/provenance.ts +290 -0
- package/server/support/project/run-name.ts +18 -0
- package/server/support/project/session-journal.ts +830 -0
- package/server/support/project/shared.ts +327 -0
- package/server/tab-bootstrap.ts +62 -0
- package/server/tab-heartbeat.ts +477 -0
- package/server/tab-lifecycle.ts +550 -0
- package/server/tab-presence.ts +1508 -0
- package/server/team-agent-mirror.ts +95 -0
- package/server/twin-auth.ts +138 -0
- package/server/vendored-lock-recorder.ts +968 -0
- package/server/worktree-identity.ts +121 -0
- package/server/worktree-management.ts +398 -0
- package/server/worktree-retention.d.mts +13 -0
- package/server/worktree-retention.mjs +205 -0
- package/src/EditorContext.tsx +169 -0
- package/src/account.ts +333 -0
- package/src/action-registry.ts +532 -0
- package/src/active-product.ts +74 -0
- package/src/active-project.ts +155 -0
- package/src/adapter-editor-config.ts +25 -0
- package/src/adapter-observation.ts +49 -0
- package/src/animation/stage-transport.ts +379 -0
- package/src/animation/three-clips-subject.ts +172 -0
- package/src/api/asset-library-wire.ts +45 -0
- package/src/api/assets.ts +365 -0
- package/src/api/base.ts +10 -0
- package/src/api/build.ts +99 -0
- package/src/api/git-wire.ts +56 -0
- package/src/api/logs.ts +92 -0
- package/src/api/project-identity.ts +74 -0
- package/src/api/project-open.ts +355 -0
- package/src/api/project-source.ts +162 -0
- package/src/api/project-state.ts +107 -0
- package/src/api/relay.ts +268 -0
- package/src/api/settings.ts +36 -0
- package/src/api/themes.ts +45 -0
- package/src/api/worktrees.ts +257 -0
- package/src/asset-compare-core.ts +171 -0
- package/src/asset-compare.ts +294 -0
- package/src/asset-editor-context.tsx +100 -0
- package/src/asset-events.ts +97 -0
- package/src/asset-inspector-actions.ts +87 -0
- package/src/asset-preview-framing.ts +357 -0
- package/src/asset-preview.ts +2802 -0
- package/src/asset-selection-viewer-registry.ts +113 -0
- package/src/asset-selection.ts +133 -0
- package/src/asset-workflow/asset-capabilities.ts +344 -0
- package/src/asset-workflow/asset-import-jobs.ts +106 -0
- package/src/asset-workflow/asset-ledger-backend.ts +126 -0
- package/src/asset-workflow/asset-ledger.ts +156 -0
- package/src/asset-workflow/asset-materialization-report.ts +140 -0
- package/src/asset-workflow/asset-pack-manifest.ts +320 -0
- package/src/asset-workflow/asset-types.ts +142 -0
- package/src/asset-workflow/asset-workflow-quality.ts +61 -0
- package/src/asset-workflow/audio-preview-player.ts +193 -0
- package/src/asset-workflow/audio-waveform.ts +22 -0
- package/src/asset-workflow/cloud-asset-client.ts +263 -0
- package/src/asset-workflow/folder-preview.ts +345 -0
- package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
- package/src/asset-workflow/image-view-scale.ts +32 -0
- package/src/asset-workflow/import-contract.ts +124 -0
- package/src/asset-workflow/ledger-write-lock.ts +244 -0
- package/src/asset-workflow/model-inspection.ts +818 -0
- package/src/asset-workflow/pixi-spritesheet.ts +197 -0
- package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +139 -0
- package/src/asset-workflow/project-asset-operations.ts +154 -0
- package/src/asset-workflow/project-asset-roots.ts +68 -0
- package/src/asset-workflow/project-content.ts +288 -0
- package/src/asset-workflow/project-source-index.ts +550 -0
- package/src/asset-workflow/thumbnail-system.ts +256 -0
- package/src/authoring/active-adapter.ts +199 -0
- package/src/authoring/active-systems.ts +422 -0
- package/src/authoring/adapter-key.ts +18 -0
- package/src/authoring/authoring-asset-url.ts +27 -0
- package/src/authoring/bootstrap-state.ts +49 -0
- package/src/authoring/boundary-authoring-adapter.ts +184 -0
- package/src/authoring/breakpoint-state.ts +43 -0
- package/src/authoring/canvas-scene-guides.ts +76 -0
- package/src/authoring/component-instance-root.ts +171 -0
- package/src/authoring/composite-authoring-adapter.ts +2110 -0
- package/src/authoring/consumer-actions.ts +520 -0
- package/src/authoring/css-numeric-style.ts +97 -0
- package/src/authoring/design-time-layers.ts +849 -0
- package/src/authoring/design-time-mount-registry.ts +235 -0
- package/src/authoring/design-time-settle.ts +343 -0
- package/src/authoring/edit-mode-authoring.ts +619 -0
- package/src/authoring/eyedropper-session.ts +60 -0
- package/src/authoring/instance-source-menu-register.ts +13 -0
- package/src/authoring/instance-source-menu.ts +135 -0
- package/src/authoring/layered-pick.ts +183 -0
- package/src/authoring/live-gesture-lock.ts +50 -0
- package/src/authoring/live-object-transform.ts +62 -0
- package/src/authoring/mount-failure-report.ts +154 -0
- package/src/authoring/mounted-root-subjects.ts +144 -0
- package/src/authoring/no-authoring-adapter.ts +55 -0
- package/src/authoring/null-inspection-subjects.tsx +75 -0
- package/src/authoring/object3d-document-persistence.ts +106 -0
- package/src/authoring/object3d-document-session-registry.ts +120 -0
- package/src/authoring/object3d-document-session.ts +1326 -0
- package/src/authoring/object3d-gesture-controller.ts +113 -0
- package/src/authoring/panel-authoring.ts +121 -0
- package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
- package/src/authoring/prefab-instance-section-model.ts +55 -0
- package/src/authoring/project-authoring-session.ts +105 -0
- package/src/authoring/provenance.ts +99 -0
- package/src/authoring/quarks-particle-systems.ts +19 -0
- package/src/authoring/react-canvas-navigation.ts +255 -0
- package/src/authoring/react-design-canvas-style.ts +20 -0
- package/src/authoring/react-story-board.ts +937 -0
- package/src/authoring/selection-scope.ts +195 -0
- package/src/authoring/shell-document-ops.ts +169 -0
- package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
- package/src/authoring/shell-viewport-policy.ts +48 -0
- package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
- package/src/authoring/stories-scope.ts +35 -0
- package/src/authoring/story-board-chrome-fit.ts +107 -0
- package/src/authoring/story-board-presentation.ts +111 -0
- package/src/authoring/three-projection-core.ts +226 -0
- package/src/authoring/viewport-pick-context.ts +40 -0
- package/src/authoring/viewport-raycast.ts +240 -0
- package/src/authoring/viewport-tool-context.ts +73 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +152 -0
- package/src/authoring/world-pan-state.ts +198 -0
- package/src/authoring/world-session-state.ts +101 -0
- package/src/authoring/write-pipe.ts +173 -0
- package/src/availability-tick.ts +66 -0
- package/src/bitmap-label.ts +120 -0
- package/src/blender-tab-metrics.ts +161 -0
- package/src/board-open-actions.ts +20 -0
- package/src/boot-routing.ts +391 -0
- package/src/build-identity.ts +15 -0
- package/src/bytes-codec.ts +62 -0
- package/src/camera-authoring.ts +175 -0
- package/src/cancellation-reason.ts +58 -0
- package/src/canvas-preview-frames.ts +482 -0
- package/src/capture-camera-pose.ts +77 -0
- package/src/chrome-registry.ts +159 -0
- package/src/chrome-slot-registry.ts +91 -0
- package/src/collaboration-client.ts +264 -0
- package/src/collaboration-presence.ts +41 -0
- package/src/command-dispatch.ts +19 -0
- package/src/command-listener.ts +2743 -0
- package/src/command-registry.ts +70 -0
- package/src/component-board-registry.ts +205 -0
- package/src/component-states-registry.ts +200 -0
- package/src/components/AgentPresentationNotice.tsx +75 -0
- package/src/components/AlignToolbar.tsx +204 -0
- package/src/components/AppRoot.tsx +394 -0
- package/src/components/ApplicationMenus.tsx +415 -0
- package/src/components/AssetBrowser.tsx +2843 -0
- package/src/components/AssetEditorShell.tsx +216 -0
- package/src/components/AssetInspectorToolSection.tsx +124 -0
- package/src/components/BoardRulers.tsx +354 -0
- package/src/components/CameraInfo.tsx +81 -0
- package/src/components/CanvasSceneViewport.tsx +556 -0
- package/src/components/CapabilityCoverageSection.tsx +185 -0
- package/src/components/CenterDocuments.tsx +22 -0
- package/src/components/ChromeSlot.tsx +20 -0
- package/src/components/CodeView.tsx +470 -0
- package/src/components/CollaborationHeaderControl.css +74 -0
- package/src/components/CompactInspectorCard.tsx +190 -0
- package/src/components/CompactInspectorShell.tsx +39 -0
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +270 -0
- package/src/components/DefaultEditorLayout.tsx +106 -0
- package/src/components/DocumentHeaderStrip.tsx +96 -0
- package/src/components/DocumentShelfRail.tsx +45 -0
- package/src/components/DocumentThumbnail.tsx +345 -0
- package/src/components/EditorLeaseGuard.tsx +372 -0
- package/src/components/ErrorBanner.tsx +123 -0
- package/src/components/FolderPreviewTile.tsx +159 -0
- package/src/components/GameHierarchy.tsx +3842 -0
- package/src/components/GameplaySessionTimeline.tsx +295 -0
- package/src/components/InspectionProjection.tsx +968 -0
- package/src/components/Inspector.tsx +233 -0
- package/src/components/InspectorCanvasPreview.tsx +35 -0
- package/src/components/InspectorFieldsSection.tsx +290 -0
- package/src/components/InspectorObjectPreview.tsx +57 -0
- package/src/components/InspectorStoriesSection.tsx +92 -0
- package/src/components/InspectorToolSection.tsx +96 -0
- package/src/components/InspectorTransformSection.tsx +245 -0
- package/src/components/LightExplorerPanel.tsx +433 -0
- package/src/components/MediaProperties.tsx +145 -0
- package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
- package/src/components/Object3DDocumentToolbar.css +100 -0
- package/src/components/Object3DDocumentToolbar.tsx +454 -0
- package/src/components/Object3DDocumentViewport.tsx +58 -0
- package/src/components/ProjectHeader.css +617 -0
- package/src/components/ProjectHeader.tsx +328 -0
- package/src/components/ProjectLayout.tsx +249 -0
- package/src/components/ReactCanvasControls.tsx +284 -0
- package/src/components/RootSelectionOverlay.tsx +3162 -0
- package/src/components/RootTextEditor.tsx +79 -0
- package/src/components/SaveStatus.tsx +70 -0
- package/src/components/StageHost.tsx +1954 -0
- package/src/components/StageOverlays.tsx +21 -0
- package/src/components/StartupErrorScreen.tsx +76 -0
- package/src/components/StartupLoadingScreen.tsx +65 -0
- package/src/components/StatsOverlay.tsx +78 -0
- package/src/components/SurfaceStateOverlay.tsx +24 -0
- package/src/components/ToolContributionSurfaces.tsx +65 -0
- package/src/components/ToolHost.tsx +370 -0
- package/src/components/ToolObject3DPreview.tsx +39 -0
- package/src/components/Toolbar.tsx +595 -0
- package/src/components/TransientHint.tsx +44 -0
- package/src/components/TransportStrip.tsx +174 -0
- package/src/components/VersionControlSection.tsx +470 -0
- package/src/components/VgaiLogo.css +83 -0
- package/src/components/VgaiLogo.tsx +35 -0
- package/src/components/ViewportControlsHint.tsx +60 -0
- package/src/components/ViewportFurniture.tsx +401 -0
- package/src/components/ViewportOverlay.tsx +145 -0
- package/src/components/ViewportOverlaysMenu.tsx +153 -0
- package/src/components/ViewportShadingMenu.tsx +300 -0
- package/src/components/ViewportViewMenu.tsx +101 -0
- package/src/components/WorkspaceDocumentSurface.tsx +204 -0
- package/src/components/WorkspaceUtilitySurface.tsx +22 -0
- package/src/components/WorktreeSwitcher.css +239 -0
- package/src/components/WorktreeSwitcher.tsx +890 -0
- package/src/components/account-documents.tsx +1162 -0
- package/src/components/asset-documents.tsx +812 -0
- package/src/components/asset-editor-persistence.ts +216 -0
- package/src/components/asset-selection-section.tsx +535 -0
- package/src/components/asset-thumbnails.tsx +312 -0
- package/src/components/asset-viewers/AudioViewer.tsx +201 -0
- package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
- package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
- package/src/components/asset-viewers/ImageViewer.tsx +300 -0
- package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
- package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
- package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
- package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
- package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
- package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
- package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
- package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
- package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
- package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
- package/src/components/asset-viewers/VideoViewer.tsx +101 -0
- package/src/components/asset-viewers/shader-source.ts +144 -0
- package/src/components/asset-workflow.css +731 -0
- package/src/components/board-guides.ts +150 -0
- package/src/components/compact-inspector.css +539 -0
- package/src/components/core-utilities.tsx +90 -0
- package/src/components/editor-notifications.css +40 -0
- package/src/components/engine-workspace.css +274 -0
- package/src/components/inspector-preview-section.tsx +238 -0
- package/src/components/inspector-property-grouping.ts +64 -0
- package/src/components/inspector-revert-label.ts +20 -0
- package/src/components/inspector-selection.ts +42 -0
- package/src/components/inspector-stories-gating.ts +171 -0
- package/src/components/inspector-transform-subject.ts +11 -0
- package/src/components/inspector-transform.ts +75 -0
- package/src/components/kind-documents.tsx +500 -0
- package/src/components/palette-action-publisher.tsx +140 -0
- package/src/components/primitives/DraftColorInput.tsx +74 -0
- package/src/components/product-shell.css +1829 -0
- package/src/components/project-tool-documents.tsx +410 -0
- package/src/components/scene-documents.tsx +223 -0
- package/src/components/stage-keyboard.tsx +37 -0
- package/src/components/stage-overlay-set.tsx +107 -0
- package/src/components/stage-presence-markers.ts +478 -0
- package/src/components/standard-viewport-dressing.ts +417 -0
- package/src/components/status-contributions.tsx +403 -0
- package/src/components/tool-documents.tsx +309 -0
- package/src/components/tool-schema-form.tsx +262 -0
- package/src/components/use-after-paint.ts +41 -0
- package/src/components/use-project-image-assets.ts +86 -0
- package/src/components/viewport-header-controls.css +90 -0
- package/src/components/viewport-surface-status.tsx +55 -0
- package/src/components/workspace-history.ts +32 -0
- package/src/components/workspace-static-panel-registry.tsx +152 -0
- package/src/components/workspace-surfaces.css +621 -0
- package/src/components/world-documents.tsx +586 -0
- package/src/components/world-overlay-gestures.ts +1694 -0
- package/src/composite-screenshot.ts +1641 -0
- package/src/console-sync.ts +131 -0
- package/src/constraint-helper.ts +338 -0
- package/src/content-entry-source-registry.ts +184 -0
- package/src/coverage/authoring-seam-evidence.ts +300 -0
- package/src/coverage/canvas-reveal.ts +192 -0
- package/src/coverage/design-time-surfaces.ts +101 -0
- package/src/coverage/live-seam-evidence.ts +11 -0
- package/src/coverage/ontology-invariants.ts +466 -0
- package/src/coverage/session-vitals.ts +501 -0
- package/src/coverage/system-seam-evidence.ts +72 -0
- package/src/crash-null-boundary.ts +36 -0
- package/src/creation-site-edit.ts +1479 -0
- package/src/creation-site-registry.ts +160 -0
- package/src/delegate-harness-registry.ts +143 -0
- package/src/document-context-registry.ts +177 -0
- package/src/document-open-registry.ts +200 -0
- package/src/document-preview-source.ts +20 -0
- package/src/document-renderer-session.ts +138 -0
- package/src/editor-api.ts +46 -0
- package/src/editor-chrome-capture.ts +139 -0
- package/src/editor-commands.ts +164 -0
- package/src/editor-console.ts +563 -0
- package/src/editor-current-view.ts +82 -0
- package/src/editor-document-probe.ts +881 -0
- package/src/editor-git-client.ts +115 -0
- package/src/editor-host-door.ts +462 -0
- package/src/editor-hotkeys.ts +842 -0
- package/src/editor-lease-view.ts +39 -0
- package/src/editor-lease.ts +415 -0
- package/src/editor-mode.ts +19 -0
- package/src/editor-notifications.ts +140 -0
- package/src/editor-presence.ts +563 -0
- package/src/editor-presentation-activity.ts +50 -0
- package/src/editor-presentation-notice.ts +42 -0
- package/src/editor-runtime.tsx +145 -0
- package/src/editor-server-response.ts +86 -0
- package/src/editor-session-attribution.ts +75 -0
- package/src/editor-session-mode.ts +54 -0
- package/src/editor-shell-store.ts +1304 -0
- package/src/editor-state-facets.ts +74 -0
- package/src/editor-styles.css +33 -0
- package/src/editor-view-presentation.ts +810 -0
- package/src/editor-viewport.ts +5302 -0
- package/src/entity-lod.ts +31 -0
- package/src/entity-object.ts +91 -0
- package/src/files/file-provider.ts +62 -0
- package/src/files/project-files.ts +264 -0
- package/src/finders/index.ts +136 -0
- package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
- package/src/frame/bridge.tsx +1408 -0
- package/src/frame/product.ts +77 -0
- package/src/gameplay-dom-recording.ts +319 -0
- package/src/gameplay-export-state.ts +14 -0
- package/src/gameplay-replay.ts +417 -0
- package/src/gameplay-session-time.ts +9 -0
- package/src/gameplay-sessions.ts +204 -0
- package/src/harness-chat-types.ts +253 -0
- package/src/hierarchy-component-marks.ts +298 -0
- package/src/hierarchy-drop.ts +91 -0
- package/src/hierarchy-expansion-state.ts +80 -0
- package/src/hierarchy-header-slot.ts +52 -0
- package/src/hierarchy-internals.ts +197 -0
- package/src/hierarchy-kind-icon.ts +217 -0
- package/src/hierarchy-mark-reader.ts +73 -0
- package/src/hierarchy-menu-registry.ts +67 -0
- package/src/hierarchy-node-rows.ts +307 -0
- package/src/hierarchy-panel-view.ts +280 -0
- package/src/hierarchy-projection.ts +76 -0
- package/src/hierarchy-row-cache.ts +243 -0
- package/src/hierarchy-row-model.ts +308 -0
- package/src/hierarchy-rows.ts +11 -0
- package/src/hierarchy-walk.ts +86 -0
- package/src/history/editor-session.ts +25 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-delegate.ts +163 -0
- package/src/history/history-limit-notices.ts +43 -0
- package/src/history/history-service.ts +1173 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +386 -0
- package/src/history/project-root-history-backends.ts +139 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +103 -0
- package/src/history/source-history-backend.ts +546 -0
- package/src/history/types.ts +124 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hotkeys.ts +188 -0
- package/src/inference-diagnostics.ts +69 -0
- package/src/initial-project.ts +80 -0
- package/src/inspection/active-subject.ts +578 -0
- package/src/inspection/active-surface.ts +142 -0
- package/src/inspection/compose.ts +1064 -0
- package/src/inspection/display.ts +170 -0
- package/src/inspection/document-subject.ts +109 -0
- package/src/inspection/game-subject.ts +85 -0
- package/src/inspection/model.ts +542 -0
- package/src/inspection/null-subject.ts +115 -0
- package/src/inspection/serialize.ts +357 -0
- package/src/inspection/use-active-inspection.ts +180 -0
- package/src/inspector-presentation.ts +201 -0
- package/src/inspector-section-registry.ts +221 -0
- package/src/instance-source-actions.ts +163 -0
- package/src/instanced-presentation.ts +164 -0
- package/src/js-heap.ts +71 -0
- package/src/key-actions.ts +91 -0
- package/src/keymap-presets.ts +401 -0
- package/src/layout-policy.ts +31 -0
- package/src/learn-links.ts +73 -0
- package/src/light-explorer-model.ts +134 -0
- package/src/live-canvas-frame.ts +55 -0
- package/src/live-document.ts +279 -0
- package/src/live-module-source.ts +230 -0
- package/src/live-session-registry.ts +220 -0
- package/src/live-transition.ts +633 -0
- package/src/manifest-project.ts +107 -0
- package/src/model-thumbnail.ts +523 -0
- package/src/native-selection-style.ts +202 -0
- package/src/object3d-document-write-policy.ts +137 -0
- package/src/packaged-runtime.ts +108 -0
- package/src/palettes/maya.palette.json +57 -0
- package/src/palettes/substance.palette.json +57 -0
- package/src/pasteboard-module.ts +81 -0
- package/src/performance-profiler.ts +367 -0
- package/src/performance-sources.ts +69 -0
- package/src/play-boot-phase.ts +145 -0
- package/src/presentation-surface.ts +248 -0
- package/src/project-adapter.ts +1143 -0
- package/src/project-asset-refresh.ts +26 -0
- package/src/project-declaration-refresh.ts +31 -0
- package/src/project-local-state.ts +118 -0
- package/src/project-manager.ts +243 -0
- package/src/project-module-changes.ts +163 -0
- package/src/project-module-split.ts +266 -0
- package/src/project-provenance.ts +115 -0
- package/src/project-ready.ts +42 -0
- package/src/project-session-reset.ts +67 -0
- package/src/project-shape.ts +68 -0
- package/src/project-tool-discovery.ts +66 -0
- package/src/project-tools.ts +107 -0
- package/src/project-work-types.ts +149 -0
- package/src/projection/three.ts +898 -0
- package/src/projection/types.ts +44 -0
- package/src/readiness.ts +113 -0
- package/src/reflection-probe-helper.ts +142 -0
- package/src/reported-play-state.ts +90 -0
- package/src/resolve-relative-specifier.ts +33 -0
- package/src/scene-document-plan.ts +315 -0
- package/src/scene-framing.ts +315 -0
- package/src/scene-live-open.ts +210 -0
- package/src/scene-view-fog.ts +89 -0
- package/src/scoped-game-css.ts +152 -0
- package/src/session-orphan-record.ts +193 -0
- package/src/session-tombstone.ts +126 -0
- package/src/settings/settings-provider.ts +82 -0
- package/src/settings-store.ts +345 -0
- package/src/shared-view-restore.ts +42 -0
- package/src/shell-store-door.ts +45 -0
- package/src/source-conflict.ts +122 -0
- package/src/spatial-handle-visuals.ts +332 -0
- package/src/stage-context.ts +341 -0
- package/src/stage-store-registry.ts +63 -0
- package/src/stale-chunk-recovery.ts +34 -0
- package/src/state-report-deferral.ts +73 -0
- package/src/storage/host-files-storage.ts +97 -0
- package/src/storage/http-storage.ts +174 -0
- package/src/storage/index.ts +75 -0
- package/src/storage/mem-storage.ts +149 -0
- package/src/storage/path-lock.ts +44 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/types.ts +116 -0
- package/src/stories/StoryComponentThumbnail.tsx +184 -0
- package/src/stories/StoryPreviewMount.tsx +306 -0
- package/src/stories/component-content-source.tsx +106 -0
- package/src/stories/component-states-source.ts +78 -0
- package/src/stories/compose-project-stories.ts +255 -0
- package/src/stories/pixi-story-model.ts +30 -0
- package/src/stories/prefabs-finder.ts +54 -0
- package/src/stories/prefabs-from-stories.ts +182 -0
- package/src/stories/project-story-discovery.ts +78 -0
- package/src/stories/project-story-regions.ts +24 -0
- package/src/stories/story-actions.ts +54 -0
- package/src/stories/story-capture-command.ts +124 -0
- package/src/stories/story-capture.ts +584 -0
- package/src/stories/story-declared-medium.ts +126 -0
- package/src/stories/story-discovery.ts +176 -0
- package/src/stories/story-document-openers.ts +36 -0
- package/src/stories/story-dom-runtime.ts +78 -0
- package/src/stories/story-grouping.ts +111 -0
- package/src/stories/story-lane.ts +88 -0
- package/src/stories/story-mount-turn.ts +27 -0
- package/src/stories/story-opener.ts +260 -0
- package/src/stories/story-pixi-preview.ts +408 -0
- package/src/stories/story-presentation.ts +215 -0
- package/src/stories/story-registry.ts +517 -0
- package/src/stories/story-three-preview.ts +807 -0
- package/src/stories/three-story-model.ts +96 -0
- package/src/story-three-preview-runtime.ts +56 -0
- package/src/surface-keyboard.ts +112 -0
- package/src/surface-state.ts +124 -0
- package/src/tab-bootstrap.js +628 -0
- package/src/tab-census.ts +198 -0
- package/src/tab-lifecycle-client.ts +196 -0
- package/src/theme-library.ts +844 -0
- package/src/theme-preference.ts +378 -0
- package/src/theme.css +3437 -0
- package/src/three-viewport/bone-selection-highlight.ts +119 -0
- package/src/three-viewport/camera-fit.ts +41 -0
- package/src/three-viewport/interactive-renderer.ts +132 -0
- package/src/three-viewport/selection-brackets.ts +310 -0
- package/src/three-viewport/selection-outline.ts +183 -0
- package/src/three-viewport/skeleton-helper.ts +61 -0
- package/src/three-viewport/source-color.ts +167 -0
- package/src/three-viewport/studio-environment.ts +96 -0
- package/src/three-viewport-presentation.ts +23 -0
- package/src/tool-contribution-play.ts +74 -0
- package/src/tool-loader.ts +1798 -0
- package/src/transform-mode-request.ts +57 -0
- package/src/transient-hint.ts +78 -0
- package/src/trigger-volume-helper.ts +116 -0
- package/src/ui-source/adapter-region-includes.ts +241 -0
- package/src/ui-source/ensure-import.ts +132 -0
- package/src/ui-source/file-region-resolver.ts +302 -0
- package/src/ui-source/inspect.ts +775 -0
- package/src/ui-source/oid-transform.ts +1845 -0
- package/src/ui-source/plan-csf-story.ts +196 -0
- package/src/ui-source/plan-extract-component.ts +421 -0
- package/src/ui-source/plan-fork-component.ts +621 -0
- package/src/ui-source/plan-named-style.ts +95 -0
- package/src/ui-source/plan-source-edit.ts +353 -0
- package/src/ui-source/r3f-contract-resolver.ts +327 -0
- package/src/ui-source/r3f-diagnostic-index.ts +165 -0
- package/src/ui-source/r3f-environment-binding.ts +133 -0
- package/src/ui-source/r3f-joint-binding.ts +251 -0
- package/src/ui-source/r3f-lod-binding.ts +91 -0
- package/src/ui-source/r3f-particle-binding.ts +235 -0
- package/src/ui-source/r3f-physics-binding.ts +483 -0
- package/src/ui-source/r3f-project-contracts.ts +444 -0
- package/src/ui-source/relative-import-specifier.ts +34 -0
- package/src/ui-source/reparent-guard.ts +544 -0
- package/src/ui-source/source-edit-request.ts +97 -0
- package/src/ui-source/source-write-backend.ts +618 -0
- package/src/ui-source/syntactic-prop-specs.ts +296 -0
- package/src/ui-source/tier-source-write-backend.ts +279 -0
- package/src/ui-source/ts-ast.ts +171 -0
- package/src/ui-source/utility-class-support.ts +169 -0
- package/src/ui-source/write-component-default.ts +133 -0
- package/src/ui-source/writer.ts +1989 -0
- package/src/viewport-activation-timings.ts +840 -0
- package/src/viewport-authoring-policy.ts +186 -0
- package/src/viewport-controls-hint.ts +57 -0
- package/src/viewport-door.ts +237 -0
- package/src/viewport-shading-boundary.ts +12 -0
- package/src/vite-error-surface.ts +141 -0
- package/src/wait-until.ts +37 -0
- package/src/workspace-areas.ts +156 -0
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-available-documents.ts +127 -0
- package/src/workspace-core-utilities.ts +31 -0
- package/src/workspace-document-ids.ts +59 -0
- package/src/workspace-document-registry.ts +612 -0
- package/src/workspace-document-restore.ts +145 -0
- package/src/workspace-host-commands.ts +141 -0
- package/src/workspace-persistence-gate.ts +40 -0
- package/src/workspace-play-utilities.ts +44 -0
- package/src/workspace-presets.ts +425 -0
- package/src/workspace-regions.ts +294 -0
- package/src/workspace-state-persistence.ts +536 -0
- package/src/workspace-static-panels.ts +73 -0
- package/src/workspace-status-registry.ts +121 -0
- package/src/workspace-style.ts +239 -0
- package/src/workspace-utility-commands.ts +74 -0
- package/src/workspace-utility-registry.ts +263 -0
- package/src/workspace-viewport-rect.ts +97 -0
- package/src/world-adoption.ts +115 -0
- package/src/world-document-routing.ts +104 -0
- package/vite-plugin-creation-site-write.ts +184 -0
- package/vite-plugin-creation-site.ts +80 -0
- package/vite-plugin-game-static.ts +303 -0
- package/vite-plugin-module-doorways.ts +336 -0
- package/vite-plugin-product-contributions.ts +197 -0
- package/vite-plugin-project-game-static.ts +125 -0
- package/vite-plugin-project-jsx-js.ts +65 -0
- package/vite-plugin-project-root-absolute-assets.ts +359 -0
- package/vite-plugin-shared-react.ts +420 -0
- package/vite-plugin-shared-three.ts +203 -0
- package/vite-plugin-ui-oid.ts +2106 -0
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor session's console stream, as a STRUCTURED FILE an agent can read
|
|
3
|
+
* at any moment: `<project>/logs/editor-<sessionstamp>.jsonl`.
|
|
4
|
+
*
|
|
5
|
+
* WHY. Agents do not watch terminals; they read files. Every discipline signal
|
|
6
|
+
* the editor server already produces — a save that failed validation, a
|
|
7
|
+
* build-discipline tripwire crossing (`build-discipline.ts`), the session's own
|
|
8
|
+
* lifecycle — was delivered ONLY as a terminal print, which in detach mode goes
|
|
9
|
+
* to a raw capture nobody parses and in foreground scrolls past whoever was not
|
|
10
|
+
* looking. Same measured shape as the tripwires' own origin story: the
|
|
11
|
+
* mechanism was right and the delivery assumption was false.
|
|
12
|
+
*
|
|
13
|
+
* SO THE JOURNAL IS THE RECORD AND THE PRINTS ARE ITS RENDERERS. Every caller
|
|
14
|
+
* emits the event here first and renders second, from one emit point, so no
|
|
15
|
+
* line can reach a terminal without a durable twin on disk. Nothing new is
|
|
16
|
+
* measured for the journal's sake: each event is a fact one of those surfaces
|
|
17
|
+
* was about to print anyway.
|
|
18
|
+
*
|
|
19
|
+
* IT LIVES BESIDE `logs/play-*.jsonl`, in the project, deliberately: that is
|
|
20
|
+
* the established idiom for "durable evidence this session produced", the
|
|
21
|
+
* scaffold already gitignores `logs/`, and `build-discipline.ts` already skips
|
|
22
|
+
* `logs/` when dating source (so a journal line can never read as a source
|
|
23
|
+
* change and make a tripwire cry wolf on its own output).
|
|
24
|
+
*
|
|
25
|
+
* SCHEMA-LIGHT ON PURPOSE. One discriminated union, one writer, no zod: nothing
|
|
26
|
+
* reads a journal back through a validator — an agent reads it, and `JSON.parse`
|
|
27
|
+
* per line is the whole contract. Append-only, one object per line, no levels,
|
|
28
|
+
* no transports, no config.
|
|
29
|
+
*
|
|
30
|
+
* BOUNDS. Same rule the play logs follow (`/__editor/log-session`): prune to the
|
|
31
|
+
* newest `MAX_JOURNAL_FILES` when a new one is opened. Within a session the file
|
|
32
|
+
* grows unbounded, which is what an append-only record means.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { appendFileSync, mkdirSync, readdirSync, readFileSync, unlinkSync } from 'node:fs';
|
|
36
|
+
import { join } from 'node:path';
|
|
37
|
+
import type { TripwireTier } from './build-discipline';
|
|
38
|
+
import type { BlenderTabMetrics, RecordedTabCensus } from '@volter/editor-sdk/project/tab-census';
|
|
39
|
+
|
|
40
|
+
/** `editor-` + an ISO instant with `:`/`.` flattened + `.jsonl` — the same
|
|
41
|
+
* lexicographic-order-is-chronological-order shape `play-*.jsonl` uses. */
|
|
42
|
+
const PREFIX = 'editor-';
|
|
43
|
+
const SUFFIX = '.jsonl';
|
|
44
|
+
|
|
45
|
+
/** Journals kept per project. Matches the play logs' own `MAX_LOG_FILES`. */
|
|
46
|
+
const MAX_JOURNAL_FILES = 20;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* One journal line's payload. `at` (ISO) is added by the writer, so a caller
|
|
50
|
+
* only ever describes WHAT happened.
|
|
51
|
+
*
|
|
52
|
+
* The tripwire arm reuses `build-discipline.ts`'s vocabulary verbatim — its
|
|
53
|
+
* `TripwireTier`, its tripwire names, and the same inputs its banners are
|
|
54
|
+
* computed from — so the JSONL and the printed banner can never disagree about
|
|
55
|
+
* what fired or how loud it was.
|
|
56
|
+
*/
|
|
57
|
+
export type SessionJournalEvent =
|
|
58
|
+
/** This server began serving this project (the process's own start). */
|
|
59
|
+
| { readonly kind: 'session-started'; readonly project: string; readonly pid: number }
|
|
60
|
+
/** `POST /__editor/open-project` switched the live session's project. */
|
|
61
|
+
| {
|
|
62
|
+
readonly kind: 'project-opened';
|
|
63
|
+
readonly project: string;
|
|
64
|
+
readonly previousProject: string | null;
|
|
65
|
+
}
|
|
66
|
+
/** The session is going down (best-effort: a SIGKILL logs nothing). */
|
|
67
|
+
| { readonly kind: 'session-shutdown' }
|
|
68
|
+
/**
|
|
69
|
+
* ONE RESOURCE THE SHUTDOWN COULD NOT STOP, by name.
|
|
70
|
+
*
|
|
71
|
+
* The host's shutdown list runs each task independently and bounds each one
|
|
72
|
+
* (`packages/editor/server/process-shutdown.ts`); this is what a task writes
|
|
73
|
+
* when its bound expires or it throws. Measured 2026-09-21 under load average
|
|
74
|
+
* ~30: a `vgai close` died inside the tab notify before the workbench's
|
|
75
|
+
* children were stopped, and the ONLY evidence was a journal that stopped —
|
|
76
|
+
* no `session-shutdown` line, a Code-OSS server orphaned at PPID 1 on its
|
|
77
|
+
* reserved port, and the next `vgai edit` refusing by name. A silence names
|
|
78
|
+
* nothing; this line names the resource.
|
|
79
|
+
*/
|
|
80
|
+
| {
|
|
81
|
+
readonly kind: 'session-shutdown-task';
|
|
82
|
+
readonly task: string;
|
|
83
|
+
readonly outcome: 'timeout' | 'failed';
|
|
84
|
+
readonly ms: number;
|
|
85
|
+
readonly detail?: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* WHO HEARD THE SESSION END. Written by `tab-lifecycle.ts`'s
|
|
89
|
+
* `notifySessionEnded` after it broadcasts `tab-close` and WAITS (bounded at
|
|
90
|
+
* 2s) for every tab that was present at that moment to acknowledge it.
|
|
91
|
+
*
|
|
92
|
+
* Measured 2026-09-19/20: the broadcast used to be followed by a blind
|
|
93
|
+
* 150 ms sleep and the HTTP server coming down, so nothing ever recorded
|
|
94
|
+
* whether a page received it. A page that did not kept its Blender engine
|
|
95
|
+
* worker — one engine thread plus a 16-thread pthread pool — at 100%+ CPU
|
|
96
|
+
* for minutes after `vgai close`, and the box swapped. These two lines are
|
|
97
|
+
* the difference, and `vgai close` prints whichever one it finds.
|
|
98
|
+
*/
|
|
99
|
+
| { readonly kind: 'session-end-acked'; readonly tabs: number; readonly ms: number }
|
|
100
|
+
/** …and the other outcome: these tabs never said they got it, so whatever
|
|
101
|
+
* their pages hold is still running. */
|
|
102
|
+
| {
|
|
103
|
+
readonly kind: 'session-end-unacked';
|
|
104
|
+
readonly tabId8s: readonly string[];
|
|
105
|
+
readonly ms: number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* WHERE EDITOR BOOT TIME WENT — written ONCE, when the first page proves it
|
|
109
|
+
* is RUNNING the editor document (its command listener attaches), which is
|
|
110
|
+
* the honest end of the wait `vgai edit` narrates as "Vite cold start".
|
|
111
|
+
*
|
|
112
|
+
* The gap between "Editor ready at …" and that line is the largest stage of
|
|
113
|
+
* opening a project and nothing used to record it: on an imported Unity port
|
|
114
|
+
* it measured 83–139s against a template's 5s, and finding out why took a
|
|
115
|
+
* hand-rolled request trace plus a V8 CPU profile of the dev server. Every
|
|
116
|
+
* field here is a fact the host already had.
|
|
117
|
+
*
|
|
118
|
+
* READ `loopDelayP99Ms` FIRST. It is what separates "the server was busy" —
|
|
119
|
+
* where every slow response in the window is queueing, not work — from "the
|
|
120
|
+
* server was idle and the browser was the wait". See
|
|
121
|
+
* `packages/editor/server/boot-timings.ts`.
|
|
122
|
+
*/
|
|
123
|
+
| {
|
|
124
|
+
readonly kind: 'boot';
|
|
125
|
+
/** Process start → Vite's `createServer` resolved. */
|
|
126
|
+
readonly viteMs: number;
|
|
127
|
+
/** …→ the dependency scan settled. */
|
|
128
|
+
readonly depScanMs: number;
|
|
129
|
+
/** …→ HTTP listening (what "Editor ready at" announces). */
|
|
130
|
+
readonly listenMs: number;
|
|
131
|
+
/** Listening → the first page running the editor document. */
|
|
132
|
+
readonly boundMs: number;
|
|
133
|
+
/** Process start → the declared world warmup finished; `null` if none ran. */
|
|
134
|
+
readonly worldWarmupDoneMs: number | null;
|
|
135
|
+
readonly loopDelayP99Ms: number;
|
|
136
|
+
readonly loopDelayMaxMs: number;
|
|
137
|
+
}
|
|
138
|
+
/** A save-validation verdict — the same facts `runFileValidation` reports. */
|
|
139
|
+
| {
|
|
140
|
+
readonly kind: 'validation';
|
|
141
|
+
/** Project-relative, forward-slash. */
|
|
142
|
+
readonly path: string;
|
|
143
|
+
/** The validatable kind the server classified this file as. */
|
|
144
|
+
readonly fileKind: string;
|
|
145
|
+
readonly ok: boolean;
|
|
146
|
+
readonly errors?: readonly string[];
|
|
147
|
+
readonly warnings?: readonly string[];
|
|
148
|
+
}
|
|
149
|
+
/** A build-discipline tripwire CROSSED a step (never a repeat at one tier). */
|
|
150
|
+
| {
|
|
151
|
+
readonly kind: 'tripwire';
|
|
152
|
+
readonly tripwire: 'commit-cadence';
|
|
153
|
+
readonly tier: TripwireTier;
|
|
154
|
+
readonly ageMs: number;
|
|
155
|
+
readonly fileCount: number;
|
|
156
|
+
}
|
|
157
|
+
| {
|
|
158
|
+
readonly kind: 'tripwire';
|
|
159
|
+
readonly tripwire: 'unplayed-session';
|
|
160
|
+
readonly tier: TripwireTier;
|
|
161
|
+
readonly servingForMs: number;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* A play-mode log session opened or closed.
|
|
165
|
+
*
|
|
166
|
+
* `name` is the run's OPTIONAL slug (`vgai play --name <text>`), `null` for
|
|
167
|
+
* an unnamed run — the same slug that goes in the log filename, so grepping
|
|
168
|
+
* the journal for a run and listing `logs/` for it are the same question.
|
|
169
|
+
*/
|
|
170
|
+
| {
|
|
171
|
+
readonly kind: 'play';
|
|
172
|
+
readonly action: 'start' | 'stop';
|
|
173
|
+
readonly name: string | null;
|
|
174
|
+
readonly logFile: string | null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* A play run's VIDEO evidence opened or landed.
|
|
178
|
+
*
|
|
179
|
+
* Every `vgai play` records — there is no flag — so this row is the answer
|
|
180
|
+
* to "where is the footage of that run", and it has to be in the journal
|
|
181
|
+
* rather than only in a terminal ack: the runs whose footage matters most
|
|
182
|
+
* are the unattended ones, whose ack nobody read.
|
|
183
|
+
*
|
|
184
|
+
* `reason` is what ENDED the recording (`'idle-autostop'`, `'stop'`), null
|
|
185
|
+
* on the `started` row. `rotates` says whether the next play overwrites this
|
|
186
|
+
* file — the unnamed clip does, a `--record <name>` keepsake does not.
|
|
187
|
+
*/
|
|
188
|
+
| {
|
|
189
|
+
readonly kind: 'play-recording';
|
|
190
|
+
readonly action: 'started' | 'finalized';
|
|
191
|
+
/** Basename under the project's `.vgai/recordings/`. */
|
|
192
|
+
readonly file: string;
|
|
193
|
+
readonly rotates: boolean;
|
|
194
|
+
readonly reason: string | null;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* A relayed command ran out of budget, and WHICH PLAY-BOOT STEP the page was
|
|
198
|
+
* inside when it did.
|
|
199
|
+
*
|
|
200
|
+
* The row exists because its absence was the defect. Measured 2026-08-20 at
|
|
201
|
+
* N=20000 on the canvas lane: `play` (120s), `screenshot` (15s) and `stop`
|
|
202
|
+
* (30s) all expired against a tab whose WORKER heartbeat kept beating at
|
|
203
|
+
* 0.4s, and the journal's whole account of it was three `command-result`
|
|
204
|
+
* lines saying "the tab is present … and did not respond" — true, and about
|
|
205
|
+
* the tab rather than the page. `phase` is the page's own last word before it
|
|
206
|
+
* went quiet (`editor/src/play-boot-phase.ts`); `null` means no play boot was
|
|
207
|
+
* in flight, which is itself an answer.
|
|
208
|
+
*/
|
|
209
|
+
| {
|
|
210
|
+
readonly kind: 'play-stall';
|
|
211
|
+
readonly command: string;
|
|
212
|
+
readonly requestId8: string;
|
|
213
|
+
readonly phase: string | null;
|
|
214
|
+
/** How long the page had been in that phase. `null` with a null phase. */
|
|
215
|
+
readonly phaseAgeMs: number | null;
|
|
216
|
+
/** The budget the command actually waited out. */
|
|
217
|
+
readonly waitedMs: number;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* THE TRANSPORT ARM. Everything below is one line per transport or
|
|
221
|
+
* presence fact, and it exists because the journal was BLIND to all of
|
|
222
|
+
* them: on 2026-08-09 a session refused five `vgai play` commands against
|
|
223
|
+
* a healthy connected tab and the whole file it left behind was
|
|
224
|
+
* `session-started` plus validation lines. A relay that can refuse has to
|
|
225
|
+
* be able to say who it refused, to which tab, and on what evidence.
|
|
226
|
+
*
|
|
227
|
+
* Ids are truncated to 8 characters (`clientId8`, `tabId8`, `requestId8`)
|
|
228
|
+
* — enough to correlate lines within one session, short enough that a
|
|
229
|
+
* human can scan a column of them. No payload bodies, ever: a state
|
|
230
|
+
* snapshot or a command argument list would turn an append-only record
|
|
231
|
+
* into a memory dump.
|
|
232
|
+
*/
|
|
233
|
+
/** A control connection opened (either transport). */
|
|
234
|
+
| {
|
|
235
|
+
readonly kind: 'client-connected';
|
|
236
|
+
readonly clientId8: string;
|
|
237
|
+
readonly transport: 'ws' | 'sse';
|
|
238
|
+
readonly participant: string | null;
|
|
239
|
+
}
|
|
240
|
+
/** A control connection closed, and how many pending commands it settled. */
|
|
241
|
+
| {
|
|
242
|
+
readonly kind: 'client-disconnected';
|
|
243
|
+
readonly clientId8: string;
|
|
244
|
+
readonly transport: 'ws' | 'sse';
|
|
245
|
+
/** WebSocket close code, or null for SSE (which has none). */
|
|
246
|
+
readonly code: number | null;
|
|
247
|
+
readonly reason: string | null;
|
|
248
|
+
readonly commandsSettled: number;
|
|
249
|
+
}
|
|
250
|
+
/** The server granted a socket the duplex control capability. */
|
|
251
|
+
| { readonly kind: 'duplex-granted'; readonly clientId8: string }
|
|
252
|
+
/** A page proved it is speaking the exact generation the server granted. */
|
|
253
|
+
| {
|
|
254
|
+
readonly kind: 'control-lifecycle-confirmed';
|
|
255
|
+
readonly clientId8: string;
|
|
256
|
+
readonly connectionGeneration8: string;
|
|
257
|
+
}
|
|
258
|
+
/** A stale or contradictory control frame was refused. */
|
|
259
|
+
| {
|
|
260
|
+
readonly kind: 'control-lifecycle-rejected';
|
|
261
|
+
readonly clientId8: string;
|
|
262
|
+
readonly mismatch:
|
|
263
|
+
| 'serverGeneration'
|
|
264
|
+
| 'connectionGeneration'
|
|
265
|
+
| 'clientId'
|
|
266
|
+
| 'tabId'
|
|
267
|
+
| 'pageGeneration';
|
|
268
|
+
}
|
|
269
|
+
/** A command left the relay for one tab's command channel. */
|
|
270
|
+
| {
|
|
271
|
+
readonly kind: 'command-relayed';
|
|
272
|
+
readonly command: string;
|
|
273
|
+
readonly requestId8: string;
|
|
274
|
+
readonly tabId8: string | null;
|
|
275
|
+
readonly clientId8: string | null;
|
|
276
|
+
}
|
|
277
|
+
/** The tab said its command listener PICKED THE COMMAND UP. */
|
|
278
|
+
| { readonly kind: 'command-receipt'; readonly requestId8: string }
|
|
279
|
+
/**
|
|
280
|
+
* The last tab's connection went away while UNRECEIPTED commands were still
|
|
281
|
+
* queued, and this many were swept. Receipted ones are deliberately not
|
|
282
|
+
* counted here — they keep their own work budget.
|
|
283
|
+
*/
|
|
284
|
+
| { readonly kind: 'command-swept-on-last-tab-gone'; readonly settled: number }
|
|
285
|
+
/** The command settled — by the tab's own answer or by the relay refusing. */
|
|
286
|
+
| {
|
|
287
|
+
readonly kind: 'command-result';
|
|
288
|
+
readonly requestId8: string;
|
|
289
|
+
readonly ok: boolean;
|
|
290
|
+
/** Present only when `ok` is false; the refusal/failure text. */
|
|
291
|
+
readonly error?: string;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* The relay HELD a command instead of refusing it: the target tab is
|
|
295
|
+
* present by heartbeat but its command channel is not carrying right now
|
|
296
|
+
* (mid-reload, or a channel that has not answered an echo).
|
|
297
|
+
*/
|
|
298
|
+
| {
|
|
299
|
+
readonly kind: 'command-held';
|
|
300
|
+
readonly requestId8: string;
|
|
301
|
+
readonly tabId8: string;
|
|
302
|
+
readonly reason: 'no-channel' | 'unacknowledged';
|
|
303
|
+
}
|
|
304
|
+
/** The main-thread echo probe over a duplex socket. */
|
|
305
|
+
| {
|
|
306
|
+
readonly kind: 'echo-probe';
|
|
307
|
+
readonly clientId8: string;
|
|
308
|
+
readonly answered: boolean;
|
|
309
|
+
readonly waitedMs: number;
|
|
310
|
+
}
|
|
311
|
+
/** The blessed tab changed (or was chosen for the first time). */
|
|
312
|
+
| {
|
|
313
|
+
readonly kind: 'tab-blessed';
|
|
314
|
+
readonly tabId8: string;
|
|
315
|
+
readonly previousTabId8: string | null;
|
|
316
|
+
readonly reason: 'sticky' | 'oldest' | 'claimed';
|
|
317
|
+
}
|
|
318
|
+
/** An extra tab was told to yield. */
|
|
319
|
+
| { readonly kind: 'tab-yielded'; readonly tabId8: string }
|
|
320
|
+
/**
|
|
321
|
+
* PRESENCE, IN TAB VOCABULARY. These five say what the tab table saw, and
|
|
322
|
+
* they deliberately do NOT use socket words: a socket closing is not a tab
|
|
323
|
+
* leaving, and that conflation is what produced "editor tab lost —
|
|
324
|
+
* reopening" against a tab that had never gone anywhere.
|
|
325
|
+
*/
|
|
326
|
+
/** A tabId beat for the first time. */
|
|
327
|
+
| {
|
|
328
|
+
readonly kind: 'tab-appeared';
|
|
329
|
+
readonly tabId8: string;
|
|
330
|
+
readonly visibility: 'visible' | 'hidden';
|
|
331
|
+
}
|
|
332
|
+
/** Beats stopped arriving for longer than the notice threshold. */
|
|
333
|
+
| { readonly kind: 'tab-heartbeat-gap'; readonly tabId8: string; readonly sinceMs: number }
|
|
334
|
+
/** A gap closed — the same tab resumed beating. `gapMs` is its full length. */
|
|
335
|
+
| { readonly kind: 'tab-gap-closed'; readonly tabId8: string; readonly gapMs: number }
|
|
336
|
+
/** Same tabId, new epoch: the page reloaded. The tab never left. */
|
|
337
|
+
| { readonly kind: 'tab-reloaded'; readonly tabId8: string; readonly epochCount: number }
|
|
338
|
+
/** The page announced main-thread work before starting it (a play-boot
|
|
339
|
+
* step, a model's `building src/models/x.ts`), or cleared it (`null`). */
|
|
340
|
+
| { readonly kind: 'page-phase'; readonly phase: string | null }
|
|
341
|
+
/**
|
|
342
|
+
* What the project's source watcher saw: one line per chokidar event under
|
|
343
|
+
* `src/` (`add`/`change`/`unlink`), with the file's mtime as the watcher
|
|
344
|
+
* found it (`null` when the path was gone). The instrument for "why did
|
|
345
|
+
* the page reload / the table refresh" — a reload on a file whose mtime
|
|
346
|
+
* never moved is a phantom event, and this is where it shows.
|
|
347
|
+
*/
|
|
348
|
+
| {
|
|
349
|
+
readonly kind: 'src-watch';
|
|
350
|
+
readonly event: string;
|
|
351
|
+
readonly path: string;
|
|
352
|
+
readonly mtimeMs: number | null;
|
|
353
|
+
}
|
|
354
|
+
/** Absent past its grace: this tab is gone. */
|
|
355
|
+
| { readonly kind: 'tab-departed'; readonly tabId8: string; readonly absentMs: number }
|
|
356
|
+
/**
|
|
357
|
+
* WHAT THE TAB WAS HOLDING WHEN IT DIED WITHOUT A GOODBYE.
|
|
358
|
+
*
|
|
359
|
+
* Emitted beside `client-disconnected` on an ABNORMAL close (1006 — the
|
|
360
|
+
* socket ended with no close frame, which is what a killed renderer process
|
|
361
|
+
* leaves behind; an ordinary tab close sends one). Measured 2026-08-10: a
|
|
362
|
+
* game tab's renderer was killed repeatedly at Chrome's undocumented
|
|
363
|
+
* per-process ceiling and the whole session record said `1006` and nothing
|
|
364
|
+
* else — the recovery worked perfectly and the CAUSE was unrecorded.
|
|
365
|
+
*
|
|
366
|
+
* `census` is the tab's last resource profile and `censusAgeMs` how stale it
|
|
367
|
+
* was; both null for a tab that never reported one (an older page, a
|
|
368
|
+
* tunnelled tab). Numbers only — this stays a journal line, not a dump.
|
|
369
|
+
*/
|
|
370
|
+
| {
|
|
371
|
+
readonly kind: 'tab-death-profile';
|
|
372
|
+
readonly tabId8: string;
|
|
373
|
+
/** The abnormal close code that triggered the line. */
|
|
374
|
+
readonly code: number;
|
|
375
|
+
readonly censusAgeMs: number | null;
|
|
376
|
+
/** {@link RecordedTabCensus} rather than `TabCensus`: the writer files a
|
|
377
|
+
* full census, but a line READ back may have been written before the
|
|
378
|
+
* mount census existed. */
|
|
379
|
+
readonly census: RecordedTabCensus | null;
|
|
380
|
+
}
|
|
381
|
+
/** Two epochs beating under one tabId — "Duplicate Tab" copied sessionStorage. */
|
|
382
|
+
| { readonly kind: 'tab-duplicated'; readonly tabId8: string }
|
|
383
|
+
/**
|
|
384
|
+
* The tab is present but its PAGE never came up this page-load. Presence
|
|
385
|
+
* proves the tab exists, not that the document works — a main thread that
|
|
386
|
+
* died after the inline bootstrap keeps beating and holding its control
|
|
387
|
+
* connection, and can run nothing. Such a tab is passed over for blessing
|
|
388
|
+
* and named in the refusal.
|
|
389
|
+
*
|
|
390
|
+
* `reason` says which stage it is stuck at: `'no-channel'` (the page never
|
|
391
|
+
* opened a control connection at all) or `'no-command-listener'` (it did,
|
|
392
|
+
* and the module graph behind it never produced a listener).
|
|
393
|
+
*/
|
|
394
|
+
| {
|
|
395
|
+
readonly kind: 'tab-unresponsive';
|
|
396
|
+
readonly tabId8: string;
|
|
397
|
+
readonly reason: 'no-channel' | 'no-command-listener';
|
|
398
|
+
readonly unresponsiveForMs: number;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* An uncaught error or unhandled rejection in a tab's PAGE, captured by
|
|
402
|
+
* `index.html`'s inline bootstrap — before the module graph, so it is
|
|
403
|
+
* recorded even when the boot that would have reported it is the thing that
|
|
404
|
+
* died. This is the line that says WHY a `tab-unresponsive` tab is
|
|
405
|
+
* unresponsive.
|
|
406
|
+
*/
|
|
407
|
+
| {
|
|
408
|
+
readonly kind: 'page-error';
|
|
409
|
+
readonly tabId8: string;
|
|
410
|
+
readonly message: string;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* One batch of occurrences of ONE console error/warning condition, as the
|
|
414
|
+
* server's unresolved-console ledger recorded it
|
|
415
|
+
* (`packages/editor/server/console-ledger.ts`).
|
|
416
|
+
*
|
|
417
|
+
* This is the DURABLE half of the loudness convention, and the reason it is
|
|
418
|
+
* a row per observation rather than a row per distinct message: `count` is
|
|
419
|
+
* the running total after this batch, so the file answers "how many times
|
|
420
|
+
* did this actually fire, and when did it stop" long after the ledger (and
|
|
421
|
+
* the tab, and the server) are gone. A repeat is never filtered out here —
|
|
422
|
+
* deduping repeats to silence is precisely the failure that made a session's
|
|
423
|
+
* eleven errors read as one line and then nothing.
|
|
424
|
+
*/
|
|
425
|
+
| {
|
|
426
|
+
readonly kind: 'console-entry';
|
|
427
|
+
readonly id: string;
|
|
428
|
+
readonly severity: 'error' | 'warn';
|
|
429
|
+
readonly source: string | null;
|
|
430
|
+
/** Occurrences in THIS batch. */
|
|
431
|
+
readonly added: number;
|
|
432
|
+
/** Running total for this condition, across every page-load. */
|
|
433
|
+
readonly count: number;
|
|
434
|
+
readonly message: string;
|
|
435
|
+
}
|
|
436
|
+
/** A condition cleared by the honest re-test: the page reloaded and it did
|
|
437
|
+
* not recur. `count` is what it reached before it stopped. */
|
|
438
|
+
| {
|
|
439
|
+
readonly kind: 'console-retired';
|
|
440
|
+
readonly id: string;
|
|
441
|
+
readonly severity: 'error' | 'warn';
|
|
442
|
+
readonly count: number;
|
|
443
|
+
readonly message: string;
|
|
444
|
+
}
|
|
445
|
+
/** A condition waved through BY NAME (`vgai console ack`). The audit row is
|
|
446
|
+
* the whole point: an acknowledgment records who and why, and never erases
|
|
447
|
+
* what was acknowledged. */
|
|
448
|
+
| {
|
|
449
|
+
readonly kind: 'console-ack';
|
|
450
|
+
readonly id: string;
|
|
451
|
+
readonly severity: 'error' | 'warn';
|
|
452
|
+
readonly count: number;
|
|
453
|
+
readonly by: string;
|
|
454
|
+
readonly reason: string;
|
|
455
|
+
readonly message: string;
|
|
456
|
+
}
|
|
457
|
+
/** A condition cleared by its OWNER, which proved it gone by an event the
|
|
458
|
+
* reload rule cannot see — a play remount resolving its own
|
|
459
|
+
* `Restart required` warning. `by` names that owner, never a person. */
|
|
460
|
+
| {
|
|
461
|
+
readonly kind: 'console-resolved';
|
|
462
|
+
readonly id: string;
|
|
463
|
+
readonly severity: 'error' | 'warn';
|
|
464
|
+
readonly count: number;
|
|
465
|
+
readonly by: string;
|
|
466
|
+
readonly message: string;
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
/** A parsed journal line: the event plus when it was appended. */
|
|
470
|
+
export type SessionJournalLine = SessionJournalEvent & { readonly at: string };
|
|
471
|
+
|
|
472
|
+
/** An open journal. `append` never throws — a journal that breaks a save would
|
|
473
|
+
* be worse than a journal that misses a line. */
|
|
474
|
+
export interface SessionJournal {
|
|
475
|
+
/** Absolute path of the file being appended to. */
|
|
476
|
+
readonly path: string;
|
|
477
|
+
append(event: SessionJournalEvent): void;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** `editor-2026-08-09T12-34-56-789Z.jsonl` for a given instant. */
|
|
481
|
+
export function sessionJournalFilename(startedAt: Date): string {
|
|
482
|
+
return `${PREFIX}${startedAt.toISOString().replace(/[:.]/g, '-')}${SUFFIX}`;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Open (create) this session's journal under `<projectRoot>/logs/`.
|
|
487
|
+
*
|
|
488
|
+
* `null` when the directory cannot be created or the first write fails —
|
|
489
|
+
* an unwritable project must degrade to "no journal", never to a crashed boot.
|
|
490
|
+
*/
|
|
491
|
+
export function openSessionJournal(
|
|
492
|
+
projectRoot: string,
|
|
493
|
+
startedAt: Date = new Date(),
|
|
494
|
+
): SessionJournal | null {
|
|
495
|
+
const logsDir = join(projectRoot, 'logs');
|
|
496
|
+
try {
|
|
497
|
+
mkdirSync(logsDir, { recursive: true });
|
|
498
|
+
} catch {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
pruneJournals(logsDir);
|
|
502
|
+
const path = join(logsDir, sessionJournalFilename(startedAt));
|
|
503
|
+
const journal: SessionJournal = {
|
|
504
|
+
path,
|
|
505
|
+
append(event) {
|
|
506
|
+
const line: SessionJournalLine = { at: new Date().toISOString(), ...event };
|
|
507
|
+
try {
|
|
508
|
+
appendFileSync(path, `${JSON.stringify(line)}\n`, 'utf-8');
|
|
509
|
+
} catch {
|
|
510
|
+
/* a journal that breaks the caller is worse than a missing line */
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
};
|
|
514
|
+
try {
|
|
515
|
+
appendFileSync(path, '', 'utf-8');
|
|
516
|
+
} catch {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
return journal;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** Drop the oldest journals so opening one more stays at `MAX_JOURNAL_FILES`. */
|
|
523
|
+
function pruneJournals(logsDir: string): void {
|
|
524
|
+
try {
|
|
525
|
+
const files = readdirSync(logsDir)
|
|
526
|
+
.filter((f) => f.startsWith(PREFIX) && f.endsWith(SUFFIX))
|
|
527
|
+
.sort();
|
|
528
|
+
for (const f of files.slice(0, Math.max(0, files.length - MAX_JOURNAL_FILES + 1))) {
|
|
529
|
+
try {
|
|
530
|
+
unlinkSync(join(logsDir, f));
|
|
531
|
+
} catch {
|
|
532
|
+
/* already gone */
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} catch {
|
|
536
|
+
/* no logs dir yet */
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* The newest journal in a project, or `null`.
|
|
542
|
+
*
|
|
543
|
+
* File-native on purpose (the same shape as the CLI's newest-play-log reader):
|
|
544
|
+
* the journal path is a fact about the PROJECT DIRECTORY, so `vgai status` and
|
|
545
|
+
* the boot pointer can name it without a server round-trip — and can still name
|
|
546
|
+
* it after the session that wrote it is gone.
|
|
547
|
+
*/
|
|
548
|
+
export function newestSessionJournal(projectRoot: string): string | null {
|
|
549
|
+
try {
|
|
550
|
+
const files = readdirSync(join(projectRoot, 'logs'))
|
|
551
|
+
.filter((f) => f.startsWith(PREFIX) && f.endsWith(SUFFIX))
|
|
552
|
+
.sort();
|
|
553
|
+
const newest = files.at(-1);
|
|
554
|
+
return newest ? join(projectRoot, 'logs', newest) : null;
|
|
555
|
+
} catch {
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* The newest journal's last `limit` lines of the requested kinds, oldest first.
|
|
562
|
+
*
|
|
563
|
+
* WHY A READER EXISTS AT ALL. The journal was written as a file an agent
|
|
564
|
+
* "can read at any time", and the measured answer to that invitation was: it
|
|
565
|
+
* doesn't. The foundry probe's journal held the notice-tier tripwire crossing
|
|
566
|
+
* that predicted its own end-of-run batch commit, and nothing that agent ran
|
|
567
|
+
* ever rendered a line of it. So the record grows a RENDERER on the command
|
|
568
|
+
* agents already poll (`vgai status`) — same principle as the tripwires' own
|
|
569
|
+
* origin: the mechanism was right and the delivery assumption was false.
|
|
570
|
+
*
|
|
571
|
+
* Every failure is the same case — no project, no journal, unreadable file, a
|
|
572
|
+
* line that will not parse — and it is the empty list, never a throw. A status
|
|
573
|
+
* command that crashed on a malformed log line would be a worse outcome than
|
|
574
|
+
* one that says nothing about it.
|
|
575
|
+
*
|
|
576
|
+
* The whole file is read, which is what bounds this: a journal is one editor
|
|
577
|
+
* session's surface-worthy transitions (the happy-path silence rule keeps
|
|
578
|
+
* clean saves out), the pruner caps how many exist, and the alternative — a
|
|
579
|
+
* reverse streaming reader over a file measured in kilobytes — is machinery
|
|
580
|
+
* with nothing to buy.
|
|
581
|
+
*/
|
|
582
|
+
export function readRecentJournalEvents(
|
|
583
|
+
projectRoot: string,
|
|
584
|
+
kinds: readonly SessionJournalEvent['kind'][],
|
|
585
|
+
limit: number,
|
|
586
|
+
): SessionJournalLine[] {
|
|
587
|
+
const path = newestSessionJournal(projectRoot);
|
|
588
|
+
if (path === null || limit <= 0) return [];
|
|
589
|
+
let raw: string;
|
|
590
|
+
try {
|
|
591
|
+
raw = readFileSync(path, 'utf-8');
|
|
592
|
+
} catch {
|
|
593
|
+
return [];
|
|
594
|
+
}
|
|
595
|
+
const wanted = new Set<string>(kinds);
|
|
596
|
+
const matched: SessionJournalLine[] = [];
|
|
597
|
+
for (const line of raw.split('\n')) {
|
|
598
|
+
if (line === '') continue;
|
|
599
|
+
let parsed: SessionJournalLine;
|
|
600
|
+
try {
|
|
601
|
+
parsed = JSON.parse(line) as SessionJournalLine;
|
|
602
|
+
} catch {
|
|
603
|
+
continue; // a torn final line is not a reason to report nothing
|
|
604
|
+
}
|
|
605
|
+
if (parsed && typeof parsed === 'object' && wanted.has(parsed.kind)) matched.push(parsed);
|
|
606
|
+
}
|
|
607
|
+
return matched.slice(-limit);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* One journal line as a scannable terminal line: `HH:MM:SS`, the kind, and
|
|
612
|
+
* the few facts that line is about.
|
|
613
|
+
*
|
|
614
|
+
* ONE OWNER of this rendering, for `sessionJournalPointerLine`'s reason — the
|
|
615
|
+
* events already have exactly one wording as data, and a second prose form per
|
|
616
|
+
* caller is how a reader ends up believing there are two records.
|
|
617
|
+
*/
|
|
618
|
+
/** Split out of {@link formatJournalLine}'s switch for the same reason as
|
|
619
|
+
* {@link playRecordingLine} below. Seconds, because that is the unit the
|
|
620
|
+
* reader's complaint is in ("opening this project takes two minutes"), except
|
|
621
|
+
* the loop-delay pair — a p99 is a millisecond fact and rounding it to
|
|
622
|
+
* seconds would erase the only number that says WHO was waiting. */
|
|
623
|
+
function bootLine(line: Extract<SessionJournalEvent, { kind: 'boot' }>): string {
|
|
624
|
+
const s = (ms: number): string => `${Math.round(ms / 100) / 10}s`;
|
|
625
|
+
return (
|
|
626
|
+
`boot ready ${s(line.listenMs)} (vite ${s(line.viteMs)}, dep-scan ${s(line.depScanMs)}) ` +
|
|
627
|
+
`→ page bound +${s(line.boundMs)}` +
|
|
628
|
+
(line.worldWarmupDoneMs === null ? '' : `, world warmup done ${s(line.worldWarmupDoneMs)}`) +
|
|
629
|
+
` — loop delay p99 ${line.loopDelayP99Ms}ms / max ${line.loopDelayMaxMs}ms`
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/** Split out of {@link formatJournalLine}'s switch so this row's three
|
|
634
|
+
* conditionals do not push that already-long function further over the
|
|
635
|
+
* complexity bound. */
|
|
636
|
+
function playRecordingLine(line: Extract<SessionJournalEvent, { kind: 'play-recording' }>): string {
|
|
637
|
+
const why = line.reason === null ? '' : ` (${line.reason})`;
|
|
638
|
+
const life = line.rotates ? ' — replaced by the next play' : ' — kept';
|
|
639
|
+
return `play-recording ${line.action} .vgai/recordings/${line.file}${why}${life}`;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
export function formatJournalLine(line: SessionJournalLine): string {
|
|
643
|
+
const at = line.at.slice(11, 19);
|
|
644
|
+
switch (line.kind) {
|
|
645
|
+
case 'tripwire':
|
|
646
|
+
return line.tripwire === 'commit-cadence'
|
|
647
|
+
? `journal: ${at} tripwire commit-cadence ${line.tier} (${line.fileCount} files, ${Math.round(line.ageMs / 60_000)}m)`
|
|
648
|
+
: `journal: ${at} tripwire unplayed-session ${line.tier} (${Math.round(line.servingForMs / 60_000)}m)`;
|
|
649
|
+
case 'validation':
|
|
650
|
+
return `journal: ${at} validation ${line.ok ? 'ok' : 'FAILED'} ${line.path}`;
|
|
651
|
+
case 'play':
|
|
652
|
+
return `journal: ${at} play ${line.action}${line.name === null ? '' : ` [${line.name}]`}`;
|
|
653
|
+
case 'play-recording':
|
|
654
|
+
return `journal: ${at} ${playRecordingLine(line)}`;
|
|
655
|
+
case 'play-stall':
|
|
656
|
+
return (
|
|
657
|
+
`journal: ${at} play-stall ${line.command} ${line.requestId8} after ` +
|
|
658
|
+
`${Math.round(line.waitedMs / 1000)}s — ` +
|
|
659
|
+
(line.phase === null
|
|
660
|
+
? 'no play boot was in flight'
|
|
661
|
+
: `stuck in "${line.phase}" for ${Math.round((line.phaseAgeMs ?? 0) / 1000)}s`)
|
|
662
|
+
);
|
|
663
|
+
case 'session-started':
|
|
664
|
+
return `journal: ${at} session-started pid ${line.pid}`;
|
|
665
|
+
case 'project-opened':
|
|
666
|
+
return `journal: ${at} project-opened ${line.project}`;
|
|
667
|
+
case 'session-shutdown':
|
|
668
|
+
return `journal: ${at} session-shutdown`;
|
|
669
|
+
case 'session-shutdown-task':
|
|
670
|
+
return (
|
|
671
|
+
`journal: ${at} session-shutdown-task ${line.task} ${line.outcome} after ${line.ms}ms` +
|
|
672
|
+
(line.detail === undefined ? '' : ` — ${line.detail}`)
|
|
673
|
+
);
|
|
674
|
+
case 'session-end-acked':
|
|
675
|
+
return line.tabs === 0
|
|
676
|
+
? `journal: ${at} session-end-acked no tab was present`
|
|
677
|
+
: `journal: ${at} session-end-acked ${line.tabs} tab(s) in ${line.ms}ms`;
|
|
678
|
+
case 'session-end-unacked':
|
|
679
|
+
return (
|
|
680
|
+
`journal: ${at} session-end-unacked ${line.tabId8s.join(' ')} after ${line.ms}ms — ` +
|
|
681
|
+
'their pages may still hold what the session started'
|
|
682
|
+
);
|
|
683
|
+
case 'boot':
|
|
684
|
+
return `journal: ${at} ${bootLine(line)}`;
|
|
685
|
+
// The TRANSPORT arm. One short line each, and every one of them names the
|
|
686
|
+
// TAB or the request it is about — a column of ids is how these lines get
|
|
687
|
+
// correlated, and a paragraph per line is how a reader stops reading.
|
|
688
|
+
case 'client-connected':
|
|
689
|
+
return `journal: ${at} client-connected ${line.clientId8} (${line.transport})`;
|
|
690
|
+
case 'client-disconnected':
|
|
691
|
+
return `journal: ${at} client-disconnected ${line.clientId8} code ${line.code ?? '-'} settled ${line.commandsSettled}`;
|
|
692
|
+
case 'duplex-granted':
|
|
693
|
+
return `journal: ${at} duplex-granted ${line.clientId8}`;
|
|
694
|
+
case 'control-lifecycle-confirmed':
|
|
695
|
+
return `journal: ${at} control-lifecycle-confirmed ${line.clientId8} connection ${line.connectionGeneration8}`;
|
|
696
|
+
case 'control-lifecycle-rejected':
|
|
697
|
+
return `journal: ${at} control-lifecycle-rejected ${line.clientId8} stale ${line.mismatch}`;
|
|
698
|
+
case 'command-relayed':
|
|
699
|
+
return `journal: ${at} command-relayed ${line.command} ${line.requestId8} -> tab ${line.tabId8 ?? 'none'}`;
|
|
700
|
+
case 'command-receipt':
|
|
701
|
+
return `journal: ${at} command-receipt ${line.requestId8}`;
|
|
702
|
+
case 'command-swept-on-last-tab-gone':
|
|
703
|
+
return `journal: ${at} command-swept-on-last-tab-gone ${line.settled} unreceipted`;
|
|
704
|
+
case 'command-result':
|
|
705
|
+
return `journal: ${at} command-result ${line.requestId8} ${line.ok ? 'ok' : `FAILED ${line.error ?? ''}`}`;
|
|
706
|
+
case 'command-held':
|
|
707
|
+
return `journal: ${at} command-held ${line.requestId8} tab ${line.tabId8} (${line.reason})`;
|
|
708
|
+
case 'echo-probe':
|
|
709
|
+
return `journal: ${at} echo-probe ${line.clientId8} ${line.answered ? 'answered' : 'UNANSWERED'} in ${line.waitedMs}ms`;
|
|
710
|
+
case 'tab-blessed':
|
|
711
|
+
return `journal: ${at} tab-blessed ${line.tabId8} (${line.reason})`;
|
|
712
|
+
case 'tab-yielded':
|
|
713
|
+
return `journal: ${at} tab-yielded ${line.tabId8}`;
|
|
714
|
+
case 'tab-appeared':
|
|
715
|
+
return `journal: ${at} tab-appeared ${line.tabId8} (${line.visibility})`;
|
|
716
|
+
case 'tab-heartbeat-gap':
|
|
717
|
+
return `journal: ${at} tab-heartbeat-gap ${line.tabId8} ${Math.round(line.sinceMs / 100) / 10}s`;
|
|
718
|
+
case 'tab-gap-closed':
|
|
719
|
+
return `journal: ${at} tab-gap-closed ${line.tabId8} after ${Math.round(line.gapMs / 100) / 10}s`;
|
|
720
|
+
case 'tab-reloaded':
|
|
721
|
+
return `journal: ${at} tab-reloaded ${line.tabId8} (page load ${line.epochCount})`;
|
|
722
|
+
case 'page-phase':
|
|
723
|
+
return `journal: ${at} page-phase ${line.phase ?? '(clear)'}`;
|
|
724
|
+
case 'src-watch':
|
|
725
|
+
return `journal: ${at} src-watch ${line.event} ${line.path}${line.mtimeMs === null ? ' (gone)' : ''}`;
|
|
726
|
+
case 'tab-departed':
|
|
727
|
+
return `journal: ${at} tab-departed ${line.tabId8} absent ${Math.round(line.absentMs / 100) / 10}s`;
|
|
728
|
+
case 'tab-duplicated':
|
|
729
|
+
return `journal: ${at} tab-duplicated ${line.tabId8}`;
|
|
730
|
+
case 'tab-unresponsive':
|
|
731
|
+
return `journal: ${at} tab-unresponsive ${line.tabId8} ${line.reason} for ${Math.round(line.unresponsiveForMs / 1000)}s`;
|
|
732
|
+
case 'page-error':
|
|
733
|
+
return `journal: ${at} page-error ${line.tabId8} ${line.message}`;
|
|
734
|
+
case 'tab-death-profile':
|
|
735
|
+
return `journal: ${at} tab-death-profile ${line.tabId8} code ${line.code} — ${tabDeathProfileBody(line)}`;
|
|
736
|
+
// The CONSOLE arm. `count` is the running total for that condition, so a
|
|
737
|
+
// reader scanning the column sees a repeat climbing rather than the same
|
|
738
|
+
// line over and over with nothing to distinguish the fiftieth from the
|
|
739
|
+
// first. Every line leads with the ack id, because that is what a reader
|
|
740
|
+
// types next.
|
|
741
|
+
case 'console-entry':
|
|
742
|
+
return `journal: ${at} console-${line.severity} ${line.id} +${line.added} (total ${line.count}) ${line.source === null ? '' : `[${line.source}] `}${line.message.split('\n')[0]}`;
|
|
743
|
+
case 'console-retired':
|
|
744
|
+
return `journal: ${at} console-retired ${line.id} after ${line.count} — did not recur after reload`;
|
|
745
|
+
case 'console-ack':
|
|
746
|
+
return `journal: ${at} console-ack ${line.id} (×${line.count}) by ${line.by}: ${line.reason}`;
|
|
747
|
+
case 'console-resolved':
|
|
748
|
+
return `journal: ${at} console-resolved ${line.id} after ${line.count} — ${line.by} cleared the condition it raised`;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/** The census half of a `tab-death-profile` line, split out so the switch above
|
|
753
|
+
* stays one expression per case. */
|
|
754
|
+
function tabDeathProfileBody(line: SessionJournalEvent & { kind: 'tab-death-profile' }): string {
|
|
755
|
+
const census = formatTabCensus(line.census);
|
|
756
|
+
if (line.censusAgeMs === null) return census;
|
|
757
|
+
return `${census} (sampled ${Math.round(line.censusAgeMs / 100) / 10}s before)`;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/** The resource profile a `tab-death-profile` line and a `vgai status` tab row
|
|
761
|
+
* both quote. ONE wording, for `sessionJournalPointerLine`'s reason: two
|
|
762
|
+
* phrasings of the same numbers is how a reader ends up believing there are
|
|
763
|
+
* two measurements. Renderer counts appear only when the page could read them
|
|
764
|
+
* (no mounted render-debug adapter = the words are absent, never a zero). */
|
|
765
|
+
/**
|
|
766
|
+
* The Blender block's words: what the Blender worker and this tab's main thread
|
|
767
|
+
* have been doing. Absent entirely in a tab with no Blender session.
|
|
768
|
+
*
|
|
769
|
+
* `in flight` is the field that speaks during a wedge — the 2026-09-16 call
|
|
770
|
+
* that held the worker 1,800s produced no other number anywhere in the product.
|
|
771
|
+
* A field the page could not measure is OMITTED rather than printed as 0: no
|
|
772
|
+
* call yet, or no Long Tasks API (Safari/Firefox), is not "never stalled".
|
|
773
|
+
*/
|
|
774
|
+
function formatBlenderMetrics(blender: BlenderTabMetrics): string {
|
|
775
|
+
const s = (ms: number): string => `${Math.round(ms / 100) / 10}s`;
|
|
776
|
+
const parts = [
|
|
777
|
+
blender.inFlightMs === null ? 'idle' : `IN FLIGHT ${s(blender.inFlightMs)}`,
|
|
778
|
+
// First after the wedge field, because it is the tab's LARGEST number and
|
|
779
|
+
// the one the heap line above is routinely mistaken for.
|
|
780
|
+
...(blender.wasmMemoryMB === null || blender.wasmMemoryMB === undefined
|
|
781
|
+
? []
|
|
782
|
+
: [`engine memory ${blender.wasmMemoryMB}MB`]),
|
|
783
|
+
...(blender.lastCallMs === null ? [] : [`last call ${s(blender.lastCallMs)}`]),
|
|
784
|
+
...(blender.maxCallMs === null ? [] : [`max ${s(blender.maxCallMs)}`]),
|
|
785
|
+
`over 5s ${blender.callsOver5s}`,
|
|
786
|
+
`over 30s ${blender.callsOver30s}`,
|
|
787
|
+
...(blender.longestTaskMs === null
|
|
788
|
+
? []
|
|
789
|
+
: [
|
|
790
|
+
`longest main-thread task ${s(blender.longestTaskMs)} (${blender.tasksOver100ms} over 100ms)`,
|
|
791
|
+
]),
|
|
792
|
+
...(blender.lastCallLongestTaskMs === null
|
|
793
|
+
? []
|
|
794
|
+
: [`${s(blender.lastCallLongestTaskMs)} of that during the last call`]),
|
|
795
|
+
];
|
|
796
|
+
return `blender: ${parts.join(', ')}`;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
export function formatTabCensus(census: RecordedTabCensus | null): string {
|
|
800
|
+
if (census === null) return 'no resource census';
|
|
801
|
+
const parts = [
|
|
802
|
+
census.heapUsedMB === null
|
|
803
|
+
? 'heap n/a'
|
|
804
|
+
: `heap ${census.heapUsedMB}MB${census.heapLimitMB === null ? '' : `/${census.heapLimitMB}MB`}`,
|
|
805
|
+
`canvas ${census.canvasMB}MB in ${census.canvases}`,
|
|
806
|
+
];
|
|
807
|
+
if (census.mountEpochs !== undefined) parts.splice(1, 0, `mount epochs ${census.mountEpochs}`);
|
|
808
|
+
if (census.textures !== undefined) parts.push(`tex ${census.textures}`);
|
|
809
|
+
if (census.geometries !== undefined) parts.push(`geo ${census.geometries}`);
|
|
810
|
+
if (census.programs !== undefined) parts.push(`prog ${census.programs}`);
|
|
811
|
+
const line = parts.join(', ');
|
|
812
|
+
// On its own line: the Blender block answers a different question from the
|
|
813
|
+
// resource profile (is the tab ANSWERING, not what is it holding), and
|
|
814
|
+
// running the two together is how a reader stops seeing either.
|
|
815
|
+
return census.blender === undefined
|
|
816
|
+
? line
|
|
817
|
+
: `${line}\n ${formatBlenderMetrics(census.blender)}`;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* The ONE wording for "here is the journal" — printed by the editor server's
|
|
822
|
+
* boot block, by `vgai edit`'s ready/detach lines, and by `vgai status`.
|
|
823
|
+
*
|
|
824
|
+
* One owner of the sentence, for `build-discipline.ts`'s reason: three
|
|
825
|
+
* processes point at this file, and a second phrasing of the same pointer is
|
|
826
|
+
* how a reader ends up believing there are two things.
|
|
827
|
+
*/
|
|
828
|
+
export function sessionJournalPointerLine(journalPath: string): string {
|
|
829
|
+
return `Session journal: ${journalPath} — structured JSONL, read it any time`;
|
|
830
|
+
}
|