summer-engine 3.3.0 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +4 -2
  3. package/.codex-plugin/plugin.json +5 -3
  4. package/.cursor-plugin/plugin.json +4 -2
  5. package/.factory-plugin/plugin.json +4 -2
  6. package/.opencode/INSTALL.md +8 -8
  7. package/.opencode/plugins/summer.js +2 -2
  8. package/AGENTS.md +7 -7
  9. package/CHANGELOG.md +50 -0
  10. package/GEMINI.md +1 -1
  11. package/NOTICE +1 -1
  12. package/README.md +32 -35
  13. package/assets/api-docs.json.gz +0 -0
  14. package/assets/autopilot/run.sh +2 -2
  15. package/assets/navigation/web-routes.json +1 -1
  16. package/commands/gameskill.md +2 -2
  17. package/commands/summer.md +25 -25
  18. package/dist/cli/commands/install.d.ts +10 -0
  19. package/dist/cli/commands/install.js +55 -10
  20. package/dist/cli/commands/open.d.ts +1 -1
  21. package/dist/cli/commands/run.d.ts +1 -1
  22. package/dist/cli/commands/run.js +1 -1
  23. package/dist/cli/commands/skills.js +2 -2
  24. package/dist/cli/commands/tool.d.ts +7 -0
  25. package/dist/cli/commands/tool.js +36 -1
  26. package/dist/core/api-client.d.ts +5 -6
  27. package/dist/core/api-client.js +12 -15
  28. package/dist/core/auth.js +2 -2
  29. package/dist/core/capabilities/asset-import.d.ts +0 -1
  30. package/dist/core/capabilities/camera-view.d.ts +1 -1
  31. package/dist/core/capabilities/camera-view.js +2 -2
  32. package/dist/core/capabilities/capture-gameplay.d.ts +69 -0
  33. package/dist/core/capabilities/capture-gameplay.js +283 -0
  34. package/dist/core/capabilities/connect-signal.d.ts +1 -2
  35. package/dist/core/capabilities/connect-signal.js +1 -2
  36. package/dist/core/capabilities/console-read.d.ts +2 -2
  37. package/dist/core/capabilities/console-read.js +2 -2
  38. package/dist/core/capabilities/creator.js +1 -1
  39. package/dist/core/capabilities/doctor.d.ts +9 -1
  40. package/dist/core/capabilities/doctor.js +47 -4
  41. package/dist/core/capabilities/engine-receipt.d.ts +5 -7
  42. package/dist/core/capabilities/engine-receipt.js +7 -9
  43. package/dist/core/capabilities/engine-run.d.ts +4 -0
  44. package/dist/core/capabilities/engine-run.js +33 -5
  45. package/dist/core/capabilities/export-engine-version.d.ts +15 -0
  46. package/dist/core/capabilities/export-engine-version.js +24 -0
  47. package/dist/core/capabilities/export-game.d.ts +9 -1
  48. package/dist/core/capabilities/export-game.js +115 -39
  49. package/dist/core/capabilities/export-presets.d.ts +1 -1
  50. package/dist/core/capabilities/export-presets.js +1 -1
  51. package/dist/core/capabilities/fabricate-mesh.d.ts +2 -3
  52. package/dist/core/capabilities/fabricate-mesh.js +2 -3
  53. package/dist/core/capabilities/frame-quality.d.ts +4 -4
  54. package/dist/core/capabilities/frame-quality.js +4 -4
  55. package/dist/core/capabilities/grep.d.ts +1 -1
  56. package/dist/core/capabilities/grep.js +1 -1
  57. package/dist/core/capabilities/image-generation.d.ts +9 -0
  58. package/dist/core/capabilities/image-generation.js +19 -1
  59. package/dist/core/capabilities/navigation/open.d.ts +1 -1
  60. package/dist/core/capabilities/navigation/open.js +1 -1
  61. package/dist/core/capabilities/navigation/render-product-map.js +1 -1
  62. package/dist/core/capabilities/navigation/targets.js +2 -2
  63. package/dist/core/capabilities/placement-batch.d.ts +1 -1
  64. package/dist/core/capabilities/placement-batch.js +2 -2
  65. package/dist/core/capabilities/play-determinism.d.ts +1 -1
  66. package/dist/core/capabilities/play-determinism.js +1 -1
  67. package/dist/core/capabilities/publish-build.d.ts +15 -2
  68. package/dist/core/capabilities/publish-build.js +78 -28
  69. package/dist/core/capabilities/publish-download.d.ts +1 -2
  70. package/dist/core/capabilities/publish-download.js +2 -3
  71. package/dist/core/capabilities/replace-node.d.ts +1 -3
  72. package/dist/core/capabilities/replace-node.js +1 -3
  73. package/dist/core/capabilities/runtime-control.d.ts +5 -7
  74. package/dist/core/capabilities/runtime-control.js +7 -9
  75. package/dist/core/capabilities/scene-batch.d.ts +1 -1
  76. package/dist/core/capabilities/scene-batch.js +1 -1
  77. package/dist/core/capabilities/scene-kind.js +1 -1
  78. package/dist/core/capabilities/scene-readback.d.ts +3 -4
  79. package/dist/core/capabilities/scene-readback.js +3 -4
  80. package/dist/core/capabilities/scene-script.d.ts +2 -2
  81. package/dist/core/capabilities/scene-script.js +2 -2
  82. package/dist/core/capabilities/seeing/shot-store.d.ts +1 -1
  83. package/dist/core/capabilities/seeing/shot-store.js +1 -1
  84. package/dist/core/capabilities/summer-bundle.d.ts +2 -3
  85. package/dist/core/capabilities/summer-bundle.js +2 -3
  86. package/dist/core/capabilities/surface-snap.d.ts +1 -1
  87. package/dist/core/capabilities/surface-snap.js +1 -1
  88. package/dist/core/capabilities/tool-dispatch.js +11 -1
  89. package/dist/core/capabilities/ui-control.d.ts +1 -2
  90. package/dist/core/capabilities/ui-control.js +1 -2
  91. package/dist/core/capabilities/world-snapshot.d.ts +1 -1
  92. package/dist/core/capabilities/world-snapshot.js +1 -1
  93. package/dist/core/capability-skew.d.ts +2 -3
  94. package/dist/core/capability-skew.js +4 -5
  95. package/dist/core/config.js +2 -2
  96. package/dist/core/engine-install.d.ts +16 -0
  97. package/dist/core/engine-install.js +32 -2
  98. package/dist/core/feedback/client.js +1 -1
  99. package/dist/core/hosted-mcp.d.ts +115 -0
  100. package/dist/core/hosted-mcp.js +164 -0
  101. package/dist/core/launch-posture.d.ts +6 -3
  102. package/dist/core/launch-posture.js +18 -5
  103. package/dist/core/library-read.js +1 -2
  104. package/dist/core/templates.d.ts +1 -2
  105. package/dist/core/templates.js +1 -2
  106. package/dist/mcp/hosted-mount.d.ts +13 -59
  107. package/dist/mcp/hosted-mount.js +24 -30
  108. package/dist/mcp/server.d.ts +2 -2
  109. package/dist/mcp/server.js +5 -4
  110. package/dist/mcp/tools/creator-tools.js +14 -4
  111. package/dist/mcp/tools/debug-tools.js +1 -1
  112. package/dist/mcp/tools/generate-tools.js +19 -9
  113. package/dist/mcp/tools/navigation-tools.d.ts +1 -1
  114. package/dist/mcp/tools/navigation-tools.js +1 -1
  115. package/dist/mcp/tools/spatial-tools.js +1 -1
  116. package/dist/mcp/tools/visual-tools.js +2 -2
  117. package/dist/mcp/tools/with-engine.d.ts +1 -2
  118. package/dist/mcp/tools/with-engine.js +1 -2
  119. package/gemini-extension.json +3 -1
  120. package/hooks/session-start.sh +2 -2
  121. package/integrations/README.md +70 -76
  122. package/integrations/claude/README.md +0 -3
  123. package/integrations/codex/README.md +1 -2
  124. package/integrations/copilot-jetbrains/README.md +1 -1
  125. package/integrations/cursor/README.md +1 -2
  126. package/integrations/factory/README.md +15 -11
  127. package/integrations/mistral-vibe/README.md +1 -1
  128. package/integrations/opencode/README.md +1 -1
  129. package/integrations/roo-code/README.md +1 -1
  130. package/integrations/rovo-dev/README.md +1 -1
  131. package/integrations/trae/README.md +1 -1
  132. package/integrations/windsurf/README.md +3 -3
  133. package/library/references/collaborative-protocol/collaborative-protocol.md +5 -5
  134. package/library/references/gd-style/gd-style.md +4 -6
  135. package/library/references/godot-version/godot-version.md +3 -3
  136. package/library/references/headless-routing/headless-routing.md +2 -2
  137. package/library/references/mcp-fresh-chat-playbook/mcp-fresh-chat-playbook.md +2 -2
  138. package/library/references/mcp-tools-reference/mcp-tools-reference.md +18 -16
  139. package/library/references/product-map/product-map.md +1 -1
  140. package/library/references/public-surface-language/public-surface-language.md +1 -1
  141. package/library/references/summer-folder/summer-folder.md +1 -1
  142. package/library/skills/3d-lighting/SKILL.md +4 -4
  143. package/library/skills/README.md +11 -9
  144. package/library/skills/adaptive-music/SKILL.md +7 -7
  145. package/library/skills/ai-characters-in-games/SKILL.md +1 -1
  146. package/library/skills/ambient-bed/SKILL.md +12 -14
  147. package/library/skills/animated-loop/SKILL.md +42 -42
  148. package/library/skills/animation-tree/SKILL.md +6 -6
  149. package/library/skills/art-direction/SKILL.md +8 -11
  150. package/library/skills/asset-strategy/SKILL.md +3 -3
  151. package/library/skills/audio-direction/SKILL.md +8 -10
  152. package/library/skills/auto-fire-targeting/SKILL.md +4 -3
  153. package/library/skills/bounded-engine-runs/SKILL.md +95 -0
  154. package/library/skills/bounded-engine-runs/resource.yaml +29 -0
  155. package/library/skills/brainstorm-game/SKILL.md +5 -5
  156. package/library/skills/brainstorming/SKILL.md +9 -15
  157. package/library/skills/celeste-momentum-platforming/SKILL.md +8 -13
  158. package/library/skills/celeste-momentum-platforming/resource.yaml +1 -1
  159. package/library/skills/character-animation-wiring/SKILL.md +2 -2
  160. package/library/skills/character-model/SKILL.md +15 -26
  161. package/library/skills/character-movement/SKILL.md +1 -1
  162. package/library/skills/character-movement/references/movement-3d.md +1 -1
  163. package/library/skills/character-portrait/SKILL.md +4 -4
  164. package/library/skills/cinematic-cutscene/SKILL.md +38 -38
  165. package/library/skills/combat-basics/SKILL.md +1 -1
  166. package/library/skills/compatibility-renderer-traps/SKILL.md +78 -0
  167. package/library/skills/compatibility-renderer-traps/resource.yaml +28 -0
  168. package/library/skills/concept-art/SKILL.md +4 -4
  169. package/library/skills/create-asset-sheet/SKILL.md +12 -31
  170. package/library/skills/debug/SKILL.md +5 -4
  171. package/library/skills/debugging-game-feel/SKILL.md +1 -4
  172. package/library/skills/debugging-game-feel/resource.yaml +1 -1
  173. package/library/skills/design-level/SKILL.md +5 -5
  174. package/library/skills/design-mechanic/SKILL.md +6 -6
  175. package/library/skills/design-npc/SKILL.md +8 -15
  176. package/library/skills/diagnosing-perf-regressions/SKILL.md +5 -10
  177. package/library/skills/dispatching-parallel-agents/SKILL.md +1 -19
  178. package/library/skills/environment-kit/SKILL.md +5 -5
  179. package/library/skills/export-and-ship/SKILL.md +25 -24
  180. package/library/skills/fabricating-assets/SKILL.md +1 -1
  181. package/library/skills/facial-and-lipsync/SKILL.md +5 -5
  182. package/library/skills/fps-controller/SKILL.md +17 -16
  183. package/library/skills/fps-controller/resource.yaml +0 -2
  184. package/library/skills/game-feel/SKILL.md +6 -7
  185. package/library/skills/gameskill/SKILL.md +3 -3
  186. package/library/skills/gdscript-patterns/SKILL.md +3 -3
  187. package/library/skills/generate-motion/SKILL.md +5 -22
  188. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +72 -90
  189. package/library/skills/godogen-visual-proof-game-generation/resource.yaml +0 -1
  190. package/library/skills/headless-scripting/SKILL.md +8 -8
  191. package/library/skills/headless-scripting/resource.yaml +1 -0
  192. package/library/skills/in-game-purchases/SKILL.md +123 -118
  193. package/library/skills/in-game-purchases/resource.yaml +2 -2
  194. package/library/skills/instantiate-asset-pack/SKILL.md +7 -3
  195. package/library/skills/investigating-bugs/SKILL.md +3 -11
  196. package/library/skills/investigating-bugs/condition-based-waiting.md +0 -10
  197. package/library/skills/investigating-bugs/root-cause-tracing.md +0 -8
  198. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +15 -15
  199. package/library/skills/lumera-single-image-scene-reconstruction/resource.yaml +0 -1
  200. package/library/skills/make-game/SKILL.md +3 -5
  201. package/library/skills/make-game/references/2d-platformer.md +1 -1
  202. package/library/skills/multiplayer-project/SKILL.md +1 -1
  203. package/library/skills/multiplayer-testing/SKILL.md +4 -4
  204. package/library/skills/music-track/SKILL.md +40 -38
  205. package/library/skills/new-project/SKILL.md +4 -6
  206. package/library/skills/organic-model/SKILL.md +9 -9
  207. package/library/skills/pixel-art/SKILL.md +6 -6
  208. package/library/skills/play/SKILL.md +3 -4
  209. package/library/skills/playtesting-a-feature/SKILL.md +4 -6
  210. package/library/skills/procedural-animation/SKILL.md +10 -11
  211. package/library/skills/prop-model/SKILL.md +4 -4
  212. package/library/skills/psx-retro-rendering/SKILL.md +20 -20
  213. package/library/skills/psx-retro-rendering/resource.yaml +2 -3
  214. package/library/skills/realtime-wet-surfaces/SKILL.md +15 -15
  215. package/library/skills/realtime-wet-surfaces/resource.yaml +1 -2
  216. package/library/skills/remote-deploy/SKILL.md +20 -21
  217. package/library/skills/retarget/SKILL.md +19 -23
  218. package/library/skills/running-in-the-cloud/SKILL.md +5 -5
  219. package/library/skills/scene-composition/SKILL.md +5 -5
  220. package/library/skills/scene-hierarchy-design/SKILL.md +3 -3
  221. package/library/skills/scene-hierarchy-design/resource.yaml +0 -1
  222. package/library/skills/scene-scripting/SKILL.md +3 -3
  223. package/library/skills/scene-to-level/SKILL.md +28 -31
  224. package/library/skills/skill-create/SKILL.md +1 -1
  225. package/library/skills/skill-improve/SKILL.md +9 -9
  226. package/library/skills/skill-test/SKILL.md +10 -10
  227. package/library/skills/skintokens-auto-rigging/SKILL.md +18 -18
  228. package/library/skills/skintokens-auto-rigging/resource.yaml +3 -3
  229. package/library/skills/skybox-panorama/SKILL.md +8 -9
  230. package/library/skills/sound-effect/SKILL.md +19 -19
  231. package/library/skills/spatial-placement/SKILL.md +2 -2
  232. package/library/skills/spatial-placement/references/kit-placement-tools.md +12 -22
  233. package/library/skills/sprite-sheet/SKILL.md +10 -10
  234. package/library/skills/summer-analytics/SKILL.md +3 -3
  235. package/library/skills/summer-friends/SKILL.md +10 -10
  236. package/library/skills/summer-leaderboards/SKILL.md +6 -6
  237. package/library/skills/summer-match-results/SKILL.md +13 -13
  238. package/library/skills/summer-matchmaking/SKILL.md +11 -11
  239. package/library/skills/summer-parties/SKILL.md +9 -9
  240. package/library/skills/summer-player-data/SKILL.md +12 -12
  241. package/library/skills/summer-store/SKILL.md +6 -6
  242. package/library/skills/summer-world-chat/SKILL.md +9 -9
  243. package/library/skills/summer-world-saves/SKILL.md +7 -7
  244. package/library/skills/text-to-motion/rigging-for-text-motion.md +1 -1
  245. package/library/skills/tileable-texture/SKILL.md +1 -1
  246. package/library/skills/trailer-shot/SKILL.md +36 -36
  247. package/library/skills/tune-performance/SKILL.md +13 -13
  248. package/library/skills/tune-performance/resource.yaml +1 -1
  249. package/library/skills/ui-graphics/SKILL.md +7 -7
  250. package/library/skills/use-widget-asset/SKILL.md +7 -7
  251. package/library/skills/using-summer/SKILL.md +5 -7
  252. package/library/skills/vehicle-model/SKILL.md +6 -7
  253. package/library/skills/vehicle-model/resource.yaml +2 -2
  254. package/library/skills/verification-before-completion/SKILL.md +5 -6
  255. package/library/skills/vfx-dissolve/SKILL.md +4 -4
  256. package/library/skills/vfx-fire/SKILL.md +6 -6
  257. package/library/skills/vfx-fire/_building-blocks/additive-billboard-particles.md +3 -3
  258. package/library/skills/vfx-hit-spark/SKILL.md +4 -4
  259. package/library/skills/vfx-hit-spark/_building-blocks/additive-billboard-particles.md +3 -3
  260. package/library/skills/vfx-lightning/SKILL.md +5 -5
  261. package/library/skills/vfx-lightning/_building-blocks/additive-billboard-particles.md +3 -3
  262. package/library/skills/vfx-lightning/_building-blocks/trauma-shake-snippet.md +9 -3
  263. package/library/skills/vfx-magic-glow/SKILL.md +4 -4
  264. package/library/skills/vfx-magic-glow/_building-blocks/additive-billboard-particles.md +3 -3
  265. package/library/skills/vfx-muzzle-flash/SKILL.md +5 -5
  266. package/library/skills/vfx-muzzle-flash/_building-blocks/additive-billboard-particles.md +3 -3
  267. package/library/skills/vfx-muzzle-flash/_building-blocks/trauma-shake-snippet.md +9 -3
  268. package/library/skills/vfx-smoke/SKILL.md +5 -5
  269. package/library/skills/vfx-water-ripple/SKILL.md +2 -2
  270. package/library/skills/vfx-water-ripple/_building-blocks/additive-billboard-particles.md +3 -3
  271. package/library/skills/voice-line/SKILL.md +9 -9
  272. package/library/skills/web-compatibility-look/resource.yaml +1 -0
  273. package/library/skills/world-building-3d/references/spatial-tools.md +2 -2
  274. package/library/skills/writing-plans/SKILL.md +4 -4
  275. package/library/skills/writing-skills/SKILL.md +8 -8
  276. package/library/tools/capture-gameplay/resource.yaml +73 -0
  277. package/library/tools/creator-publish/resource.yaml +4 -4
  278. package/library/tools/creator-releases/resource.yaml +5 -5
  279. package/library/tools/generate-image/resource.yaml +15 -1
  280. package/library/tools/publish-build/resource.yaml +6 -2
  281. package/package.json +1 -1
  282. package/registry/generated/aliases.json +1 -0
  283. package/registry/generated/counts.json +3 -3
  284. package/registry/generated/gemini-extension.json +3 -1
  285. package/registry/generated/index.json +248 -135
  286. package/registry/generated/marketplace.claude.json +2 -2
  287. package/registry/generated/plugin.claude.json +4 -2
  288. package/registry/generated/plugin.codex.json +5 -3
  289. package/registry/generated/plugin.cursor.json +4 -2
  290. package/registry/generated/plugin.factory.json +4 -2
  291. package/registry/generated/skills-index.md +11 -9
  292. package/registry/generated/skills-registry.json +33 -6
  293. package/registry/schemas/domains.json +1 -1
