flockbay 0.10.15 → 0.10.16

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 (31) hide show
  1. package/dist/codex/flockbayMcpStdioBridge.cjs +339 -0
  2. package/dist/codex/flockbayMcpStdioBridge.mjs +339 -0
  3. package/dist/{index--o4BPz5o.cjs → index-Cau-_Qvn.cjs} +2683 -609
  4. package/dist/{index-CUp3juDS.mjs → index-DtmFQzXY.mjs} +2684 -611
  5. package/dist/index.cjs +3 -5
  6. package/dist/index.mjs +3 -5
  7. package/dist/lib.cjs +7 -9
  8. package/dist/lib.d.cts +219 -531
  9. package/dist/lib.d.mts +219 -531
  10. package/dist/lib.mjs +7 -9
  11. package/dist/{runCodex-o6PCbHQ7.mjs → runCodex-Di9eHddq.mjs} +263 -42
  12. package/dist/{runCodex-D3eT-TvB.cjs → runCodex-DzP3VUa-.cjs} +264 -43
  13. package/dist/{runGemini-Bt0oEj_g.mjs → runGemini-BS6sBU_V.mjs} +63 -28
  14. package/dist/{runGemini-CBxZp6I7.cjs → runGemini-CpmehDQ2.cjs} +64 -29
  15. package/dist/{types-DGd6ea2Z.mjs → types-CwzNqYEx.mjs} +465 -1142
  16. package/dist/{types-C-jnUdn_.cjs → types-SUAKq-K0.cjs} +466 -1146
  17. package/package.json +1 -1
  18. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp +195 -6
  19. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp +376 -5
  20. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPCommandSchema.cpp +731 -0
  21. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPCommonUtils.cpp +476 -8
  22. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp +1518 -94
  23. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/MCPServerRunnable.cpp +7 -4
  24. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/UnrealMCPBridge.cpp +150 -112
  25. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPBlueprintCommands.h +2 -1
  26. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPBlueprintNodeCommands.h +4 -1
  27. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPCommandSchema.h +42 -0
  28. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPEditorCommands.h +21 -0
  29. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/UnrealMCP.Build.cs +4 -1
  30. package/dist/flockbayScreenshotGate-DJX3Is5d.mjs +0 -136
  31. package/dist/flockbayScreenshotGate-DkxU24cR.cjs +0 -138
@@ -4,8 +4,8 @@ import { randomUUID, createHash } from 'node:crypto';
4
4
  import os from 'node:os';
5
5
  import path, { resolve, join as join$1, basename } from 'node:path';
6
6
  import { mkdir, writeFile, readFile } from 'node:fs/promises';
7
- import { l as logger, b as packageJson, A as ApiClient, r as readSettings, p as projectPath, c as configuration } from './types-DGd6ea2Z.mjs';
8
- import { s as shouldCountToolCall, c as consumeToolQuota, f as formatQuotaDeniedReason, h as hashObject, i as initialMachineMetadata, n as notifyDaemonSessionStarted, M as MessageQueue2, g as buildProjectCapsule, a as setLatestUserImages, b as MessageBuffer, w as withUserImagesMarker, P as PLATFORM_SYSTEM_PROMPT, r as registerKillSessionHandler, d as startFlockbayServer, m as extractUserImagesMarker, o as getLatestUserImages, j as autoFinalizeCoordinationWorkItem, E as ElicitationHub, l as stopCaffeinate } from './index-CUp3juDS.mjs';
7
+ import { l as logger, b as packageJson, A as ApiClient, r as readSettings, p as projectPath, c as configuration } from './types-CwzNqYEx.mjs';
8
+ import { s as shouldCountToolCall, c as consumeToolQuota, f as formatQuotaDeniedReason, h as hashObject, i as initialMachineMetadata, n as notifyDaemonSessionStarted, M as MessageQueue2, g as buildProjectCapsule, a as setLatestUserImages, b as MessageBuffer, w as withUserImagesMarker, P as PLATFORM_SYSTEM_PROMPT, r as registerKillSessionHandler, d as startFlockbayServer, o as extractUserImagesMarker, p as getLatestUserImages, j as autoFinalizeCoordinationWorkItem, E as ElicitationHub, k as detectScreenshotsForGate, m as stopCaffeinate } from './index-DtmFQzXY.mjs';
9
9
  import { spawn, spawnSync } from 'node:child_process';
