flockbay 0.10.20 → 0.10.22
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/dist/{index-CX0Z8pmz.mjs → index-BjZUYSzh.mjs} +471 -31
- package/dist/{index-D_mglYG0.cjs → index-DQTqwzYd.cjs} +471 -31
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{runCodex-Biis9GFw.mjs → runCodex-B7fGICdv.mjs} +11 -13
- package/dist/{runCodex-CXJW0tzo.cjs → runCodex-CaWagdzG.cjs} +11 -13
- package/dist/{runGemini-FOBXtEU6.cjs → runGemini-8w5P093W.cjs} +234 -49
- package/dist/{runGemini-BSH4b0wu.mjs → runGemini-CK43WQk8.mjs} +234 -49
- package/dist/{types-C4QeUggl.mjs → types-CMWcip0F.mjs} +41 -3
- package/dist/{types-BYHCKlu_.cjs → types-Z2OYpI8c.cjs} +41 -2
- package/package.json +1 -1
- package/scripts/claude_version_utils.cjs +66 -12
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp +32 -11
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/UnrealMCP.Build.cs +1 -0
|
@@ -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-
|
|
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,
|
|
7
|
+
import { l as logger, b as packageJson, A as ApiClient, r as readSettings, p as projectPath, c as configuration } from './types-CMWcip0F.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, r as registerKillSessionHandler, d as startFlockbayServer, o as extractUserImagesMarker, p as getLatestUserImages, P as PLATFORM_SYSTEM_PROMPT, j as autoFinalizeCoordinationWorkItem, E as ElicitationHub, k as detectScreenshotsForGate, m as stopCaffeinate } from './index-BjZUYSzh.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';
|
|
@@ -43,6 +43,17 @@ const KNOWN_TOOL_PATTERNS = {
|
|
|
43
43
|
save_memory: ["save_memory", "save-memory"],
|
|
44
44
|
think: ["think"]
|
|
45
45
|
};
|
|
46
|
+
const MCP_FLOCKBAY_TOOL_RE = /\bmcp__flockbay__[a-z0-9_]+\b/gi;
|
|
47
|
+
const UNREAL_MCP_TOOL_RE = /\bunreal_mcp_[a-z0-9_]+\b/gi;
|
|
48
|
+
function extractFirstToolNameFromText(text) {
|
|
49
|
+
if (!text) return null;
|
|
50
|
+
const lower = text.toLowerCase();
|
|
51
|
+
const mcpMatch = lower.match(MCP_FLOCKBAY_TOOL_RE);
|
|
52
|
+
if (mcpMatch && mcpMatch[0]) return mcpMatch[0];
|
|
53
|
+
const unrealMatch = lower.match(UNREAL_MCP_TOOL_RE);
|
|
54
|
+
if (unrealMatch && unrealMatch[0]) return `mcp__flockbay__${unrealMatch[0]}`;
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
46
57
|
function isInvestigationTool(toolCallId, toolKind) {
|
|
47
58
|
return toolCallId.includes("codebase_investigator") || toolCallId.includes("investigator") || typeof toolKind === "string" && toolKind.includes("investigator");
|
|
48
59
|
}
|
|
@@ -55,6 +66,18 @@ function extractToolNameFromId(toolCallId) {
|
|
|
55
66
|
}
|
|
56
67
|
}
|
|
57
68
|
}
|
|
69
|
+
const uuidLike = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(toolCallId);
|
|
70
|
+
if (!uuidLike) {
|
|
71
|
+
const prefix = toolCallId.split(/[-:]/, 1)[0] || "";
|
|
72
|
+
const trimmed = prefix.trim();
|
|
73
|
+
if (trimmed.length >= 2 && trimmed.length <= 160) {
|
|
74
|
+
const hasNonHexAlpha = /[g-z]/i.test(trimmed) || /[A-Z]/.test(trimmed) || /_/.test(trimmed);
|
|
75
|
+
const saneChars = /^[A-Za-z0-9_]+$/.test(trimmed);
|
|
76
|
+
if (saneChars && hasNonHexAlpha) {
|
|
77
|
+
return trimmed;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
58
81
|
return null;
|
|
59
82
|
}
|
|
60
83
|
function determineToolName(toolName, toolCallId, input, params, context) {
|
|
@@ -65,20 +88,30 @@ function determineToolName(toolName, toolCallId, input, params, context) {
|
|
|
65
88
|
if (idToolName) {
|
|
66
89
|
return idToolName;
|
|
67
90
|
}
|
|
91
|
+
const idTextToolName = extractFirstToolNameFromText(toolCallId);
|
|
92
|
+
if (idTextToolName) {
|
|
93
|
+
return idTextToolName;
|
|
94
|
+
}
|
|
68
95
|
if (input && typeof input === "object") {
|
|
69
|
-
const inputStr = JSON.stringify(input)
|
|
96
|
+
const inputStr = JSON.stringify(input);
|
|
97
|
+
const extracted = extractFirstToolNameFromText(inputStr);
|
|
98
|
+
if (extracted) return extracted;
|
|
99
|
+
const inputLower = inputStr.toLowerCase();
|
|
70
100
|
for (const [toolName2, patterns] of Object.entries(KNOWN_TOOL_PATTERNS)) {
|
|
71
101
|
for (const pattern of patterns) {
|
|
72
|
-
if (
|
|
102
|
+
if (inputLower.includes(pattern.toLowerCase())) {
|
|
73
103
|
return toolName2;
|
|
74
104
|
}
|
|
75
105
|
}
|
|
76
106
|
}
|
|
77
107
|
}
|
|
78
|
-
const paramsStr = JSON.stringify(params)
|
|
108
|
+
const paramsStr = JSON.stringify(params);
|
|
109
|
+
const paramsExtracted = extractFirstToolNameFromText(paramsStr);
|
|
110
|
+
if (paramsExtracted) return paramsExtracted;
|
|
111
|
+
const paramsLower = paramsStr.toLowerCase();
|
|
79
112
|
for (const [toolName2, patterns] of Object.entries(KNOWN_TOOL_PATTERNS)) {
|
|
80
113
|
for (const pattern of patterns) {
|
|
81
|
-
if (
|
|
114
|
+
if (paramsLower.includes(pattern.toLowerCase())) {
|
|
82
115
|
return toolName2;
|
|
83
116
|
}
|
|
84
117
|
}
|
|
@@ -315,7 +348,7 @@ class AcpSdkBackend {
|
|
|
315
348
|
this.emit({
|
|
316
349
|
type: "status",
|
|
317
350
|
status: "error",
|
|
318
|
-
detail: "Model not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite"
|
|
351
|
+
detail: "Model not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite, gemini-3-pro-preview, gemini-3-flash-preview"
|
|
319
352
|
});
|
|
320
353
|
} else if (/authentication required/i.test(text) || /login required/i.test(text) || /invalid.*(api key|key)/i.test(text) || /permission denied/i.test(text)) {
|
|
321
354
|
const excerpt = this.stderrExcerpt(20);
|
|
@@ -1818,7 +1851,7 @@ class GeminiPermissionHandler {
|
|
|
1818
1851
|
const decision = result.decision;
|
|
1819
1852
|
const reason = result.reason;
|
|
1820
1853
|
const kind = decision === "approved" || decision === "approved_for_session" ? "policy_allow" : decision === "abort" && reason === "permission_prompt_required" ? "policy_prompt" : "policy_block";
|
|
1821
|
-
const summary = kind === "policy_allow" ? "Allowed." : kind === "policy_prompt" ? "Waiting for permission to run this tool." : reason ? `Blocked: ${reason}` : "Blocked by
|
|
1854
|
+
const summary = kind === "policy_allow" ? "Allowed." : kind === "policy_prompt" ? "Waiting for permission to run this tool." : reason ? `Blocked: ${reason}` : "Blocked by Policy.";
|
|
1822
1855
|
const nextSteps = [];
|
|
1823
1856
|
if (kind === "policy_block" && typeof reason === "string") {
|
|
1824
1857
|
if (reason.includes("docs_index_read") || reason.includes("Documentation index")) nextSteps.push("Call `mcp__flockbay__docs_index_read`.");
|
|
@@ -2370,6 +2403,65 @@ async function runGemini(opts) {
|
|
|
2370
2403
|
seen: /* @__PURE__ */ new Set(),
|
|
2371
2404
|
inAutoReview: false
|
|
2372
2405
|
};
|
|
2406
|
+
const conversationTurns = [];
|
|
2407
|
+
const MAX_TURNS_TO_KEEP = 80;
|
|
2408
|
+
const pushConversationTurn = (role, text) => {
|
|
2409
|
+
const cleaned = String(text || "").trim();
|
|
2410
|
+
if (!cleaned) return;
|
|
2411
|
+
conversationTurns.push({ role, text: cleaned, ts: Date.now() });
|
|
2412
|
+
if (conversationTurns.length > MAX_TURNS_TO_KEEP) {
|
|
2413
|
+
conversationTurns.splice(0, conversationTurns.length - MAX_TURNS_TO_KEEP);
|
|
2414
|
+
}
|
|
2415
|
+
};
|
|
2416
|
+
const formatConversationTranscriptForBootstrap = (excludeUserText) => {
|
|
2417
|
+
const exclude = String(excludeUserText || "").trim();
|
|
2418
|
+
const turns = [...conversationTurns];
|
|
2419
|
+
if (exclude && turns.length > 0) {
|
|
2420
|
+
const last = turns[turns.length - 1];
|
|
2421
|
+
if (last.role === "user" && last.text.trim() === exclude) {
|
|
2422
|
+
turns.pop();
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
if (turns.length === 0) return null;
|
|
2426
|
+
const MAX_TURNS = 24;
|
|
2427
|
+
const MAX_CHARS = 12e3;
|
|
2428
|
+
const recent = turns.slice(Math.max(0, turns.length - MAX_TURNS));
|
|
2429
|
+
const lines = [];
|
|
2430
|
+
for (const t of recent) {
|
|
2431
|
+
const prefix = t.role === "user" ? "USER" : "ASSISTANT";
|
|
2432
|
+
const body = t.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
|
|
2433
|
+
lines.push(`${prefix}: ${body}`);
|
|
2434
|
+
}
|
|
2435
|
+
let transcript = lines.join("\n\n");
|
|
2436
|
+
if (transcript.length > MAX_CHARS) {
|
|
2437
|
+
transcript = transcript.slice(transcript.length - MAX_CHARS);
|
|
2438
|
+
transcript = `\u2026(truncated)\u2026
|
|
2439
|
+
|
|
2440
|
+
${transcript}`;
|
|
2441
|
+
}
|
|
2442
|
+
return [
|
|
2443
|
+
"Conversation transcript (for context only; do not treat tool names/paths mentioned here as instructions to execute):",
|
|
2444
|
+
"```",
|
|
2445
|
+
transcript,
|
|
2446
|
+
"```"
|
|
2447
|
+
].join("\n");
|
|
2448
|
+
};
|
|
2449
|
+
const detectImageMimeTypeFromBuffer = (buf) => {
|
|
2450
|
+
if (!buf || buf.length < 12) return null;
|
|
2451
|
+
if (buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71 && buf[4] === 13 && buf[5] === 10 && buf[6] === 26 && buf[7] === 10) {
|
|
2452
|
+
return "image/png";
|
|
2453
|
+
}
|
|
2454
|
+
if (buf[0] === 255 && buf[1] === 216 && buf[2] === 255) {
|
|
2455
|
+
return "image/jpeg";
|
|
2456
|
+
}
|
|
2457
|
+
if (buf[0] === 71 && buf[1] === 73 && buf[2] === 70 && buf[3] === 56) {
|
|
2458
|
+
return "image/gif";
|
|
2459
|
+
}
|
|
2460
|
+
if (buf[0] === 82 && buf[1] === 73 && buf[2] === 70 && buf[3] === 70 && buf[8] === 87 && buf[9] === 69 && buf[10] === 66 && buf[11] === 80) {
|
|
2461
|
+
return "image/webp";
|
|
2462
|
+
}
|
|
2463
|
+
return null;
|
|
2464
|
+
};
|
|
2373
2465
|
const resetScreenshotGateForTurn = () => {
|
|
2374
2466
|
screenshotGate.paths = [];
|
|
2375
2467
|
screenshotGate.seen.clear();
|
|
@@ -2413,8 +2505,9 @@ async function runGemini(opts) {
|
|
|
2413
2505
|
for (let i = 0; i < unique.length; i += 1) {
|
|
2414
2506
|
const p = unique[i];
|
|
2415
2507
|
const buf = await readFile(p);
|
|
2508
|
+
const mimeType = detectImageMimeTypeFromBuffer(buf) || "image/png";
|
|
2416
2509
|
blocks.push({ type: "text", text: `Image ${i + 1}: ${basename(p)}` });
|
|
2417
|
-
blocks.push({ type: "image", data: buf.toString("base64"), mimeType
|
|
2510
|
+
blocks.push({ type: "image", data: buf.toString("base64"), mimeType });
|
|
2418
2511
|
}
|
|
2419
2512
|
return blocks;
|
|
2420
2513
|
};
|
|
@@ -2456,29 +2549,15 @@ async function runGemini(opts) {
|
|
|
2456
2549
|
}
|
|
2457
2550
|
}
|
|
2458
2551
|
const originalUserMessage = message.content.text;
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
if (message.meta?.hasOwnProperty("appendSystemPrompt")) {
|
|
2463
|
-
const raw = message.meta.appendSystemPrompt;
|
|
2464
|
-
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
2465
|
-
preambleParts.push(raw);
|
|
2466
|
-
} else {
|
|
2467
|
-
preambleParts.push(PLATFORM_SYSTEM_PROMPT);
|
|
2468
|
-
}
|
|
2469
|
-
} else {
|
|
2470
|
-
preambleParts.push(PLATFORM_SYSTEM_PROMPT);
|
|
2471
|
-
}
|
|
2472
|
-
if (projectCapsule) preambleParts.push(projectCapsule);
|
|
2473
|
-
const preamble = preambleParts.map((p) => String(p || "").trim()).filter(Boolean).join("\n\n");
|
|
2474
|
-
fullPrompt = preamble.length > 0 ? preamble + "\n\n" + originalUserMessage : originalUserMessage;
|
|
2475
|
-
isFirstMessage = false;
|
|
2476
|
-
}
|
|
2552
|
+
pushConversationTurn("user", originalUserMessage);
|
|
2553
|
+
const appendSystemPrompt = message.meta?.hasOwnProperty("appendSystemPrompt") && typeof message.meta.appendSystemPrompt === "string" ? message.meta.appendSystemPrompt : null;
|
|
2554
|
+
const fullPrompt = originalUserMessage;
|
|
2477
2555
|
const mode = {
|
|
2478
2556
|
permissionMode: messagePermissionMode || "default",
|
|
2479
2557
|
model: messageModel,
|
|
2480
|
-
originalUserMessage
|
|
2558
|
+
originalUserMessage,
|
|
2481
2559
|
// Store original message separately
|
|
2560
|
+
appendSystemPrompt
|
|
2482
2561
|
};
|
|
2483
2562
|
const promptText = images.length > 0 ? withUserImagesMarker(fullPrompt, images.length) : fullPrompt;
|
|
2484
2563
|
messageQueue.push(promptText, mode, images.length > 0 ? { isolate: true } : void 0);
|
|
@@ -2488,19 +2567,14 @@ async function runGemini(opts) {
|
|
|
2488
2567
|
const keepAliveInterval = setInterval(() => {
|
|
2489
2568
|
session.keepAlive(thinking, "remote");
|
|
2490
2569
|
}, 2e3);
|
|
2491
|
-
let isFirstMessage = true;
|
|
2492
2570
|
const autoPrompt = String(process.env.FLOCKBAY_AUTO_PROMPT || "").trim();
|
|
2493
2571
|
const autoExitOnIdle = String(process.env.FLOCKBAY_AUTO_EXIT_ON_IDLE || "").trim() === "1";
|
|
2494
2572
|
if (autoPrompt) {
|
|
2495
|
-
|
|
2496
|
-
if (projectCapsule) preambleParts.push(projectCapsule);
|
|
2497
|
-
const preamble = preambleParts.map((p) => String(p || "").trim()).filter(Boolean).join("\n\n");
|
|
2498
|
-
const fullPrompt = preamble.length > 0 ? preamble + "\n\n" + autoPrompt : autoPrompt;
|
|
2499
|
-
isFirstMessage = false;
|
|
2500
|
-
messageQueue.push(fullPrompt, {
|
|
2573
|
+
messageQueue.push(autoPrompt, {
|
|
2501
2574
|
permissionMode: "default",
|
|
2502
2575
|
model: void 0,
|
|
2503
|
-
originalUserMessage: autoPrompt
|
|
2576
|
+
originalUserMessage: autoPrompt,
|
|
2577
|
+
appendSystemPrompt: null
|
|
2504
2578
|
});
|
|
2505
2579
|
}
|
|
2506
2580
|
const sendReady = () => {
|
|
@@ -2515,6 +2589,30 @@ async function runGemini(opts) {
|
|
|
2515
2589
|
logger.debug("[Gemini] Failed to send ready push", pushError);
|
|
2516
2590
|
}
|
|
2517
2591
|
};
|
|
2592
|
+
let pendingTurnCompletion = null;
|
|
2593
|
+
const beginTurnCompletionWait = () => {
|
|
2594
|
+
if (pendingTurnCompletion) {
|
|
2595
|
+
logger.debug("[Gemini] beginTurnCompletionWait called while a turn is already pending; replacing pending wait");
|
|
2596
|
+
try {
|
|
2597
|
+
pendingTurnCompletion.resolve("error");
|
|
2598
|
+
} catch {
|
|
2599
|
+
}
|
|
2600
|
+
pendingTurnCompletion = null;
|
|
2601
|
+
}
|
|
2602
|
+
let resolve2;
|
|
2603
|
+
const promise = new Promise((res) => {
|
|
2604
|
+
resolve2 = res;
|
|
2605
|
+
});
|
|
2606
|
+
pendingTurnCompletion = { sawRunning: false, resolve: resolve2, promise };
|
|
2607
|
+
return promise;
|
|
2608
|
+
};
|
|
2609
|
+
const maybeResolveTurnCompletion = (status) => {
|
|
2610
|
+
if (!pendingTurnCompletion) return;
|
|
2611
|
+
if (!pendingTurnCompletion.sawRunning) return;
|
|
2612
|
+
const pending = pendingTurnCompletion;
|
|
2613
|
+
pendingTurnCompletion = null;
|
|
2614
|
+
pending.resolve(status);
|
|
2615
|
+
};
|
|
2518
2616
|
const emitReadyIfIdle = () => {
|
|
2519
2617
|
if (shouldExit) {
|
|
2520
2618
|
return false;
|
|
@@ -2542,10 +2640,36 @@ async function runGemini(opts) {
|
|
|
2542
2640
|
let wasSessionCreated = false;
|
|
2543
2641
|
let abortNote = null;
|
|
2544
2642
|
let abortNoteSentToSession = false;
|
|
2643
|
+
let abortRequested = false;
|
|
2644
|
+
let readySentForAbort = false;
|
|
2645
|
+
function normalizeCancelNote(input) {
|
|
2646
|
+
if (typeof input === "string") return input.trim() ? input.trim() : null;
|
|
2647
|
+
if (!input || typeof input !== "object") return null;
|
|
2648
|
+
const note = typeof input.note === "string" ? String(input.note).trim() : "";
|
|
2649
|
+
if (note) return note;
|
|
2650
|
+
const reason = typeof input.reason === "string" ? String(input.reason).trim() : "";
|
|
2651
|
+
if (reason) return reason;
|
|
2652
|
+
return null;
|
|
2653
|
+
}
|
|
2654
|
+
function makeAbortError(message = "Aborted") {
|
|
2655
|
+
const error = new Error(message);
|
|
2656
|
+
error.name = "AbortError";
|
|
2657
|
+
return error;
|
|
2658
|
+
}
|
|
2659
|
+
function abortable(signal, promise) {
|
|
2660
|
+
if (signal.aborted) return Promise.reject(makeAbortError());
|
|
2661
|
+
return new Promise((resolve2, reject) => {
|
|
2662
|
+
const onAbort = () => reject(makeAbortError());
|
|
2663
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2664
|
+
promise.then(resolve2, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
2665
|
+
});
|
|
2666
|
+
}
|
|
2545
2667
|
async function handleAbort(note, options) {
|
|
2546
2668
|
logger.debug("[Gemini] Abort requested - stopping current task");
|
|
2547
|
-
|
|
2548
|
-
|
|
2669
|
+
abortRequested = true;
|
|
2670
|
+
const normalizedNote = normalizeCancelNote(note);
|
|
2671
|
+
if (normalizedNote) {
|
|
2672
|
+
abortNote = normalizedNote;
|
|
2549
2673
|
abortNoteSentToSession = Boolean(options?.alreadySentToSession);
|
|
2550
2674
|
}
|
|
2551
2675
|
session.sendCodexMessage({
|
|
@@ -2556,6 +2680,13 @@ async function runGemini(opts) {
|
|
|
2556
2680
|
diffProcessor.reset();
|
|
2557
2681
|
try {
|
|
2558
2682
|
abortController.abort();
|
|
2683
|
+
if (pendingTurnCompletion) {
|
|
2684
|
+
try {
|
|
2685
|
+
pendingTurnCompletion.resolve("error");
|
|
2686
|
+
} catch {
|
|
2687
|
+
}
|
|
2688
|
+
pendingTurnCompletion = null;
|
|
2689
|
+
}
|
|
2559
2690
|
messageQueue.reset();
|
|
2560
2691
|
if (geminiBackend && acpSessionId) {
|
|
2561
2692
|
await geminiBackend.cancel(acpSessionId);
|
|
@@ -2694,6 +2825,7 @@ async function runGemini(opts) {
|
|
|
2694
2825
|
};
|
|
2695
2826
|
let accumulatedResponse = "";
|
|
2696
2827
|
let isResponseInProgress = false;
|
|
2828
|
+
let currentResponseMessageId = null;
|
|
2697
2829
|
function setupGeminiMessageHandler(backend) {
|
|
2698
2830
|
backend.onMessage((msg) => {
|
|
2699
2831
|
switch (msg.type) {
|
|
@@ -2723,6 +2855,9 @@ async function runGemini(opts) {
|
|
|
2723
2855
|
if (msg.status === "running") {
|
|
2724
2856
|
thinking = true;
|
|
2725
2857
|
session.keepAlive(thinking, "remote");
|
|
2858
|
+
if (pendingTurnCompletion) {
|
|
2859
|
+
pendingTurnCompletion.sawRunning = true;
|
|
2860
|
+
}
|
|
2726
2861
|
session.sendCodexMessage({
|
|
2727
2862
|
type: "task_started",
|
|
2728
2863
|
id: randomUUID()
|
|
@@ -2763,14 +2898,18 @@ async function runGemini(opts) {
|
|
|
2763
2898
|
logger.debug(`[gemini] Sending complete message to mobile (length: ${finalMessageText.length}): ${finalMessageText.substring(0, 100)}...`);
|
|
2764
2899
|
logger.debug(`[gemini] Full message payload:`, JSON.stringify(messagePayload, null, 2));
|
|
2765
2900
|
session.sendCodexMessage(messagePayload);
|
|
2901
|
+
pushConversationTurn("assistant", finalMessageText);
|
|
2766
2902
|
accumulatedResponse = "";
|
|
2767
2903
|
isResponseInProgress = false;
|
|
2768
2904
|
}
|
|
2905
|
+
maybeResolveTurnCompletion(msg.status);
|
|
2769
2906
|
} else if (msg.status === "error") {
|
|
2770
2907
|
thinking = false;
|
|
2771
2908
|
session.keepAlive(thinking, "remote");
|
|
2772
2909
|
accumulatedResponse = "";
|
|
2773
2910
|
isResponseInProgress = false;
|
|
2911
|
+
currentResponseMessageId = null;
|
|
2912
|
+
maybeResolveTurnCompletion("error");
|
|
2774
2913
|
const errorMessage = stringifyErrorish(msg.detail || "Unknown error");
|
|
2775
2914
|
messageBuffer.addMessage(`Error: ${errorMessage}`, "status");
|
|
2776
2915
|
session.sendCodexMessage({
|
|
@@ -2967,6 +3106,7 @@ async function runGemini(opts) {
|
|
|
2967
3106
|
if (!message) {
|
|
2968
3107
|
break;
|
|
2969
3108
|
}
|
|
3109
|
+
let startedSessionThisTurn = false;
|
|
2970
3110
|
if (wasSessionCreated && currentModeHash && message.hash !== currentModeHash) {
|
|
2971
3111
|
logger.debug("[Gemini] Mode changed \u2013 restarting Gemini session");
|
|
2972
3112
|
messageBuffer.addMessage("\u2550".repeat(40), "status");
|
|
@@ -2993,8 +3133,9 @@ async function runGemini(opts) {
|
|
|
2993
3133
|
const actualModel = determineGeminiModel(modelToUse, localConfigForModel);
|
|
2994
3134
|
logger.debug(`[gemini] Model change - modelToUse=${modelToUse}, actualModel=${actualModel}`);
|
|
2995
3135
|
logger.debug("[gemini] Starting new ACP session with model:", actualModel);
|
|
2996
|
-
const { sessionId } = await geminiBackend.startSession();
|
|
3136
|
+
const { sessionId } = await abortable(abortController.signal, geminiBackend.startSession());
|
|
2997
3137
|
acpSessionId = sessionId;
|
|
3138
|
+
startedSessionThisTurn = true;
|
|
2998
3139
|
logger.debug(`[gemini] New ACP session started: ${acpSessionId}`);
|
|
2999
3140
|
logger.debug(`[gemini] Calling updateDisplayedModel with: ${actualModel}`);
|
|
3000
3141
|
updateDisplayedModel(actualModel, false);
|
|
@@ -3009,6 +3150,15 @@ async function runGemini(opts) {
|
|
|
3009
3150
|
let retryThisTurn = false;
|
|
3010
3151
|
let skipAutoFinalize = false;
|
|
3011
3152
|
try {
|
|
3153
|
+
const turnSignal = abortController.signal;
|
|
3154
|
+
const sendPromptAndWait = async (prompt) => {
|
|
3155
|
+
if (!geminiBackend || !acpSessionId) {
|
|
3156
|
+
throw new Error("Gemini backend or session not initialized");
|
|
3157
|
+
}
|
|
3158
|
+
const completion = beginTurnCompletionWait();
|
|
3159
|
+
await abortable(turnSignal, geminiBackend.sendPrompt(acpSessionId, prompt));
|
|
3160
|
+
return await abortable(turnSignal, completion);
|
|
3161
|
+
};
|
|
3012
3162
|
if (first || !wasSessionCreated) {
|
|
3013
3163
|
if (!geminiBackend) {
|
|
3014
3164
|
await refreshGeminiCloudToken("before-backend-create");
|
|
@@ -3033,8 +3183,9 @@ async function runGemini(opts) {
|
|
|
3033
3183
|
if (!acpSessionId) {
|
|
3034
3184
|
logger.debug("[gemini] Starting ACP session...");
|
|
3035
3185
|
updatePermissionMode(message.mode.permissionMode);
|
|
3036
|
-
const { sessionId } = await geminiBackend.startSession();
|
|
3186
|
+
const { sessionId } = await abortable(turnSignal, geminiBackend.startSession());
|
|
3037
3187
|
acpSessionId = sessionId;
|
|
3188
|
+
startedSessionThisTurn = true;
|
|
3038
3189
|
logger.debug(`[gemini] ACP session started: ${acpSessionId}`);
|
|
3039
3190
|
wasSessionCreated = true;
|
|
3040
3191
|
currentModeHash = message.hash;
|
|
@@ -3052,26 +3203,48 @@ async function runGemini(opts) {
|
|
|
3052
3203
|
}
|
|
3053
3204
|
const promptToSend = message.message;
|
|
3054
3205
|
const parsedPrompt = extractUserImagesMarker(promptToSend);
|
|
3055
|
-
const promptText = parsedPrompt.text;
|
|
3056
3206
|
const promptImages = parsedPrompt.hasImages ? getLatestUserImages().slice(0, parsedPrompt.count) : [];
|
|
3207
|
+
const originalUserMessage = (message.mode?.originalUserMessage || parsedPrompt.text || "").trim();
|
|
3208
|
+
let promptText = parsedPrompt.text;
|
|
3209
|
+
if (startedSessionThisTurn) {
|
|
3210
|
+
const override = typeof message.mode?.appendSystemPrompt === "string" ? message.mode.appendSystemPrompt.trim() : "";
|
|
3211
|
+
const systemPrompt = override.length > 0 ? override : PLATFORM_SYSTEM_PROMPT;
|
|
3212
|
+
const preambleParts = [systemPrompt];
|
|
3213
|
+
if (projectCapsule) preambleParts.push(projectCapsule);
|
|
3214
|
+
const transcript = formatConversationTranscriptForBootstrap(originalUserMessage);
|
|
3215
|
+
if (transcript) preambleParts.push(transcript);
|
|
3216
|
+
const preamble = preambleParts.map((p) => String(p || "").trim()).filter(Boolean).join("\n\n");
|
|
3217
|
+
promptText = preamble.length > 0 ? `${preamble}
|
|
3218
|
+
|
|
3219
|
+
${originalUserMessage}` : originalUserMessage;
|
|
3220
|
+
}
|
|
3057
3221
|
if (promptImages.length > 0) {
|
|
3058
3222
|
const blocks = [
|
|
3059
3223
|
{ type: "text", text: promptText },
|
|
3060
3224
|
...promptImages.map((img) => ({ type: "image", data: img.base64, mimeType: img.mimeType }))
|
|
3061
3225
|
];
|
|
3062
3226
|
logger.debug(`[gemini] Sending multimodal prompt blocks (textLength=${promptText.length}, images=${promptImages.length})`);
|
|
3063
|
-
await
|
|
3227
|
+
const status = await sendPromptAndWait(blocks);
|
|
3228
|
+
if (status !== "idle") {
|
|
3229
|
+
skipAutoFinalize = true;
|
|
3230
|
+
}
|
|
3064
3231
|
} else {
|
|
3065
3232
|
logger.debug(`[gemini] Sending prompt to Gemini (length: ${promptText.length}): ${promptText.substring(0, 100)}...`);
|
|
3066
3233
|
logger.debug(`[gemini] Full prompt: ${promptText}`);
|
|
3067
|
-
await
|
|
3234
|
+
const status = await sendPromptAndWait(promptText);
|
|
3235
|
+
if (status !== "idle") {
|
|
3236
|
+
skipAutoFinalize = true;
|
|
3237
|
+
}
|
|
3068
3238
|
}
|
|
3069
3239
|
logger.debug("[gemini] Prompt sent successfully");
|
|
3070
|
-
if (!screenshotGate.inAutoReview && screenshotGate.paths.length > 0) {
|
|
3240
|
+
if (!skipAutoFinalize && !screenshotGate.inAutoReview && screenshotGate.paths.length > 0) {
|
|
3071
3241
|
screenshotGate.inAutoReview = true;
|
|
3072
3242
|
messageBuffer.addMessage("Auto-reviewing screenshots\u2026", "status");
|
|
3073
3243
|
const blocks = await buildScreenshotReviewBlocks(screenshotGate.paths);
|
|
3074
|
-
await
|
|
3244
|
+
const status = await sendPromptAndWait(blocks);
|
|
3245
|
+
if (status !== "idle") {
|
|
3246
|
+
skipAutoFinalize = true;
|
|
3247
|
+
}
|
|
3075
3248
|
screenshotGate.inAutoReview = false;
|
|
3076
3249
|
}
|
|
3077
3250
|
if (first) {
|
|
@@ -3080,14 +3253,21 @@ async function runGemini(opts) {
|
|
|
3080
3253
|
} catch (error) {
|
|
3081
3254
|
logger.debug("[gemini] Error in gemini session:", error);
|
|
3082
3255
|
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
3083
|
-
|
|
3256
|
+
const treatAsAbort = abortRequested || isAbortError;
|
|
3257
|
+
if (treatAsAbort) {
|
|
3084
3258
|
skipAutoFinalize = true;
|
|
3085
|
-
|
|
3259
|
+
readySentForAbort = true;
|
|
3260
|
+
const note = abortNote || "Canceled by user";
|
|
3086
3261
|
abortNote = null;
|
|
3087
3262
|
const alreadySent = abortNoteSentToSession;
|
|
3088
3263
|
abortNoteSentToSession = false;
|
|
3264
|
+
abortRequested = false;
|
|
3265
|
+
accumulatedResponse = "";
|
|
3266
|
+
isResponseInProgress = false;
|
|
3267
|
+
currentResponseMessageId = null;
|
|
3089
3268
|
messageBuffer.addMessage(note, "status");
|
|
3090
3269
|
if (!alreadySent) session.sendSessionEvent({ type: "message", message: note });
|
|
3270
|
+
session.sendSessionEvent({ type: "ready" });
|
|
3091
3271
|
} else {
|
|
3092
3272
|
const rawErrorString = error instanceof Error ? error.message : String(error);
|
|
3093
3273
|
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);
|
|
@@ -3119,7 +3299,7 @@ async function runGemini(opts) {
|
|
|
3119
3299
|
const errorString = String(error);
|
|
3120
3300
|
if (errorCode === 404 || errorDetails.includes("notFound") || errorDetails.includes("404") || errorMessage.includes("not found") || errorMessage.includes("404")) {
|
|
3121
3301
|
const currentModel2 = displayedModel || "gemini-2.5-pro";
|
|
3122
|
-
errorMsg = `Model "${currentModel2}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite`;
|
|
3302
|
+
errorMsg = `Model "${currentModel2}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite, gemini-3-pro-preview, gemini-3-flash-preview`;
|
|
3123
3303
|
} else if (errorCode === 429 || errorDetails.includes("429") || errorMessage.includes("429") || errorString.includes("429") || errorDetails.includes("rateLimitExceeded") || errorDetails.includes("RESOURCE_EXHAUSTED") || errorMessage.includes("Rate limit exceeded") || errorMessage.includes("Resource exhausted") || errorString.includes("rateLimitExceeded") || errorString.includes("RESOURCE_EXHAUSTED")) {
|
|
3124
3304
|
errorMsg = "Gemini API rate limit exceeded. Please wait a moment and try again. The API will retry automatically.";
|
|
3125
3305
|
} else if (errorDetails.includes("quota") || errorMessage.includes("quota") || errorString.includes("quota")) {
|
|
@@ -3143,6 +3323,7 @@ async function runGemini(opts) {
|
|
|
3143
3323
|
permissionHandler.reset();
|
|
3144
3324
|
reasoningProcessor.abort();
|
|
3145
3325
|
diffProcessor.reset();
|
|
3326
|
+
abortRequested = false;
|
|
3146
3327
|
thinking = false;
|
|
3147
3328
|
session.keepAlive(thinking, "remote");
|
|
3148
3329
|
if (!retryThisTurn) {
|
|
@@ -3172,7 +3353,11 @@ async function runGemini(opts) {
|
|
|
3172
3353
|
});
|
|
3173
3354
|
}
|
|
3174
3355
|
}
|
|
3175
|
-
|
|
3356
|
+
if (readySentForAbort) {
|
|
3357
|
+
readySentForAbort = false;
|
|
3358
|
+
} else {
|
|
3359
|
+
emitReadyIfIdle();
|
|
3360
|
+
}
|
|
3176
3361
|
}
|
|
3177
3362
|
logger.debug(`[gemini] Main loop: turn completed, continuing to next iteration (queue size: ${messageQueue.size()})`);
|
|
3178
3363
|
}
|
|
@@ -11,7 +11,7 @@ import { readFile, unlink, open, stat, mkdir, writeFile, rename } from 'node:fs/
|
|
|
11
11
|
import * as z from 'zod';
|
|
12
12
|
import { z as z$1 } from 'zod';
|
|
13
13
|
import { spawn } from 'child_process';
|
|
14
|
-
import {
|
|
14
|
+
import { readdir, mkdir as mkdir$1, realpath, stat as stat$1, rm, readFile as readFile$1, open as open$1, writeFile as writeFile$1, chmod } from 'fs/promises';
|
|
15
15
|
import { randomUUID, createHash } from 'crypto';
|
|
16
16
|
import { dirname, resolve, join as join$1, relative } from 'path';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
@@ -21,7 +21,7 @@ import net from 'node:net';
|
|
|
21
21
|
import { spawn as spawn$1 } from 'node:child_process';
|
|
22
22
|
|
|
23
23
|
var name = "flockbay";
|
|
24
|
-
var version = "0.10.
|
|
24
|
+
var version = "0.10.22";
|
|
25
25
|
var description = "Flockbay CLI (local agent + daemon)";
|
|
26
26
|
var author = "Eduardo Orellana";
|
|
27
27
|
var license = "UNLICENSED";
|
|
@@ -1570,6 +1570,44 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, coordinatio
|
|
|
1570
1570
|
};
|
|
1571
1571
|
}
|
|
1572
1572
|
});
|
|
1573
|
+
rpcHandlerManager.registerHandler("fs_list_dir", async (data) => {
|
|
1574
|
+
const p = String(data?.path || "").trim();
|
|
1575
|
+
if (!p) return { success: false, error: "missing_path" };
|
|
1576
|
+
try {
|
|
1577
|
+
const out = [];
|
|
1578
|
+
const items = await readdir(p, { withFileTypes: true });
|
|
1579
|
+
for (const d of items) {
|
|
1580
|
+
out.push({
|
|
1581
|
+
name: d.name,
|
|
1582
|
+
isDir: d.isDirectory(),
|
|
1583
|
+
isFile: d.isFile(),
|
|
1584
|
+
isSymlink: d.isSymbolicLink()
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
out.sort((a, b) => {
|
|
1588
|
+
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
1589
|
+
return a.name.localeCompare(b.name);
|
|
1590
|
+
});
|
|
1591
|
+
return { success: true, path: p, entries: out };
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
const e = error;
|
|
1594
|
+
const msg = e?.message ? String(e.message) : String(error);
|
|
1595
|
+
return { success: false, path: p, error: msg };
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1598
|
+
rpcHandlerManager.registerHandler("fs_mkdir", async (data) => {
|
|
1599
|
+
const p = String(data?.path || "").trim();
|
|
1600
|
+
if (!p) return { success: false, error: "missing_path" };
|
|
1601
|
+
const recursive = data?.recursive !== false;
|
|
1602
|
+
try {
|
|
1603
|
+
await mkdir$1(p, { recursive });
|
|
1604
|
+
return { success: true, path: p };
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
const e = error;
|
|
1607
|
+
const msg = e?.message ? String(e.message) : String(error);
|
|
1608
|
+
return { success: false, path: p, error: msg };
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1573
1611
|
rpcHandlerManager.registerHandler(
|
|
1574
1612
|
"unreal-mcp-bridge-status",
|
|
1575
1613
|
async (params) => {
|
|
@@ -3774,4 +3812,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
|
|
|
3774
3812
|
}).passthrough()
|
|
3775
3813
|
]);
|
|
3776
3814
|
|
|
3777
|
-
export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, packageJson as b, configuration as c, backoff as d, delay as e, readDaemonState as f, clearDaemonState as g, readCredentials as h, unrealMcpPythonDir as i, acquireDaemonLock as j, writeDaemonState as k, logger as l, ApiMachineClient as m, releaseDaemonLock as n, clearCredentials as o, projectPath as p, clearMachineId as q, readSettings as r, sendUnrealMcpTcpCommand as s, installUnrealMcpPluginToEngine as t, updateSettings as u,
|
|
3815
|
+
export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, packageJson as b, configuration as c, backoff as d, delay as e, readDaemonState as f, clearDaemonState as g, readCredentials as h, unrealMcpPythonDir as i, acquireDaemonLock as j, writeDaemonState as k, logger as l, ApiMachineClient as m, releaseDaemonLock as n, clearCredentials as o, projectPath as p, clearMachineId as q, readSettings as r, sendUnrealMcpTcpCommand as s, installUnrealMcpPluginToEngine as t, updateSettings as u, buildAndInstallUnrealMcpPlugin as v, writeCredentials as w, getLatestDaemonLog as x, normalizeServerUrlForNode as y };
|
|
@@ -42,7 +42,7 @@ function _interopNamespaceDefault(e) {
|
|
|
42
42
|
var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
|
|
43
43
|
|
|
44
44
|
var name = "flockbay";
|
|
45
|
-
var version = "0.10.
|
|
45
|
+
var version = "0.10.22";
|
|
46
46
|
var description = "Flockbay CLI (local agent + daemon)";
|
|
47
47
|
var author = "Eduardo Orellana";
|
|
48
48
|
var license = "UNLICENSED";
|
|
@@ -770,7 +770,7 @@ class RpcHandlerManager {
|
|
|
770
770
|
}
|
|
771
771
|
}
|
|
772
772
|
|
|
773
|
-
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-
|
|
773
|
+
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-Z2OYpI8c.cjs', document.baseURI).href))));
|
|
774
774
|
function projectPath() {
|
|
775
775
|
const path = path$1.resolve(__dirname$1, "..");
|
|
776
776
|
return path;
|
|
@@ -1591,6 +1591,44 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, coordinatio
|
|
|
1591
1591
|
};
|
|
1592
1592
|
}
|
|
1593
1593
|
});
|
|
1594
|
+
rpcHandlerManager.registerHandler("fs_list_dir", async (data) => {
|
|
1595
|
+
const p = String(data?.path || "").trim();
|
|
1596
|
+
if (!p) return { success: false, error: "missing_path" };
|
|
1597
|
+
try {
|
|
1598
|
+
const out = [];
|
|
1599
|
+
const items = await fs$3.readdir(p, { withFileTypes: true });
|
|
1600
|
+
for (const d of items) {
|
|
1601
|
+
out.push({
|
|
1602
|
+
name: d.name,
|
|
1603
|
+
isDir: d.isDirectory(),
|
|
1604
|
+
isFile: d.isFile(),
|
|
1605
|
+
isSymlink: d.isSymbolicLink()
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
out.sort((a, b) => {
|
|
1609
|
+
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
1610
|
+
return a.name.localeCompare(b.name);
|
|
1611
|
+
});
|
|
1612
|
+
return { success: true, path: p, entries: out };
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
const e = error;
|
|
1615
|
+
const msg = e?.message ? String(e.message) : String(error);
|
|
1616
|
+
return { success: false, path: p, error: msg };
|
|
1617
|
+
}
|
|
1618
|
+
});
|
|
1619
|
+
rpcHandlerManager.registerHandler("fs_mkdir", async (data) => {
|
|
1620
|
+
const p = String(data?.path || "").trim();
|
|
1621
|
+
if (!p) return { success: false, error: "missing_path" };
|
|
1622
|
+
const recursive = data?.recursive !== false;
|
|
1623
|
+
try {
|
|
1624
|
+
await fs$3.mkdir(p, { recursive });
|
|
1625
|
+
return { success: true, path: p };
|
|
1626
|
+
} catch (error) {
|
|
1627
|
+
const e = error;
|
|
1628
|
+
const msg = e?.message ? String(e.message) : String(error);
|
|
1629
|
+
return { success: false, path: p, error: msg };
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1594
1632
|
rpcHandlerManager.registerHandler(
|
|
1595
1633
|
"unreal-mcp-bridge-status",
|
|
1596
1634
|
async (params) => {
|
|
@@ -3801,6 +3839,7 @@ exports.ApiSessionClient = ApiSessionClient;
|
|
|
3801
3839
|
exports.RawJSONLinesSchema = RawJSONLinesSchema;
|
|
3802
3840
|
exports.acquireDaemonLock = acquireDaemonLock;
|
|
3803
3841
|
exports.backoff = backoff;
|
|
3842
|
+
exports.buildAndInstallUnrealMcpPlugin = buildAndInstallUnrealMcpPlugin;
|
|
3804
3843
|
exports.clearCredentials = clearCredentials;
|
|
3805
3844
|
exports.clearDaemonState = clearDaemonState;
|
|
3806
3845
|
exports.clearMachineId = clearMachineId;
|