summer-engine 2.6.1 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Build real 2D and 3D games through conversation. No coding required. Export to Steam, desktop, mobile, and web. Built on the Godot team's work, customized and honed so AI agents and humans can collaborate on great games.
4
4
 
5
- **Summer** is the MIT open-source agent layer that connects your AI coding agent to Summer Engine. It is the **Summer CLI**, the **Summer MCP** server, and the **Summer agent** skills, hooks, and plugin manifests, all in one package. First-class setup works in Claude Code, Cursor, Codex, Windsurf, Cline, Roo Code, Gemini CLI, GitHub Copilot CLI, GitHub Copilot in VS Code, and OpenCode. Factory Droid uses the plugin marketplace path.
5
+ **Summer** is the MIT open-source agent layer that connects your AI coding agent to Summer Engine. It is the **Summer CLI**, the **Summer MCP** server, and the **Summer agent** skills, hooks, and plugin manifests, all in one package. First-class setup works in Claude Code, Cursor, Codex, Devin Desktop (formerly Windsurf), Cline, Roo Code, Gemini CLI, GitHub Copilot CLI, GitHub Copilot in VS Code, and OpenCode. Factory Droid uses the plugin marketplace path.
6
6
 
7
7
  - **Source:** [github.com/SummerEngine/summer-engine-agent](https://github.com/SummerEngine/summer-engine-agent)
8
8
  - **CLI setup:** [www.summerengine.com/cli](https://www.summerengine.com/cli)
@@ -41,7 +41,7 @@ The same operations are available to agents as MCP tools (`summer_cloud_push`, `
41
41
 
42
42
  ## Get started: one prompt
43
43
 
44
- Open your AI agent (Claude Code, Cursor, Codex, Copilot, Windsurf, etc.) and paste:
44
+ Open your AI agent (Claude Code, Cursor, Codex, Copilot, Devin Desktop, etc.) and paste:
45
45
 
46
46
  > Install Summer Engine and let's make a game. Read github.com/SummerEngine/summer-engine-agent, check what I already have installed, install whatever is missing, log me in, and scaffold a starter project so we can start building.
47
47
 
@@ -102,7 +102,7 @@ The relevant check ids are `node-version`, `cli-version`, `cli-version-current`,
102
102
  npx -y summer-engine@latest setup claude-code --yes --force
103
103
  ```
104
104
 
105
- Replace `claude-code` with the user's actual agent: `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `gemini`, `github-copilot`, `vscode-copilot`, or `opencode`. Use `github-copilot` for Copilot CLI and `vscode-copilot` for GitHub Copilot Chat/Agent in VS Code. Factory Droid still uses the plugin marketplace path in the install section below. This writes the current Summer skill bundle to `~/.claude/skills/<name>/SKILL.md` (or the agent's equivalent user-skill directory) AND writes the MCP server config so the agent can talk to the engine. The `--force` flag wipes any stale skill content first, so re-runs always end up with the latest copy.
105
+ Replace `claude-code` with the user's actual agent: `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `kilo-code`, `gemini`, `github-copilot`, `vscode-copilot`, `opencode`, or `lm-studio`. Use `github-copilot` for Copilot CLI and `vscode-copilot` for GitHub Copilot Chat/Agent in VS Code. Factory Droid still uses the plugin marketplace path in the install section below. This writes the current Summer skill bundle to `~/.claude/skills/<name>/SKILL.md` (or the agent's equivalent user-skill directory) AND writes the MCP server config so the agent can talk to the engine. The `--force` flag wipes any stale skill content first, so re-runs always end up with the latest copy.
106
106
 
107
107
  **First-install detection:** if `~/.claude/skills/` didn't exist before this command, Claude Code wasn't watching it and won't auto-detect the new files this session. Tell the user **once**: "Skills installed - restart your agent so they load." On subsequent installs (directory already existed), skills auto-detect mid-session and no restart is needed. **You can detect first-install vs upgrade by checking if `~/.claude/skills/` existed before Step 1; record the result before running setup.**
108
108
 
@@ -192,7 +192,7 @@ Only skip brainstorm if the user explicitly said "skip brainstorm" or "just buil
192
192
  - **Don't loop `summer login` if it times out at 120s.** Re-run it once and tell the user to be quicker; loop forever and the user is stuck.
193
193
  - **Don't jump straight into `summer create` from a vague prompt.** Invoke `summer:brainstorm-game` first (Step 6). The build skills assume `.summer/GameSoul.md` exists.
194
194
 
195
- **Using a different agent?** Replace `claude-code` with any supported agent in Step 1: `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `gemini`, `github-copilot`, `vscode-copilot`, or `opencode`. Skill targets vary per agent (Cursor uses `.cursor/rules/`, Windsurf uses `.windsurfrules`, Cline + Roo use `.clinerules/`, Copilot uses `~/.copilot/skills` or `.github/skills`, OpenCode uses agent definitions, etc.). The CLI handles the difference. After install, **Cline and Roo Code users should restart VS Code** so the extension reloads its MCP config. **Gemini users** may need to run `gemini extensions enable summer-engine` after the first install. **VS Code Copilot users** should start the `summer-engine` MCP server from Agent mode if VS Code does not autostart it. **Factory Droid** still has its own plugin path below.
195
+ **Using a different agent?** Replace `claude-code` with any supported agent in Step 1: `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `kilo-code`, `gemini`, `github-copilot`, `vscode-copilot`, `opencode`, or `lm-studio`. (`devin` is also accepted as an alias for `windsurf`.) Skill targets vary per agent (Cursor uses `.cursor/rules/`, Devin Desktop uses `.windsurfrules`, Cline + Roo use `.clinerules/`, Copilot uses `~/.copilot/skills` or `.github/skills`, OpenCode uses agent definitions, etc.). The CLI handles the difference. After install, **Cline and Roo Code users should restart VS Code** so the extension reloads its MCP config. **Gemini users** may need to run `gemini extensions enable summer-engine` after the first install. **VS Code Copilot users** should start the `summer-engine` MCP server from Agent mode if VS Code does not autostart it. **Factory Droid** still has its own plugin path below.
196
196
 
197
197
  **Power-user note:** if the user specifically wants `summer` on their `PATH` for everyday terminal use outside the AI agent, a global npm install is still possible. The agent flow doesn't need it.
198
198
 
@@ -390,7 +390,15 @@ Writes the MCP server config to VS Code's globalStorage for the Cline extension
390
390
  npx -y summer-engine@latest setup roo-code --yes
391
391
  ```
392
392
 
393
- Same shape as Cline (Roo Code is a Cline fork): writes globalStorage MCP config and `.clinerules/` files. **Restart VS Code** afterward.
393
+ Same shape as Cline (Roo Code is a Cline fork): writes globalStorage MCP config and `.clinerules/` files. **Restart VS Code** afterward. Note: Roo Code shut down in May 2026 — prefer Kilo Code, its maintained successor.
394
+
395
+ ### Kilo Code (VS Code)
396
+
397
+ ```bash
398
+ npx -y summer-engine@latest setup kilo-code --yes
399
+ ```
400
+
401
+ Writes the MCP server config to VS Code's globalStorage for the Kilo Code extension (project scope writes `.kilocode/mcp.json`) and drops rules into `.kilocode/rules/`. **Restart VS Code** so Kilo reloads its MCP config.
394
402
 
395
403
  ### Gemini CLI
396
404
 
@@ -424,7 +432,19 @@ npx -y summer-engine@latest setup opencode --yes
424
432
 
425
433
  Writes the MCP server entry into `opencode.json` (`~/.config/opencode/opencode.json` for user scope, `./opencode.json` for project) using the array-shaped `command: ["npx", "-y", "summer-engine@latest", "mcp"]` format. Restart OpenCode. Full guide: [`.opencode/INSTALL.md`](./.opencode/INSTALL.md).
426
434
 
427
- ### Windsurf and others
435
+ ### LM Studio (local models)
436
+
437
+ ```bash
438
+ npx -y summer-engine@latest setup lm-studio --yes
439
+ ```
440
+
441
+ Writes the MCP server entry into `~/.lmstudio/mcp.json` (app-global; there is no project scope). In LM Studio, toggle the `summer-engine` server on in the **Program** tab, and raise the loaded model's context length to **32k or higher** — MCP tool schemas overflow small contexts silently. LM Studio has no rules/skills folder; the MCP server's `summer_get_agent_playbook` tool covers in-chat guidance. Pair with a tool-calling-reliable local model (gpt-oss-20b on 12–16 GB VRAM, Qwen3-Coder-30B on 24 GB).
442
+
443
+ ### Ollama (local models)
444
+
445
+ Ollama is the model backend, not an MCP client — pair it with OpenCode, Kilo Code, or Goose and point that agent's provider at `http://localhost:11434/v1`. Before connecting MCP, raise Ollama's context window (defaults to 4k under 24 GB VRAM): start with `OLLAMA_CONTEXT_LENGTH=65536 ollama serve`, then `ollama ps` to confirm the model still fits on the GPU.
446
+
447
+ ### Devin Desktop (formerly Windsurf) and others
428
448
 
429
449
  ```bash
430
450
  npx -y summer-engine@latest setup <agent> --yes --force
@@ -456,7 +476,7 @@ npx -y summer-engine@latest doctor
456
476
  | `summer mcp setup <agent>` | Write MCP config for an agent. |
457
477
  | `summer setup <agent> [--yes]` | One shot: MCP config + recommended skills + doctor. |
458
478
 
459
- Agents: `claude-code`, `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `gemini`, `opencode`. Scopes: `--scope user` (default), `--scope project`.
479
+ Agents: `claude-code`, `codex`, `cursor`, `windsurf`, `cline`, `roo-code`, `kilo-code`, `gemini`, `github-copilot`, `vscode-copilot`, `opencode`, `lm-studio`. (`devin` and `devin-desktop` are accepted as aliases for `windsurf`.) Scopes: `--scope user` (default), `--scope project`.
460
480
 
461
481
  ---
462
482
 
@@ -56,7 +56,7 @@ function printIntro(version) {
56
56
  console.log(` ${sym.arrow()} ${c.bold("Summer Engine")} ${c.dim("v" + version)} ${c.dim("·")} AI-native game engine`);
57
57
  console.log("");
58
58
  console.log(` ${c.bold("Setup wizard")}`);
59
- console.log(` Open Claude Code, Cursor, Codex, Copilot, Windsurf, or another supported agent and paste:`);
59
+ console.log(` Open Claude Code, Cursor, Codex, Copilot, Devin Desktop (formerly Windsurf), or another supported agent and paste:`);
60
60
  console.log(` ${c.brand("\"Install Summer Engine and let's make a game.\"")}`);
61
61
  console.log("");
62
62
  console.log(` ${c.bold("Manual commands")}`);
@@ -10,13 +10,15 @@ const AGENT_LABEL = {
10
10
  "claude-code": "Claude Code",
11
11
  codex: "Codex",
12
12
  cursor: "Cursor",
13
- windsurf: "Windsurf",
13
+ windsurf: "Devin Desktop (Windsurf)",
14
14
  cline: "Cline",
15
15
  "roo-code": "Roo Code",
16
+ "kilo-code": "Kilo Code",
16
17
  gemini: "Gemini CLI",
17
18
  "github-copilot": "GitHub Copilot CLI",
18
19
  "vscode-copilot": "GitHub Copilot in VS Code",
19
20
  opencode: "OpenCode",
21
+ "lm-studio": "LM Studio",
20
22
  };
21
23
  export const setupCommand = new Command("setup")
22
24
  .description("Configure Summer Engine for an AI agent and run diagnostics")
@@ -76,6 +78,12 @@ function resolveScope(scopeOpt) {
76
78
  return parsed;
77
79
  }
78
80
  function setupRecommendedSkills(agent, options) {
81
+ if (agent === "lm-studio") {
82
+ return {
83
+ status: "skipped",
84
+ message: "LM Studio has no rules or skills folder. The MCP server ships summer_get_agent_playbook, so the model can pull Summer guidance in-chat.",
85
+ };
86
+ }
79
87
  const invocation = skillInstallInvocation(agent, { force: options.force });
80
88
  if (!invocation) {
81
89
  return {
@@ -113,7 +113,8 @@ function resolveScope(agent, opts) {
113
113
  if (agent === "cursor" ||
114
114
  agent === "windsurf" ||
115
115
  agent === "cline" ||
116
- agent === "roo-code") {
116
+ agent === "roo-code" ||
117
+ agent === "kilo-code") {
117
118
  return "project";
118
119
  }
119
120
  return "user";
@@ -127,11 +128,13 @@ function agentLabel(agent) {
127
128
  case "cursor":
128
129
  return "Cursor";
129
130
  case "windsurf":
130
- return "Windsurf";
131
+ return "Devin Desktop (Windsurf)";
131
132
  case "cline":
132
133
  return "Cline";
133
134
  case "roo-code":
134
135
  return "Roo Code";
136
+ case "kilo-code":
137
+ return "Kilo Code";
135
138
  case "gemini":
136
139
  return "Gemini CLI";
137
140
  case "github-copilot":
@@ -152,7 +155,7 @@ function resolveInstallLocation(agent, scope) {
152
155
  if (agent === "windsurf") {
153
156
  return { kind: "windsurf-rule-file", path: join(overrideDir, ".windsurfrules") };
154
157
  }
155
- if (agent === "cline" || agent === "roo-code") {
158
+ if (agent === "cline" || agent === "roo-code" || agent === "kilo-code") {
156
159
  return { kind: "cline-rule-dir", path: overrideDir };
157
160
  }
158
161
  if (agent === "gemini") {
@@ -187,6 +190,13 @@ function resolveInstallLocation(agent, scope) {
187
190
  ? rooCodeUserRulesDir()
188
191
  : join(process.cwd(), ".clinerules"),
189
192
  };
193
+ case "kilo-code":
194
+ return {
195
+ kind: "cline-rule-dir",
196
+ path: scope === "user"
197
+ ? join(homedir(), ".kilocode", "rules")
198
+ : join(process.cwd(), ".kilocode", "rules"),
199
+ };
190
200
  case "gemini":
191
201
  return {
192
202
  kind: "gemini-skill-dir",
@@ -390,7 +400,7 @@ function printInstallSummary(count, agent, scope, location) {
390
400
  console.log("Restart OpenCode so it picks up the new agent definitions.");
391
401
  }
392
402
  else {
393
- console.log(`Windsurf rules are in ${tildeified}`);
403
+ console.log(`Devin Desktop (formerly Windsurf) rules are in ${tildeified}`);
394
404
  }
395
405
  }
396
406
  export const skillsCommand = new Command("skills")
@@ -484,7 +494,7 @@ function installClaudeCommands(scope, force) {
484
494
  }
485
495
  /**
486
496
  * Drop a `.summer-version` marker into the install dir so doctor's
487
- * `skills-version-stale` check can detect drift later. Windsurf writes a
497
+ * `skills-version-stale` check can detect drift later. Devin Desktop (formerly Windsurf) writes a
488
498
  * single rules file with no surrounding dir, so we skip it there.
489
499
  */
490
500
  function writeMarkerForLocation(location) {
@@ -1,5 +1,5 @@
1
1
  export declare const SUMMER_MCP_SERVER_NAME = "summer-engine";
2
- export declare const supportedAgents: readonly ["codex", "claude-code", "cursor", "windsurf", "cline", "roo-code", "gemini", "github-copilot", "vscode-copilot", "opencode"];
2
+ export declare const supportedAgents: readonly ["codex", "claude-code", "cursor", "windsurf", "cline", "roo-code", "kilo-code", "gemini", "github-copilot", "vscode-copilot", "opencode", "lm-studio"];
3
3
  export type SupportedAgent = (typeof supportedAgents)[number];
4
4
  export type ConfigScope = "user" | "project";
5
5
  export interface StdioMcpServerConfig {
@@ -11,10 +11,12 @@ export const supportedAgents = [
11
11
  "windsurf",
12
12
  "cline",
13
13
  "roo-code",
14
+ "kilo-code",
14
15
  "gemini",
15
16
  "github-copilot",
16
17
  "vscode-copilot",
17
18
  "opencode",
19
+ "lm-studio",
18
20
  ];
19
21
  const agentAliases = {
20
22
  claude: "claude-code",
@@ -22,10 +24,16 @@ const agentAliases = {
22
24
  codex: "codex",
23
25
  cursor: "cursor",
24
26
  windsurf: "windsurf",
27
+ devin: "windsurf",
28
+ "devin-desktop": "windsurf",
29
+ devindesktop: "windsurf",
25
30
  cline: "cline",
26
31
  "roo-code": "roo-code",
27
32
  roo: "roo-code",
28
33
  roocode: "roo-code",
34
+ kilo: "kilo-code",
35
+ kilocode: "kilo-code",
36
+ "kilo-code": "kilo-code",
29
37
  gemini: "gemini",
30
38
  "gemini-cli": "gemini",
31
39
  copilot: "github-copilot",
@@ -39,6 +47,9 @@ const agentAliases = {
39
47
  "github-copilot-vscode": "vscode-copilot",
40
48
  opencode: "opencode",
41
49
  "open-code": "opencode",
50
+ lmstudio: "lm-studio",
51
+ "lm-studio": "lm-studio",
52
+ "lm_studio": "lm-studio",
42
53
  };
43
54
  export function parseAgent(value) {
44
55
  if (!value)
@@ -148,7 +159,7 @@ function resolveConfigTarget(agent, scope, cwd, env) {
148
159
  const warnings = [];
149
160
  if (override) {
150
161
  if (scope === "project" &&
151
- (agent === "cline" || agent === "roo-code" || agent === "gemini")) {
162
+ (agent === "cline" || agent === "roo-code" || agent === "gemini" || agent === "lm-studio")) {
152
163
  warnings.push(`${agent} MCP config has no project scope today; treating as user scope.`);
153
164
  }
154
165
  return {
@@ -212,6 +223,25 @@ function resolveConfigTarget(agent, scope, cwd, env) {
212
223
  warnings,
213
224
  };
214
225
  }
226
+ if (agent === "kilo-code") {
227
+ return {
228
+ path: scope === "user"
229
+ ? vsCodeGlobalStoragePath(env, "kilocode.kilo-code", "mcp_settings.json")
230
+ : join(cwd, ".kilocode", "mcp.json"),
231
+ format: "json",
232
+ warnings,
233
+ };
234
+ }
235
+ if (agent === "lm-studio") {
236
+ if (scope === "project") {
237
+ warnings.push("LM Studio's MCP config is app-global (~/.lmstudio/mcp.json); treating as user scope.");
238
+ }
239
+ return {
240
+ path: join(homedir(), ".lmstudio", "mcp.json"),
241
+ format: "json",
242
+ warnings,
243
+ };
244
+ }
215
245
  if (agent === "gemini") {
216
246
  if (scope === "project") {
217
247
  warnings.push("Gemini extensions are user-scoped today; writing to ~/.gemini/extensions/summer-engine/ instead of project.");
@@ -250,7 +280,7 @@ function resolveConfigTarget(agent, scope, cwd, env) {
250
280
  };
251
281
  }
252
282
  if (scope === "project") {
253
- warnings.push("Windsurf documents MCP configuration as user-scoped; project scope writes .windsurf/mcp_config.json for teams that load workspace config.");
283
+ warnings.push("Devin Desktop (formerly Windsurf) documents MCP configuration as user-scoped; project scope writes .windsurf/mcp_config.json for teams that load workspace config.");
254
284
  }
255
285
  return {
256
286
  path: scope === "user"
@@ -260,18 +290,18 @@ function resolveConfigTarget(agent, scope, cwd, env) {
260
290
  warnings,
261
291
  };
262
292
  }
263
- function vsCodeGlobalStoragePath(env, extensionId) {
293
+ function vsCodeGlobalStoragePath(env, extensionId, fileName = "cline_mcp_settings.json") {
264
294
  const os = platform();
265
295
  if (os === "win32") {
266
296
  const appData = env.APPDATA ?? join(homedir(), "AppData", "Roaming");
267
- return join(appData, "Code", "User", "globalStorage", extensionId, "settings", "cline_mcp_settings.json");
297
+ return join(appData, "Code", "User", "globalStorage", extensionId, "settings", fileName);
268
298
  }
269
299
  if (os === "darwin") {
270
- return join(homedir(), "Library", "Application Support", "Code", "User", "globalStorage", extensionId, "settings", "cline_mcp_settings.json");
300
+ return join(homedir(), "Library", "Application Support", "Code", "User", "globalStorage", extensionId, "settings", fileName);
271
301
  }
272
302
  // Linux and other Unix
273
303
  const xdg = env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
274
- return join(xdg, "Code", "User", "globalStorage", extensionId, "settings", "cline_mcp_settings.json");
304
+ return join(xdg, "Code", "User", "globalStorage", extensionId, "settings", fileName);
275
305
  }
276
306
  function opencodeUserConfigPath(env) {
277
307
  const os = platform();
@@ -305,6 +335,10 @@ function getConfigPathOverride(agent, env) {
305
335
  return env.SUMMER_CLINE_CONFIG_FILE;
306
336
  if (agent === "roo-code")
307
337
  return env.SUMMER_ROO_CODE_CONFIG_FILE;
338
+ if (agent === "kilo-code")
339
+ return env.SUMMER_KILO_CODE_CONFIG_FILE;
340
+ if (agent === "lm-studio")
341
+ return env.SUMMER_LM_STUDIO_CONFIG_FILE;
308
342
  if (agent === "gemini")
309
343
  return env.SUMMER_GEMINI_CONFIG_FILE;
310
344
  if (agent === "github-copilot")
@@ -558,15 +592,19 @@ function createNextSteps(agent, scope, path) {
558
592
  ? "Restart VS Code so Cline reloads its MCP config."
559
593
  : agent === "roo-code"
560
594
  ? "Restart VS Code so Roo Code reloads its MCP config."
561
- : agent === "gemini"
562
- ? "Run `gemini extensions enable summer-engine` (if not already enabled), then restart Gemini CLI."
563
- : agent === "github-copilot"
564
- ? "Restart Copilot CLI, or run /mcp reload and /skills reload in the active session."
565
- : agent === "vscode-copilot"
566
- ? "Restart VS Code or run MCP: List Servers, then start summer-engine in Copilot Agent mode."
567
- : agent === "opencode"
568
- ? "Restart OpenCode so it reloads opencode.json."
569
- : "Restart Windsurf and refresh MCP servers from Cascade settings.";
595
+ : agent === "kilo-code"
596
+ ? "Restart VS Code so Kilo Code reloads its MCP config."
597
+ : agent === "lm-studio"
598
+ ? "Open LM Studio, toggle on the summer-engine MCP server in the Program tab, and raise the loaded model's context length to 32k or higher."
599
+ : agent === "gemini"
600
+ ? "Run `gemini extensions enable summer-engine` (if not already enabled), then restart Gemini CLI."
601
+ : agent === "github-copilot"
602
+ ? "Restart Copilot CLI, or run /mcp reload and /skills reload in the active session."
603
+ : agent === "vscode-copilot"
604
+ ? "Restart VS Code or run MCP: List Servers, then start summer-engine in Copilot Agent mode."
605
+ : agent === "opencode"
606
+ ? "Restart OpenCode so it reloads opencode.json."
607
+ : "Restart Devin Desktop (formerly Windsurf) and refresh MCP servers from the agent settings.";
570
608
  const projectTrust = scope === "project" && agent === "codex"
571
609
  ? "Codex only loads project .codex/config.toml from trusted projects."
572
610
  : null;
@@ -2,6 +2,10 @@ export type EngineSnapshot = {
2
2
  ok: boolean;
3
3
  localPath?: string;
4
4
  path?: string;
5
+ /** Raw image bytes, base64-encoded. Carried so a caller (e.g. the MCP
6
+ * screenshot tool) can return an MCP image content block to a vision-capable
7
+ * client without re-reading localPath off disk. */
8
+ base64?: string;
5
9
  width?: number;
6
10
  height?: number;
7
11
  format?: string;
@@ -50,4 +54,17 @@ export declare class EngineApiClient {
50
54
  viewportSnapshot(): Promise<EngineSnapshot>;
51
55
  gameSnapshot(): Promise<EngineSnapshot>;
52
56
  getPort(): number;
57
+ /**
58
+ * Cheap drift probe for the MCP client cache. The engine mints a fresh
59
+ * api-token on every launch (local_api_server.cpp::_generate_api_token) and can
60
+ * bind a different port (tool_net_thread.cpp::start increments 6550..6565 when
61
+ * the old socket lingers), so a client built before an engine restart holds
62
+ * dead credentials. Re-read the on-disk creds and report whether they no longer
63
+ * match this client's snapshot.
64
+ *
65
+ * Empty / unreadable creds (engine mid-write, or just closed) are treated as
66
+ * "no drift" so a transient read never thrashes the cache — the live request
67
+ * will fail and trigger a reconnect+retry if the client really is stale.
68
+ */
69
+ credentialsChanged(): Promise<boolean>;
53
70
  }
@@ -240,6 +240,7 @@ export class EngineApiClient {
240
240
  ok: payload.ok !== false,
241
241
  localPath,
242
242
  path: localPath,
243
+ base64,
243
244
  width: numberFrom(payload.width),
244
245
  height: numberFrom(payload.height),
245
246
  format,
@@ -260,4 +261,27 @@ export class EngineApiClient {
260
261
  getPort() {
261
262
  return this.port;
262
263
  }
264
+ /**
265
+ * Cheap drift probe for the MCP client cache. The engine mints a fresh
266
+ * api-token on every launch (local_api_server.cpp::_generate_api_token) and can
267
+ * bind a different port (tool_net_thread.cpp::start increments 6550..6565 when
268
+ * the old socket lingers), so a client built before an engine restart holds
269
+ * dead credentials. Re-read the on-disk creds and report whether they no longer
270
+ * match this client's snapshot.
271
+ *
272
+ * Empty / unreadable creds (engine mid-write, or just closed) are treated as
273
+ * "no drift" so a transient read never thrashes the cache — the live request
274
+ * will fail and trigger a reconnect+retry if the client really is stale.
275
+ */
276
+ async credentialsChanged() {
277
+ try {
278
+ const [port, token] = await Promise.all([getApiPort(), getApiToken()]);
279
+ if (!token)
280
+ return false;
281
+ return port !== this.port || token !== this.token;
282
+ }
283
+ catch {
284
+ return false;
285
+ }
286
+ }
263
287
  }