10
10
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
11
11
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
@@ -13,31 +13,28 @@ import { join } from 'path';
13
13
  import { homedir } from 'os';
14
14
  import { execSync } from 'child_process';
15
15
  import fs__default from 'node:fs';
16
- import { d as detectScreenshotsForGate } from './flockbayScreenshotGate-DJX3Is5d.mjs';
17
16
  import 'axios';
18
- import 'chalk';
19
- import 'zod';
20
- import 'tweetnacl';
21
17
  import 'node:events';
22
18
  import 'socket.io-client';
19
+ import 'chalk';
20
+ import 'zod';
23
21
  import 'fs/promises';
24
22
  import 'crypto';
25
23
  import 'url';
26
24
  import 'node:process';
27
25
  import 'node:net';
28
- import 'expo-server-sdk';
29
26
  import 'node:readline';
30
27
  import 'node:url';
31
28
  import 'ps-list';
32
29
  import 'cross-spawn';
33
30
  import 'tmp';
34
- import 'qrcode-terminal';
35
31
  import 'open';
36
32
  import 'fastify';
37
33
  import 'fastify-type-provider-zod';
38
34
  import '@modelcontextprotocol/sdk/server/mcp.js';
39
35
  import 'node:http';
40
36
  import '@modelcontextprotocol/sdk/server/streamableHttp.js';
37
+ import 'tweetnacl';
41
38
  import 'http';
42
39
  import 'util';
43
40
 
