summer-engine 3.1.0 → 3.2.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +27 -5
- package/.codex-plugin/plugin.json +28 -6
- package/.cursor-plugin/plugin.json +27 -5
- package/.factory-plugin/plugin.json +27 -5
- package/.opencode/plugins/summer.js +1 -1
- package/CHANGELOG.md +102 -0
- package/README.md +18 -3
- package/assets/audit/scene_audit.gd +4136 -0
- package/assets/seeing/seeing_probe.gd +1873 -0
- package/dist/cli/commands/skills.js +20 -2
- package/dist/core/api-client.d.ts +9 -1
- package/dist/core/api-client.js +21 -4
- package/dist/core/capabilities/agent-playbook.js +46 -0
- package/dist/core/capabilities/audit/accept.d.ts +55 -0
- package/dist/core/capabilities/audit/accept.js +147 -0
- package/dist/core/capabilities/audit/args.d.ts +95 -0
- package/dist/core/capabilities/audit/args.js +89 -0
- package/dist/core/capabilities/audit/audit.d.ts +56 -0
- package/dist/core/capabilities/audit/audit.js +287 -0
- package/dist/core/capabilities/audit/frame.d.ts +14 -0
- package/dist/core/capabilities/audit/frame.js +35 -0
- package/dist/core/capabilities/audit/gaps.d.ts +96 -0
- package/dist/core/capabilities/audit/gaps.js +257 -0
- package/dist/core/capabilities/audit/judge.d.ts +162 -0
- package/dist/core/capabilities/audit/judge.js +1134 -0
- package/dist/core/capabilities/audit/math.d.ts +110 -0
- package/dist/core/capabilities/audit/math.js +263 -0
- package/dist/core/capabilities/audit/summary.d.ts +74 -0
- package/dist/core/capabilities/audit/summary.js +212 -0
- package/dist/core/capabilities/board.d.ts +31 -0
- package/dist/core/capabilities/board.js +43 -0
- package/dist/core/capabilities/camera-view.d.ts +4 -2
- package/dist/core/capabilities/camera-view.js +6 -3
- package/dist/core/capabilities/connect-signal.d.ts +85 -0
- package/dist/core/capabilities/connect-signal.js +285 -0
- package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
- package/dist/core/capabilities/engine-fallbacks.js +5 -0
- package/dist/core/capabilities/engine-ops.d.ts +5 -1
- package/dist/core/capabilities/engine-ops.js +0 -3
- package/dist/core/capabilities/engine-receipt.d.ts +12 -0
- package/dist/core/capabilities/engine-receipt.js +26 -0
- package/dist/core/capabilities/file-read.d.ts +64 -0
- package/dist/core/capabilities/file-read.js +325 -0
- package/dist/core/capabilities/game-task-plan.js +4 -2
- package/dist/core/capabilities/grep.d.ts +60 -0
- package/dist/core/capabilities/grep.js +190 -0
- package/dist/core/capabilities/inspect-node.d.ts +56 -0
- package/dist/core/capabilities/inspect-node.js +225 -0
- package/dist/core/capabilities/inspect-resource.d.ts +61 -0
- package/dist/core/capabilities/inspect-resource.js +376 -0
- package/dist/core/capabilities/math3d.d.ts +28 -0
- package/dist/core/capabilities/math3d.js +88 -0
- package/dist/core/capabilities/motion-generation.d.ts +62 -0
- package/dist/core/capabilities/motion-generation.js +138 -0
- package/dist/core/capabilities/placement-batch.d.ts +102 -0
- package/dist/core/capabilities/placement-batch.js +483 -0
- package/dist/core/capabilities/placement-math.d.ts +70 -0
- package/dist/core/capabilities/placement-math.js +209 -0
- package/dist/core/capabilities/placement-script.d.ts +42 -0
- package/dist/core/capabilities/placement-script.js +1096 -0
- package/dist/core/capabilities/placement.d.ts +485 -0
- package/dist/core/capabilities/placement.js +1731 -0
- package/dist/core/capabilities/project-context.d.ts +33 -2
- package/dist/core/capabilities/project-context.js +87 -4
- package/dist/core/capabilities/replace-node.d.ts +91 -0
- package/dist/core/capabilities/replace-node.js +423 -0
- package/dist/core/capabilities/runtime-control.d.ts +18 -2
- package/dist/core/capabilities/runtime-control.js +67 -2
- package/dist/core/capabilities/scene-batch.d.ts +63 -0
- package/dist/core/capabilities/scene-batch.js +394 -0
- package/dist/core/capabilities/scene-readback.d.ts +36 -0
- package/dist/core/capabilities/scene-readback.js +54 -0
- package/dist/core/capabilities/seeing/args.d.ts +348 -0
- package/dist/core/capabilities/seeing/args.js +178 -0
- package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
- package/dist/core/capabilities/seeing/candidates.js +282 -0
- package/dist/core/capabilities/seeing/math.d.ts +134 -0
- package/dist/core/capabilities/seeing/math.js +269 -0
- package/dist/core/capabilities/seeing/probe.d.ts +62 -0
- package/dist/core/capabilities/seeing/probe.js +188 -0
- package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
- package/dist/core/capabilities/seeing/scoring.js +639 -0
- package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
- package/dist/core/capabilities/seeing/seeing.js +1341 -0
- package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
- package/dist/core/capabilities/seeing/shot-store.js +204 -0
- package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
- package/dist/core/capabilities/surface-snap-mesh.js +425 -0
- package/dist/core/capabilities/surface-snap.d.ts +77 -0
- package/dist/core/capabilities/surface-snap.js +461 -0
- package/dist/core/capabilities/tool-dispatch.js +231 -72
- package/dist/core/capabilities/tscn.d.ts +85 -0
- package/dist/core/capabilities/tscn.js +391 -0
- package/dist/core/capabilities/world-snapshot.d.ts +58 -0
- package/dist/core/capabilities/world-snapshot.js +150 -0
- package/dist/core/capability-skew.d.ts +5 -5
- package/dist/core/capability-skew.js +14 -6
- package/dist/core/headless/worker-engine-client.d.ts +1 -1
- package/dist/core/headless/worker-engine-client.js +2 -2
- package/dist/core/library-read.d.ts +37 -5
- package/dist/core/library-read.js +264 -33
- package/dist/core/library-search.d.ts +2 -2
- package/dist/installer/agent-config.js +5 -3
- package/dist/installer/agent-table.d.ts +16 -0
- package/dist/installer/agent-table.js +52 -17
- package/dist/mcp/server.js +9 -3
- package/dist/mcp/tools/asset-tools.js +21 -3
- package/dist/mcp/tools/audit-tools.d.ts +23 -0
- package/dist/mcp/tools/audit-tools.js +60 -0
- package/dist/mcp/tools/debug-tools.js +3 -1
- package/dist/mcp/tools/file-tools.js +17 -4
- package/dist/mcp/tools/generate-tools.js +74 -66
- package/dist/mcp/tools/library-tools.js +2 -0
- package/dist/mcp/tools/perception-tools.js +6 -19
- package/dist/mcp/tools/placement-tools.d.ts +24 -0
- package/dist/mcp/tools/placement-tools.js +91 -0
- package/dist/mcp/tools/project-tools.js +18 -15
- package/dist/mcp/tools/scene-tools.js +71 -83
- package/dist/mcp/tools/seeing-tools.d.ts +24 -0
- package/dist/mcp/tools/seeing-tools.js +64 -0
- package/dist/mcp/tools/spatial-tools.js +17 -9
- package/dist/mcp/tools/visual-tools.js +59 -7
- package/gemini-extension.json +26 -4
- package/integrations/amp/README.md +1 -1
- package/integrations/crush/README.md +1 -1
- package/integrations/grok-build/README.md +1 -1
- package/integrations/hermes/README.md +1 -1
- package/integrations/kimi-code/README.md +1 -1
- package/integrations/mistral-vibe/README.md +1 -1
- package/integrations/opencode/README.md +1 -1
- package/integrations/rovo-dev/README.md +1 -1
- package/integrations/vscode-copilot/README.md +1 -1
- package/integrations/warp/README.md +1 -1
- package/integrations/windsurf/README.md +1 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/references/summer-folder/resource.yaml +1 -1
- package/library/references/summer-folder/summer-folder.md +1 -1
- package/library/skills/3d-lighting/SKILL.md +1 -1
- package/library/skills/README.md +36 -14
- package/library/skills/adaptive-music/SKILL.md +1 -1
- package/library/skills/agent-playtesting/SKILL.md +1 -1
- package/library/skills/ai-characters-in-games/SKILL.md +220 -0
- package/library/skills/ai-characters-in-games/resource.yaml +30 -0
- package/library/skills/ambient-bed/SKILL.md +1 -1
- package/library/skills/animated-loop/SKILL.md +1 -1
- package/library/skills/animation-tree/SKILL.md +1 -1
- package/library/skills/art-direction/SKILL.md +1 -1
- package/library/skills/asset-strategy/SKILL.md +2 -1
- package/library/skills/asset-strategy/resource.yaml +2 -1
- package/library/skills/audio-direction/SKILL.md +1 -1
- package/library/skills/auto-fire-targeting/SKILL.md +1 -1
- package/library/skills/brainstorm-game/SKILL.md +1 -1
- package/library/skills/brainstorming/SKILL.md +1 -1
- package/library/skills/browse-templates/SKILL.md +3 -3
- package/library/skills/browse-templates/resource.yaml +1 -1
- package/library/skills/camera-rigs/SKILL.md +128 -0
- package/library/skills/camera-rigs/resource.yaml +29 -0
- package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
- package/library/skills/character-animation-wiring/SKILL.md +1 -1
- package/library/skills/character-model/SKILL.md +1 -1
- package/library/skills/character-movement/SKILL.md +326 -0
- package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
- package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
- package/library/skills/character-movement/references/movement-3d.md +136 -0
- package/library/skills/character-movement/references/platformer-feel.md +83 -0
- package/library/skills/character-movement/resource.yaml +40 -0
- package/library/skills/character-portrait/SKILL.md +1 -1
- package/library/skills/cinematic-cutscene/SKILL.md +1 -1
- package/library/skills/combat-basics/SKILL.md +159 -0
- package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
- package/library/skills/combat-basics/resource.yaml +34 -0
- package/library/skills/concept-art/SKILL.md +1 -1
- package/library/skills/create-asset-sheet/SKILL.md +1 -1
- package/library/skills/debug/SKILL.md +1 -1
- package/library/skills/debugging-game-feel/SKILL.md +1 -1
- package/library/skills/design-level/SKILL.md +1 -1
- package/library/skills/design-mechanic/SKILL.md +1 -1
- package/library/skills/design-npc/SKILL.md +1 -1
- package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
- package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
- package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
- package/library/skills/environment-kit/SKILL.md +1 -1
- package/library/skills/export-and-ship/SKILL.md +1 -1
- package/library/skills/fabricating-assets/SKILL.md +1 -1
- package/library/skills/facial-and-lipsync/SKILL.md +1 -1
- package/library/skills/fps-controller/SKILL.md +9 -7
- package/library/skills/fps-controller/resource.yaml +4 -1
- package/library/skills/game-feel/SKILL.md +1 -1
- package/library/skills/gameskill/SKILL.md +1 -1
- package/library/skills/gdscript-patterns/SKILL.md +1 -1
- package/library/skills/generate-motion/SKILL.md +9 -9
- package/library/skills/generate-motion/resource.yaml +2 -1
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
- package/library/skills/headless-scripting/SKILL.md +1 -1
- package/library/skills/in-game-purchases/SKILL.md +254 -0
- package/library/skills/in-game-purchases/resource.yaml +29 -0
- package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
- package/library/skills/investigating-bugs/SKILL.md +1 -1
- package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
- package/library/skills/make-game/SKILL.md +25 -3
- package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
- package/library/skills/make-game/references/2d-platformer.md +117 -0
- package/library/skills/make-game/references/2d-rpg-content.md +234 -0
- package/library/skills/make-game/references/2d-rpg.md +241 -0
- package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
- package/library/skills/make-game/references/2d-survivors.md +237 -0
- package/library/skills/make-game/references/3d-fps-systems.md +216 -0
- package/library/skills/make-game/references/3d-fps.md +177 -0
- package/library/skills/make-game/references/3d-horror-systems.md +186 -0
- package/library/skills/make-game/references/3d-horror.md +201 -0
- package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
- package/library/skills/make-game/references/3d-third-person.md +215 -0
- package/library/skills/make-game/resource.yaml +6 -1
- package/library/skills/multiplayer/SKILL.md +150 -0
- package/library/skills/multiplayer/resource.yaml +33 -0
- package/library/skills/multiplayer-movement/SKILL.md +510 -0
- package/library/skills/multiplayer-movement/files.md +501 -0
- package/library/skills/multiplayer-movement/resource.yaml +30 -0
- package/library/skills/multiplayer-project/SKILL.md +521 -0
- package/library/skills/multiplayer-project/files.md +448 -0
- package/library/skills/multiplayer-project/resource.yaml +36 -0
- package/library/skills/multiplayer-publish/SKILL.md +229 -0
- package/library/skills/multiplayer-publish/export-errors.md +89 -0
- package/library/skills/multiplayer-publish/resource.yaml +28 -0
- package/library/skills/multiplayer-state/SKILL.md +438 -0
- package/library/skills/multiplayer-state/files.md +592 -0
- package/library/skills/multiplayer-state/resource.yaml +35 -0
- package/library/skills/multiplayer-testing/SKILL.md +236 -0
- package/library/skills/multiplayer-testing/resource.yaml +39 -0
- package/library/skills/music-track/SKILL.md +1 -1
- package/library/skills/navigate-summer/SKILL.md +1 -1
- package/library/skills/new-project/SKILL.md +1 -1
- package/library/skills/organic-model/SKILL.md +1 -1
- package/library/skills/pixel-art/SKILL.md +1 -1
- package/library/skills/play/SKILL.md +1 -1
- package/library/skills/playtesting-a-feature/SKILL.md +1 -1
- package/library/skills/procedural-animation/SKILL.md +2 -2
- package/library/skills/procedural-animation/resource.yaml +1 -1
- package/library/skills/prop-model/SKILL.md +1 -1
- package/library/skills/psx-retro-rendering/SKILL.md +1 -1
- package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
- package/library/skills/remote-deploy/SKILL.md +1 -1
- package/library/skills/retarget/SKILL.md +1 -1
- package/library/skills/running-in-the-cloud/SKILL.md +1 -1
- package/library/skills/save-load/SKILL.md +156 -0
- package/library/skills/save-load/resource.yaml +27 -0
- package/library/skills/scene-composition/SKILL.md +1 -1
- package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
- package/library/skills/scene-scripting/SKILL.md +1 -1
- package/library/skills/scene-to-level/SKILL.md +1 -1
- package/library/skills/skill-create/SKILL.md +1 -1
- package/library/skills/skill-improve/SKILL.md +1 -1
- package/library/skills/skill-test/SKILL.md +1 -1
- package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
- package/library/skills/skybox-panorama/SKILL.md +1 -1
- package/library/skills/sound-effect/SKILL.md +1 -1
- package/library/skills/spatial-placement/SKILL.md +134 -3
- package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
- package/library/skills/spatial-placement/resource.yaml +16 -2
- package/library/skills/sprite-sheet/SKILL.md +1 -1
- package/library/skills/summer-analytics/SKILL.md +147 -0
- package/library/skills/summer-analytics/resource.yaml +28 -0
- package/library/skills/summer-friends/SKILL.md +226 -0
- package/library/skills/summer-friends/resource.yaml +28 -0
- package/library/skills/summer-leaderboards/SKILL.md +231 -0
- package/library/skills/summer-leaderboards/resource.yaml +30 -0
- package/library/skills/summer-match-results/SKILL.md +286 -0
- package/library/skills/summer-match-results/resource.yaml +32 -0
- package/library/skills/summer-matchmaking/SKILL.md +301 -0
- package/library/skills/summer-matchmaking/resource.yaml +32 -0
- package/library/skills/summer-parties/SKILL.md +253 -0
- package/library/skills/summer-parties/resource.yaml +30 -0
- package/library/skills/summer-player-data/SKILL.md +322 -0
- package/library/skills/summer-player-data/resource.yaml +30 -0
- package/library/skills/summer-store/SKILL.md +271 -0
- package/library/skills/summer-store/resource.yaml +29 -0
- package/library/skills/summer-world-chat/SKILL.md +195 -0
- package/library/skills/summer-world-chat/resource.yaml +30 -0
- package/library/skills/summer-world-saves/SKILL.md +284 -0
- package/library/skills/summer-world-saves/resource.yaml +29 -0
- package/library/skills/text-to-motion/SKILL.md +99 -0
- package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
- package/library/skills/text-to-motion/resource.yaml +41 -0
- package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
- package/library/skills/tileable-texture/SKILL.md +1 -1
- package/library/skills/trailer-shot/SKILL.md +1 -1
- package/library/skills/tune-performance/SKILL.md +1 -1
- package/library/skills/ui-basics/SKILL.md +10 -1
- package/library/skills/ui-graphics/SKILL.md +1 -1
- package/library/skills/use-widget-asset/SKILL.md +1 -1
- package/library/skills/using-summer/SKILL.md +2 -2
- package/library/skills/using-summer/resource.yaml +2 -2
- package/library/skills/vehicle-model/SKILL.md +1 -1
- package/library/skills/verification-before-completion/SKILL.md +1 -1
- package/library/skills/verifying-scenes/SKILL.md +76 -2
- package/library/skills/verifying-scenes/resource.yaml +13 -2
- package/library/skills/vfx-dissolve/SKILL.md +1 -1
- package/library/skills/vfx-fire/SKILL.md +1 -1
- package/library/skills/vfx-hit-spark/SKILL.md +1 -1
- package/library/skills/vfx-lightning/SKILL.md +1 -1
- package/library/skills/vfx-magic-glow/SKILL.md +1 -1
- package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
- package/library/skills/vfx-smoke/SKILL.md +1 -1
- package/library/skills/vfx-water-ripple/SKILL.md +1 -1
- package/library/skills/voice-line/SKILL.md +1 -1
- package/library/skills/web-and-touch-input/SKILL.md +38 -0
- package/library/skills/web-and-touch-input/resource.yaml +28 -0
- package/library/skills/web-compatibility-look/SKILL.md +62 -0
- package/library/skills/web-compatibility-look/resource.yaml +30 -0
- package/library/skills/world-building-3d/SKILL.md +8 -1
- package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
- package/library/skills/world-building-3d/resource.yaml +2 -1
- package/library/skills/writing-plans/SKILL.md +1 -1
- package/library/skills/writing-skills/SKILL.md +1 -1
- package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
- package/library/tools/attach-to-surface/resource.yaml +142 -0
- package/library/tools/batch/resource.yaml +9 -3
- package/library/tools/connect-ports/resource.yaml +120 -0
- package/library/tools/connect-signal/resource.yaml +3 -2
- package/library/tools/debug-views/resource.yaml +94 -0
- package/library/tools/frame-nodes/resource.yaml +104 -0
- package/library/tools/frame-shot/resource.yaml +134 -0
- package/library/tools/generate-motion/resource.yaml +44 -9
- package/library/tools/get-board/resource.yaml +50 -0
- package/library/tools/get-project-context/resource.yaml +13 -2
- package/library/tools/grep/resource.yaml +77 -0
- package/library/tools/inspect-asset/resource.yaml +71 -0
- package/library/tools/inspect-node/resource.yaml +9 -2
- package/library/tools/inspect-resource/resource.yaml +16 -5
- package/library/tools/instantiate-scene/resource.yaml +27 -2
- package/library/tools/measure/resource.yaml +96 -0
- package/library/tools/place-adjacent/resource.yaml +113 -0
- package/library/tools/play/resource.yaml +15 -2
- package/library/tools/raycast/resource.yaml +101 -0
- package/library/tools/read-file/resource.yaml +36 -2
- package/library/tools/read-library/resource.yaml +2 -2
- package/library/tools/repeat-along/resource.yaml +129 -0
- package/library/tools/replace-node/resource.yaml +11 -5
- package/library/tools/scene-audit/resource.yaml +144 -0
- package/library/tools/screenshot/resource.yaml +13 -2
- package/library/tools/search-assets/resource.yaml +18 -0
- package/library/tools/shot-sheet/resource.yaml +110 -0
- package/library/tools/snap-to-surface/resource.yaml +4 -1
- package/library/tools/world-snapshot/resource.yaml +24 -3
- package/library/tools/zoom/resource.yaml +105 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +17 -3
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +26 -4
- package/registry/generated/index.json +2154 -469
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +27 -5
- package/registry/generated/plugin.codex.json +28 -6
- package/registry/generated/plugin.cursor.json +27 -5
- package/registry/generated/plugin.factory.json +27 -5
- package/registry/generated/skills-index.md +36 -14
- package/registry/generated/skills-registry.json +421 -124
- package/registry/generated/templates-registry.json +2 -2
- package/library/skills/host-authoritative-state/SKILL.md +0 -332
- package/library/skills/host-authoritative-state/resource.yaml +0 -28
- package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
- package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
- package/library/skills/setup-multiplayer/SKILL.md +0 -336
- package/library/skills/setup-multiplayer/resource.yaml +0 -28
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The person's approved planning board (summer_get_board): the look, the
|
|
4
|
+
* characters, the place, the first-minute storyboard and their picks, served by
|
|
5
|
+
* GET /api/cloud-editor/v1/board for the token's own project. With pictures,
|
|
6
|
+
* the look and the picked cards come back as images the model can see.
|
|
7
|
+
*/
|
|
8
|
+
export declare const BOARD_ENDPOINT = "/api/cloud-editor/v1/board";
|
|
9
|
+
export declare const boardArgsSchema: z.ZodObject<{
|
|
10
|
+
project: z.ZodString;
|
|
11
|
+
pictures: z.ZodOptional<z.ZodBoolean>;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
project: string;
|
|
14
|
+
pictures?: boolean | undefined;
|
|
15
|
+
}, {
|
|
16
|
+
project: string;
|
|
17
|
+
pictures?: boolean | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export type BoardArgs = z.infer<typeof boardArgsSchema>;
|
|
20
|
+
export declare function boardQuery({ project, pictures }: BoardArgs): URLSearchParams;
|
|
21
|
+
type Content = {
|
|
22
|
+
type: "text";
|
|
23
|
+
text: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: "image";
|
|
26
|
+
data: string;
|
|
27
|
+
mimeType: string;
|
|
28
|
+
};
|
|
29
|
+
/** The board as one text block, then each picture as a label and an image block. */
|
|
30
|
+
export declare function boardContent(data: Record<string, unknown>): Content[];
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The person's approved planning board (summer_get_board): the look, the
|
|
4
|
+
* characters, the place, the first-minute storyboard and their picks, served by
|
|
5
|
+
* GET /api/cloud-editor/v1/board for the token's own project. With pictures,
|
|
6
|
+
* the look and the picked cards come back as images the model can see.
|
|
7
|
+
*/
|
|
8
|
+
export const BOARD_ENDPOINT = "/api/cloud-editor/v1/board";
|
|
9
|
+
export const boardArgsSchema = z.object({
|
|
10
|
+
project: z
|
|
11
|
+
.string()
|
|
12
|
+
.min(1)
|
|
13
|
+
.max(100)
|
|
14
|
+
.describe("The game's project id (given in your build brief)"),
|
|
15
|
+
pictures: z
|
|
16
|
+
.boolean()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Also return the look and the picked cards as images (default true)"),
|
|
19
|
+
});
|
|
20
|
+
export function boardQuery({ project, pictures }) {
|
|
21
|
+
const params = new URLSearchParams({ project });
|
|
22
|
+
if (pictures !== false)
|
|
23
|
+
params.set("images", "1");
|
|
24
|
+
return params;
|
|
25
|
+
}
|
|
26
|
+
/** The board as one text block, then each picture as a label and an image block. */
|
|
27
|
+
export function boardContent(data) {
|
|
28
|
+
const { pictures, ...board } = data;
|
|
29
|
+
const images = (Array.isArray(pictures) ? pictures : []).flatMap((picture) => {
|
|
30
|
+
const match = typeof picture.dataUrl === "string" ? /^data:(image\/(?:jpeg|png|webp));base64,([A-Za-z0-9+/=]+)$/.exec(picture.dataUrl) : null;
|
|
31
|
+
if (!match)
|
|
32
|
+
return [];
|
|
33
|
+
const label = typeof picture.label === "string" ? picture.label : "Board picture";
|
|
34
|
+
return [
|
|
35
|
+
{ type: "text", text: label },
|
|
36
|
+
{ type: "image", data: match[2], mimeType: match[1] },
|
|
37
|
+
];
|
|
38
|
+
});
|
|
39
|
+
const note = images.length
|
|
40
|
+
? "Compare your latest screenshot against these pictures: palette, shapes and proportions, camera angle, composition. Fix the biggest difference."
|
|
41
|
+
: "No board pictures could be loaded; use the descriptions.";
|
|
42
|
+
return [{ type: "text", text: `${JSON.stringify(board, null, 2)}\n\n${note}` }, ...images];
|
|
43
|
+
}
|
|
@@ -94,5 +94,7 @@ export interface CameraPose {
|
|
|
94
94
|
/** camera_pose {position, look_at, fov} as the engine echoes it (3dp literals). */
|
|
95
95
|
export declare function readCameraPose(payload: Record<string, unknown> | undefined): CameraPose | null;
|
|
96
96
|
/** One compact line per mark so the model can map a numbered label in the
|
|
97
|
-
* image to a node path: `1 -> Props/Crate_02 (MeshInstance3D) @ 120,80 200x140`.
|
|
98
|
-
|
|
97
|
+
* image to a node path: `1 -> Props/Crate_02 (MeshInstance3D) @ 120,80 200x140`.
|
|
98
|
+
* `notes` (by mark id) are appended in parentheses, e.g. the occlusion test's
|
|
99
|
+
* "(hidden behind House1/Front)". */
|
|
100
|
+
export declare function formatSceneMarks(summary: SceneMarksSummary, notes?: ReadonlyMap<number, string>): string[];
|
|
@@ -196,8 +196,10 @@ export function readCameraPose(payload) {
|
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
/** One compact line per mark so the model can map a numbered label in the
|
|
199
|
-
* image to a node path: `1 -> Props/Crate_02 (MeshInstance3D) @ 120,80 200x140`.
|
|
200
|
-
|
|
199
|
+
* image to a node path: `1 -> Props/Crate_02 (MeshInstance3D) @ 120,80 200x140`.
|
|
200
|
+
* `notes` (by mark id) are appended in parentheses, e.g. the occlusion test's
|
|
201
|
+
* "(hidden behind House1/Front)". */
|
|
202
|
+
export function formatSceneMarks(summary, notes) {
|
|
201
203
|
const lines = [];
|
|
202
204
|
if (summary.unsupported) {
|
|
203
205
|
lines.push(`marks: unsupported for this scene (${summary.unsupported}) — no overlay was drawn; the image carries no numbered labels.`);
|
|
@@ -216,7 +218,8 @@ export function formatSceneMarks(summary) {
|
|
|
216
218
|
const rect = mark.screen_rect
|
|
217
219
|
? ` @ ${Math.round(mark.screen_rect.x)},${Math.round(mark.screen_rect.y)} ${Math.round(mark.screen_rect.w)}x${Math.round(mark.screen_rect.h)}`
|
|
218
220
|
: "";
|
|
219
|
-
|
|
221
|
+
const note = notes?.get(mark.id);
|
|
222
|
+
lines.push(` ${mark.id} -> ${mark.path} (${mark.class})${rect}${note ? ` (${note})` : ""}`);
|
|
220
223
|
}
|
|
221
224
|
return lines;
|
|
222
225
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connect-signal — ONE implementation of `summer_connect_signal` for both faces
|
|
3
|
+
* (src/mcp/tools/scene-tools.ts and tool-dispatch.ts) that leaves the
|
|
4
|
+
* connection in the saved scene, verified from the file.
|
|
5
|
+
*
|
|
6
|
+
* Why not the engine's ConnectSignal op: SceneOps::connect_signal (engine
|
|
7
|
+
* modules/1summer_engine/editor/ops/scene_ops.cpp) builds
|
|
8
|
+
* `Callable(receiver, method)` and registers
|
|
9
|
+
* `add_do_method(emitter, "connect", signal, cb)` — no CONNECT_PERSIST, and the
|
|
10
|
+
* op reads no flags argument. PackedScene::pack writes only persistent
|
|
11
|
+
* connections, so the receipt said ok, the batch said persisted:true, and the
|
|
12
|
+
* saved .tscn had no [connection] line (engine 0.6.0, a Clock.timeout ->
|
|
13
|
+
* Door.queue_free connection). The editor's own Node dock connects with
|
|
14
|
+
* CONNECT_PERSIST; so does this tool:
|
|
15
|
+
* 0. SaveScene (opens the scene as a tab if needed; the file is the editor
|
|
16
|
+
* state), read the file: an identical [connection] line already there is
|
|
17
|
+
* reported as done, nothing else sent;
|
|
18
|
+
* 1. RunSceneScript runs against the ACTIVE tab, so a target scene in a
|
|
19
|
+
* background tab is brought forward with OpenScene first (and the user's
|
|
20
|
+
* tab restored at the end);
|
|
21
|
+
* 2. a RunSceneScript probe: `emitter.connect(signal, Callable(receiver,
|
|
22
|
+
* method), CONNECT_PERSIST)` — an existing non-persistent connection of
|
|
23
|
+
* the same pair (an earlier ConnectSignal) is replaced by a persistent one;
|
|
24
|
+
* 3. SaveScene — the run marked the tab unsaved; this leaves it clean;
|
|
25
|
+
* 4. read the file back and find the [connection] line. Missing is
|
|
26
|
+
* failure_reason not_persisted (persisted:false), never success.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
import { type CapabilityAdvertisingClient } from "../capability-skew.js";
|
|
30
|
+
import { type JsonRecord } from "../util/json.js";
|
|
31
|
+
import { type ParsedTscn, type TscnConnection } from "./tscn.js";
|
|
32
|
+
export declare const connectSignalInputShape: {
|
|
33
|
+
scenePath: z.ZodString;
|
|
34
|
+
emitter: z.ZodString;
|
|
35
|
+
signal: z.ZodString;
|
|
36
|
+
receiver: z.ZodString;
|
|
37
|
+
method: z.ZodString;
|
|
38
|
+
};
|
|
39
|
+
export declare const connectSignalInputSchema: z.ZodObject<{
|
|
40
|
+
scenePath: z.ZodString;
|
|
41
|
+
emitter: z.ZodString;
|
|
42
|
+
signal: z.ZodString;
|
|
43
|
+
receiver: z.ZodString;
|
|
44
|
+
method: z.ZodString;
|
|
45
|
+
}, "strict", z.ZodTypeAny, {
|
|
46
|
+
method: string;
|
|
47
|
+
scenePath: string;
|
|
48
|
+
signal: string;
|
|
49
|
+
emitter: string;
|
|
50
|
+
receiver: string;
|
|
51
|
+
}, {
|
|
52
|
+
method: string;
|
|
53
|
+
scenePath: string;
|
|
54
|
+
signal: string;
|
|
55
|
+
emitter: string;
|
|
56
|
+
receiver: string;
|
|
57
|
+
}>;
|
|
58
|
+
export type ConnectSignalArgs = z.infer<typeof connectSignalInputSchema>;
|
|
59
|
+
export interface ConnectSignalClient extends CapabilityAdvertisingClient {
|
|
60
|
+
executeIdentityBoundOps(ops: Record<string, unknown>[], options?: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
61
|
+
readProjectFile(path: string, maxBytes?: number): Promise<unknown>;
|
|
62
|
+
getSceneState(scenePath?: string, options?: {
|
|
63
|
+
depth?: number;
|
|
64
|
+
limit?: number;
|
|
65
|
+
root?: string;
|
|
66
|
+
}): Promise<unknown>;
|
|
67
|
+
}
|
|
68
|
+
export declare const CONNECT_SIGNAL_FALLBACK = "connect it from summer_run_script with emitter.connect(signal, Callable(receiver, method), CONNECT_PERSIST) or ctx.connect_signal, then summer_save_scene, and confirm the [connection] line with summer_read_file";
|
|
69
|
+
/** The probe. Values are embedded as JSON string literals, which GDScript
|
|
70
|
+
* reads with the same escapes. Returns a Dictionary (RunSceneScript's
|
|
71
|
+
* `result`); expected failures return ok:false instead of raising, so the
|
|
72
|
+
* engine has nothing to roll back. */
|
|
73
|
+
export declare function connectScript(target: string, emitter: string, signal: string, receiver: string, method: string): string;
|
|
74
|
+
/** The [connection] line for signal/from/to/method in the parsed scene. */
|
|
75
|
+
export declare function findConnection(parsed: ParsedTscn, wanted: {
|
|
76
|
+
signal: string;
|
|
77
|
+
from: string;
|
|
78
|
+
to: string;
|
|
79
|
+
method: string;
|
|
80
|
+
}): TscnConnection | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* Connect `signal` of `emitter` to `method` of `receiver` in `scenePath` so the
|
|
83
|
+
* saved scene holds it, and prove that from the file.
|
|
84
|
+
*/
|
|
85
|
+
export declare function connectSignalPersisted(client: ConnectSignalClient, args: ConnectSignalArgs): Promise<JsonRecord>;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connect-signal — ONE implementation of `summer_connect_signal` for both faces
|
|
3
|
+
* (src/mcp/tools/scene-tools.ts and tool-dispatch.ts) that leaves the
|
|
4
|
+
* connection in the saved scene, verified from the file.
|
|
5
|
+
*
|
|
6
|
+
* Why not the engine's ConnectSignal op: SceneOps::connect_signal (engine
|
|
7
|
+
* modules/1summer_engine/editor/ops/scene_ops.cpp) builds
|
|
8
|
+
* `Callable(receiver, method)` and registers
|
|
9
|
+
* `add_do_method(emitter, "connect", signal, cb)` — no CONNECT_PERSIST, and the
|
|
10
|
+
* op reads no flags argument. PackedScene::pack writes only persistent
|
|
11
|
+
* connections, so the receipt said ok, the batch said persisted:true, and the
|
|
12
|
+
* saved .tscn had no [connection] line (engine 0.6.0, a Clock.timeout ->
|
|
13
|
+
* Door.queue_free connection). The editor's own Node dock connects with
|
|
14
|
+
* CONNECT_PERSIST; so does this tool:
|
|
15
|
+
* 0. SaveScene (opens the scene as a tab if needed; the file is the editor
|
|
16
|
+
* state), read the file: an identical [connection] line already there is
|
|
17
|
+
* reported as done, nothing else sent;
|
|
18
|
+
* 1. RunSceneScript runs against the ACTIVE tab, so a target scene in a
|
|
19
|
+
* background tab is brought forward with OpenScene first (and the user's
|
|
20
|
+
* tab restored at the end);
|
|
21
|
+
* 2. a RunSceneScript probe: `emitter.connect(signal, Callable(receiver,
|
|
22
|
+
* method), CONNECT_PERSIST)` — an existing non-persistent connection of
|
|
23
|
+
* the same pair (an earlier ConnectSignal) is replaced by a persistent one;
|
|
24
|
+
* 3. SaveScene — the run marked the tab unsaved; this leaves it clean;
|
|
25
|
+
* 4. read the file back and find the [connection] line. Missing is
|
|
26
|
+
* failure_reason not_persisted (persisted:false), never success.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
import { missingEngineOpResult, resolveSingleOnlyOps } from "../capability-skew.js";
|
|
30
|
+
import { ToolInputError } from "../tool-errors.js";
|
|
31
|
+
import { asRecord, stringFrom } from "../util/json.js";
|
|
32
|
+
import { executeOpsChunked, safeProjectPath } from "./engine-ops.js";
|
|
33
|
+
import { extractOpError } from "./engine-receipt.js";
|
|
34
|
+
import { buildRunSceneScriptOp } from "./scene-script.js";
|
|
35
|
+
import { isReadbackFailure, readSavedScene, readbackFailure } from "./scene-readback.js";
|
|
36
|
+
import { normalizeNodePath } from "./tscn.js";
|
|
37
|
+
// Mirrors library/tools/connect-signal/resource.yaml input_schema (parity-tested).
|
|
38
|
+
export const connectSignalInputShape = {
|
|
39
|
+
scenePath: z.string().describe("Target scene path, e.g. 'res://main.tscn'"),
|
|
40
|
+
emitter: z.string().describe("Node that fires the signal, e.g. './Player/HitArea'"),
|
|
41
|
+
signal: z.string().describe("Signal name, e.g. 'body_entered'"),
|
|
42
|
+
receiver: z.string().describe("Node with the handler script, e.g. './Player'"),
|
|
43
|
+
method: z.string().describe("Method name in the receiver's script, e.g. '_on_hit_area_body_entered'"),
|
|
44
|
+
};
|
|
45
|
+
export const connectSignalInputSchema = z.object(connectSignalInputShape).strict();
|
|
46
|
+
export const CONNECT_SIGNAL_FALLBACK = "connect it from summer_run_script with emitter.connect(signal, Callable(receiver, method), CONNECT_PERSIST) or ctx.connect_signal, then summer_save_scene, and confirm the [connection] line with summer_read_file";
|
|
47
|
+
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
48
|
+
/** The probe. Values are embedded as JSON string literals, which GDScript
|
|
49
|
+
* reads with the same escapes. Returns a Dictionary (RunSceneScript's
|
|
50
|
+
* `result`); expected failures return ok:false instead of raising, so the
|
|
51
|
+
* engine has nothing to roll back. */
|
|
52
|
+
export function connectScript(target, emitter, signal, receiver, method) {
|
|
53
|
+
const lit = (value) => JSON.stringify(value);
|
|
54
|
+
return `const TARGET_SCENE := ${lit(target)}
|
|
55
|
+
const EMITTER := ${lit(emitter)}
|
|
56
|
+
const SIGNAL_NAME := ${lit(signal)}
|
|
57
|
+
const RECEIVER := ${lit(receiver)}
|
|
58
|
+
const METHOD := ${lit(method)}
|
|
59
|
+
|
|
60
|
+
func _resolve(root: Node, path: String) -> Node:
|
|
61
|
+
if path == "" or path == "." or path == "./" or path == "/":
|
|
62
|
+
return root
|
|
63
|
+
var node := root.get_node_or_null(NodePath(path))
|
|
64
|
+
if node == null or (node != root and not root.is_ancestor_of(node)):
|
|
65
|
+
return null
|
|
66
|
+
return node
|
|
67
|
+
|
|
68
|
+
func _flags(emitter: Node, callable: Callable) -> int:
|
|
69
|
+
for c in emitter.get_signal_connection_list(SIGNAL_NAME):
|
|
70
|
+
if c["callable"] == callable:
|
|
71
|
+
return int(c["flags"])
|
|
72
|
+
return -1
|
|
73
|
+
|
|
74
|
+
func run(ctx):
|
|
75
|
+
var root: Node = ctx.get_scene_root()
|
|
76
|
+
if root == null:
|
|
77
|
+
return {"ok": false, "failure_reason": "no_edited_scene", "error": "No scene is open in the editor."}
|
|
78
|
+
if root.scene_file_path != TARGET_SCENE:
|
|
79
|
+
return {"ok": false, "failure_reason": "scene_not_active", "active_scene": root.scene_file_path, "error": "The active editor tab is " + root.scene_file_path + ", not " + TARGET_SCENE + "; nothing was connected."}
|
|
80
|
+
var emitter := _resolve(root, EMITTER)
|
|
81
|
+
if emitter == null:
|
|
82
|
+
return {"ok": false, "failure_reason": "emitter_not_found", "error": "Emitter not found: " + EMITTER}
|
|
83
|
+
var receiver := _resolve(root, RECEIVER)
|
|
84
|
+
if receiver == null:
|
|
85
|
+
return {"ok": false, "failure_reason": "receiver_not_found", "error": "Receiver not found: " + RECEIVER}
|
|
86
|
+
if not emitter.has_signal(SIGNAL_NAME):
|
|
87
|
+
var names := []
|
|
88
|
+
for s in emitter.get_signal_list():
|
|
89
|
+
names.append(String(s["name"]))
|
|
90
|
+
return {"ok": false, "failure_reason": "signal_not_found", "signals": names, "error": emitter.get_class() + " " + EMITTER + " has no signal " + SIGNAL_NAME}
|
|
91
|
+
var callable := Callable(receiver, METHOD)
|
|
92
|
+
var before := _flags(emitter, callable)
|
|
93
|
+
var previous := "none"
|
|
94
|
+
if before >= 0:
|
|
95
|
+
previous = "persistent" if (before & Object.CONNECT_PERSIST) != 0 else "not_persistent"
|
|
96
|
+
if previous == "not_persistent":
|
|
97
|
+
emitter.disconnect(SIGNAL_NAME, callable)
|
|
98
|
+
if previous != "persistent":
|
|
99
|
+
var err: int = emitter.connect(SIGNAL_NAME, callable, Object.CONNECT_PERSIST)
|
|
100
|
+
if err != OK:
|
|
101
|
+
return {"ok": false, "failure_reason": "connect_failed", "error": "connect() returned error " + str(err)}
|
|
102
|
+
var flags := _flags(emitter, callable)
|
|
103
|
+
if flags < 0 or (flags & Object.CONNECT_PERSIST) == 0:
|
|
104
|
+
return {"ok": false, "failure_reason": "connect_failed", "flags": flags, "error": "The connection is not persistent after connect() (flags " + str(flags) + ")."}
|
|
105
|
+
return {"ok": true, "previous": previous, "flags": flags, "from": str(root.get_path_to(emitter)), "to": str(root.get_path_to(receiver)), "method_exists": receiver.has_method(METHOD)}
|
|
106
|
+
`;
|
|
107
|
+
}
|
|
108
|
+
/** The [connection] line for signal/from/to/method in the parsed scene. */
|
|
109
|
+
export function findConnection(parsed, wanted) {
|
|
110
|
+
const from = normalizeNodePath(wanted.from);
|
|
111
|
+
const to = normalizeNodePath(wanted.to);
|
|
112
|
+
return parsed.connections.find((c) => c.signal === wanted.signal && c.method === wanted.method && c.from === from && c.to === to);
|
|
113
|
+
}
|
|
114
|
+
/** The scene in the active editor tab, from the published scene state. */
|
|
115
|
+
async function activeScenePath(client) {
|
|
116
|
+
try {
|
|
117
|
+
const state = asRecord(await client.getSceneState());
|
|
118
|
+
return (stringFrom(asRecord(state?.provenance)?.scenePath) ??
|
|
119
|
+
stringFrom(asRecord(state?.data)?.scenePath) ??
|
|
120
|
+
null);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** The RunSceneScript entry of a receipt (its `result` is the probe's Dictionary). */
|
|
127
|
+
function scriptEntry(receipt) {
|
|
128
|
+
const envelope = asRecord(receipt);
|
|
129
|
+
const results = Array.isArray(envelope?.results) ? envelope.results : [];
|
|
130
|
+
const entry = results.map((r) => asRecord(r)).find((r) => r?.op === "RunSceneScript") ?? asRecord(results[0]);
|
|
131
|
+
return entry ?? envelope;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Connect `signal` of `emitter` to `method` of `receiver` in `scenePath` so the
|
|
135
|
+
* saved scene holds it, and prove that from the file.
|
|
136
|
+
*/
|
|
137
|
+
export async function connectSignalPersisted(client, args) {
|
|
138
|
+
const scenePath = safeProjectPath(args.scenePath);
|
|
139
|
+
if (!scenePath.endsWith(".tscn")) {
|
|
140
|
+
throw new ToolInputError("summer_connect_signal verifies the connection in the saved text scene, so scenePath must be a .tscn file. For a binary .scn: " +
|
|
141
|
+
CONNECT_SIGNAL_FALLBACK + ".");
|
|
142
|
+
}
|
|
143
|
+
const signal = args.signal.trim();
|
|
144
|
+
const method = args.method.trim();
|
|
145
|
+
const emitter = args.emitter.trim();
|
|
146
|
+
const receiver = args.receiver.trim();
|
|
147
|
+
if (!IDENTIFIER.test(signal))
|
|
148
|
+
throw new ToolInputError(`Invalid signal "${args.signal}": pass a signal name such as 'body_entered'.`);
|
|
149
|
+
if (!IDENTIFIER.test(method))
|
|
150
|
+
throw new ToolInputError(`Invalid method "${args.method}": pass a method name such as '_on_timer_timeout'.`);
|
|
151
|
+
for (const [label, value] of [["emitter", emitter], ["receiver", receiver]]) {
|
|
152
|
+
if (value === "" || /[\u0000-\u001f"]/.test(value)) {
|
|
153
|
+
throw new ToolInputError(`Invalid ${label} path "${value}": pass a scene-relative node path such as './World/Door' ('.' is the scene root).`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const wanted = { signal, from: emitter, to: receiver, method };
|
|
157
|
+
const describe = `${normalizeNodePath(emitter)}:${signal} -> ${normalizeNodePath(receiver)}:${method}`;
|
|
158
|
+
for (const op of ["RunSceneScript", "SaveScene"]) {
|
|
159
|
+
const missing = missingEngineOpResult(client, op, CONNECT_SIGNAL_FALLBACK);
|
|
160
|
+
if (missing)
|
|
161
|
+
return { ...missing };
|
|
162
|
+
}
|
|
163
|
+
const singleOnly = resolveSingleOnlyOps(client);
|
|
164
|
+
const save = () => executeOpsChunked((chunk) => client.executeIdentityBoundOps(chunk, { scenePath }), [{ op: "SaveScene" }], singleOnly);
|
|
165
|
+
// 0. Save (the file is the editor state; the scene is open as a tab), read.
|
|
166
|
+
const pre = await save();
|
|
167
|
+
if (extractOpError(pre)) {
|
|
168
|
+
return { ...(asRecord(pre) ?? {}), note: `Nothing was connected: SaveScene on ${scenePath} failed before the connection.` };
|
|
169
|
+
}
|
|
170
|
+
const before = await readSavedScene(client, scenePath, `Connect it by hand: ${CONNECT_SIGNAL_FALLBACK}.`);
|
|
171
|
+
if (isReadbackFailure(before))
|
|
172
|
+
return { ...before, note: "Nothing was connected." };
|
|
173
|
+
const existing = findConnection(before.parsed, wanted);
|
|
174
|
+
if (existing) {
|
|
175
|
+
return {
|
|
176
|
+
ok: true,
|
|
177
|
+
persisted: true,
|
|
178
|
+
verified: true,
|
|
179
|
+
already_connected: true,
|
|
180
|
+
scenePath,
|
|
181
|
+
connection: { signal, from: existing.from, to: existing.to, method },
|
|
182
|
+
line: existing.raw,
|
|
183
|
+
note: `The saved ${scenePath} already holds this connection; nothing was changed.`,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
// 1. The probe runs in the active tab: bring the target forward if needed.
|
|
187
|
+
const warnings = [];
|
|
188
|
+
const active = await activeScenePath(client);
|
|
189
|
+
let restoreTo = null;
|
|
190
|
+
if (active !== scenePath) {
|
|
191
|
+
const opened = await client.executeIdentityBoundOps([{ op: "OpenScene", path: scenePath }]);
|
|
192
|
+
const openError = extractOpError(opened);
|
|
193
|
+
if (openError) {
|
|
194
|
+
return readbackFailure("scene_activation_failed", `Nothing was connected: the probe runs in the active editor tab and ${scenePath} could not be brought forward (${openError}).`, { persisted: false, verified: false });
|
|
195
|
+
}
|
|
196
|
+
restoreTo = active;
|
|
197
|
+
if (!active)
|
|
198
|
+
warnings.push(`The active tab could not be read, so ${scenePath} was brought forward and left open as the active tab.`);
|
|
199
|
+
}
|
|
200
|
+
const restoreTab = async () => {
|
|
201
|
+
if (!restoreTo)
|
|
202
|
+
return;
|
|
203
|
+
const back = await client.executeIdentityBoundOps([{ op: "OpenScene", path: restoreTo }]);
|
|
204
|
+
if (extractOpError(back))
|
|
205
|
+
warnings.push(`${scenePath} was brought forward for the probe; switching back to ${restoreTo} failed.`);
|
|
206
|
+
};
|
|
207
|
+
const tabNote = restoreTo ? { tab_switched: { to: scenePath, restored: restoreTo } } : {};
|
|
208
|
+
// 2. Connect with CONNECT_PERSIST.
|
|
209
|
+
const { op, timeoutMs } = buildRunSceneScriptOp({
|
|
210
|
+
source: connectScript(scenePath, emitter, signal, receiver, method),
|
|
211
|
+
max_seconds: 10,
|
|
212
|
+
checkpoint: false,
|
|
213
|
+
});
|
|
214
|
+
const ran = await client.executeIdentityBoundOps([op], undefined, timeoutMs);
|
|
215
|
+
const runError = extractOpError(ran);
|
|
216
|
+
const outcome = asRecord(scriptEntry(ran)?.result);
|
|
217
|
+
if (runError || !outcome) {
|
|
218
|
+
await restoreTab();
|
|
219
|
+
return readbackFailure("connect_failed", `Nothing was connected: the RunSceneScript probe failed (${runError ?? "no result from the probe"}). ${scenePath} on disk is unchanged.`, { persisted: false, verified: false, ...tabNote, ...(warnings.length ? { warnings } : {}) });
|
|
220
|
+
}
|
|
221
|
+
if (outcome.ok !== true) {
|
|
222
|
+
// The run marked the active tab unsaved; it was saved a moment ago, so a
|
|
223
|
+
// save of the target (when it is the one the probe ran in) leaves it clean.
|
|
224
|
+
if (outcome.failure_reason !== "scene_not_active")
|
|
225
|
+
await save();
|
|
226
|
+
await restoreTab();
|
|
227
|
+
return readbackFailure(String(outcome.failure_reason ?? "connect_failed"), `Nothing was connected (${describe}): ${String(outcome.error ?? "the probe refused")}` +
|
|
228
|
+
(outcome.failure_reason === "scene_not_active"
|
|
229
|
+
? " The engine marks the tab a script ran in as unsaved; that tab's content is unchanged."
|
|
230
|
+
: ""), {
|
|
231
|
+
persisted: false,
|
|
232
|
+
verified: false,
|
|
233
|
+
...(Array.isArray(outcome.signals) ? { signals: outcome.signals } : {}),
|
|
234
|
+
...tabNote,
|
|
235
|
+
...(warnings.length ? { warnings } : {}),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
// 3. Save: the run marked the tab unsaved.
|
|
239
|
+
const saved = await save();
|
|
240
|
+
await restoreTab();
|
|
241
|
+
const from = normalizeNodePath(String(outcome.from ?? emitter));
|
|
242
|
+
const to = normalizeNodePath(String(outcome.to ?? receiver));
|
|
243
|
+
if (extractOpError(saved)) {
|
|
244
|
+
return {
|
|
245
|
+
...(asRecord(saved) ?? {}),
|
|
246
|
+
ok: false,
|
|
247
|
+
persisted: false,
|
|
248
|
+
verified: false,
|
|
249
|
+
note: `${describe} is connected in the editor (persistent), but SaveScene failed, so ${scenePath} on disk does not hold it. Fix the save error, then call summer_save_scene.`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
// 4. Read the file back.
|
|
253
|
+
const after = await readSavedScene(client, scenePath);
|
|
254
|
+
if (isReadbackFailure(after)) {
|
|
255
|
+
return { ...after, persisted: false, verified: false, note: "The connection was made and saved, but the file could not be read back to verify it." };
|
|
256
|
+
}
|
|
257
|
+
const line = findConnection(after.parsed, { signal, from, to, method });
|
|
258
|
+
const persistence = asRecord(asRecord(saved)?.scenePersistence);
|
|
259
|
+
// SaveScene's meta.dirty is the tab's unsaved flag right after the save.
|
|
260
|
+
const savedResults = asRecord(saved)?.results;
|
|
261
|
+
const saveMeta = asRecord((Array.isArray(savedResults) ? savedResults.map((r) => asRecord(r)) : []).find((r) => r?.op === "SaveScene")?.meta);
|
|
262
|
+
const tabClean = typeof saveMeta?.dirty === "boolean" ? { tab_clean: !saveMeta.dirty } : {};
|
|
263
|
+
if (!line) {
|
|
264
|
+
return readbackFailure("not_persisted", `summer_connect_signal did NOT persist (persisted:false): the saved ${scenePath} has no [connection signal="${signal}" from="${from}" to="${to}" method="${method}"] line ` +
|
|
265
|
+
`although the editor reports the connection with flags ${String(outcome.flags)}. Do not report it as done: read the saved file (summer_read_file) and ${CONNECT_SIGNAL_FALLBACK}.`, { persisted: false, verified: false, ...tabNote });
|
|
266
|
+
}
|
|
267
|
+
if (outcome.method_exists === false) {
|
|
268
|
+
warnings.push(`${to} has no method ${method} yet: the connection is saved, but the signal raises an error when it fires until the receiver's script defines it.`);
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
ok: true,
|
|
272
|
+
persisted: true,
|
|
273
|
+
verified: true,
|
|
274
|
+
scenePath,
|
|
275
|
+
connection: { signal, from, to, method },
|
|
276
|
+
line: line.raw,
|
|
277
|
+
...(outcome.previous === "not_persistent" ? { replaced_non_persistent: true } : {}),
|
|
278
|
+
via: "RunSceneScript: emitter.connect(signal, Callable(receiver, method), CONNECT_PERSIST), then SaveScene",
|
|
279
|
+
...(persistence ? { scenePersistence: { ...persistence, verified: true } } : {}),
|
|
280
|
+
...tabClean,
|
|
281
|
+
...tabNote,
|
|
282
|
+
...(warnings.length ? { warnings } : {}),
|
|
283
|
+
undo: "One Ctrl+Z in that scene reverts the connection (the scene script's undo step).",
|
|
284
|
+
};
|
|
285
|
+
}
|
|
@@ -22,5 +22,10 @@ export declare const SNAP_TO_SURFACE_FALLBACK = "read the support's position and
|
|
|
22
22
|
export declare const ALIGN_DISTRIBUTE_FALLBACK = "compute the shared anchor or spacing from the subjects' positions and sizes (summer_inspect_node / summer_inspect_resource) and set each subject's position with summer_set_prop (or one summer_run_editor_script)";
|
|
23
23
|
export declare const NAVIGATION_PROBE_FALLBACK = "probe reachability from a RunVerification probe (NavigationServer3D.map_get_path \u2014 see the playbook's rawOpsViaBatch)";
|
|
24
24
|
export declare const STARCAST_FALLBACK = "read the subject and its neighbours with summer_get_scene_tree + summer_inspect_node and judge support, contact, and clearance from their positions and sizes, then verify with summer_screenshot";
|
|
25
|
+
/** summer_replace_node's instantiate/move/remove path (core/capabilities/replace-node.ts)
|
|
26
|
+
* when the engine lacks one of the ops it sends. */
|
|
27
|
+
export declare const REPLACE_NODE_FALLBACK = "replace by hand: summer_remove_node, then summer_instantiate_scene with the same parent and name, then summer_set_prop for the transform, and confirm the saved scene with summer_read_file";
|
|
28
|
+
/** summer_grep when the engine lacks the Grep op (core/capabilities/grep.ts). */
|
|
29
|
+
export declare const GREP_FALLBACK = "read the file you need with summer_read_file (offset/limit to page it; json_path/keys to pick entries out of a .json)";
|
|
25
30
|
/** Every fallback, for the parity test: none may mention summer_world_snapshot. */
|
|
26
31
|
export declare const ENGINE_OP_FALLBACKS: Readonly<Record<string, string>>;
|
|
@@ -22,6 +22,11 @@ export const SNAP_TO_SURFACE_FALLBACK = "read the support's position and mesh/sh
|
|
|
22
22
|
export const ALIGN_DISTRIBUTE_FALLBACK = "compute the shared anchor or spacing from the subjects' positions and sizes (summer_inspect_node / summer_inspect_resource) and set each subject's position with summer_set_prop (or one summer_run_editor_script)";
|
|
23
23
|
export const NAVIGATION_PROBE_FALLBACK = "probe reachability from a RunVerification probe (NavigationServer3D.map_get_path — see the playbook's rawOpsViaBatch)";
|
|
24
24
|
export const STARCAST_FALLBACK = "read the subject and its neighbours with summer_get_scene_tree + summer_inspect_node and judge support, contact, and clearance from their positions and sizes, then verify with summer_screenshot";
|
|
25
|
+
/** summer_replace_node's instantiate/move/remove path (core/capabilities/replace-node.ts)
|
|
26
|
+
* when the engine lacks one of the ops it sends. */
|
|
27
|
+
export const REPLACE_NODE_FALLBACK = "replace by hand: summer_remove_node, then summer_instantiate_scene with the same parent and name, then summer_set_prop for the transform, and confirm the saved scene with summer_read_file";
|
|
28
|
+
/** summer_grep when the engine lacks the Grep op (core/capabilities/grep.ts). */
|
|
29
|
+
export const GREP_FALLBACK = "read the file you need with summer_read_file (offset/limit to page it; json_path/keys to pick entries out of a .json)";
|
|
25
30
|
/** Every fallback, for the parity test: none may mention summer_world_snapshot. */
|
|
26
31
|
export const ENGINE_OP_FALLBACKS = {
|
|
27
32
|
GetWorldSnapshot: WORLD_SNAPSHOT_FALLBACK,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* the scene-mutation single-op contract and of the guarded-file safety checks.
|
|
5
5
|
*/
|
|
6
6
|
import type { EngineApiClient } from "../api-client.js";
|
|
7
|
+
import { resolveSingleOnlyOps } from "../capability-skew.js";
|
|
7
8
|
import { type JsonRecord } from "../util/json.js";
|
|
8
9
|
export declare function isSingleOnlyOp(kind: string, singleOnly: ReadonlySet<string>): boolean;
|
|
9
10
|
/** Append the transaction-boundary SaveScene (or validate the caller's). */
|
|
@@ -19,7 +20,10 @@ export declare function executeOpsChunked(send: (chunk: JsonRecord[]) => Promise
|
|
|
19
20
|
/** Scene mutation entry point: appends the transaction-boundary SaveScene, then
|
|
20
21
|
* dispatches with the single-op contract (mutations batch together, SaveScene
|
|
21
22
|
* and other single-only ops travel alone). */
|
|
22
|
-
|
|
23
|
+
/** The slice of EngineApiClient a scene mutation needs (structural, so the
|
|
24
|
+
* placement tools and tests can pass narrower clients). */
|
|
25
|
+
export type SceneMutationClient = Pick<EngineApiClient, "executeIdentityBoundOps"> & Parameters<typeof resolveSingleOnlyOps>[0];
|
|
26
|
+
export declare function executeSceneMutation(client: SceneMutationClient, scenePath: string, ops: JsonRecord[], options?: JsonRecord): Promise<unknown>;
|
|
23
27
|
/** Normalize a project path; refuses anything that is not a traversal-free res:// path. */
|
|
24
28
|
export declare function safeProjectPath(path: string): string;
|
|
25
29
|
export declare function validSha256(value: unknown): value is string;
|
|
@@ -88,9 +88,6 @@ export async function executeOpsChunked(send, ops, singleOnly) {
|
|
|
88
88
|
const last = (receipts[receipts.length - 1] ?? {});
|
|
89
89
|
return { ...last, results: combinedResults, requests: chunks.length, receipts };
|
|
90
90
|
}
|
|
91
|
-
/** Scene mutation entry point: appends the transaction-boundary SaveScene, then
|
|
92
|
-
* dispatches with the single-op contract (mutations batch together, SaveScene
|
|
93
|
-
* and other single-only ops travel alone). */
|
|
94
91
|
export function executeSceneMutation(client, scenePath, ops, options) {
|
|
95
92
|
return executeOpsChunked((chunk) => client.executeIdentityBoundOps(chunk, { ...(options ?? {}), scenePath }), sceneMutationOps(ops), resolveSingleOnlyOps(client));
|
|
96
93
|
}
|
|
@@ -36,6 +36,18 @@ export declare function getFailureReason(value: {
|
|
|
36
36
|
* Exported for unit tests.
|
|
37
37
|
*/
|
|
38
38
|
export declare function extractOpError(result: unknown): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* The engine stamps `scenePersistence.persisted` on every scene-targeted batch
|
|
41
|
+
* (ops_executor.cpp: scene_persistence["persisted"] =
|
|
42
|
+
* explicit_scene_save_succeeded). It only means SaveScene returned OK, not
|
|
43
|
+
* that the file holds the change: a ReparentNode subtree whose owners the
|
|
44
|
+
* engine cleared, or a ConnectSignal made without CONNECT_PERSIST, is missing
|
|
45
|
+
* from a file whose save "persisted". Rename it to
|
|
46
|
+
* what it says — `saved` — on every receipt the client hands out, including
|
|
47
|
+
* the per-request receipts of a chunked merge. Tools that read the saved file
|
|
48
|
+
* back add their own `verified` (scene-readback.ts).
|
|
49
|
+
*/
|
|
50
|
+
export declare function honestSceneReceipt<T>(result: T): T;
|
|
39
51
|
/**
|
|
40
52
|
* An older engine answers an unknown op with a per-op "unknown op: <Kind>"
|
|
41
53
|
* (ops_executor.cpp fallthrough). Amend the envelope's error so the model gets
|
|
@@ -96,6 +96,32 @@ export function extractOpError(result) {
|
|
|
96
96
|
}
|
|
97
97
|
return null;
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* The engine stamps `scenePersistence.persisted` on every scene-targeted batch
|
|
101
|
+
* (ops_executor.cpp: scene_persistence["persisted"] =
|
|
102
|
+
* explicit_scene_save_succeeded). It only means SaveScene returned OK, not
|
|
103
|
+
* that the file holds the change: a ReparentNode subtree whose owners the
|
|
104
|
+
* engine cleared, or a ConnectSignal made without CONNECT_PERSIST, is missing
|
|
105
|
+
* from a file whose save "persisted". Rename it to
|
|
106
|
+
* what it says — `saved` — on every receipt the client hands out, including
|
|
107
|
+
* the per-request receipts of a chunked merge. Tools that read the saved file
|
|
108
|
+
* back add their own `verified` (scene-readback.ts).
|
|
109
|
+
*/
|
|
110
|
+
export function honestSceneReceipt(result) {
|
|
111
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
112
|
+
return result;
|
|
113
|
+
const envelope = result;
|
|
114
|
+
let out = null;
|
|
115
|
+
const persistence = envelope.scenePersistence;
|
|
116
|
+
if (persistence && typeof persistence === "object" && !Array.isArray(persistence) && "persisted" in persistence) {
|
|
117
|
+
const { persisted, ...rest } = persistence;
|
|
118
|
+
out = { ...envelope, scenePersistence: { ...rest, saved: persisted === true } };
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(envelope.receipts)) {
|
|
121
|
+
out = { ...(out ?? envelope), receipts: envelope.receipts.map((receipt) => honestSceneReceipt(receipt)) };
|
|
122
|
+
}
|
|
123
|
+
return (out ?? result);
|
|
124
|
+
}
|
|
99
125
|
/** The per-op text an older engine answers for a Kind its dispatch ladder does
|
|
100
126
|
* not know (ops_executor.cpp fallthrough: `unknown op: <Kind>`). */
|
|
101
127
|
const UNKNOWN_OP_PATTERN = /unknown op/i;
|