@@ -12,8 +12,7 @@ import { BuildToolError, readSummerBundle } from "./summer-bundle.js";
12
12
  /**
13
13
  * summer_publish_build: upload a summer.games export to the creator's game
14
14
  * through the same creator store routes Studio's export upload uses
15
- * (publicsummerengine /api/creator-store/* -> summer-platform
16
- * /v1/management/*, build-publications, ADR 0033):
15
+ * (/api/creator-store/*, build-publications):
17
16
  *
18
17
  * POST games/{g}/build-publications declare the export (idempotent)
19
18
  * POST .../{p}:source-upload open the part plan (64 MiB parts)
@@ -24,8 +23,7 @@ import { BuildToolError, readSummerBundle } from "./summer-bundle.js";
24
23
  * POST games/{g}/builds/{b}/client-packages name the Build's client pack
25
24
  * POST .../{p}:publish only with publish=true
26
25
  *
27
- * Mirrors publicsummerengine src/lib/creator-store/pack-upload.ts
28
- * (uploadExportedGame), including its idempotency keys, so a retry continues
26
+ * Mirrors Studio's export upload, including its idempotency keys, so a retry continues
29
27
  * the same publication instead of creating another one.
30
28
  *
31
29
  * Auth: the Summer store OAuth token from "summer login --store" (audience
@@ -38,11 +36,11 @@ const CONCURRENCY = 3;
38
36
  const PART_RETRIES = 3;
39
37
  const DEFAULT_WAIT_SECONDS = 600;
40
38
  const POLL_MS = 3_000;
41
- /** summer-platform client versions: vMAJOR.MINOR.PATCH[-rN]. */
39
+ /** Store client versions: vMAJOR.MINOR.PATCH[-rN]. */
42
40
  export const CLIENT_VERSION = /^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-r[1-9]\d*)?$/;