@@ -934,15 +931,6 @@ ${excerpt}` : base;
934
931
  }
935
932
  }
936
933
  }
937
- if (update.messageChunk) {
938
- const chunk = update.messageChunk;
939
- if (chunk.textDelta) {
940
- this.emit({
941
- type: "model-output",
942
- textDelta: chunk.textDelta
943
- });
944
- }
945
- }
946
934
  if (update.plan) {
947
935
  this.emit({
948
936
  type: "event",
@@ -1878,11 +1866,14 @@ class GeminiPermissionHandler {
1878
1866
  }
1879
1867
  this.pendingRequests.clear();
1880
1868
  this.session.updateAgentState((currentState) => {
1881
- const pendingRequests = currentState.requests || {};
1882
- const completedRequests = { ...currentState.completedRequests };
1869
+ const pendingRaw = currentState?.requests;
1870
+ const pendingRequests = pendingRaw && typeof pendingRaw === "object" ? pendingRaw : {};
1871
+ const completedRaw = currentState?.completedRequests;
1872
+ const completedRequests = completedRaw && typeof completedRaw === "object" ? { ...completedRaw } : {};
1883
1873
  for (const [id, request] of Object.entries(pendingRequests)) {
1874
+ const reqObj = request && typeof request === "object" ? request : { value: request };
1884
1875
  completedRequests[id] = {
1885
- ...request,
1876
+ ...reqObj,
1886
1877
  completedAt: Date.now(),
1887
1878
  status: "canceled",
1888
1879
  reason: "Session reset"
@@ -2348,8 +2339,19 @@ async function runGemini(opts) {
2348
2339
  lifecycleStateSince: Date.now(),
2349
2340
  flavor: "gemini"
2350
2341
  };
2351
- const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
2342
+ const response = opts.sessionId ? await api.getSessionById(opts.sessionId) : await api.getOrCreateSession({ tag: sessionTag, metadata, state });
2352
2343
  const session = api.sessionSyncClient(response);
2344
+ if (opts.sessionId) {
2345
+ session.updateMetadata((currentMetadata) => ({
2346
+ ...currentMetadata,
2347
+ ...metadata,
2348
+ // Preserve user-facing fields set by other clients.
2349
+ name: currentMetadata?.name,
2350
+ summary: currentMetadata?.summary
2351
+ }));
2352
+ }
2353
+ await session.connectAndWait(15e3);
2354
+ session.keepAlive(false, "remote");
2353
2355
  try {
2354
2356
  logger.debug(`[START] Reporting session ${response.id} to daemon`);
2355
2357
  const result = await notifyDaemonSessionStarted(response.id, metadata);
@@ -2396,9 +2398,13 @@ async function runGemini(opts) {
2396
2398
  text: [
2397
2399
  `You just generated ${unique.length} screenshot${unique.length === 1 ? "" : "s"}.`,
2398
2400
  "",
2399
- "Visually inspect EVERY screenshot and report:",
2400
- "- One short bullet per image describing what you see",
2401
- "- Whether the screenshots match the user's request",
2401
+ "Visually inspect EVERY screenshot and report (this is for YOUR validation, not ceremony):",
2402
+ "1) List 2\u20135 concrete acceptance criteria for the user\u2019s request.",
2403
+ "2) One short bullet per image describing what you see.",
2404
+ "3) For each acceptance criterion: mark it as Verified / Not visible / Failed based ONLY on the screenshots.",
2405
+ ' - If "Not visible", say what evidence is missing.',
2406
+ ' - If "Failed", say what is wrong.',
2407
+ "4) If anything is not Verified: state the next tool/action you will take to fix or validate it (do not claim completion yet).",
2402
2408
  "",
2403
2409
  "Do not run any tools in this step."
2404
2410
  ].join("\n")
@@ -2534,8 +2540,14 @@ async function runGemini(opts) {
2534
2540
  let geminiBackend = null;
2535
2541
  let acpSessionId = null;
2536
2542
  let wasSessionCreated = false;
2537
- async function handleAbort() {
2543
+ let abortNote = null;
2544
+ let abortNoteSentToSession = false;
2545
+ async function handleAbort(note, options) {
2538
2546
  logger.debug("[Gemini] Abort requested - stopping current task");
2547
+ if (typeof note === "string" && note.trim()) {
2548
+ abortNote = note.trim();
2549
+ abortNoteSentToSession = Boolean(options?.alreadySentToSession);
2550
+ }
2539
2551
  session.sendCodexMessage({
2540
2552
  type: "turn_aborted",
2541
2553
  id: randomUUID()
@@ -2590,7 +2602,7 @@ async function runGemini(opts) {
2590
2602
  process.exit(1);
2591
2603
  }
2592
2604
  };
2593
- session.rpcHandlerManager.registerHandler("abort", handleAbort);
2605
+ session.rpcHandlerManager.registerHandler("cancel-generation", handleAbort);
2594
2606
  registerKillSessionHandler(session.rpcHandlerManager, handleKillSession);
2595
2607
  const messageBuffer = new MessageBuffer();
2596
2608
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
@@ -2648,6 +2660,21 @@ async function runGemini(opts) {
2648
2660
  }
2649
2661
  const elicitationHub = new ElicitationHub();
2650
2662
  const flockbayServer = await startFlockbayServer(session, { elicitationHub });
2663
+ const unsubscribeUnrealIssue = (() => {
2664
+ const onIssue = flockbayServer?.unreal?.onIssue;
2665
+ if (typeof onIssue !== "function") return null;
2666
+ return onIssue((event) => {
2667
+ try {
2668
+ const kind = typeof event?.kind === "string" ? event.kind : "unreachable";
2669
+ const base = typeof event?.message === "string" && event.message.trim() ? event.message.trim() : "Unreal Editor issue detected.";
2670
+ const msg = kind === "process_exit" ? `Unreal Editor crashed. ${base}` : `Unreal Editor is not reachable. ${base}`;
2671
+ session.sendSessionEvent({ type: "message", message: msg });
2672
+ void handleAbort(msg, { alreadySentToSession: true });
2673
+ } catch (err) {
2674
+ logger.debug("[Gemini] Failed to handle Unreal issue event", err);
2675
+ }
2676
+ });
2677
+ })();
2651
2678
  const bridgeCommand = join$1(projectPath(), "bin", "flockbay-mcp.mjs");
2652
2679
  const mcpServers = {
2653
2680
  flockbay: {
@@ -3055,8 +3082,12 @@ async function runGemini(opts) {
3055
3082
  const isAbortError = error instanceof Error && error.name === "AbortError";
3056
3083
  if (isAbortError) {
3057
3084
  skipAutoFinalize = true;
3058
- messageBuffer.addMessage("Aborted by user", "status");
3059
- session.sendSessionEvent({ type: "message", message: "Aborted by user" });
3085
+ const note = abortNote || "Aborted by user";
3086
+ abortNote = null;
3087
+ const alreadySent = abortNoteSentToSession;
3088
+ abortNoteSentToSession = false;
3089
+ messageBuffer.addMessage(note, "status");
3090
+ if (!alreadySent) session.sendSessionEvent({ type: "message", message: note });
3060
3091
  } else {
3061
3092
  const rawErrorString = error instanceof Error ? error.message : String(error);
3062
3093
  const looksLikeAuthOrQuotaError = /unauthenticated|unauthorized|invalid (api )?key|api key not valid|permission denied|forbidden|expired|quota|usage limit|rate limit|resource exhausted|resource_exhausted|status 401|status 403|status 429|\b401\b|\b403\b|\b429\b/i.test(rawErrorString);
@@ -3148,6 +3179,10 @@ async function runGemini(opts) {
3148
3179
  }
3149
3180
  } finally {
3150
3181
  logger.debug("[gemini]: Final cleanup start");
3182
+ try {
3183
+ if (typeof unsubscribeUnrealIssue === "function") unsubscribeUnrealIssue();
3184
+ } catch {
3185
+ }
3151
3186
  try {
3152
3187
  session.sendSessionDeath();
3153
3188
  await session.flush();
@@ -6,8 +6,8 @@ var node_crypto = require('node:crypto');
6
6
  var os = require('node:os');
7
7
  var path = require('node:path');
8
8
  var fs$2 = require('node:fs/promises');
9
- var types = require('./types-C-jnUdn_.cjs');
10
- var index = require('./index--o4BPz5o.cjs');
9
+ var types = require('./types-SUAKq-K0.cjs');
10
+ var index = require('./index-Cau-_Qvn.cjs');
11
11
  var node_child_process = require('node:child_process');
12
12
  var sdk = require('@agentclientprotocol/sdk');
13
13
  var fs = require('fs');
@@ -15,31 +15,28 @@ var path$1 = require('path');
15
15
  var os$1 = require('os');
16
16
  var child_process = require('child_process');
17
17
  var fs$1 = require('node:fs');
18
- var flockbayScreenshotGate = require('./flockbayScreenshotGate-DkxU24cR.cjs');
19
18
  require('axios');
20
- require('chalk');
21
- require('zod');
22
- require('tweetnacl');
23
19
  require('node:events');
24
20
  require('socket.io-client');
21
+ require('chalk');
22
+ require('zod');
25
23
  require('fs/promises');
26
24
  require('crypto');
27
25
  require('url');
28
26
  require('node:process');
29
27
  require('node:net');
30
- require('expo-server-sdk');
31
28
  require('node:readline');
32
29
  require('node:url');
33
30
  require('ps-list');
34
31
  require('cross-spawn');
35
32
  require('tmp');
36
- require('qrcode-terminal');
37
33
  require('open');
38
34
  require('fastify');
39
35
  require('fastify-type-provider-zod');
40
36
  require('@modelcontextprotocol/sdk/server/mcp.js');
41
37
  require('node:http');
42
38
  require('@modelcontextprotocol/sdk/server/streamableHttp.js');
39
+ require('tweetnacl');
43
40
  require('http');
44
41
  require('util');
45
42
 
@@ -936,15 +933,6 @@ ${excerpt}` : base;
936
933
  }
