summer-engine 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +3 -3
  4. package/.cursor-plugin/plugin.json +2 -2
  5. package/.factory-plugin/plugin.json +2 -2
  6. package/CHANGELOG.md +8 -0
  7. package/README.md +0 -4
  8. package/dist/cli/commands/login.d.ts +0 -10
  9. package/dist/cli/commands/login.js +2 -82
  10. package/dist/cli/index.js +0 -4
  11. package/dist/core/auth.d.ts +0 -8
  12. package/dist/core/auth.js +5 -35
  13. package/dist/core/capabilities/seeing/args.d.ts +2 -2
  14. package/dist/core/capabilities/seeing/seeing.d.ts +1 -1
  15. package/dist/core/capabilities/sfx-choices.d.ts +2 -15
  16. package/dist/core/capabilities/sfx-choices.js +26 -64
  17. package/dist/core/capabilities/tool-dispatch.js +2 -54
  18. package/dist/core/config.d.ts +1 -7
  19. package/dist/core/config.js +5 -75
  20. package/dist/mcp/server.d.ts +2 -1
  21. package/dist/mcp/server.js +6 -5
  22. package/dist/mcp/tools/creator-tools.js +0 -71
  23. package/dist/mcp/tools/generate-tools.js +3 -4
  24. package/gemini-extension.json +1 -1
  25. package/library/references/mcp-tools-reference/mcp-tools-reference.md +2 -5
  26. package/library/skills/publish-your-game/SKILL.md +2 -0
  27. package/library/tools/generate-sfx-choices/resource.yaml +2 -2
  28. package/package.json +1 -1
  29. package/registry/generated/aliases.json +0 -3
  30. package/registry/generated/counts.json +2 -2
  31. package/registry/generated/gemini-extension.json +1 -1
  32. package/registry/generated/index.json +4 -107
  33. package/registry/generated/marketplace.claude.json +2 -2
  34. package/registry/generated/plugin.claude.json +2 -2
  35. package/registry/generated/plugin.codex.json +3 -3
  36. package/registry/generated/plugin.cursor.json +2 -2
  37. package/registry/generated/plugin.factory.json +2 -2
  38. package/dist/cli/commands/publish.d.ts +0 -2
  39. package/dist/cli/commands/publish.js +0 -21
  40. package/dist/cli/commands/releases.d.ts +0 -2
  41. package/dist/cli/commands/releases.js +0 -20
  42. package/dist/core/capabilities/creator.d.ts +0 -65
  43. package/dist/core/capabilities/creator.js +0 -374
  44. package/library/tools/creator-config/resource.yaml +0 -63
  45. package/library/tools/creator-publish/resource.yaml +0 -74
  46. package/library/tools/creator-releases/resource.yaml +0 -53
