flockbay 0.10.21 → 0.10.23
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-Bhkn02hu.cjs → index-BtB1Sqpy.cjs} +353 -17
- package/dist/{index-By332wvJ.mjs → index-CRGcIpET.mjs} +354 -17
- 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-d2KQX2mn.mjs → runCodex-BC3IImzm.mjs} +11 -28
- package/dist/{runCodex-CH4lz1QX.cjs → runCodex-ozeIEfAo.cjs} +11 -28
- package/dist/{runGemini-DNSymY04.cjs → runGemini-B-sAKY5u.cjs} +135 -20
- package/dist/{runGemini-Cn0C7MS1.mjs → runGemini-C43IKGUU.mjs} +135 -20
- package/dist/{types-mXJc7o0P.mjs → types-CNn15BaT.mjs} +47 -6
- package/dist/{types-DeH24uWs.cjs → types-DvlwEGpS.cjs} +47 -5
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var ink = require('ink');
|
|
4
4
|
var React = require('react');
|
|
5
|
-
var types = require('./types-
|
|
5
|
+
var types = require('./types-DvlwEGpS.cjs');
|
|
6
6
|
var index_js = require('@modelcontextprotocol/sdk/client/index.js');
|
|
7
7
|
var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
8
8
|
var z = require('zod');
|
|
@@ -12,7 +12,7 @@ var fs = require('node:fs');
|
|
|
12
12
|
var os = require('node:os');
|
|
13
13
|
var path = require('node:path');
|
|
14
14
|
var node_child_process = require('node:child_process');
|
|
15
|
-
var index = require('./index-
|
|
15
|
+
var index = require('./index-BtB1Sqpy.cjs');
|
|
16
16
|
require('axios');
|
|
17
17
|
require('node:events');
|
|
18
18
|
require('socket.io-client');
|
|
@@ -252,18 +252,18 @@ function buildMcpElicitationResult(decision, requestedSchemaRaw, options) {
|
|
|
252
252
|
if (!raw) return "";
|
|
253
253
|
if (/^Blocked\b/.test(raw)) return raw;
|
|
254
254
|
if (raw === "ledger_read_required") {
|
|
255
|
-
return "Blocked by
|
|
255
|
+
return "Blocked by Policy (automatic; not the user): read the ledger before making file edits. Next: call mcp__flockbay__ledger_read, then retry the edit.";
|
|
256
256
|
}
|
|
257
257
|
if (raw === "docs_index_read_required") {
|
|
258
|
-
return "Blocked by
|
|
258
|
+
return "Blocked by Policy (automatic; not the user): read the game Documentation index before making edits. Next: call mcp__flockbay__docs_index_read, then retry the edit.";
|
|
259
259
|
}
|
|
260
260
|
if (raw.startsWith("file_claim_required:")) {
|
|
261
261
|
const withoutPrefix = raw.slice("file_claim_required:".length);
|
|
262
262
|
const file = withoutPrefix.split("(")[0]?.trim() || "the file";
|
|
263
|
-
return `Blocked by
|
|
263
|
+
return `Blocked by Policy (automatic; not the user): claim ${file} before editing it. Next: claim the file via mcp__flockbay__ledger_claim (or mcp__flockbay__coordination_claim_files), then retry the edit.`;
|
|
264
264
|
}
|
|
265
265
|
if (raw === "read_only_mode") {
|
|
266
|
-
return "Blocked by
|
|
266
|
+
return "Blocked by Policy (automatic; not the user): this session is in read-only mode. Next: switch permission mode to allow edits, then retry.";
|
|
267
267
|
}
|
|
268
268
|
return `Blocked: ${raw}`;
|
|
269
269
|
};
|
|
@@ -1742,7 +1742,7 @@ function buildPolicyHint(reason, decision, gate) {
|
|
|
1742
1742
|
};
|
|
1743
1743
|
}
|
|
1744
1744
|
return {
|
|
1745
|
-
summary: "Blocked by
|
|
1745
|
+
summary: "Blocked by Policy.",
|
|
1746
1746
|
nextSteps: []
|
|
1747
1747
|
};
|
|
1748
1748
|
}
|
|
@@ -1754,7 +1754,7 @@ function buildPolicyHint(reason, decision, gate) {
|
|
|
1754
1754
|
}
|
|
1755
1755
|
if (raw === "ledger_read_required") {
|
|
1756
1756
|
return {
|
|
1757
|
-
summary: "Blocked by
|
|
1757
|
+
summary: "Blocked by Policy (automatic; not the user): read the ledger before making file edits.",
|
|
1758
1758
|
nextSteps: [
|
|
1759
1759
|
"Call `mcp__flockbay__ledger_read` (or `mcp__flockbay__coordination_ledger_snapshot`).",
|
|
1760
1760
|
"Then retry the file edit."
|
|
@@ -1763,7 +1763,7 @@ function buildPolicyHint(reason, decision, gate) {
|
|
|
1763
1763
|
}
|
|
1764
1764
|
if (raw === "docs_index_read_required") {
|
|
1765
1765
|
return {
|
|
1766
|
-
summary: "Blocked by
|
|
1766
|
+
summary: "Blocked by Policy (automatic; not the user): read the game Documentation index before making edits.",
|
|
1767
1767
|
nextSteps: [
|
|
1768
1768
|
"Call `mcp__flockbay__docs_index_read`.",
|
|
1769
1769
|
"Then retry the edit."
|
|
@@ -1774,7 +1774,7 @@ function buildPolicyHint(reason, decision, gate) {
|
|
|
1774
1774
|
const withoutPrefix = raw.slice("file_claim_required:".length);
|
|
1775
1775
|
const file = withoutPrefix.split("(")[0]?.trim() || "the file";
|
|
1776
1776
|
return {
|
|
1777
|
-
summary: `Blocked by
|
|
1777
|
+
summary: `Blocked by Policy (automatic; not the user): claim \`${file}\` before editing it.`,
|
|
1778
1778
|
nextSteps: [
|
|
1779
1779
|
`Claim the file via \`mcp__flockbay__ledger_claim\` or \`mcp__flockbay__coordination_claim_files\` (files: ["${file}"]).`,
|
|
1780
1780
|
"Then retry the file edit."
|
|
@@ -1783,7 +1783,7 @@ function buildPolicyHint(reason, decision, gate) {
|
|
|
1783
1783
|
}
|
|
1784
1784
|
if (raw === "read_only_mode") {
|
|
1785
1785
|
return {
|
|
1786
|
-
summary: "Blocked by
|
|
1786
|
+
summary: "Blocked by Policy (automatic; not the user): this session is in read-only mode.",
|
|
1787
1787
|
nextSteps: [
|
|
1788
1788
|
"Switch permission mode to allow edits (disable read-only).",
|
|
1789
1789
|
"Then retry the action."
|
|
@@ -2383,7 +2383,6 @@ async function runCodex(opts) {
|
|
|
2383
2383
|
appendSystemPrompt: index.PLATFORM_SYSTEM_PROMPT
|
|
2384
2384
|
});
|
|
2385
2385
|
}
|
|
2386
|
-
const bypassUeGates = String(process.env.FLOCKBAY_DEV_BYPASS_UE_GATES || "") === "1";
|
|
2387
2386
|
let currentPermissionMode = void 0;
|
|
2388
2387
|
let currentModel = void 0;
|
|
2389
2388
|
const defaultAppendSystemPrompt = index.PLATFORM_SYSTEM_PROMPT;
|
|
@@ -3451,22 +3450,6 @@ Error: ${message}`,
|
|
|
3451
3450
|
try {
|
|
3452
3451
|
resetScreenshotGateForTurn();
|
|
3453
3452
|
const overrides = { approvalPolicy: "untrusted", sandbox: "workspace-write" };
|
|
3454
|
-
if (!bypassUeGates) {
|
|
3455
|
-
const detection = await index.detectUnrealProject(process.cwd());
|
|
3456
|
-
if (!detection.ok) {
|
|
3457
|
-
session.sendCodexMessage({
|
|
3458
|
-
type: "message",
|
|
3459
|
-
message: "Select an Unreal project folder (the folder containing a *.uproject) to continue.",
|
|
3460
|
-
id: node_crypto.randomUUID()
|
|
3461
|
-
});
|
|
3462
|
-
if (wasCreated) {
|
|
3463
|
-
client.clearSession();
|
|
3464
|
-
wasCreated = false;
|
|
3465
|
-
currentModeHash = null;
|
|
3466
|
-
}
|
|
3467
|
-
continue;
|
|
3468
|
-
}
|
|
3469
|
-
}
|
|
3470
3453
|
if (!wasCreated) {
|
|
3471
3454
|
const projectCapsule = await index.buildProjectCapsule({ startDir: process.cwd() });
|
|
3472
3455
|
const modelOverride = resolveCodexModelOverride(message.mode.model);
|
|
@@ -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-
|
|
10
|
-
var index = require('./index-
|
|
9
|
+
var types = require('./types-DvlwEGpS.cjs');
|
|
10
|
+
var index = require('./index-BtB1Sqpy.cjs');
|
|
11
11
|
var node_child_process = require('node:child_process');
|
|
12
12
|
var sdk = require('@agentclientprotocol/sdk');
|
|
13
13
|
var fs = require('fs');
|
|
@@ -45,6 +45,17 @@ const KNOWN_TOOL_PATTERNS = {
|
|
|
45
45
|
save_memory: ["save_memory", "save-memory"],
|
|
46
46
|
think: ["think"]
|
|
47
47
|
};
|
|
48
|
+
const MCP_FLOCKBAY_TOOL_RE = /\bmcp__flockbay__[a-z0-9_]+\b/gi;
|
|
49
|
+
const UNREAL_MCP_TOOL_RE = /\bunreal_mcp_[a-z0-9_]+\b/gi;
|
|
50
|
+
function extractFirstToolNameFromText(text) {
|
|
51
|
+
if (!text) return null;
|
|
52
|
+
const lower = text.toLowerCase();
|
|
53
|
+
const mcpMatch = lower.match(MCP_FLOCKBAY_TOOL_RE);
|
|
54
|
+
if (mcpMatch && mcpMatch[0]) return mcpMatch[0];
|
|
55
|
+
const unrealMatch = lower.match(UNREAL_MCP_TOOL_RE);
|
|
56
|
+
if (unrealMatch && unrealMatch[0]) return `mcp__flockbay__${unrealMatch[0]}`;
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
48
59
|
function isInvestigationTool(toolCallId, toolKind) {
|
|
49
60
|
return toolCallId.includes("codebase_investigator") || toolCallId.includes("investigator") || typeof toolKind === "string" && toolKind.includes("investigator");
|
|
50
61
|
}
|
|
@@ -79,20 +90,30 @@ function determineToolName(toolName, toolCallId, input, params, context) {
|
|
|
79
90
|
if (idToolName) {
|
|
80
91
|
return idToolName;
|
|
81
92
|
}
|
|
93
|
+
const idTextToolName = extractFirstToolNameFromText(toolCallId);
|
|
94
|
+
if (idTextToolName) {
|
|
95
|
+
return idTextToolName;
|
|
96
|
+
}
|
|
82
97
|
if (input && typeof input === "object") {
|
|
83
|
-
const inputStr = JSON.stringify(input)
|
|
98
|
+
const inputStr = JSON.stringify(input);
|
|
99
|
+
const extracted = extractFirstToolNameFromText(inputStr);
|
|
100
|
+
if (extracted) return extracted;
|
|
101
|
+
const inputLower = inputStr.toLowerCase();
|
|
84
102
|
for (const [toolName2, patterns] of Object.entries(KNOWN_TOOL_PATTERNS)) {
|
|
85
103
|
for (const pattern of patterns) {
|
|
86
|
-
if (
|
|
104
|
+
if (inputLower.includes(pattern.toLowerCase())) {
|
|
87
105
|
return toolName2;
|
|
88
106
|
}
|
|
89
107
|
}
|
|
90
108
|
}
|
|
91
109
|
}
|
|
92
|
-
const paramsStr = JSON.stringify(params)
|
|
110
|
+
const paramsStr = JSON.stringify(params);
|
|
111
|
+
const paramsExtracted = extractFirstToolNameFromText(paramsStr);
|
|
112
|
+
if (paramsExtracted) return paramsExtracted;
|
|
113
|
+
const paramsLower = paramsStr.toLowerCase();
|
|
93
114
|
for (const [toolName2, patterns] of Object.entries(KNOWN_TOOL_PATTERNS)) {
|
|
94
115
|
for (const pattern of patterns) {
|
|
95
|
-
if (
|
|
116
|
+
if (paramsLower.includes(pattern.toLowerCase())) {
|
|
96
117
|
return toolName2;
|
|
97
118
|
}
|
|
98
119
|
}
|
|
@@ -329,7 +350,7 @@ class AcpSdkBackend {
|
|
|
329
350
|
this.emit({
|
|
330
351
|
type: "status",
|
|
331
352
|
status: "error",
|
|
332
|
-
detail: "Model not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite"
|
|
353
|
+
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"
|
|
333
354
|
});
|
|
334
355
|
} else if (/authentication required/i.test(text) || /login required/i.test(text) || /invalid.*(api key|key)/i.test(text) || /permission denied/i.test(text)) {
|
|
335
356
|
const excerpt = this.stderrExcerpt(20);
|
|
@@ -1832,7 +1853,7 @@ class GeminiPermissionHandler {
|
|
|
1832
1853
|
const decision = result.decision;
|
|
1833
1854
|
const reason = result.reason;
|
|
1834
1855
|
const kind = decision === "approved" || decision === "approved_for_session" ? "policy_allow" : decision === "abort" && reason === "permission_prompt_required" ? "policy_prompt" : "policy_block";
|
|
1835
|
-
const summary = kind === "policy_allow" ? "Allowed." : kind === "policy_prompt" ? "Waiting for permission to run this tool." : reason ? `Blocked: ${reason}` : "Blocked by
|
|
1856
|
+
const summary = kind === "policy_allow" ? "Allowed." : kind === "policy_prompt" ? "Waiting for permission to run this tool." : reason ? `Blocked: ${reason}` : "Blocked by Policy.";
|
|
1836
1857
|
const nextSteps = [];
|
|
1837
1858
|
if (kind === "policy_block" && typeof reason === "string") {
|
|
1838
1859
|
if (reason.includes("docs_index_read") || reason.includes("Documentation index")) nextSteps.push("Call `mcp__flockbay__docs_index_read`.");
|
|
@@ -2570,6 +2591,30 @@ ${transcript}`;
|
|
|
2570
2591
|
types.logger.debug("[Gemini] Failed to send ready push", pushError);
|
|
2571
2592
|
}
|
|
2572
2593
|
};
|
|
2594
|
+
let pendingTurnCompletion = null;
|
|
2595
|
+
const beginTurnCompletionWait = () => {
|
|
2596
|
+
if (pendingTurnCompletion) {
|
|
2597
|
+
types.logger.debug("[Gemini] beginTurnCompletionWait called while a turn is already pending; replacing pending wait");
|
|
2598
|
+
try {
|
|
2599
|
+
pendingTurnCompletion.resolve("error");
|
|
2600
|
+
} catch {
|
|
2601
|
+
}
|
|
2602
|
+
pendingTurnCompletion = null;
|
|
2603
|
+
}
|
|
2604
|
+
let resolve2;
|
|
2605
|
+
const promise = new Promise((res) => {
|
|
2606
|
+
resolve2 = res;
|
|
2607
|
+
});
|
|
2608
|
+
pendingTurnCompletion = { sawRunning: false, resolve: resolve2, promise };
|
|
2609
|
+
return promise;
|
|
2610
|
+
};
|
|
2611
|
+
const maybeResolveTurnCompletion = (status) => {
|
|
2612
|
+
if (!pendingTurnCompletion) return;
|
|
2613
|
+
if (!pendingTurnCompletion.sawRunning) return;
|
|
2614
|
+
const pending = pendingTurnCompletion;
|
|
2615
|
+
pendingTurnCompletion = null;
|
|
2616
|
+
pending.resolve(status);
|
|
2617
|
+
};
|
|
2573
2618
|
const emitReadyIfIdle = () => {
|
|
2574
2619
|
if (shouldExit) {
|
|
2575
2620
|
return false;
|
|
@@ -2597,10 +2642,36 @@ ${transcript}`;
|
|
|
2597
2642
|
let wasSessionCreated = false;
|
|
2598
2643
|
let abortNote = null;
|
|
2599
2644
|
let abortNoteSentToSession = false;
|
|
2645
|
+
let abortRequested = false;
|
|
2646
|
+
let readySentForAbort = false;
|
|
2647
|
+
function normalizeCancelNote(input) {
|
|
2648
|
+
if (typeof input === "string") return input.trim() ? input.trim() : null;
|
|
2649
|
+
if (!input || typeof input !== "object") return null;
|
|
2650
|
+
const note = typeof input.note === "string" ? String(input.note).trim() : "";
|
|
2651
|
+
if (note) return note;
|
|
2652
|
+
const reason = typeof input.reason === "string" ? String(input.reason).trim() : "";
|
|
2653
|
+
if (reason) return reason;
|
|
2654
|
+
return null;
|
|
2655
|
+
}
|
|
2656
|
+
function makeAbortError(message = "Aborted") {
|
|
2657
|
+
const error = new Error(message);
|
|
2658
|
+
error.name = "AbortError";
|
|
2659
|
+
return error;
|
|
2660
|
+
}
|
|
2661
|
+
function abortable(signal, promise) {
|
|
2662
|
+
if (signal.aborted) return Promise.reject(makeAbortError());
|
|
2663
|
+
return new Promise((resolve2, reject) => {
|
|
2664
|
+
const onAbort = () => reject(makeAbortError());
|
|
2665
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2666
|
+
promise.then(resolve2, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
2667
|
+
});
|
|
2668
|
+
}
|
|
2600
2669
|
async function handleAbort(note, options) {
|
|
2601
2670
|
types.logger.debug("[Gemini] Abort requested - stopping current task");
|
|
2602
|
-
|
|
2603
|
-
|
|
2671
|
+
abortRequested = true;
|
|
2672
|
+
const normalizedNote = normalizeCancelNote(note);
|
|
2673
|
+
if (normalizedNote) {
|
|
2674
|
+
abortNote = normalizedNote;
|
|
2604
2675
|
abortNoteSentToSession = Boolean(options?.alreadySentToSession);
|
|
2605
2676
|
}
|
|
2606
2677
|
session.sendCodexMessage({
|
|
@@ -2611,6 +2682,13 @@ ${transcript}`;
|
|
|
2611
2682
|
diffProcessor.reset();
|
|
2612
2683
|
try {
|
|
2613
2684
|
abortController.abort();
|
|
2685
|
+
if (pendingTurnCompletion) {
|
|
2686
|
+
try {
|
|
2687
|
+
pendingTurnCompletion.resolve("error");
|
|
2688
|
+
} catch {
|
|
2689
|
+
}
|
|
2690
|
+
pendingTurnCompletion = null;
|
|
2691
|
+
}
|
|
2614
2692
|
messageQueue.reset();
|
|
2615
2693
|
if (geminiBackend && acpSessionId) {
|
|
2616
2694
|
await geminiBackend.cancel(acpSessionId);
|
|
@@ -2749,6 +2827,7 @@ ${transcript}`;
|
|
|
2749
2827
|
};
|
|
2750
2828
|
let accumulatedResponse = "";
|
|
2751
2829
|
let isResponseInProgress = false;
|
|
2830
|
+
let currentResponseMessageId = null;
|
|
2752
2831
|
function setupGeminiMessageHandler(backend) {
|
|
2753
2832
|
backend.onMessage((msg) => {
|
|
2754
2833
|
switch (msg.type) {
|
|
@@ -2778,6 +2857,9 @@ ${transcript}`;
|
|
|
2778
2857
|
if (msg.status === "running") {
|
|
2779
2858
|
thinking = true;
|
|
2780
2859
|
session.keepAlive(thinking, "remote");
|
|
2860
|
+
if (pendingTurnCompletion) {
|
|
2861
|
+
pendingTurnCompletion.sawRunning = true;
|
|
2862
|
+
}
|
|
2781
2863
|
session.sendCodexMessage({
|
|
2782
2864
|
type: "task_started",
|
|
2783
2865
|
id: node_crypto.randomUUID()
|
|
@@ -2822,11 +2904,14 @@ ${transcript}`;
|
|
|
2822
2904
|
accumulatedResponse = "";
|
|
2823
2905
|
isResponseInProgress = false;
|
|
2824
2906
|
}
|
|
2907
|
+
maybeResolveTurnCompletion(msg.status);
|
|
2825
2908
|
} else if (msg.status === "error") {
|
|
2826
2909
|
thinking = false;
|
|
2827
2910
|
session.keepAlive(thinking, "remote");
|
|
2828
2911
|
accumulatedResponse = "";
|
|
2829
2912
|
isResponseInProgress = false;
|
|
2913
|
+
currentResponseMessageId = null;
|
|
2914
|
+
maybeResolveTurnCompletion("error");
|
|
2830
2915
|
const errorMessage = stringifyErrorish(msg.detail || "Unknown error");
|
|
2831
2916
|
messageBuffer.addMessage(`Error: ${errorMessage}`, "status");
|
|
2832
2917
|
session.sendCodexMessage({
|
|
@@ -3050,7 +3135,7 @@ ${transcript}`;
|
|
|
3050
3135
|
const actualModel = determineGeminiModel(modelToUse, localConfigForModel);
|
|
3051
3136
|
types.logger.debug(`[gemini] Model change - modelToUse=${modelToUse}, actualModel=${actualModel}`);
|
|
3052
3137
|
types.logger.debug("[gemini] Starting new ACP session with model:", actualModel);
|
|
3053
|
-
const { sessionId } = await geminiBackend.startSession();
|
|
3138
|
+
const { sessionId } = await abortable(abortController.signal, geminiBackend.startSession());
|
|
3054
3139
|
acpSessionId = sessionId;
|
|
3055
3140
|
startedSessionThisTurn = true;
|
|
3056
3141
|
types.logger.debug(`[gemini] New ACP session started: ${acpSessionId}`);
|
|
@@ -3067,6 +3152,15 @@ ${transcript}`;
|
|
|
3067
3152
|
let retryThisTurn = false;
|
|
3068
3153
|
let skipAutoFinalize = false;
|
|
3069
3154
|
try {
|
|
3155
|
+
const turnSignal = abortController.signal;
|
|
3156
|
+
const sendPromptAndWait = async (prompt) => {
|
|
3157
|
+
if (!geminiBackend || !acpSessionId) {
|
|
3158
|
+
throw new Error("Gemini backend or session not initialized");
|
|
3159
|
+
}
|
|
3160
|
+
const completion = beginTurnCompletionWait();
|
|
3161
|
+
await abortable(turnSignal, geminiBackend.sendPrompt(acpSessionId, prompt));
|
|
3162
|
+
return await abortable(turnSignal, completion);
|
|
3163
|
+
};
|
|
3070
3164
|
if (first || !wasSessionCreated) {
|
|
3071
3165
|
if (!geminiBackend) {
|
|
3072
3166
|
await refreshGeminiCloudToken("before-backend-create");
|
|
@@ -3091,7 +3185,7 @@ ${transcript}`;
|
|
|
3091
3185
|
if (!acpSessionId) {
|
|
3092
3186
|
types.logger.debug("[gemini] Starting ACP session...");
|
|
3093
3187
|
updatePermissionMode(message.mode.permissionMode);
|
|
3094
|
-
const { sessionId } = await geminiBackend.startSession();
|
|
3188
|
+
const { sessionId } = await abortable(turnSignal, geminiBackend.startSession());
|
|
3095
3189
|
acpSessionId = sessionId;
|
|
3096
3190
|
startedSessionThisTurn = true;
|
|
3097
3191
|
types.logger.debug(`[gemini] ACP session started: ${acpSessionId}`);
|
|
@@ -3132,18 +3226,27 @@ ${originalUserMessage}` : originalUserMessage;
|
|
|
3132
3226
|
...promptImages.map((img) => ({ type: "image", data: img.base64, mimeType: img.mimeType }))
|
|
3133
3227
|
];
|
|
3134
3228
|
types.logger.debug(`[gemini] Sending multimodal prompt blocks (textLength=${promptText.length}, images=${promptImages.length})`);
|
|
3135
|
-
await
|
|
3229
|
+
const status = await sendPromptAndWait(blocks);
|
|
3230
|
+
if (status !== "idle") {
|
|
3231
|
+
skipAutoFinalize = true;
|
|
3232
|
+
}
|
|
3136
3233
|
} else {
|
|
3137
3234
|
types.logger.debug(`[gemini] Sending prompt to Gemini (length: ${promptText.length}): ${promptText.substring(0, 100)}...`);
|
|
3138
3235
|
types.logger.debug(`[gemini] Full prompt: ${promptText}`);
|
|
3139
|
-
await
|
|
3236
|
+
const status = await sendPromptAndWait(promptText);
|
|
3237
|
+
if (status !== "idle") {
|
|
3238
|
+
skipAutoFinalize = true;
|
|
3239
|
+
}
|
|
3140
3240
|
}
|
|
3141
3241
|
types.logger.debug("[gemini] Prompt sent successfully");
|
|
3142
|
-
if (!screenshotGate.inAutoReview && screenshotGate.paths.length > 0) {
|
|
3242
|
+
if (!skipAutoFinalize && !screenshotGate.inAutoReview && screenshotGate.paths.length > 0) {
|
|
3143
3243
|
screenshotGate.inAutoReview = true;
|
|
3144
3244
|
messageBuffer.addMessage("Auto-reviewing screenshots\u2026", "status");
|
|
3145
3245
|
const blocks = await buildScreenshotReviewBlocks(screenshotGate.paths);
|
|
3146
|
-
await
|
|
3246
|
+
const status = await sendPromptAndWait(blocks);
|
|
3247
|
+
if (status !== "idle") {
|
|
3248
|
+
skipAutoFinalize = true;
|
|
3249
|
+
}
|
|
3147
3250
|
screenshotGate.inAutoReview = false;
|
|
3148
3251
|
}
|
|
3149
3252
|
if (first) {
|
|
@@ -3152,14 +3255,21 @@ ${originalUserMessage}` : originalUserMessage;
|
|
|
3152
3255
|
} catch (error) {
|
|
3153
3256
|
types.logger.debug("[gemini] Error in gemini session:", error);
|
|
3154
3257
|
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
3155
|
-
|
|
3258
|
+
const treatAsAbort = abortRequested || isAbortError;
|
|
3259
|
+
if (treatAsAbort) {
|
|
3156
3260
|
skipAutoFinalize = true;
|
|
3157
|
-
|
|
3261
|
+
readySentForAbort = true;
|
|
3262
|
+
const note = abortNote || "Canceled by user";
|
|
3158
3263
|
abortNote = null;
|
|
3159
3264
|
const alreadySent = abortNoteSentToSession;
|
|
3160
3265
|
abortNoteSentToSession = false;
|
|
3266
|
+
abortRequested = false;
|
|
3267
|
+
accumulatedResponse = "";
|
|
3268
|
+
isResponseInProgress = false;
|
|
3269
|
+
currentResponseMessageId = null;
|
|
3161
3270
|
messageBuffer.addMessage(note, "status");
|
|
3162
3271
|
if (!alreadySent) session.sendSessionEvent({ type: "message", message: note });
|
|
3272
|
+
session.sendSessionEvent({ type: "ready" });
|
|
3163
3273
|
} else {
|
|
3164
3274
|
const rawErrorString = error instanceof Error ? error.message : String(error);
|
|
3165
3275
|
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);
|
|
@@ -3191,7 +3301,7 @@ ${originalUserMessage}` : originalUserMessage;
|
|
|
3191
3301
|
const errorString = String(error);
|
|
3192
3302
|
if (errorCode === 404 || errorDetails.includes("notFound") || errorDetails.includes("404") || errorMessage.includes("not found") || errorMessage.includes("404")) {
|
|
3193
3303
|
const currentModel2 = displayedModel || "gemini-2.5-pro";
|
|
3194
|
-
errorMsg = `Model "${currentModel2}" not found. Available models: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite`;
|
|
3304
|
+
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`;
|
|
3195
3305
|
} 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")) {
|
|
3196
3306
|
errorMsg = "Gemini API rate limit exceeded. Please wait a moment and try again. The API will retry automatically.";
|
|
3197
3307
|
} else if (errorDetails.includes("quota") || errorMessage.includes("quota") || errorString.includes("quota")) {
|
|
@@ -3215,6 +3325,7 @@ ${originalUserMessage}` : originalUserMessage;
|
|
|
3215
3325
|
permissionHandler.reset();
|
|
3216
3326
|
reasoningProcessor.abort();
|
|
3217
3327
|
diffProcessor.reset();
|
|
3328
|
+
abortRequested = false;
|
|
3218
3329
|
thinking = false;
|
|
3219
3330
|
session.keepAlive(thinking, "remote");
|
|
3220
3331
|
if (!retryThisTurn) {
|
|
@@ -3244,7 +3355,11 @@ ${originalUserMessage}` : originalUserMessage;
|
|
|
3244
3355
|
});
|
|
3245
3356
|
}
|
|
3246
3357
|
}
|
|
3247
|
-
|
|
3358
|
+
if (readySentForAbort) {
|
|
3359
|
+
readySentForAbort = false;
|
|
3360
|
+
} else {
|
|
3361
|
+
emitReadyIfIdle();
|
|
3362
|
+
}
|
|
3248
3363
|
}
|
|
3249
3364
|
types.logger.debug(`[gemini] Main loop: turn completed, continuing to next iteration (queue size: ${messageQueue.size()})`);
|
|
3250
3365
|
}
|