937
934
  }
938
935
  }
939
- if (update.messageChunk) {
940
- const chunk = update.messageChunk;
941
- if (chunk.textDelta) {
942
- this.emit({
943
- type: "model-output",
944
- textDelta: chunk.textDelta
945
- });
946
- }
947
- }
948
936
  if (update.plan) {
949
937
  this.emit({
950
938
  type: "event",
@@ -1880,11 +1868,14 @@ class GeminiPermissionHandler {
1880
1868
  }
1881
1869
  this.pendingRequests.clear();
1882
1870
  this.session.updateAgentState((currentState) => {
1883
- const pendingRequests = currentState.requests || {};
1884
- const completedRequests = { ...currentState.completedRequests };
1871
+ const pendingRaw = currentState?.requests;
1872
+ const pendingRequests = pendingRaw && typeof pendingRaw === "object" ? pendingRaw : {};
1873
+ const completedRaw = currentState?.completedRequests;
1874
+ const completedRequests = completedRaw && typeof completedRaw === "object" ? { ...completedRaw } : {};
1885
1875
  for (const [id, request] of Object.entries(pendingRequests)) {
1876
+ const reqObj = request && typeof request === "object" ? request : { value: request };
1886
1877
  completedRequests[id] = {
1887
- ...request,
1878
+ ...reqObj,
1888
1879
  completedAt: Date.now(),
1889
1880
  status: "canceled",
1890
1881
  reason: "Session reset"
@@ -2350,8 +2341,19 @@ async function runGemini(opts) {
2350
2341
  lifecycleStateSince: Date.now(),
2351
2342
  flavor: "gemini"
2352
2343
  };
2353
- const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
2344
+ const response = opts.sessionId ? await api.getSessionById(opts.sessionId) : await api.getOrCreateSession({ tag: sessionTag, metadata, state });
2354
2345
  const session = api.sessionSyncClient(response);
2346
+ if (opts.sessionId) {
2347
+ session.updateMetadata((currentMetadata) => ({
2348
+ ...currentMetadata,
2349
+ ...metadata,
2350
+ // Preserve user-facing fields set by other clients.
2351
+ name: currentMetadata?.name,
2352
+ summary: currentMetadata?.summary
2353
+ }));
2354
+ }
2355
+ await session.connectAndWait(15e3);
2356
+ session.keepAlive(false, "remote");
2355
2357
  try {
2356
2358
  types.logger.debug(`[START] Reporting session ${response.id} to daemon`);
2357
2359
  const result = await index.notifyDaemonSessionStarted(response.id, metadata);
@@ -2380,7 +2382,7 @@ async function runGemini(opts) {
2380
2382
  if (!output) return;
2381
2383
  const cwdHint = output?.cwd;
2382
2384
  const cwd = typeof cwdHint === "string" && cwdHint.trim().length > 0 ? cwdHint.trim() : process.cwd();
2383
- const detected = flockbayScreenshotGate.detectScreenshotsForGate({ output, cwd });
2385
+ const detected = index.detectScreenshotsForGate({ output, cwd });
2384
2386
  if (detected.paths.length === 0) return;
2385
2387
  for (const p of detected.paths) {
2386
2388
  const trimmed = String(p || "").trim();
@@ -2398,9 +2400,13 @@ async function runGemini(opts) {
2398
2400
  text: [
2399
2401
  `You just generated ${unique.length} screenshot${unique.length === 1 ? "" : "s"}.`,
2400
2402
  "",
2401
- "Visually inspect EVERY screenshot and report:",
2402
- "- One short bullet per image describing what you see",
2403
- "- Whether the screenshots match the user's request",
2403
+ "Visually inspect EVERY screenshot and report (this is for YOUR validation, not ceremony):",
2404
+ "1) List 2\u20135 concrete acceptance criteria for the user\u2019s request.",
2405
+ "2) One short bullet per image describing what you see.",
2406
+ "3) For each acceptance criterion: mark it as Verified / Not visible / Failed based ONLY on the screenshots.",
2407
+ ' - If "Not visible", say what evidence is missing.',
2408
+ ' - If "Failed", say what is wrong.',
2409
+ "4) If anything is not Verified: state the next tool/action you will take to fix or validate it (do not claim completion yet).",
2404
2410
  "",
2405
2411
  "Do not run any tools in this step."
2406
2412
  ].join("\n")
@@ -2536,8 +2542,14 @@ async function runGemini(opts) {
2536
2542
  let geminiBackend = null;
2537
2543
  let acpSessionId = null;
2538
2544
  let wasSessionCreated = false;
2539
- async function handleAbort() {
2545
+ let abortNote = null;
2546
+ let abortNoteSentToSession = false;
2547
+ async function handleAbort(note, options) {
2540
2548
  types.logger.debug("[Gemini] Abort requested - stopping current task");
2549
+ if (typeof note === "string" && note.trim()) {
2550
+ abortNote = note.trim();
2551
+ abortNoteSentToSession = Boolean(options?.alreadySentToSession);
2552
+ }
2541
2553
  session.sendCodexMessage({
2542
2554
  type: "turn_aborted",
2543
2555
  id: node_crypto.randomUUID()
@@ -2592,7 +2604,7 @@ async function runGemini(opts) {
2592
2604
  process.exit(1);
2593
2605
  }
2594
2606
  };
2595
- session.rpcHandlerManager.registerHandler("abort", handleAbort);
2607
+ session.rpcHandlerManager.registerHandler("cancel-generation", handleAbort);
2596
2608
  index.registerKillSessionHandler(session.rpcHandlerManager, handleKillSession);
2597
2609
  const messageBuffer = new index.MessageBuffer();
2598
2610
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
@@ -2650,6 +2662,21 @@ async function runGemini(opts) {
2650
2662
  }
2651
2663
  const elicitationHub = new index.ElicitationHub();
2652
2664
  const flockbayServer = await index.startFlockbayServer(session, { elicitationHub });
2665
+ const unsubscribeUnrealIssue = (() => {
2666
+ const onIssue = flockbayServer?.unreal?.onIssue;
2667
+ if (typeof onIssue !== "function") return null;
2668
+ return onIssue((event) => {
2669
+ try {
2670
+ const kind = typeof event?.kind === "string" ? event.kind : "unreachable";
2671
+ const base = typeof event?.message === "string" && event.message.trim() ? event.message.trim() : "Unreal Editor issue detected.";
2672
+ const msg = kind === "process_exit" ? `Unreal Editor crashed. ${base}` : `Unreal Editor is not reachable. ${base}`;
2673
+ session.sendSessionEvent({ type: "message", message: msg });
2674
+ void handleAbort(msg, { alreadySentToSession: true });
2675
+ } catch (err) {
2676
+ types.logger.debug("[Gemini] Failed to handle Unreal issue event", err);
2677
+ }
2678
+ });
2679
+ })();
2653
2680
  const bridgeCommand = path.join(types.projectPath(), "bin", "flockbay-mcp.mjs");
2654
2681
  const mcpServers = {
2655
2682
  flockbay: {
@@ -3057,8 +3084,12 @@ async function runGemini(opts) {
3057
3084
  const isAbortError = error instanceof Error && error.name === "AbortError";
3058
3085
  if (isAbortError) {
3059
3086
  skipAutoFinalize = true;
3060
- messageBuffer.addMessage("Aborted by user", "status");
3061
- session.sendSessionEvent({ type: "message", message: "Aborted by user" });
3087
+ const note = abortNote || "Aborted by user";
3088
+ abortNote = null;
3089
+ const alreadySent = abortNoteSentToSession;
3090
+ abortNoteSentToSession = false;
3091
+ messageBuffer.addMessage(note, "status");
3092
+ if (!alreadySent) session.sendSessionEvent({ type: "message", message: note });
3062
3093
  } else {
3063
3094
  const rawErrorString = error instanceof Error ? error.message : String(error);
3064
3095
  const looksLikeAuthOrQuotaError = /unauthenticated|unauthorized|invalid (api )?key|api key not valid|permission denied|forbidden|expired|quota|usage limit|rate limit|resource exhausted|resource_exhausted|status 401|status 403|status 429|\b401\b|\b403\b|\b429\b/i.test(rawErrorString);
@@ -3150,6 +3181,10 @@ async function runGemini(opts) {
3150
3181
  }
3151
3182
  } finally {
3152
3183
  types.logger.debug("[gemini]: Final cleanup start");
3184
+ try {
3185
+ if (typeof unsubscribeUnrealIssue === "function") unsubscribeUnrealIssue();
3186
+ } catch {
3187
+ }
3153
3188
  try {
3154
3189
  session.sendSessionDeath();
3155
3190
  await session.flush();