fluxflow-cli 1.0.7 → 1.0.9
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/ARCHITECTURE.md +3 -1
- package/README.md +2 -0
- package/dist/fluxflow.js +94 -42
- package/package.json +1 -1
package/ARCHITECTURE.md
CHANGED
|
@@ -25,7 +25,9 @@ The execution flow of a single user prompt follows this loop:
|
|
|
25
25
|
5. **Turn Management & Continuation**: The model is instructed to append `[turn: finish]` if its goal is complete, or `[turn: continue]` if it expects tool results.
|
|
26
26
|
- If tools were called or `[turn: continue]` is present, the loop increments and re-prompts the model with the newly gathered `[TOOL_RESULT]` data.
|
|
27
27
|
- If `[turn: finish]` is detected and no further tools were called, the main loop terminates, passing the final synthesized context to the background Janitor process.
|
|
28
|
-
6. **Loop Limits & Resilience**: To prevent infinite loops or excessive API usage, **Flux mode** is capped at 50 iterations per user prompt, while **Flow mode** is capped at 5.
|
|
28
|
+
6. **Loop Limits & Resilience**: To prevent infinite loops or excessive API usage, **Flux mode** is capped at 50 iterations per user prompt, while **Flow mode** is capped at 5.
|
|
29
|
+
- **Multi-Stage Failover**: The loop features a sophisticated 8-attempt retry engine with random backoff (800ms - 2s).
|
|
30
|
+
- **Critical Fallback Pivot**: If the primary model fails 5 consecutive times, the agent surgically pivots to a lighter, high-concurrency fallback model (`gemini-3.1-flash-lite-preview`) for the final 3 attempts to ensure session navigation through API congestion.
|
|
29
31
|
|
|
30
32
|
## The Dual-Model System
|
|
31
33
|
|
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ Security isn't an afterthought; it's a boundary.
|
|
|
40
40
|
- **External Path Hardlock**: Restricts the agent to your Current Working Directory (CWD) unless you explicitly unlock it.
|
|
41
41
|
- **Human-in-the-Loop (HITL)**: Every file write and terminal command requires your high-fidelity approval.
|
|
42
42
|
- **XOR Vaulting**: All local session histories, memories, and API keys are obfuscated and encrypted at rest.
|
|
43
|
+
- **Adaptive Failover**: Automatic multi-stage retry logic with high-concurrency fallback model switching (Gemini 3.1 Flash Lite) during peak API congestion.
|
|
43
44
|
|
|
44
45
|
### 🧹 **The Background Janitor**
|
|
45
46
|
While you move at high speed, the Janitor follows behind—refining session titles, compressing data, and ensuring your context window remains at absolute peak performance.
|
|
@@ -50,6 +51,7 @@ While you move at high speed, the Janitor follows behind—refining session titl
|
|
|
50
51
|
- **Deep File-System Interaction**: Edit, move, and refactor code across multiple files with atomic precision.
|
|
51
52
|
- **Real-Time Web Intelligence**: Autonomous web-searching via DuckDuckGo for live news and technical research.
|
|
52
53
|
- **Autonomous Project Alignment**: Automatically detects and adheres to project-specific instructions in `Agent.md`, `Skills.md`, and `Fluxflow.md` for high-fidelity alignment with your coding standards and custom workflows.
|
|
54
|
+
- **High-Reliability Fallback**: Automatic failover to a lighter, high-concurrency model during peak traffic to ensure zero session loss.
|
|
53
55
|
- **Persistent Memory**: The agent learns from your preferences and project requirements across sessions.
|
|
54
56
|
|
|
55
57
|
---
|
package/dist/fluxflow.js
CHANGED
|
@@ -12,7 +12,7 @@ import { MultilineInput } from "ink-multiline-input";
|
|
|
12
12
|
import TextInput2 from "ink-text-input";
|
|
13
13
|
|
|
14
14
|
// src/components/ChatLayout.jsx
|
|
15
|
-
import React2 from "react";
|
|
15
|
+
import React2, { memo } from "react";
|
|
16
16
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
17
17
|
|
|
18
18
|
// src/components/TerminalBox.jsx
|
|
@@ -172,6 +172,19 @@ function ChatLayout({ messages, showFullThinking }) {
|
|
|
172
172
|
) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Thinking..."), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, flexDirection: "column" }, formatThinkText(content))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: content }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]"))));
|
|
173
173
|
}));
|
|
174
174
|
}
|
|
175
|
+
var ChatLayout_default = memo(ChatLayout, (prevProps, nextProps) => {
|
|
176
|
+
if (prevProps.showFullThinking !== nextProps.showFullThinking) return false;
|
|
177
|
+
if (prevProps.messages.length !== nextProps.messages.length) return false;
|
|
178
|
+
for (let i = 0; i < prevProps.messages.length; i++) {
|
|
179
|
+
const prev = prevProps.messages[i];
|
|
180
|
+
const next = nextProps.messages[i];
|
|
181
|
+
if (prev.id !== next.id) return false;
|
|
182
|
+
if (prev.text !== next.text) return false;
|
|
183
|
+
if (prev.memoryUpdated !== next.memoryUpdated) return false;
|
|
184
|
+
if (prev.color !== next.color) return false;
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
});
|
|
175
188
|
|
|
176
189
|
// src/components/StatusBar.jsx
|
|
177
190
|
import React3 from "react";
|
|
@@ -356,7 +369,7 @@ ${mode === "Flux" ? `
|
|
|
356
369
|
Results will be provided in the next loop as: [TOOL_RESULT]: [content]
|
|
357
370
|
WHEN CALLING TOOLS, YOU **MUST** END YOUR RESPONSE WITH '[turn: continue]' AFTER CALLING FUNCTIONS.
|
|
358
371
|
Do NOT over-use tools. Use them only when strictly necessary for the user's objective. You can stack multiple tool calls 1-by-1.
|
|
359
|
-
|
|
372
|
+
Distinguish clearly between tool discussion and execution. Use the 'tool:' prefix ONLY when calling a function. When discussing tools with the user, refer to them by name as nouns (e.g., 'write_file', 'list_files') to avoid accidental triggers and context bloat.
|
|
360
373
|
-- END FUNCTION CALLING PROTOCOL --`.trim();
|
|
361
374
|
|
|
362
375
|
// src/data/janitor_tools.js
|
|
@@ -392,7 +405,7 @@ var thinking_prompts_default = {
|
|
|
392
405
|
};
|
|
393
406
|
|
|
394
407
|
// src/utils/prompts.js
|
|
395
|
-
var getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, tempMemories = "", userMemories = "", isMemoryEnabled = true) => {
|
|
408
|
+
var getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, tempMemories = "", userMemories = "", isContext50 = false, isMemoryEnabled = true) => {
|
|
396
409
|
let levelKey = thinkingLevel;
|
|
397
410
|
if (thinkingLevel === "Low") levelKey = "Minimal";
|
|
398
411
|
if (thinkingLevel === "xHigh" || thinkingLevel === "Max") levelKey = "Max";
|
|
@@ -402,7 +415,7 @@ var getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, tempMe
|
|
|
402
415
|
const userInstrStr = profile.instructions && profile.instructions?.length > 0 ? `. User Instructions: ${profile.instructions}.` : "";
|
|
403
416
|
const dateTimeStr = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
404
417
|
const cwdStr = process.cwd();
|
|
405
|
-
const tempMemoriesStr = tempMemories?.length > 0 ? `
|
|
418
|
+
const tempMemoriesStr = tempMemories?.length > 0 && !isContext50 ? `
|
|
406
419
|
-- RECENT CONTEXT FROM OTHER CHAT THREADS --
|
|
407
420
|
${tempMemories}
|
|
408
421
|
------------------------------------------
|
|
@@ -548,15 +561,10 @@ var deleteChat = async (id) => {
|
|
|
548
561
|
const history = await loadHistory();
|
|
549
562
|
delete history[id];
|
|
550
563
|
await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
delete temp[id];
|
|
556
|
-
await fs3.writeJson(TEMP_MEM_FILE, temp, { spaces: 2 });
|
|
557
|
-
}
|
|
558
|
-
} catch (e) {
|
|
559
|
-
}
|
|
564
|
+
const temp = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
565
|
+
if (temp[id]) {
|
|
566
|
+
delete temp[id];
|
|
567
|
+
writeEncryptedJson(TEMP_MEM_FILE, temp);
|
|
560
568
|
}
|
|
561
569
|
return history;
|
|
562
570
|
});
|
|
@@ -1278,14 +1286,15 @@ var getAIStream = async function* (modelName, history, settings, steeringCallbac
|
|
|
1278
1286
|
const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
|
|
1279
1287
|
const mainUserMemories = persistentStorage.map((m) => `- ${m.memory}`).join("\n");
|
|
1280
1288
|
const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
|
|
1281
|
-
const
|
|
1289
|
+
const isContext50 = (sessionStats.tokens || 0) >= 54e3;
|
|
1290
|
+
const systemInstruction = getSystemInstruction(profile, thinkingLevel, mode, systemSettings, otherMemories, mainUserMemories, isMemoryEnabled, isContext50);
|
|
1282
1291
|
const firstUserMsg = `${systemInstruction}
|
|
1283
1292
|
|
|
1284
1293
|
USER_PROMPT: ${agentText}`.trim();
|
|
1285
1294
|
modifiedHistory.push({ role: "user", text: firstUserMsg });
|
|
1286
1295
|
let lastUsage = null;
|
|
1287
|
-
const MAX_LOOPS = mode === "Flux" ? 50 :
|
|
1288
|
-
const MAX_RETRIES =
|
|
1296
|
+
const MAX_LOOPS = mode === "Flux" ? 50 : 7;
|
|
1297
|
+
const MAX_RETRIES = 7;
|
|
1289
1298
|
yield { type: "status", content: "Working..." };
|
|
1290
1299
|
TERMINATION_SIGNAL = false;
|
|
1291
1300
|
let fullAgentResponse = "";
|
|
@@ -1314,8 +1323,15 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
1314
1323
|
if (!await checkQuota("agent", settings)) {
|
|
1315
1324
|
throw new Error("Error: Daily Quota Exausted for Agent");
|
|
1316
1325
|
}
|
|
1326
|
+
let targetModel = modelName;
|
|
1327
|
+
if (retryCount >= 5) {
|
|
1328
|
+
targetModel = "gemini-3.1-flash-lite-preview";
|
|
1329
|
+
yield { type: "model_update", content: "Trying with fallback model" };
|
|
1330
|
+
} else if (retryCount > 0) {
|
|
1331
|
+
yield { type: "model_update", content: null };
|
|
1332
|
+
}
|
|
1317
1333
|
stream = await client.models.generateContentStream({
|
|
1318
|
-
model:
|
|
1334
|
+
model: targetModel,
|
|
1319
1335
|
contents,
|
|
1320
1336
|
config: {
|
|
1321
1337
|
temperature: mode === "Flux" ? 0.9 : 1.3,
|
|
@@ -1326,31 +1342,39 @@ USER_PROMPT: ${agentText}`.trim();
|
|
|
1326
1342
|
}
|
|
1327
1343
|
});
|
|
1328
1344
|
success = true;
|
|
1345
|
+
yield { type: "model_update", content: null };
|
|
1329
1346
|
} catch (err) {
|
|
1330
|
-
const errMsg = err.message || String(err);
|
|
1347
|
+
const errMsg = err.status || err.error && err.error.message || String(err);
|
|
1331
1348
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1332
1349
|
const agentErrDir = path13.join(LOGS_DIR, "agent");
|
|
1333
|
-
if (!fs12.existsSync(agentErrDir)) {
|
|
1334
|
-
fs12.mkdirSync(agentErrDir, { recursive: true });
|
|
1335
|
-
}
|
|
1350
|
+
if (!fs12.existsSync(agentErrDir)) fs12.mkdirSync(agentErrDir, { recursive: true });
|
|
1336
1351
|
fs12.appendFileSync(path13.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
|
|
1337
1352
|
`);
|
|
1338
|
-
|
|
1339
|
-
if (isRetryable && retryCount < MAX_RETRIES) {
|
|
1353
|
+
if (retryCount < MAX_RETRIES) {
|
|
1340
1354
|
retryCount++;
|
|
1355
|
+
const waitTime = Math.floor(Math.random() * (2e3 - 800 + 1)) + 800;
|
|
1341
1356
|
yield { type: "status", content: `Retrying (${retryCount}/${MAX_RETRIES})...` };
|
|
1342
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
1357
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
1343
1358
|
} else {
|
|
1344
1359
|
throw new Error(`Model cannot be reached: ${errMsg}`);
|
|
1345
1360
|
}
|
|
1346
1361
|
}
|
|
1347
1362
|
}
|
|
1348
1363
|
let turnText = "";
|
|
1364
|
+
let lastToolSniffed = null;
|
|
1349
1365
|
for await (const chunk of stream) {
|
|
1350
1366
|
if (TERMINATION_SIGNAL) break;
|
|
1351
1367
|
if (chunk.text) {
|
|
1352
1368
|
turnText += chunk.text;
|
|
1353
1369
|
yield { type: "text", content: chunk.text };
|
|
1370
|
+
if (turnText.includes("tool:functions.")) {
|
|
1371
|
+
const parts = turnText.split("tool:functions.");
|
|
1372
|
+
const potentialTool = parts[parts.length - 1].split("(")[0].trim();
|
|
1373
|
+
if (potentialTool && /^[a-z_]+$/.test(potentialTool) && potentialTool !== lastToolSniffed) {
|
|
1374
|
+
lastToolSniffed = potentialTool;
|
|
1375
|
+
yield { type: "status", content: `Working (${potentialTool})...` };
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1354
1378
|
}
|
|
1355
1379
|
if (chunk.usageMetadata) {
|
|
1356
1380
|
lastUsage = chunk.usageMetadata;
|
|
@@ -1505,11 +1529,16 @@ ${boxBottom}
|
|
|
1505
1529
|
}
|
|
1506
1530
|
const cleanResultForAI = result.split(/\r?\n/).filter((line) => !line.includes("[UI_CONTEXT]")).join("\n");
|
|
1507
1531
|
toolResults.push(`[TOOL_RESULT]: ${cleanResultForAI}`);
|
|
1508
|
-
|
|
1532
|
+
let uiContent = `[TOOL_RESULT]: ${result}`;
|
|
1533
|
+
if (toolCall.toolName === "view_file" || toolCall.toolName === "web_scrape") {
|
|
1534
|
+
uiContent = `[TOOL_RESULT]: ${label} (Context Locked for UI Clarity)`;
|
|
1535
|
+
}
|
|
1536
|
+
yield { type: "tool_result", content: uiContent };
|
|
1509
1537
|
if (toolCall.toolName === "memory" && result.includes("SUCCESS")) {
|
|
1510
1538
|
yield { type: "memory_updated" };
|
|
1511
1539
|
}
|
|
1512
1540
|
}
|
|
1541
|
+
yield { type: "status", content: "Working..." };
|
|
1513
1542
|
}
|
|
1514
1543
|
const cleanedTurnText = turnText.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/\[?\s*(turn\s*:)?\s*(continue|finish)\s*\]?/gi, "").trim();
|
|
1515
1544
|
if (hasFinish || !shouldContinue && toolResults.length === 0) {
|
|
@@ -1708,13 +1737,11 @@ function ResumeModal({ onSelect, onDelete, onClose }) {
|
|
|
1708
1737
|
// src/components/MemoryModal.jsx
|
|
1709
1738
|
import React7, { useState as useState3, useEffect as useEffect2 } from "react";
|
|
1710
1739
|
import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
|
|
1711
|
-
import path15 from "path";
|
|
1712
|
-
var MEMORIES_PATH = path15.join(process.cwd(), "secret", "memories.json");
|
|
1713
1740
|
function MemoryModal({ onClose }) {
|
|
1714
1741
|
const [memories, setMemories] = useState3([]);
|
|
1715
1742
|
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1716
1743
|
const loadMemories = () => {
|
|
1717
|
-
const data = readEncryptedJson(
|
|
1744
|
+
const data = readEncryptedJson(MEMORIES_FILE, []);
|
|
1718
1745
|
setMemories(data);
|
|
1719
1746
|
};
|
|
1720
1747
|
useEffect2(() => {
|
|
@@ -1727,7 +1754,7 @@ function MemoryModal({ onClose }) {
|
|
|
1727
1754
|
if (input === "x" && memories.length > 0) {
|
|
1728
1755
|
const idToDelete = memories[selectedIndex].id;
|
|
1729
1756
|
const updated = memories.filter((m) => m.id !== idToDelete);
|
|
1730
|
-
writeEncryptedJson(
|
|
1757
|
+
writeEncryptedJson(MEMORIES_FILE, updated);
|
|
1731
1758
|
setMemories(updated);
|
|
1732
1759
|
if (selectedIndex >= updated.length && updated.length > 0) {
|
|
1733
1760
|
setSelectedIndex(updated.length - 1);
|
|
@@ -1831,9 +1858,11 @@ function App() {
|
|
|
1831
1858
|
const [escTimer, setEscTimer] = useState4(null);
|
|
1832
1859
|
const [queuedPrompt, setQueuedPrompt] = useState4(null);
|
|
1833
1860
|
const [resolutionData, setResolutionData] = useState4(null);
|
|
1861
|
+
const [tempModelOverride, setTempModelOverride] = useState4(null);
|
|
1834
1862
|
const [messages, setMessages] = useState4([
|
|
1835
1863
|
{ id: "welcome", role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome to Flux Flow! Type /help for commands.\n" }
|
|
1836
1864
|
]);
|
|
1865
|
+
const queuedPromptRef = useRef(null);
|
|
1837
1866
|
const [completedIndex, setCompletedIndex] = useState4(1);
|
|
1838
1867
|
useInput3((inputText, key) => {
|
|
1839
1868
|
if (key.escape) {
|
|
@@ -1921,6 +1950,7 @@ function App() {
|
|
|
1921
1950
|
if (isProcessing) {
|
|
1922
1951
|
const hintText = absoluteClean.trim();
|
|
1923
1952
|
setQueuedPrompt(hintText);
|
|
1953
|
+
queuedPromptRef.current = hintText;
|
|
1924
1954
|
setMessages((prev) => {
|
|
1925
1955
|
setCompletedIndex(prev.length + 1);
|
|
1926
1956
|
return [...prev, { id: "hint-" + Date.now(), role: "user", text: `[STEERING HINT: QUEUED]
|
|
@@ -2184,9 +2214,19 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2184
2214
|
}
|
|
2185
2215
|
},
|
|
2186
2216
|
async () => {
|
|
2187
|
-
if (
|
|
2188
|
-
const p =
|
|
2217
|
+
if (queuedPromptRef.current) {
|
|
2218
|
+
const p = queuedPromptRef.current;
|
|
2189
2219
|
setQueuedPrompt(null);
|
|
2220
|
+
queuedPromptRef.current = null;
|
|
2221
|
+
setMessages((prev) => {
|
|
2222
|
+
const newMsgs = [...prev];
|
|
2223
|
+
const hintMsg = newMsgs.reverse().find((m) => m.text?.includes("[STEERING HINT: QUEUED]"));
|
|
2224
|
+
if (hintMsg) {
|
|
2225
|
+
hintMsg.text = hintMsg.text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: INJECTED]");
|
|
2226
|
+
hintMsg.color = "cyan";
|
|
2227
|
+
}
|
|
2228
|
+
return newMsgs.reverse();
|
|
2229
|
+
});
|
|
2190
2230
|
return p;
|
|
2191
2231
|
}
|
|
2192
2232
|
return null;
|
|
@@ -2201,6 +2241,10 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2201
2241
|
setStatusText(packet.content);
|
|
2202
2242
|
continue;
|
|
2203
2243
|
}
|
|
2244
|
+
if (packet.type === "model_update") {
|
|
2245
|
+
setTempModelOverride(packet.content);
|
|
2246
|
+
continue;
|
|
2247
|
+
}
|
|
2204
2248
|
if (packet.type === "turn_reset") {
|
|
2205
2249
|
currentThinkId = null;
|
|
2206
2250
|
currentAgentId = null;
|
|
@@ -2300,9 +2344,19 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2300
2344
|
} finally {
|
|
2301
2345
|
setIsProcessing(false);
|
|
2302
2346
|
setStatusText(null);
|
|
2303
|
-
if (
|
|
2304
|
-
setResolutionData(
|
|
2347
|
+
if (queuedPromptRef.current) {
|
|
2348
|
+
setResolutionData(queuedPromptRef.current);
|
|
2305
2349
|
setQueuedPrompt(null);
|
|
2350
|
+
const hintToResolve = queuedPromptRef.current;
|
|
2351
|
+
queuedPromptRef.current = null;
|
|
2352
|
+
setMessages((prev) => {
|
|
2353
|
+
const newMsgs = [...prev];
|
|
2354
|
+
const hintMsg = newMsgs.reverse().find((m) => m.text?.includes("[STEERING HINT: QUEUED]"));
|
|
2355
|
+
if (hintMsg) {
|
|
2356
|
+
hintMsg.text = hintMsg.text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: FINISHED_TURN]");
|
|
2357
|
+
}
|
|
2358
|
+
return newMsgs.reverse();
|
|
2359
|
+
});
|
|
2306
2360
|
setActiveView("resolution");
|
|
2307
2361
|
}
|
|
2308
2362
|
setMessages((prev) => {
|
|
@@ -2371,7 +2425,7 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2371
2425
|
CommandMenu,
|
|
2372
2426
|
{
|
|
2373
2427
|
title: "\u{1F916} Select AI Model",
|
|
2374
|
-
items: [{ label: "Gemma 4 31B (Default)", value: "gemma-4-31b-it" }, { label: "Gemini 3.1 Pro (Req. paid API Key)", value: "gemini-3.1-pro-preview" }, { label: "Gemini 3 Flash", value: "gemini-3-flash-preview" }, { label: "Gemini 3.1 Flash Lite", value: "gemini-3.1-flash-lite" }, { label: "Cancel", value: "Cancel" }],
|
|
2428
|
+
items: [{ label: "Gemma 4 31B (Default)", value: "gemma-4-31b-it" }, { label: "Gemini 3.1 Pro (Req. paid API Key)", value: "gemini-3.1-pro-preview" }, { label: "Gemini 3 Flash", value: "gemini-3-flash-preview" }, { label: "Gemini 3.1 Flash Lite", value: "gemini-3.1-flash-lite-preview" }, { label: "Cancel", value: "Cancel" }],
|
|
2375
2429
|
onSelect: (item) => {
|
|
2376
2430
|
if (item.value !== "Cancel") setActiveModel(item.value);
|
|
2377
2431
|
setActiveView("chat");
|
|
@@ -2737,7 +2791,7 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2737
2791
|
return acc + Math.max(1, Math.ceil(line.length / wrapWidth));
|
|
2738
2792
|
}, 0);
|
|
2739
2793
|
const maxLines = Math.max(1, wrappedLines);
|
|
2740
|
-
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" },
|
|
2794
|
+
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 0, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, null, statusText && /* @__PURE__ */ React8.createElement(Text8, { color: "magenta", italic: true }, "\u23F3 ", statusText)), /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, "(", tempModelOverride || activeModel, ")")), suggestions.length > 0 && /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "\u{1F4A1} Suggestions: "), suggestions.map((s, i) => /* @__PURE__ */ React8.createElement(Text8, { key: s, color: "yellow", bold: i === 0 }, " ", s, " "))), /* @__PURE__ */ React8.createElement(Box8, { backgroundColor: "#333333", paddingX: 1, paddingY: 1, width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, maxLines > 3 ? /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, "[\u{1F4E6} ", maxLines, " lines of text in buffer - Full content will be sent]"), /* @__PURE__ */ React8.createElement(
|
|
2741
2795
|
Box8,
|
|
2742
2796
|
{
|
|
2743
2797
|
flexDirection: "row",
|
|
@@ -2747,7 +2801,7 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2747
2801
|
alignItems: "flex-end"
|
|
2748
2802
|
},
|
|
2749
2803
|
/* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "\u276F ")),
|
|
2750
|
-
/* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(
|
|
2804
|
+
/* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1, position: "relative" }, input.split("\n").pop() === "" && !isProcessing && /* @__PURE__ */ React8.createElement(Box8, { position: "absolute", paddingLeft: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, "Type your message...")), /* @__PURE__ */ React8.createElement(
|
|
2751
2805
|
MultilineInput,
|
|
2752
2806
|
{
|
|
2753
2807
|
value: input.split("\n").pop() || "",
|
|
@@ -2758,14 +2812,13 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2758
2812
|
setInput(lines.join("\n"));
|
|
2759
2813
|
},
|
|
2760
2814
|
onSubmit: handleSubmit,
|
|
2761
|
-
placeholder: escPressed ? "Press ESC again to cancel the request." : isProcessing ? "Flux Flow is thinking..." : "Type your message or /command...",
|
|
2762
2815
|
keyBindings: {
|
|
2763
2816
|
submit: (key) => key.return && !key.shift && !key.ctrl,
|
|
2764
2817
|
newline: (key) => key.return && key.shift || key.return && key.ctrl
|
|
2765
2818
|
}
|
|
2766
2819
|
}
|
|
2767
|
-
))
|
|
2768
|
-
)) : /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "row", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "\u276F ")), /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(
|
|
2820
|
+
)))
|
|
2821
|
+
)) : /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "row", width: "100%", paddingY: 0 }, /* @__PURE__ */ React8.createElement(Box8, { flexShrink: 0, width: 3 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "\u276F ")), /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1 }, /* @__PURE__ */ React8.createElement(Box8, { flexGrow: 1, position: "relative" }, input === "" && !isProcessing && /* @__PURE__ */ React8.createElement(Box8, { position: "absolute", paddingLeft: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray", dimColor: true }, escPressed ? " Press ESC again to cancel the request." : " Type your message or /command...")), /* @__PURE__ */ React8.createElement(
|
|
2769
2822
|
MultilineInput,
|
|
2770
2823
|
{
|
|
2771
2824
|
value: input,
|
|
@@ -2774,17 +2827,16 @@ OUTPUT: ${execOutputRef.current}`;
|
|
|
2774
2827
|
setInput(cleanVal);
|
|
2775
2828
|
},
|
|
2776
2829
|
onSubmit: handleSubmit,
|
|
2777
|
-
placeholder: escPressed ? "Press ESC again to cancel the request." : isProcessing ? "Flux Flow is thinking..." : "Type your message or /command...",
|
|
2778
2830
|
maxRows: 3,
|
|
2779
2831
|
keyBindings: {
|
|
2780
2832
|
submit: (key) => key.return && !key.shift && !key.ctrl,
|
|
2781
2833
|
newline: (key) => key.return && key.shift || key.return && key.ctrl
|
|
2782
2834
|
}
|
|
2783
2835
|
}
|
|
2784
|
-
))))));
|
|
2836
|
+
)))))));
|
|
2785
2837
|
}
|
|
2786
2838
|
};
|
|
2787
|
-
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, messages.slice(0, completedIndex).map((msg, idx) => /* @__PURE__ */ React8.createElement(
|
|
2839
|
+
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, messages.slice(0, completedIndex).map((msg, idx) => /* @__PURE__ */ React8.createElement(ChatLayout_default, { key: msg.id || idx, messages: [msg], showFullThinking }))), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", padding: 1, width: "100%" }, activeView === "chat" && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React8.createElement(ChatLayout_default, { messages: messages.slice(completedIndex), showFullThinking }), activeCommand && /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(TerminalBox, { command: activeCommand, output: execOutput }))), isInitializing ? /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "double", borderColor: "magenta", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "magenta" }, "\u{1F30A} Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "bold", borderColor: "yellow", padding: 1, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: "yellow", bold: true }, "\u{1F511} API KEY REQUIRED"), /* @__PURE__ */ React8.createElement(Text8, null, "Please enter your Gemini API Key to initialize the agent's brain."), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, "\u276F "), /* @__PURE__ */ React8.createElement(
|
|
2788
2840
|
TextInput2,
|
|
2789
2841
|
{
|
|
2790
2842
|
value: tempKey,
|