43
41
  const INTAKE_DONE = ["preview_ready", "published", "ready", "succeeded"];
44
42
  const NOT_ACTIONABLE = "deployment_not_actionable";
45
- /** Games without a server run on the Summer Games phone apps only (summer-platform gameplatforms.StandaloneTargets). */
43
+ /** Games without a server run on the Summer Games phone apps only (the store's standalone targets). */
46
44
  const STANDALONE_TARGETS = ["ios", "android"];
47
45
  /** Why the Platform did not take the export, in plain words (same text Studio shows). */
48
46
  const INTAKE_FAILURES = {
@@ -61,9 +59,11 @@ export const defaultDependencies = {
61
59
  now: Date.now,
62
60
  token: () => getStoreAccessToken(),
63
61
  };
64
- export function storeError(status, code, message) {
62
+ export function storeError(status, code, message, requestId) {
65
63
  if (status === 401) {
66
- return new BuildToolError("store_auth_refused", "The Summer store refused this sign-in.", 'Recovery: run "summer login --store --force" and retry. If it is refused again, the store does not accept agent sign-ins yet: upload the .zip in Studio (Store > your game > Exports) until it does.', status);
64
+ // The token was sent and refused: signing in again gives the same token
65
+ // shape and the same answer, so it is not the first thing to try.
66
+ return new BuildToolError("store_auth_refused", `The Summer store did not accept this sign-in${message ? `: ${message.replace(/\.$/, "")}` : ""}.`, 'Recovery: run "summer doctor" (Store line) to see the store\'s answer. Signing in again does not change a refusal of a valid sign-in. Until the store accepts agent sign-in for this account, upload the .zip in Studio (Store > your game > Exports).', status, requestId ? { requestId } : undefined);
67
67
  }
68
68
  if (status === 403 && code === "owner_approval_required") {
69
69
  return new BuildToolError("owner_approval_required", message || "Only the game's owner can do this.", "Recovery: nothing went live. Submit with summer_store_submit and give the owner the approval link it returns; a live game's new download is finished by the owner in Studio.", status);
@@ -104,7 +104,8 @@ export function createStore(baseUrl, token, deps) {
104
104
  const body = (json && typeof json === "object" ? json : {});
105
105
  if (!response.ok) {
106
106
  const error = body.error && typeof body.error === "object" ? body.error : {};
107
- throw storeError(response.status, typeof error.code === "string" ? error.code : `http_${response.status}`, typeof error.message === "string" ? error.message : undefined);
107
+ const requestId = [error.requestId, body.requestId, response.headers.get("x-request-id")].find((value) => typeof value === "string" && value);
108
+ throw storeError(response.status, typeof error.code === "string" ? error.code : `http_${response.status}`, typeof error.message === "string" ? error.message : undefined, requestId);
108
109
  }
109
110
  return body;
110
111
  },
@@ -260,18 +261,68 @@ function targetPlatforms(bundle) {
260
261
  // The Build declares exactly what the bundle declares (declaration_mismatch otherwise).
261
262
  return bundle.targetPlatforms;
262
263
  }
263
- /** gameId is required: the error carries the person's games when the store answers. */
264
- async function listGamesOrThrow(deps) {
265
- let games;
264
+ /** The signed-in person's store games, or why the store did not list them. */
265
+ async function listGames(deps) {
266
266
  try {
267
- const store = createStore(await resolveGatewayUrl(), await deps.token(), deps);
267
+ const store = createStore(await resolveGatewayUrl(), await storeToken(deps), deps);
268
268
  const page = await store.call("GET", "games");
269
- games = (page.items ?? []).map((game) => ({ id: game.id, name: game.name, slug: game.slug, status: game.status }));
269
+ return { games: (page.items ?? []).map((game) => ({ id: game.id, name: game.name, slug: game.slug, status: game.status, targets: game.supportedPlatforms ?? [] })) };
270
270
  }
271
- catch {
272
- games = undefined;
271
+ catch (error) {
272
+ return { error: error instanceof Error ? error.message : String(error) };
273
+ }
274
+ }
275
+ /** gameId is required: the error carries the person's games, or why the store did not list them. */
276
+ async function listGamesOrThrow(deps) {
277
+ const listed = await listGames(deps);
278
+ if ("games" in listed) {
279
+ throw new BuildToolError("game_required", listed.games.length
280
+ ? "gameId is required: pick one of your store games below (the export is uploaded to one existing game)."
281
+ : "gameId is required, and this account has no store game yet.", listed.games.length
282
+ ? "Recovery: call again with gameId from the list."
283
+ : "Recovery: create the store game first (summer_store_create_game), then call again with its gameId.", undefined, { games: listed.games });
284
+ }
285
+ throw new BuildToolError("game_required", `gameId is required, and the store did not list your games: ${listed.error}`, "Recovery: copy the game id from Studio (the store page URL) and retry.", undefined, { listError: listed.error });
286
+ }
287
+ async function storeToken(deps) {
288
+ try {
289
+ return await deps.token();
290
+ }
291
+ catch (error) {
292
+ if (error instanceof OAuthError)
293
+ throw new BuildToolError(error.code, error.message.replace(` ${error.recovery}`, ""), error.recovery);
294
+ throw error;
295
+ }
296
+ }
297
+ /**
298
+ * Before the person confirms, prove the target is real: the game exists for
299
+ * this account, and its store page lists the platforms the export targets.
300
+ */
301
+ export async function checkStoreGame(gameId, exportTargets, deps) {
302
+ const store = createStore(await resolveGatewayUrl(), await storeToken(deps), deps);
303
+ let game;
304
+ try {
305
+ game = await store.call("GET", `games/${seg(gameId)}`);
306
+ }
307
+ catch (error) {
308
+ if (error instanceof BuildToolError && error.status === 404) {
309
+ const listed = await listGames(deps);
310
+ throw new BuildToolError("game_not_found", "games" in listed
311
+ ? `There is no store game "${gameId}" for this account, so nothing can be uploaded to it.`
312
+ : `The store did not find game "${gameId}", and did not list your games either: ${listed.error}`, "games" in listed && listed.games.length
313
+ ? "Recovery: call again with gameId from the list."
314
+ : "Recovery: create the store game first (summer_store_create_game), or copy its id from Studio (the store page URL).", 404, "games" in listed ? { games: listed.games } : undefined);
315
+ }
316
+ throw error;
273
317
  }
274
- throw new BuildToolError("game_required", "gameId is required: the export is uploaded to one existing game.", games ? "Recovery: pick the game's id from the list and retry." : "Recovery: copy the game id from Studio (the store page URL) and retry.", undefined, games ? { games } : undefined);
318
+ const targets = Array.isArray(game.supportedPlatforms) ? game.supportedPlatforms.filter((value) => typeof value === "string") : [];
319
+ // The store page speaks catalog names (ios, android, macos, windows, linux, web) or store ids (macos-universal, ...).
320
+ const catalog = new Set(targets.map((target) => target.replace(/-(universal|x64)$/, "")));
321
+ const unlisted = exportTargets.filter((target) => !catalog.has(target));
322
+ const warnings = unlisted.length
323
+ ? [`The store page of "${game.name ?? gameId}" does not list ${unlisted.join(", ")} (it lists ${targets.join(", ") || "no platforms"}). Players on ${unlisted.join(", ")} will not see this build until the store page lists them: add them with summer_store_update_listing.`]
324
+ : [];
325
+ return { game: { gameId, name: typeof game.name === "string" ? game.name : null, status: typeof game.status === "string" ? game.status : null, targets }, warnings };
275
326
  }
276
327
  export async function publishBuild(input, overrides = {}) {
277
328
  const deps = { ...defaultDependencies, ...overrides };
@@ -288,7 +339,11 @@ export async function publishBuild(input, overrides = {}) {
288
339
  const clientVersion = normalizeClientVersion(input.clientVersion);
289
340
  if (!gameId)
290
341
  return listGamesOrThrow(deps);
291
- return publishDownload({ gameId, file, platform, clientVersion, confirm: input.confirm, waitSeconds: input.waitSeconds, face: input.face }, deps);
342
+ const download = { gameId, file, platform, clientVersion, confirm: input.confirm, waitSeconds: input.waitSeconds, face: input.face };
343
+ if (input.confirm === true)
344
+ return publishDownload(download, deps);
345
+ const preview = await publishDownload(download, deps);
346
+ return { ...preview, game: (await checkStoreGame(gameId, [], deps)).game };
292
347
  }
293
348
  if (extname(file).toLowerCase() !== ".zip") {
294
349
  throw new BuildToolError("export_format_unsupported", `${file} is not a summer.games .zip export.`, "Recovery: export with summer_export_game and upload that .zip.");
@@ -313,23 +368,18 @@ export async function publishBuild(input, overrides = {}) {
313
368
  if (!gameId)
314
369
  return listGamesOrThrow(deps);
315
370
  if (input.confirm !== true) {
371
+ const checked = await checkStoreGame(gameId, platforms, deps);
316
372
  return {
317
373
  ok: true,
318
374
  status: "confirmation_required",
319
375
  target,
320
- next: `Show the user this exact game, file, digest, size, version and publish choice. Only after they approve, call again with confirm=true. Nothing was uploaded.`,
376
+ game: checked.game,
377
+ ...(checked.warnings.length ? { warnings: checked.warnings } : {}),
378
+ next: `Show the user this exact game (name and id), file, digest, size, version and publish choice. Only after they approve, call again with confirm=true. Nothing was uploaded.`,
321
379
  };
322
380
  }
323
381
  const audit = { at: new Date(deps.now()).toISOString(), operation: "publish_build", face: input.face, ...target };
324
- let token;
325
- try {
326
- token = await deps.token();
327
- }
328
- catch (error) {
329
- if (error instanceof OAuthError)
330
- throw new BuildToolError(error.code, error.message.replace(` ${error.recovery}`, ""), error.recovery);
331
- throw error;
332
- }
382
+ const token = await storeToken(deps);
333
383
  const store = createStore(await resolveGatewayUrl(), token, deps);
334
384
  await appendStoreJsonLine("creator-audit.jsonl", { ...audit, outcome: "started" });
335
385
  // Same keys Studio derives: the same bytes, version and scene are one declaration.
@@ -1,7 +1,6 @@
1
1
  import { type PublishBuildDependencies } from "./publish-build.js";
2
2
  /**
3
- * The store's second build path (summer-platform store versions,
4
- * internal/creatorstore): a web build (an HTML5 .zip played on summer.games)
3
+ * The store's second build path (store versions): a web build (an HTML5 .zip played on summer.games)
5
4
  * or a native download (macOS universal, Windows x64, Linux x64) that players
6
5
  * download. summer_export_game format "download" makes these files;
7
6
  * summer_publish_build uploads them here, through the same creator store
@@ -7,8 +7,7 @@ import { readZipEntries } from "../util/zip.js";
7
7
  import { BuildToolError } from "./summer-bundle.js";
8
8
  import { createStore, readGrants, uploadParts } from "./publish-build.js";
9
9
  /**
10
- * The store's second build path (summer-platform store versions,
11
- * internal/creatorstore): a web build (an HTML5 .zip played on summer.games)
10
+ * The store's second build path (store versions): a web build (an HTML5 .zip played on summer.games)
12
11
  * or a native download (macOS universal, Windows x64, Linux x64) that players
13
12
  * download. summer_export_game format "download" makes these files;
14
13
  * summer_publish_build uploads them here, through the same creator store
@@ -25,7 +24,7 @@ import { createStore, readGrants, uploadParts } from "./publish-build.js";
25
24
  * already live the store refuses an agent's :complete (the owner finishes it).
26
25
  */
27
26
  export const DOWNLOAD_PLATFORMS = ["web", "macos-universal", "windows-x64", "linux-x64"];
28
- /** summer-platform creatorstore limits (versions.go, webzip.go). */
27
+ /** The store's upload limits for store versions and web zips. */
29
28
  const WEB_MAX_BYTES = 500 * 1024 * 1024;
30
29
  const WEB_MAX_FILES = 2000;
31
30
  const WEB_MAX_FILE_BYTES = 200 * 1024 * 1024;
@@ -3,9 +3,7 @@
3
3
  * (src/mcp/tools/scene-tools.ts and tool-dispatch.ts), built from ops whose
4
4
  * results persist, and verified against the SAVED scene file.
5
5
  *
6
- * Why not the engine's ReplaceNode op for scenes: SceneOps::replace_node
7
- * (engine modules/1summer_engine/editor/ops/scene_ops.cpp, scene branch and
8
- * the async _finish_replace_node) instantiates the new PackedScene and hands
6
+ * Why not the engine's ReplaceNode op for scenes: for a scene it instantiates the new PackedScene and hands
9
7
  * it to SceneTreeDock::replace_node — the editor's "Change Type" path — which
10
8
  * ends in Node::replace_by (scene/main/node.cpp). replace_by finishes with
11
9
  * `p_node->set_scene_file_path(get_scene_file_path())`: the new instance is
@@ -3,9 +3,7 @@
3
3
  * (src/mcp/tools/scene-tools.ts and tool-dispatch.ts), built from ops whose
4
4
  * results persist, and verified against the SAVED scene file.
5
5
  *
6
- * Why not the engine's ReplaceNode op for scenes: SceneOps::replace_node
7
- * (engine modules/1summer_engine/editor/ops/scene_ops.cpp, scene branch and
8
- * the async _finish_replace_node) instantiates the new PackedScene and hands
6
+ * Why not the engine's ReplaceNode op for scenes: for a scene it instantiates the new PackedScene and hands
9
7
  * it to SceneTreeDock::replace_node — the editor's "Change Type" path — which
10
8
  * ends in Node::replace_by (scene/main/node.cpp). replace_by finishes with
11
9
  * `p_node->set_scene_file_path(get_scene_file_path())`: the new instance is
@@ -6,8 +6,7 @@
6
6
  * both faces validate the same inputs, send the same op and wait the same
7
7
  * budget.
8
8
  *
9
- * Engine contract (frozen): doc/SUMMER/SCENE_SCRIPTING_CONTRACTS.md "Wave I —
10
- * Runtime control & playtest ops" + editor/ops/runtime_ops.h. Every op except
9
+ * Engine contract (frozen): the runtime control and playtest ops. Every op except
11
10
  * ListGameInstances is SINGLE-ONLY and ASYNC (a debugger round-trip needs the
12
11
  * reply channel), requires a RUNNING game, accepts `instance?` (default
13
12
  * "main"), stamps the game frame it describes, rounds floats to 3dp and
@@ -499,9 +498,8 @@ export declare function playRequestsLocalPlay(args: PlayGameArgs): boolean;
499
498
  /**
500
499
  * Quiet is the default whenever an agent drives play — both faces of this tool
501
500
  * (MCP and `summer tool`) ARE agent faces, so the only way to a focus-stealing
502
- * launch is an explicit focus:true. Quiet travels as PlayGame `agent:true`
503
- * (debug_ops.cpp play_game -> GameView::set_agent_quiet_play), which the engine
504
- * has honoured since 0.5.45 (c7c490d84f3, 2026-07-02).
501
+ * launch is an explicit focus:true. Quiet travels as PlayGame `agent:true`,
502
+ * which the engine has honoured since 0.5.45.
505
503
  */
506
504
  export declare function playIsQuiet(args: PlayGameArgs): boolean;
507
505
  /** True when the call needs the PlayGame OP (instance-aware / determinism
@@ -529,8 +527,8 @@ export declare function withLocalPlayEcho(result: unknown, args: PlayGameArgs):
529
527
  export declare const PLAY_QUIET_NOT_SUPPORTED = "This Summer Engine build did not echo `agent_quiet` in its PlayGame result \u2014 it predates quiet play and has most likely switched the editor to the Game tab and taken focus. Update Summer Engine (restart it after updating) for launches that leave the user's screen alone.";
530
528
  /**
531
529
  * Quiet was requested: the engine echoes `agent_quiet` when it understood the
532
- * flag (debug_ops.cpp play_game — in the launch branch AND the already-running
533
- * branch on current engines). No echo at all means an engine that predates
530
+ * flag (in the launch branch AND the already-running branch on current
531
+ * engines). No echo at all means an engine that predates
534
532
  * the flag ignored it — say so instead of letting the model believe the user
535
533
  * was left alone. The field is the contract; the `note` text is never matched.
536
534
  */
@@ -6,8 +6,7 @@
6
6
  * both faces validate the same inputs, send the same op and wait the same
7
7
  * budget.
8
8
  *
9
- * Engine contract (frozen): doc/SUMMER/SCENE_SCRIPTING_CONTRACTS.md "Wave I —
10
- * Runtime control & playtest ops" + editor/ops/runtime_ops.h. Every op except
9
+ * Engine contract (frozen): the runtime control and playtest ops. Every op except
11
10
  * ListGameInstances is SINGLE-ONLY and ASYNC (a debugger round-trip needs the
12
11
  * reply channel), requires a RUNNING game, accepts `instance?` (default
13
12
  * "main"), stamps the game frame it describes, rounds floats to 3dp and
@@ -59,7 +58,7 @@ export const RUNTIME_CONTROL_OP_KINDS = [
59
58
  "ListGameInstances",
60
59
  ];
61
60
  // ---------------------------------------------------------------------------
62
- // Budgets — mirror the engine's watchdogs (runtime_ops.cpp) plus relay headroom.
61
+ // Budgets — mirror the engine's runtime-op watchdogs plus relay headroom.
63
62
  // ---------------------------------------------------------------------------
64
63
  /** Default editor-side watchdog for a runtime op (_RT_DEFAULT_TIMEOUT_SEC). */
65
64
  export const RUNTIME_WATCHDOG_DEFAULT_SEC = 10;
@@ -882,9 +881,8 @@ export function playRequestsLocalPlay(args) {
882
881
  /**
883
882
  * Quiet is the default whenever an agent drives play — both faces of this tool
884
883
  * (MCP and `summer tool`) ARE agent faces, so the only way to a focus-stealing
885
- * launch is an explicit focus:true. Quiet travels as PlayGame `agent:true`
886
- * (debug_ops.cpp play_game -> GameView::set_agent_quiet_play), which the engine
887
- * has honoured since 0.5.45 (c7c490d84f3, 2026-07-02).
884
+ * launch is an explicit focus:true. Quiet travels as PlayGame `agent:true`,
885
+ * which the engine has honoured since 0.5.45.
888
886
  */
889
887
  export function playIsQuiet(args) {
890
888
  return args.focus !== true;
@@ -894,7 +892,7 @@ export function playIsQuiet(args) {
894
892
  export function playNeedsOp(args) {
895
893
  return (
896
894
  // The /api/play rung builds a PlayGame op with ONLY `scene`
897
- // (local_api_server.cpp play branch), so `agent:true` has to ride the op.
895
+ // (the engine's /api/play handler), so `agent:true` has to ride the op.
898
896
  playIsQuiet(args) ||
899
897
  (typeof args.instance === "string" && args.instance.trim().length > 0) ||
900
898
  args.mode !== undefined ||
@@ -1031,8 +1029,8 @@ export function withLocalPlayEcho(result, args) {
1031
1029
  export const PLAY_QUIET_NOT_SUPPORTED = "This Summer Engine build did not echo `agent_quiet` in its PlayGame result — it predates quiet play and has most likely switched the editor to the Game tab and taken focus. Update Summer Engine (restart it after updating) for launches that leave the user's screen alone.";
1032
1030
  /**
1033
1031
  * Quiet was requested: the engine echoes `agent_quiet` when it understood the
1034
- * flag (debug_ops.cpp play_game — in the launch branch AND the already-running
1035
- * branch on current engines). No echo at all means an engine that predates
1032
+ * flag (in the launch branch AND the already-running branch on current
1033
+ * engines). No echo at all means an engine that predates
1036
1034
  * the flag ignored it — say so instead of letting the model believe the user
1037
1035
  * was left alone. The field is the contract; the `note` text is never matched.
1038
1036
  */
@@ -4,7 +4,7 @@
4
4
  * re-owning step every MCP path that sends ReparentNode shares.
5
5
  *
6
6
  * Two engine ops answer ok for changes SaveScene then leaves out of the file
7
- * (engine modules/1summer_engine/editor/ops/scene_ops.cpp, engine 0.6.0):
7
+ * (engine 0.6.0):
8
8
  *
9
9
  * - ReparentNode (SceneOps::reparent_node) re-owns only the node it moves
10
10
  * (`add_do_method(node, "set_owner", root)`). The remove_child before it
@@ -4,7 +4,7 @@
4
4
  * re-owning step every MCP path that sends ReparentNode shares.
5
5
  *
6
6
  * Two engine ops answer ok for changes SaveScene then leaves out of the file
7
- * (engine modules/1summer_engine/editor/ops/scene_ops.cpp, engine 0.6.0):
7
+ * (engine 0.6.0):
8
8
  *
9
9
  * - ReparentNode (SceneOps::reparent_node) re-owns only the node it moves
10
10
  * (`add_do_method(node, "set_owner", root)`). The remove_child before it
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Is a scene 2D or 3D? Decided from a scene-tree read, the way the engine's
3
- * ScenePreview decides it (preview_ops.cpp `_scene_has_3d` / `_scene_has_2d`):
3
+ * ScenePreview decides it:
4
4
  * any Node3D anywhere in the tree makes the scene 3D; otherwise any CanvasItem
5
5
  * makes it 2D; a tree of plain Nodes is neither.
6
6
  *
@@ -4,10 +4,9 @@
4
4
  * trusting the engine receipt (summer_replace_node, summer_batch with
5
5
  * ReparentNode, summer_connect_signal).
6
6
  *
7
- * The engine's `scenePersistence.persisted` only says SaveScene returned OK
8
- * (ops_executor.cpp: scene_persistence["persisted"] =
9
- * explicit_scene_save_succeeded); what the file holds is only known by reading
10
- * it. The client renames that flag to `saved` (engine-receipt.ts
7
+ * The engine's `scenePersistence.persisted` only says SaveScene returned OK;
8
+ * what the file holds is only known by reading it. The client renames that
9
+ * flag to `saved` (engine-receipt.ts
11
10
  * honestSceneReceipt); `verified` is set only by tools that read back here.
12
11
  */
13
12
  import { type JsonRecord } from "../util/json.js";
@@ -4,10 +4,9 @@
4
4
  * trusting the engine receipt (summer_replace_node, summer_batch with
5
5
  * ReparentNode, summer_connect_signal).
6
6
  *
7
- * The engine's `scenePersistence.persisted` only says SaveScene returned OK
8
- * (ops_executor.cpp: scene_persistence["persisted"] =
9
- * explicit_scene_save_succeeded); what the file holds is only known by reading
10
- * it. The client renames that flag to `saved` (engine-receipt.ts
7
+ * The engine's `scenePersistence.persisted` only says SaveScene returned OK;
8
+ * what the file holds is only known by reading it. The client renames that
9
+ * flag to `saved` (engine-receipt.ts
11
10
  * honestSceneReceipt); `verified` is set only by tools that read back here.
12
11
  */
13
12
  import { asRecord } from "../util/json.js";
@@ -5,8 +5,8 @@
5
5
  *
6
6
  * Server-side clamps: RunSceneScript blocks between frames of the LIVE editor,
7
7
  * so its ceiling is deliberately low; RunEditorScript boots a whole headless
8
- * child editor, so its floor covers the boot cost (editor_script_ops.cpp:
9
- * 15..600 default 120).
8
+ * child editor, so its floor covers the boot cost (engine clamp: 15..600,
9
+ * default 120).
10
10
  */
11
11
  export declare const SCENE_SCRIPT_MIN_SECONDS = 5;
12
12
  export declare const SCENE_SCRIPT_MAX_SECONDS = 120;
@@ -5,8 +5,8 @@
5
5
  *
6
6
  * Server-side clamps: RunSceneScript blocks between frames of the LIVE editor,
7
7
  * so its ceiling is deliberately low; RunEditorScript boots a whole headless
8
- * child editor, so its floor covers the boot cost (editor_script_ops.cpp:
9
- * 15..600 default 120).
8
+ * child editor, so its floor covers the boot cost (engine clamp: 15..600,
9
+ * default 120).
10
10
  */
11
11
  export const SCENE_SCRIPT_MIN_SECONDS = 5;
12
12
  export const SCENE_SCRIPT_MAX_SECONDS = 120;
@@ -2,7 +2,7 @@ export declare const SHOTS_DIR: string;
2
2
  export declare const SAVED_SUBDIR = "saved";
3
3
  export declare const SHOT_MAX_EDGE = 1024;
4
4
  export declare const SHOTS_MAX_TOTAL_BYTES: number;
5
- /** Same grammar as camera bookmark names (preview_ops.cpp). */
5
+ /** Same grammar as camera bookmark names. */
6
6
  export declare const SHOT_NAME_PATTERN: RegExp;
7
7
  export declare class ShotStoreError extends Error {
8
8
  readonly reason: "bad_name" | "not_jpeg" | "too_large" | "over_cap" | "unsafe_path" | "no_project";
@@ -22,7 +22,7 @@ export const SHOTS_DIR = join(".summer", "shots");
22
22
  export const SAVED_SUBDIR = "saved";
23
23
  export const SHOT_MAX_EDGE = 1024;
24
24
  export const SHOTS_MAX_TOTAL_BYTES = 20 * 1024 * 1024;
25
- /** Same grammar as camera bookmark names (preview_ops.cpp). */
25
+ /** Same grammar as camera bookmark names. */
26
26
  export const SHOT_NAME_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
27
27
  export class ShotStoreError extends Error {
28
28
  reason;
@@ -1,9 +1,8 @@
1
1
  /**
2
- * The summer.games export (summer-platform ADR 0033): one .zip with
2
+ * The summer.games export: one .zip with
3
3
  * summer-bundle.json, client.pck, and for a game with a host server.pck plus
4
4
  * config/. This reads what the build upload declares; the Platform verifies
5
- * the uploaded bytes again. Same rules as Studio's browser check
6
- * (publicsummerengine src/lib/creator-store/pck-check.ts, checkBundle).
5
+ * the uploaded bytes again. Same rules as Studio's browser check.
7
6
  */
8
7
  export declare const BUNDLE_SCHEMA = "summer.bundle.v1";
9
8
  export declare const BUNDLE_MANIFEST = "summer-bundle.json";
@@ -2,11 +2,10 @@ import { createHash } from "node:crypto";
2
2
  import { createReadStream } from "node:fs";
3
3
  import { readZipEntries, readZipEntry, ZipReadError } from "../util/zip.js";
4
4
  /**
5
- * The summer.games export (summer-platform ADR 0033): one .zip with
5
+ * The summer.games export: one .zip with
6
6
  * summer-bundle.json, client.pck, and for a game with a host server.pck plus
7
7
  * config/. This reads what the build upload declares; the Platform verifies
8
- * the uploaded bytes again. Same rules as Studio's browser check
9
- * (publicsummerengine src/lib/creator-store/pck-check.ts, checkBundle).
8
+ * the uploaded bytes again. Same rules as Studio's browser check.
10
9
  */
11
10
  export const BUNDLE_SCHEMA = "summer.bundle.v1";
12
11
  export const BUNDLE_MANIFEST = "summer-bundle.json";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * surface-snap — ONE implementation of `summer_snap_to_surface` for both faces
3
3
  * (src/mcp/tools/spatial-tools.ts and tool-dispatch.ts), around the engine's
4
- * SnapToSurface op (modules/1summer_engine/editor/ops/surface_snap_ops.cpp).
4
+ * SnapToSurface op.
5
5
  *
6
6
  * Two gaps in the engine op, closed here with ops main already ships:
7
7
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * surface-snap — ONE implementation of `summer_snap_to_surface` for both faces
3
3
  * (src/mcp/tools/spatial-tools.ts and tool-dispatch.ts), around the engine's
4
- * SnapToSurface op (modules/1summer_engine/editor/ops/surface_snap_ops.cpp).
4
+ * SnapToSurface op.
5
5
  *
6
6
  * Two gaps in the engine op, closed here with ops main already ships:
7
7
  *
@@ -49,6 +49,7 @@ import { shapeEngineLogResponse } from "../log-filters.js";
49
49
  import { listCreatorReleases, publishCreator, } from "./creator.js";
50
50
  import { exportGame } from "./export-game.js";
51
51
  import { exportTemplates } from "./export-templates.js";
52
+ import { captureGameplay } from "./capture-gameplay.js";
52
53
  import { publishBuild } from "./publish-build.js";
53
54
  import { CONFIG_KEYS, getConfigValue, isConfigKey, readSummerConfig, resolveGatewayUrl, setConfigValue, unsetConfigValue, } from "../config.js";
54
55
  import { createDebugReportArtifact } from "./debug-report.js";
@@ -630,6 +631,15 @@ export const TOOL_DISPATCH = [
630
631
  summerVersion: optStr(args, "summerVersion"),
631
632
  });
632
633
  }),
634
+ entry("summer_capture_gameplay", "Capture real gameplay frames offscreen, without a running editor", false, (args) => captureGameplay({
635
+ project: optStr(args, "project"),
636
+ scene: optStr(args, "scene"),
637
+ resolution: optStr(args, "resolution"),
638
+ frames: optNumberOrUndefined(args, "frames"),
639
+ waitSeconds: optNumberOrUndefined(args, "waitSeconds"),
640
+ intervalSeconds: optNumberOrUndefined(args, "intervalSeconds"),
641
+ out: optStr(args, "out"),
642
+ })),
633
643
  entry("summer_publish_build", "Upload a summer.games export to your store game (confirm-gated)", false, (args) => publishBuild({
634
644
  gameId: optStr(args, "gameId"),
635
645
  file: optStr(args, "file"),
@@ -651,7 +661,7 @@ export const TOOL_DISPATCH = [
651
661
  confirm: args.confirm === true,
652
662
  face: "cli",
653
663
  })),
654
- entry("summer_creator_releases", "List creator-owned releases", false, (args) => listCreatorReleases({
664
+ entry("summer_creator_releases", "Deprecated: list releases on the legacy creator API; use summer_store_releases", false, (args) => listCreatorReleases({
655
665
  projectId: optStr(args, "projectId"),
656
666
  limit: typeof args.limit === "number" ? args.limit : 20,
657
667
  cursor: optStr(args, "cursor"),
@@ -4,8 +4,7 @@
4
4
  * both faces validate the same arguments, send the same op, and render the
5
5
  * engine's failure taxonomy the same way.
6
6
  *
7
- * Seven engine ops (modules/1summer_engine/editor/ops/ui_ops.cpp, frozen in
8
- * doc/SUMMER/SCENE_SCRIPTING_CONTRACTS.md "Wave L") behind four tools:
7
+ * Seven engine ops (a frozen engine contract) behind four tools:
9
8
  *
10
9
  * summer_ui_actions mode:"list" -> UiListActions (read)
11
10
  * mode:"invoke" -> UiInvoke (mutates)
@@ -4,8 +4,7 @@
4
4
  * both faces validate the same arguments, send the same op, and render the
5
5
  * engine's failure taxonomy the same way.
6
6
  *
7
- * Seven engine ops (modules/1summer_engine/editor/ops/ui_ops.cpp, frozen in
8
- * doc/SUMMER/SCENE_SCRIPTING_CONTRACTS.md "Wave L") behind four tools:
7
+ * Seven engine ops (a frozen engine contract) behind four tools:
9
8
  *
10
9
  * summer_ui_actions mode:"list" -> UiListActions (read)
11
10
  * mode:"invoke" -> UiInvoke (mutates)
@@ -3,7 +3,7 @@
3
3
  * `summer_world_snapshot` (src/mcp/tools/perception-tools.ts and
4
4
  * tool-dispatch.ts).
5
5
  *
6
- * The engine's GetWorldSnapshot (world_snapshot_ops.cpp) takes only
6
+ * The engine's GetWorldSnapshot op takes only
7
7
  * scene_path / max_nodes / max_lights / include_2d and returns every node it
8
8
  * walked, about 250 bytes each: a scene of a few thousand nodes is far too
9
9
  * big to read.
@@ -3,7 +3,7 @@
3
3
  * `summer_world_snapshot` (src/mcp/tools/perception-tools.ts and
4
4
  * tool-dispatch.ts).
5
5
  *
6
- * The engine's GetWorldSnapshot (world_snapshot_ops.cpp) takes only
6
+ * The engine's GetWorldSnapshot op takes only
7
7
  * scene_path / max_nodes / max_lights / include_2d and returns every node it
8
8
  * walked, about 250 bytes each: a scene of a few thousand nodes is far too
9
9
  * big to read.
@@ -77,7 +77,7 @@ export interface EngineCapabilities {
77
77
  * `summer` capture ships, and the offscreen instance cap. Absent = engine
78
78
  * predates runtime control (or advertises the kinds only in opKinds). */
79
79
  runtimeControl?: EngineRuntimeControlCapabilities;
80
- /** Editor navigation advert (engine `Navigate` op, navigate_ops.cpp): the
80
+ /** Editor navigation advert (engine `Navigate` op): the
81
81
  * destination ids this build can open. Absent = engine predates the op;
82
82
  * summer_open then falls back to the legacy per-surface ops it can map. */
83
83
  navigation?: EngineNavigationCapabilities;
@@ -174,8 +174,7 @@ export declare function missingEngineEventsResult(client: CapabilityAdvertisingC
174
174
  export declare function buildCapabilitySkewWarning(health: unknown): string | null;
175
175
  /**
176
176
  * Engine ops that MUST be dispatched as their own single-op request. Mirrors
177
- * _summer_requires_single_async_dispatch (local_api_server.cpp, engine
178
- * 0.5.60+): the engine rejects any multi-op batch containing one of these
177
+ * the engine's single-async-op list (engine 0.5.60+): the engine rejects any multi-op batch containing one of these
179
178
  * WHOLESALE — nothing in the batch executes, and the batch fails with per-op
180
179
  * failure_reason "unsupported_transport"/"skipped". Git ops are covered by a
181
180
  * prefix check in the dispatchers.
@@ -46,7 +46,7 @@ export const CLI_KNOWN_OP_NEEDS = [
46
46
  // summer_grep (core/capabilities/grep.ts)
47
47
  "Grep",
48
48
  // Navigation (summer_open: core/capabilities/navigation/) — Navigate is the
49
- // one-table op (engine navigate_ops.cpp); the other three are the legacy
49
+ // engine's one-table op; the other three are the legacy
50
50
  // fallbacks on engines that predate it.
51
51
  "Navigate", "OpenResource", "FocusDock", "RevealInFileSystem",
52
52
  // Project + input
@@ -334,8 +334,7 @@ export function buildCapabilitySkewWarning(health) {
334
334
  // ---------------------------------------------------------------------------
335
335
  /**
336
336
  * Engine ops that MUST be dispatched as their own single-op request. Mirrors
337
- * _summer_requires_single_async_dispatch (local_api_server.cpp, engine
338
- * 0.5.60+): the engine rejects any multi-op batch containing one of these
337
+ * the engine's single-async-op list (engine 0.5.60+): the engine rejects any multi-op batch containing one of these
339
338
  * WHOLESALE — nothing in the batch executes, and the batch fails with per-op
340
339
  * failure_reason "unsupported_transport"/"skipped". Git ops are covered by a
341
340
  * prefix check in the dispatchers.
@@ -353,12 +352,12 @@ export const FALLBACK_SINGLE_ONLY_OPS = new Set([
353
352
  "RunCommand", "RunVerification", "RunEditorScript", "RunSceneScript",
354
353
  "ImportFromUrl", "ImportFromUrlBatch", "ExtractZipFromUrl",
355
354
  // Mesh fabrication: a headless Blender child on the same async single-op lane as
356
- // RunEditorScript (local_api_server.cpp SUMMER_SINGLE_ASYNC_OPS).
355
+ // RunEditorScript.
357
356
  "FabricateMesh",
358
357
  // runtime control (RuntimeOps::async_op_kinds): every op below rides
359
358
  // the `summer` debugger capture, so like GameSnapshot each needs the async
360
359
  // single-op reply channel. ListGameInstances is deliberately NOT here — it
361
- // is a cheap synchronous editor read that batches fine (runtime_ops.h).
360
+ // is a cheap synchronous editor read that batches fine.
362
361
  "SetRuntimeProp", "CallRuntimeMethod", "SpawnRuntimeScene", "FreeRuntimeNode",
363
362
  "RuntimeAnimation", "RuntimeAnimationTree", "GetRuntimeBones",
364
363
  "GamePause", "GameSpeed", "GameStep",
@@ -30,7 +30,7 @@ function validateGatewayUrl(value) {
30
30
  }
31
31
  const local = parsed.hostname === "localhost" ||
32
32
  parsed.hostname === "127.0.0.1" ||
33
- parsed.hostname === "::1";
33
+ parsed.hostname === "[::1]";
34
34
  if (parsed.protocol !== "https:" && !(local && parsed.protocol === "http:")) {
35
35
  throw new Error("gateway.url must use HTTPS (HTTP is allowed only for localhost). Recovery: set an HTTPS URL and retry.");
36
36
  }
@@ -49,7 +49,7 @@ function validateCreatorApiUrl(value) {
49
49
  }
50
50
  const local = parsed.hostname === "localhost" ||
51
51
  parsed.hostname === "127.0.0.1" ||
52
- parsed.hostname === "::1";
52
+ parsed.hostname === "[::1]";
53
53
  if (parsed.protocol !== "https:" && !(local && parsed.protocol === "http:")) {
54
54
  throw new Error("creator.apiUrl must use HTTPS (HTTP is allowed only for localhost). Recovery: set an HTTPS URL and retry.");
55
55
  }