@@ -11,8 +11,8 @@
11
11
  {
12
12
  "name": "summer",
13
13
  "source": "./",
14
- "version": "3.6.0",
15
- "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 107-tool MCP bridge to the local desktop app.",
14
+ "version": "3.7.0",
15
+ "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 104-tool MCP bridge to the local desktop app.",
16
16
  "author": {
17
17
  "name": "Summer Engine",
18
18
  "email": "founders@summerengine.com"
@@ -2,8 +2,8 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "_generated": "GENERATED from integrations/claude — do not edit; npm run generate:registry",
4
4
  "name": "summer",
5
- "version": "3.6.0",
6
- "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 107-tool MCP bridge to the local desktop app.",
5
+ "version": "3.7.0",
6
+ "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 104-tool MCP bridge to the local desktop app.",
7
7
  "author": {
8
8
  "name": "Summer Engine",
9
9
  "email": "founders@summerengine.com",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "_generated": "GENERATED from integrations/codex — do not edit; npm run generate:registry",
3
3
  "name": "summer",
4
- "version": "3.6.0",
5
- "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 107-tool MCP bridge. Build games by talking.",
4
+ "version": "3.7.0",
5
+ "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 104-tool MCP bridge. Build games by talking.",
6
6
  "author": {
7
7
  "name": "Summer Engine",
8
8
  "email": "founders@summerengine.com",
@@ -151,7 +151,7 @@
151
151
  "interface": {
152
152
  "displayName": "Summer",
153
153
  "shortDescription": "Superpowers for AI game dev. Build games by talking.",
154
- "longDescription": "Summer turns Codex into a game-dev studio for Summer Engine. Users make Summer games with the Summer SDK and GDScript. The plugin adds game-dev skills, lifecycle hooks, and a 107-tool MCP bridge for guarded project files, scene editing, asset import, play mode, diagnostics, generation, and creator workflows. Git, shell, and grep stay with the host agent. Summer follows its upstream technical base continuously; version-sensitive work uses the bundled compatibility reference instead of a fixed onboarding version. Session-start orients the session. Pre-commit doctor (opt-in) blocks bad commits.",
154
+ "longDescription": "Summer turns Codex into a game-dev studio for Summer Engine. Users make Summer games with the Summer SDK and GDScript. The plugin adds game-dev skills, lifecycle hooks, and a 104-tool MCP bridge for guarded project files, scene editing, asset import, play mode, diagnostics, generation, and creator workflows. Git, shell, and grep stay with the host agent. Summer follows its upstream technical base continuously; version-sensitive work uses the bundled compatibility reference instead of a fixed onboarding version. Session-start orients the session. Pre-commit doctor (opt-in) blocks bad commits.",
155
155
  "developerName": "Summer Engine",
156
156
  "category": "Coding",
157
157
  "capabilities": [
@@ -3,8 +3,8 @@
3
3
  "_generated": "GENERATED from integrations/cursor — do not edit; npm run generate:registry",
4
4
  "name": "summer",
5
5
  "displayName": "Summer",
6
- "version": "3.6.0",
7
- "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 107-tool MCP bridge to the local desktop app.",
6
+ "version": "3.7.0",
7
+ "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 104-tool MCP bridge to the local desktop app.",
8
8
  "author": {
9
9
  "name": "Summer Engine",
10
10
  "email": "founders@summerengine.com"
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "_generated": "GENERATED from integrations/factory — do not edit; npm run generate:registry",
3
3
  "name": "summer",
4
- "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 107-tool MCP bridge to the local desktop app.",
5
- "version": "3.6.0",
4
+ "description": "Agent tooling for Summer Engine: game-dev skills, lifecycle hooks, and a 104-tool MCP bridge to the local desktop app.",
5
+ "version": "3.7.0",
6
6
  "author": {
7
7
  "name": "Summer Engine",
8
8
  "email": "founders@summerengine.com",
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to summer-engine will be documented here. Following [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## [3.7.0] - 2026-10-13
6
+
7
+ Sound-effect choices open in your Studio tab right away, agents act on Summer's review feedback, and the legacy creator path is gone.
8
+
9
+ - `summer_generate_sfx_choices` no longer waits 20 s and then reports `studio_tab_timeout` before it opens the page. Summer now sends the choices page to your open Summer Studio tab as it makes the versions, and the tool reads that answer: when your tab took it, no browser opens; when no tab is open, it opens your browser as before. Signing in to the store is no longer needed for the tab to open.
10
+ - **Removed the legacy Summercraft creator path.** The deprecated `summer_creator_publish`, `summer_creator_releases` and `summer_creator_config` MCP tools (and their `summer tool` slugs and library entries), the `summer publish` and `summer releases` commands, `summer login --creator` and the `creator.apiUrl`, `creator.projectId` and `creator.channel` config keys are gone. Publish to summer.games with `summer_export_game`, then `summer_publish_build`, then the hosted `summer_store_submit` and `summer_store_review`. `gateway.url` stays settable with `summer config`; `summer logout` still removes a `~/.summer/creator-token` an older CLI left behind.
11
+ - **Agents read Summer's review feedback and resubmit.** When Summer staff ask for changes to a submitted game, or do not approve it, they can now write feedback for the creator. The hosted `summer_store_review` tool returns the decision, that feedback and the next step; `publish-your-game` and the MCP instructions tell the agent to fix what the feedback names and call `summer_store_submit` again, and never to call the game live before Summer approves it.
12
+
5
13
  ## [3.6.0] - 2026-10-12
6
14
 
7
15
  Pick your sound effects by ear, and hosted games: agents check that players can actually join before a release.
package/README.md CHANGED
@@ -322,7 +322,6 @@ We tell you before we touch your disk.
322
322
  | `summer-engine` npm package (CLI + MCP + library) | ~2 MB download, ~6 MB unpacked | first `npx -y summer-engine@latest ...` call | [npmjs.com/package/summer-engine](https://www.npmjs.com/package/summer-engine) |
323
323
  | Summer Engine app | ~1 GB (engine + bundled Git/runtime tools) | `npx -y summer-engine@latest install` | Summer's signed releases |
324
324
  | Auth token | ~1 KB | `npx -y summer-engine@latest login` | Browser to `~/.summer/auth-token` |
325
- | Creator token | ~50 bytes | only when you run `summer login --creator` and mint one | One-time browser value to `~/.summer/creator-token`; never replaces the auth token |
326
325
  | Store sign-in | ~2 KB | only when you run `summer login --store` | Browser OAuth (PKCE, loopback port 47615-47617) to `~/.summer/oauth-token` with its refresh token, plus this machine's public client id in `~/.summer/oauth-client`; never replaces the auth token |
327
326
  | Skill files | small, bundled | in the npm package | no extra network call |
328
327
  | Generated assets (3D / image / audio / video) | varies | only on explicit `summer_generate_*` calls when that provider route is enabled for the account | Summer Engine Studio |
@@ -352,11 +351,8 @@ matchmaking are not promised by this package.
352
351
  | `summer install [--yes]` | Download Summer Engine. Prints URL and size first; never replaces an installed engine without confirmation. |
353
352
  | `summer login` | Browser-based core Summer sign-in. |
354
353
  | `summer login --store` | Browser sign-in to the Summer store so agents can upload builds (`summer_publish_build`). |
355
- | `summer login --creator` | Deprecated: connect a creator token for the legacy `summer publish`. |
356
354
  | `summer logout` | Clear auth tokens. |
357
355
  | `summer config [get\|set\|unset]` | Read or update the shared non-secret `~/.summer/config.json`. |
358
- | `summer publish [project] --artifact <game.pck> --version <value> [--confirm]` | Deprecated: use `summer tool export-game`, then `summer tool publish-build`. |
359
- | `summer releases [--cursor <value>]` | List your creator release history. |
360
356
  | `summer status` | Engine state, port, auth. |
361
357
  | `summer doctor` | Diagnose Node, login, engine, project memory, MCP. |
362
358
  | `summer plan <goal>` | Route a game-building goal into skills, MCP tools, gates, and verification. |
@@ -1,15 +1,5 @@
1
1
  import { Command } from "commander";
2
2
  export declare const loginCommand: Command;
3
- export interface CreatorLoginDependencies {
4
- openUrl: (url: string) => Promise<unknown>;
5
- readSecret: (prompt: string) => Promise<string>;
6
- log: (message: string) => void;
7
- }
8
- /**
9
- * The browser session mints the token; the token is pasted once into a hidden
10
- * prompt. This deliberately does not replace the core Summer login JWT.
11
- */
12
- export declare function runCreatorLogin(overrides?: Partial<CreatorLoginDependencies>): Promise<void>;
13
3
  export interface LoginDependencies {
14
4
  fetch: typeof fetch;
15
5
  openUrl: (url: string) => Promise<unknown>;
@@ -1,8 +1,8 @@
1
1
  import { Command } from "commander";
2
2
  import { randomUUID } from "crypto";
3
3
  import open from "open";
4
- import { getAuthToken, getCreatorToken, saveCreatorToken, saveLoginSession, } from "../../core/auth.js";
5
- import { getCreatorApiUrl, resolveGatewayUrl } from "../../core/config.js";
4
+ import { getAuthToken, saveLoginSession, } from "../../core/auth.js";
5
+ import { resolveGatewayUrl } from "../../core/config.js";
6
6
  import { readStoredOAuthToken, runStoreLogin } from "../../core/oauth.js";
7
7
  const POLL_INTERVAL_MS = 2000;
8
8
  // One generous window on ONE session id. First-time users may need to create an
@@ -14,7 +14,6 @@ const POLL_TIMEOUT_MS = 900000;
14
14
  const HEARTBEAT_MS = 30000;
15
15
  export const loginCommand = new Command("login")
16
16
  .description("Sign in to Summer Engine via your browser")
17
- .option("--creator", "Connect a separately scoped Summercraft creator token for publishing")
18
17
  .option("--store", "Sign in to the Summer store so agents can upload builds (summer_publish_build)")
19
18
  .option("--force", "Force re-authentication even if already logged in")
20
19
  .action(async (opts) => {
@@ -28,15 +27,6 @@ export const loginCommand = new Command("login")
28
27
  console.log("Signed in to the Summer store. Your core Summer login was not changed.");
29
28
  return;
30
29
  }
31
- if (opts.creator) {
32
- const existingCreator = await getCreatorToken();
33
- if (existingCreator && !opts.force) {
34
- console.log("A creator token is already connected. Use --creator --force to replace it.");
35
- return;
36
- }
37
- await runCreatorLogin();
38
- return;
39
- }
40
30
  const existing = await getAuthToken();
41
31
  if (existing && !opts.force) {
42
32
  console.log("Already logged in. Use --force to re-authenticate.");
@@ -44,76 +34,6 @@ export const loginCommand = new Command("login")
44
34
  }
45
35
  await runLogin();
46
36
  });
47
- async function readHiddenLine(prompt) {
48
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
49
- process.stdout.write(prompt);
50
- let value = "";
51
- for await (const chunk of process.stdin)
52
- value += String(chunk);
53
- return value.trim();
54
- }
55
- process.stdout.write(prompt);
56
- const input = process.stdin;
57
- const wasRaw = input.isRaw;
58
- input.setRawMode(true);
59
- input.resume();
60
- return new Promise((resolve, reject) => {
61
- let value = "";
62
- const finish = (error) => {
63
- input.off("data", onData);
64
- input.setRawMode(Boolean(wasRaw));
65
- input.pause();
66
- process.stdout.write("\n");
67
- if (error)
68
- reject(error);
69
- else
70
- resolve(value.trim());
71
- };
72
- const onData = (chunk) => {
73
- for (const char of String(chunk)) {
74
- if (char === "\u0003") {
75
- finish(new Error("Creator token entry cancelled."));
76
- return;
77
- }
78
- if (char === "\r" || char === "\n" || char === "\u0004") {
79
- finish();
80
- return;
81
- }
82
- if (char === "\u007f" || char === "\b") {
83
- value = value.slice(0, -1);
84
- }
85
- else if (char >= " ") {
86
- value += char;
87
- }
88
- }
89
- };
90
- input.on("data", onData);
91
- });
92
- }
93
- const defaultCreatorLoginDependencies = {
94
- openUrl: open,
95
- readSecret: readHiddenLine,
96
- log: console.log,
97
- };
98
- /**
99
- * The browser session mints the token; the token is pasted once into a hidden
100
- * prompt. This deliberately does not replace the core Summer login JWT.
101
- */
102
- export async function runCreatorLogin(overrides = {}) {
103
- const deps = { ...defaultCreatorLoginDependencies, ...overrides };
104
- const settingsUrl = `${await getCreatorApiUrl()}/creator/settings/tokens`;
105
- deps.log(`Opening creator token settings: ${settingsUrl}`);
106
- deps.log('Mint a token with the exact "publish" scope, copy its one-time sc_ value, then return here.');
107
- try {
108
- await deps.openUrl(settingsUrl);
109
- }
110
- catch {
111
- deps.log("Could not open the browser. Copy the URL above and open it manually.");
112
- }
113
- const token = await deps.readSecret("Creator token (input hidden): ");
114
- await saveCreatorToken(token);
115
- deps.log("Creator publishing connected. Your core Summer login token was not changed.");
116
- }
117
37
  /** The gateway answered with a terminal 4xx — retrying the same URL cannot help. */
118
38
  class LoginRejectedError extends Error {
119
39
  }
package/dist/cli/index.js CHANGED
@@ -14,8 +14,6 @@ import { doctorCommand } from "./commands/doctor.js";
14
14
  import { planCommand } from "./commands/plan.js";
15
15
  import { debugCommand } from "./commands/debug.js";
16
16
  import { configCommand } from "./commands/config.js";
17
- import { publishCommand } from "./commands/publish.js";
18
- import { releasesCommand } from "./commands/releases.js";
19
17
  import { eventsCommand } from "./commands/events.js";
20
18
  import { toolCommand } from "./commands/tool.js";
21
19
  import { getBanner } from "./banner.js";
@@ -51,8 +49,6 @@ export function runCli(mcpCommand) {
51
49
  program.addCommand(debugCommand);
52
50
  program.addCommand(planCommand);
53
51
  program.addCommand(configCommand);
54
- program.addCommand(publishCommand);
55
- program.addCommand(releasesCommand);
56
52
  program.addCommand(eventsCommand);
57
53
  program.addCommand(toolCommand);
58
54
  program.parseAsync().catch((err) => {
@@ -14,7 +14,6 @@ export interface CredentialMetadata {
14
14
  interface CredentialMetadataDocument {
15
15
  schemaVersion: 1;
16
16
  auth?: CredentialMetadata;
17
- creator?: CredentialMetadata;
18
17
  }
19
18
  export interface LoginSession {
20
19
  token: string;
@@ -37,13 +36,6 @@ export declare function getAuthToken(): Promise<string | null>;
37
36
  */
38
37
  export declare function hasEnvAuthToken(): boolean;
39
38
  export declare function saveAuthToken(token: string, scopes?: string[]): Promise<void>;
40
- /**
41
- * Summercraft creator tokens are a separate credential audience from the
42
- * core-compatible Summer CLI JWT. Never store an sc_ token in auth-token:
43
- * the engine reads that file and expects the summer-cli JWT contract.
44
- */
45
- export declare function getCreatorToken(): Promise<string | null>;
46
- export declare function saveCreatorToken(token: string): Promise<void>;
47
39
  export declare function getUserInfo(): Promise<SummerUserInfo | null>;
48
40
  export declare function saveUserInfo(info: SummerUserInfo): Promise<void>;
49
41
  export declare function saveLoginSession(session: LoginSession): Promise<void>;
package/dist/core/auth.js CHANGED
@@ -3,7 +3,8 @@ export { getSummerDir } from "./store.js";
3
3
  const AUTH_TOKEN_FILE = "auth-token";
4
4
  /** Written by v2 `summer login` for the removed Summer Cloud sync. Only ever removed now (logout). */
5
5
  const LEGACY_CLOUD_TOKEN_FILE = "cloud-token";
6
- const CREATOR_TOKEN_FILE = "creator-token";
6
+ /** Written by the removed `summer login --creator` (Summercraft). Only ever removed now (logout). */
7
+ const LEGACY_CREATOR_TOKEN_FILE = "creator-token";
7
8
  const USER_FILE = "user.json";
8
9
  const METADATA_FILE = "credential-metadata.json";
9
10
  /** The Summer store OAuth token (core/oauth.ts OAUTH_TOKEN_FILE). */
@@ -78,8 +79,8 @@ async function updateMetadata(kind, value, current) {
78
79
  };
79
80
  if (!next.auth)
80
81
  delete next.auth;
81
- if (!next.creator)
82
- delete next.creator;
82
+ // Drop the removed Summercraft creator entry left by older CLIs.
83
+ delete next.creator;
83
84
  await writeStoreJson(METADATA_FILE, next);
84
85
  }
85
86
  /**
@@ -115,27 +116,6 @@ export async function saveAuthToken(token, scopes = []) {
115
116
  await writeStoreText(AUTH_TOKEN_FILE, `${clean}\n`);
116
117
  await updateMetadata("auth", decodeJwtMetadata(clean, scopes), metadata);
117
118
  }
118
- /**
119
- * Summercraft creator tokens are a separate credential audience from the
120
- * core-compatible Summer CLI JWT. Never store an sc_ token in auth-token:
121
- * the engine reads that file and expects the summer-cli JWT contract.
122
- */
123
- export async function getCreatorToken() {
124
- const token = await readStoreText(CREATOR_TOKEN_FILE);
125
- return token?.trim() || null;
126
- }
127
- export async function saveCreatorToken(token) {
128
- const clean = token.trim();
129
- if (!/^sc_[A-Za-z0-9_-]{32,}$/.test(clean)) {
130
- throw new Error('That is not a Summercraft creator API token. Recovery: open /creator/settings/tokens, mint a token with the "publish" scope, and paste the complete one-time sc_ value.');
131
- }
132
- await writeStoreText(CREATOR_TOKEN_FILE, `${clean}\n`);
133
- await updateMetadata("creator", {
134
- audience: ["summercraft-creator"],
135
- scopes: ["publish"],
136
- tokenType: "api",
137
- });
138
- }
139
119
  export async function getUserInfo() {
140
120
  return readStoreJson(USER_FILE);
141
121
  }
@@ -173,21 +153,11 @@ export async function saveLoginSession(session) {
173
153
  export async function getCredentialMetadata() {
174
154
  const metadata = await readMetadata();
175
155
  const token = await getAuthToken();
176
- const creatorToken = await getCreatorToken();
177
156
  return {
178
157
  schemaVersion: 1,
179
158
  ...(token
180
159
  ? { auth: metadata.auth ?? decodeJwtMetadata(token) }
181
160
  : {}),
182
- ...(creatorToken
183
- ? {
184
- creator: metadata.creator ?? {
185
- audience: ["summercraft-creator"],
186
- scopes: [],
187
- tokenType: "api",
188
- },
189
- }
190
- : {}),
191
161
  };
192
162
  }
193
163
  export async function clearAuthCredentials() {
@@ -195,7 +165,7 @@ export async function clearAuthCredentials() {
195
165
  for (const file of [
196
166
  AUTH_TOKEN_FILE,
197
167
  LEGACY_CLOUD_TOKEN_FILE,
198
- CREATOR_TOKEN_FILE,
168
+ LEGACY_CREATOR_TOKEN_FILE,
199
169
  STORE_OAUTH_TOKEN_FILE,
200
170
  USER_FILE,
201
171
  METADATA_FILE,
@@ -306,7 +306,7 @@ export declare const frameShotArgsSchema: z.ZodObject<{
306
306
  render: z.ZodOptional<z.ZodEnum<["sheet", "best", "none"]>>;
307
307
  max_size: z.ZodOptional<z.ZodNumber>;
308
308
  }, "strict", z.ZodTypeAny, {
309
- shot: "detail" | "establishing" | "eye_level" | "low_angle" | "corridor";
309
+ shot: "establishing" | "eye_level" | "low_angle" | "detail" | "corridor";
310
310
  spawn?: string | undefined;
311
311
  scenePath?: string | undefined;
312
312
  fov?: number | undefined;
@@ -326,7 +326,7 @@ export declare const frameShotArgsSchema: z.ZodObject<{
326
326
  max_soft_fraction?: number | undefined;
327
327
  save_bookmark?: boolean | undefined;
328
328
  }, {
329
- shot: "detail" | "establishing" | "eye_level" | "low_angle" | "corridor";
329
+ shot: "establishing" | "eye_level" | "low_angle" | "detail" | "corridor";
330
330
  spawn?: string | undefined;
331
331
  scenePath?: string | undefined;
332
332
  fov?: number | undefined;
@@ -230,4 +230,4 @@ export interface CorridorScanFloor {
230
230
  }
231
231
  export declare function scanFloorText(floor: CorridorScanFloor, subject: Aabb): string;
232
232
  export declare function frameShot(client: SeeingClient, args: FrameShotArgs): Promise<SeeingResult>;
233
- export declare const SHOT_TYPE_DEFAULTS: Record<"detail" | "establishing" | "eye_level" | "low_angle" | "corridor", import("./candidates.js").ShotDefaults>;
233
+ export declare const SHOT_TYPE_DEFAULTS: Record<"establishing" | "eye_level" | "low_angle" | "detail" | "corridor", import("./candidates.js").ShotDefaults>;
@@ -7,6 +7,8 @@ import { z } from "zod";
7
7
  * POST /api/mcp/generate/audio/choices makes the versions and a choice set,
8
8
  * the person listens and approves one or more on the Studio page
9
9
  * /studio/choices/<id>, and GET /api/mcp/choices/<id> reads the decision.
10
+ * The server queues the page for the person's open Studio tab itself
11
+ * (studioTab.queued); only when no tab took it does this open a browser.
10
12
  * An approval makes that model the person's default for later
11
13
  * summer_generate_audio sound_effects calls without a modelId.
12
14
  *
@@ -58,17 +60,9 @@ export interface GatewayReply {
58
60
  error?: string;
59
61
  status: number;
60
62
  }
61
- export type StudioOpenOutcome = {
62
- ok: true;
63
- } | {
64
- ok: false;
65
- reason: string;
66
- };
67
63
  export interface SfxChoicesDeps {
68
64
  post(endpoint: string, body: Record<string, unknown>, timeoutMs: number): Promise<GatewayReply>;
69
65
  get(endpoint: string, params: URLSearchParams, timeoutMs: number): Promise<GatewayReply>;
70
- /** Move the person's open Studio tab to a /studio path (reviewUrl: the same page as a full URL). */
71
- studioOpen(path: string, reviewUrl: string): Promise<StudioOpenOutcome>;
72
66
  openUrl(url: string): Promise<unknown>;
73
67
  canOpenBrowser(): boolean;
74
68
  }
@@ -77,13 +71,6 @@ export type ShownIn = "studio_tab" | "browser" | "not_opened";
77
71
  export declare function canOpenReviewUrl(url: string): boolean;
78
72
  /** False where a browser launch cannot reach the person: CI, or Linux with no display. */
79
73
  export declare function defaultCanOpenBrowser(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
80
- /**
81
- * Move the person's open Studio tab through the hosted Summer Engine MCP's
82
- * summer_studio_open (needs the store sign-in). Only for the production site:
83
- * the hosted MCP moves tabs on summerengine.com, so a review page on another
84
- * origin (a preview deploy, local web dev) is opened in the browser instead.
85
- */
86
- export declare function openInStudioTab(path: string, reviewUrl: string): Promise<StudioOpenOutcome>;
87
74
  export declare function sfxChoicesRequestBody(args: z.output<typeof sfxChoicesArgsSchema>): Record<string, unknown>;
88
75
  export declare function generateSfxChoices(args: z.output<typeof sfxChoicesArgsSchema>, deps: SfxChoicesDeps): Promise<Record<string, unknown>>;
89
76
  export declare function choicesEndpoint(choiceSetId: string): string;
@@ -1,6 +1,4 @@
1
1
  import { z } from "zod";
2
- import { hasStoreSignIn, hostedResultPayload, withHostedMcp } from "../hosted-mcp.js";
3
- import { resolveMcpResourceUrl } from "../oauth.js";
4
2
  /**
5
3
  * Sound-effect choices (summer_generate_sfx_choices + summer_get_choices).
6
4
  *
@@ -9,6 +7,8 @@ import { resolveMcpResourceUrl } from "../oauth.js";
9
7
  * POST /api/mcp/generate/audio/choices makes the versions and a choice set,
10
8
  * the person listens and approves one or more on the Studio page
11
9
  * /studio/choices/<id>, and GET /api/mcp/choices/<id> reads the decision.
10
+ * The server queues the page for the person's open Studio tab itself
11
+ * (studioTab.queued); only when no tab took it does this open a browser.
12
12
  * An approval makes that model the person's default for later
13
13
  * summer_generate_audio sound_effects calls without a modelId.
14
14
  *
@@ -22,7 +22,6 @@ export const CHOICES_ENDPOINT_PREFIX = "/api/mcp/choices/";
22
22
  /** Three models run in parallel on the server; the slowest decides. */
23
23
  export const SFX_CHOICES_TIMEOUT_MS = 240_000;
24
24
  export const MAX_CHOICES_WAIT_SECONDS = 50;
25
- const STUDIO_OPEN_TIMEOUT_MS = 20_000;
26
25
  export const sfxChoicesArgsSchema = z.object({
27
26
  text: z
28
27
  .string()
@@ -48,7 +47,7 @@ export const sfxChoicesArgsSchema = z.object({
48
47
  open: z
49
48
  .boolean()
50
49
  .default(true)
51
- .describe("Show the choices page to the person (their open Studio tab, else the browser). false: only return the URL"),
50
+ .describe("Show the choices page in the browser when no open Studio tab took it. false: no browser, only return the URL"),
52
51
  });
53
52
  export const getChoicesArgsSchema = z.object({
54
53
  choiceSetId: z.string().min(1).max(200).describe("The choiceSetId from summer_generate_sfx_choices"),
@@ -90,70 +89,28 @@ export function defaultCanOpenBrowser(env = process.env, platform = process.plat
90
89
  return false;
91
90
  return true;
92
91
  }
93
- /**
94
- * Move the person's open Studio tab through the hosted Summer Engine MCP's
95
- * summer_studio_open (needs the store sign-in). Only for the production site:
96
- * the hosted MCP moves tabs on summerengine.com, so a review page on another
97
- * origin (a preview deploy, local web dev) is opened in the browser instead.
98
- */
99
- export async function openInStudioTab(path, reviewUrl) {
100
- if (!isSummerHost(hostOf(reviewUrl)))
101
- return { ok: false, reason: "review_page_not_on_summerengine_com" };
102
- let resource;
103
- try {
104
- resource = resolveMcpResourceUrl();
105
- }
106
- catch {
107
- return { ok: false, reason: "hosted_mcp_url_invalid" };
108
- }
109
- if (!isSummerHost(hostOf(resource)))
110
- return { ok: false, reason: "hosted_mcp_not_summerengine_com" };
111
- if (!(await hasStoreSignIn()))
112
- return { ok: false, reason: "no_store_sign_in" };
113
- let timer;
114
- const timeout = new Promise((resolve) => {
115
- timer = setTimeout(() => resolve({ ok: false, reason: "studio_tab_timeout" }), STUDIO_OPEN_TIMEOUT_MS);
116
- timer.unref?.();
117
- });
118
- const attempt = (async () => {
119
- try {
120
- const result = await withHostedMcp((client) => client.callTool({ name: "summer_studio_open", arguments: { path } }));
121
- const payload = hostedResultPayload(result);
122
- if (payload.isError || payload.json?.ok === false) {
123
- const code = typeof payload.json?.error === "string" ? payload.json.error : "studio_open_failed";
124
- return { ok: false, reason: code };
125
- }
126
- return { ok: true };
127
- }
128
- catch (error) {
129
- return { ok: false, reason: error instanceof Error ? error.message : String(error) };
130
- }
131
- })();
132
- try {
133
- return await Promise.race([attempt, timeout]);
134
- }
135
- finally {
136
- clearTimeout(timer);
137
- }
92
+ /** The server's studioTab answer: queued for the person's open tab, or why not. Missing on older servers. */
93
+ function studioTabAnswer(value) {
94
+ if (!value || typeof value !== "object")
95
+ return null;
96
+ const record = value;
97
+ return {
98
+ queued: record.queued === true,
99
+ ...(typeof record.reason === "string" ? { reason: record.reason } : {}),
100
+ };
138
101
  }
139
- async function showChoicesPage(reviewUrl, studioPath, deps) {
140
- let studioTab;
141
- if (studioPath) {
142
- const moved = await deps.studioOpen(studioPath, reviewUrl);
143
- if (moved.ok)
144
- return { shown: "studio_tab" };
145
- studioTab = moved.reason;
146
- }
102
+ async function showChoicesPage(reviewUrl, studioTab, deps) {
103
+ const notQueued = studioTab ? (studioTab.reason ?? "not_queued") : undefined;
147
104
  if (!canOpenReviewUrl(reviewUrl))
148
- return { shown: "not_opened", studioTab, browser: "not_a_summer_url" };
105
+ return { shown: "not_opened", studioTab: notQueued, browser: "not_a_summer_url" };
149
106
  if (!deps.canOpenBrowser())
150
- return { shown: "not_opened", studioTab, browser: "no_browser_here" };
107
+ return { shown: "not_opened", studioTab: notQueued, browser: "no_browser_here" };
151
108
  try {
152
109
  await deps.openUrl(reviewUrl);
153
- return { shown: "browser", studioTab };
110
+ return { shown: "browser", studioTab: notQueued };
154
111
  }
155
112
  catch (error) {
156
- return { shown: "not_opened", studioTab, browser: error instanceof Error ? error.message : String(error) };
113
+ return { shown: "not_opened", studioTab: notQueued, browser: error instanceof Error ? error.message : String(error) };
157
114
  }
158
115
  }
159
116
  // ---------------------------------------------------------------------------
@@ -198,9 +155,14 @@ export async function generateSfxChoices(args, deps) {
198
155
  hint: "Read failed[] for each model's error. Fix the request (a longer durationSeconds, a plainer description) or call summer_generate_audio with one modelId.",
199
156
  };
200
157
  }
201
- const shown = args.open
202
- ? await showChoicesPage(reviewUrl, studioPath, deps)
203
- : { shown: "not_opened", browser: "open_false" };
158
+ // The server queued the Studio-tab open already; that happens even with
159
+ // open:false, so report it rather than hide it.
160
+ const studioTab = studioTabAnswer(data.studioTab);
161
+ const shown = studioTab?.queued
162
+ ? { shown: "studio_tab" }
163
+ : args.open
164
+ ? await showChoicesPage(reviewUrl, studioTab, deps)
165
+ : { shown: "not_opened", browser: "open_false" };
204
166
  const count = clips.length;
205
167
  const where = shown.shown === "studio_tab"
206
168
  ? "I opened them in your Summer Studio tab"