fluxflow-cli 1.19.6 → 1.21.0

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 (3) hide show
  1. package/README.md +4 -1
  2. package/dist/fluxflow.js +665 -198
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -27,7 +27,10 @@ npm install -g fluxflow-cli
27
27
  fluxflow-cli
28
28
  ```
29
29
 
30
- *The agent will prompt you for your Gemini API Key on the first run and store it securely in an XOR-encrypted vault.* Free API Key recomemded to use Gemma 4 (Default Model).
30
+ *The agent will prompt you for your API Key on the first run and store it securely in an encrypted vault.* Choose from multiple supported AI providers:
31
+ - **Google GenAI** (Gemini & Gemma models)
32
+ - **DeepSeek** (Native DeepSeek API)
33
+ - **OpenRouter** (Access to hundreds of models; *Experimental*)
31
34
 
32
35
  ---
33
36
 
package/dist/fluxflow.js CHANGED
@@ -1171,12 +1171,12 @@ var init_main_tools = __esm({
1171
1171
  }
1172
1172
  return _isPsAvailable;
1173
1173
  };
1174
- TOOL_PROTOCOL = (mode, osDetected) => `
1174
+ TOOL_PROTOCOL = (mode, osDetected, isMultiModal, aiProvider) => `
1175
1175
  -- TOOL DEFINITIONS --
1176
1176
  Access to internal tools. MUST use the exact syntax on a new line: [tool:functions.ToolName(args)]
1177
1177
  MANDATORY TOOL POLICY:
1178
1178
  - **MAX 3 TOOL CALLS PER TURN. Next Turn, verify results, plan next**
1179
- ${mode === "Flux" ? "- USE multiple search & replace on patch tool if editing same file/path with many edits\n" : ""}- Use contextually BEST tool, no brute force, no spamming
1179
+ ${mode === "Flux" ? "- USE multiple search & replace on patch tool if editing same file/path with many edits \u2190 **MANDATORY where possible**\n" : ""}- Use contextually BEST tool, no brute force, no spamming
1180
1180
  ${mode === "Flux" ? "- **File Tools >> Code in chat**\n" : ""}
1181
1181
  - COMMUNICATION TOOLS -
1182
1182
  1. [tool:functions.Ask(question="...", optionA="option::description", ...MAX 4)]. Ambiguity Resolution. Mandatory Triggers: Path Divergence, Security, Risk Mitigation. ask >> finish. Suggest best options; don't ask for preferences
@@ -1186,7 +1186,7 @@ ${mode === "Flux" ? "- **File Tools >> Code in chat**\n" : ""}
1186
1186
  2. [tool:functions.WebScrape(url="...")]. Proactive use for specific webpage/docs/api
1187
1187
 
1188
1188
  ${mode === "Flux" ? `- PROJECT TOOLS (path = relative to CWD, path separator: '/') -
1189
- 1. [tool:functions.ReadFile(path="...", startLine=number, endLine=number)]. Supports images/docs. User gives image/doc: VIEW FIRST
1189
+ 1. [tool:functions.ReadFile(path="...", startLine=number, endLine=number)]. ${aiProvider !== "Google" ? `${isMultiModal ? `Supports images/docs. User gives image/doc: VIEW FIRST` : `No Multimodal support`}` : `Supports images/docs. User gives image/doc: VIEW FIRST`}
1190
1190
  2. [tool:functions.ReadFolder(path="...")]. Detailed DIR stats
1191
1191
  3. [tool:functions.PatchFile(path="...", replaceContent1="exact string", newContent1="...", ...MAX 8)]. Surgical Patch. **Multiple patch on same file/path? Use replaceContent2, newContent2 etc >>> multiple spams**. Unsure? ReadFile > guessing.
1192
1192
  4. [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile >>> WriteFile. Verify Imports
@@ -1706,7 +1706,8 @@ function SettingsMenu({
1706
1706
  setInputConfig,
1707
1707
  saveSettings: saveSettings2,
1708
1708
  quotas,
1709
- setMessages
1709
+ setMessages,
1710
+ aiProvider
1710
1711
  }) {
1711
1712
  const [activeColumn, setActiveColumn] = useState3("categories");
1712
1713
  const [selectedCategoryIndex, setSelectedCategoryIndex] = useState3(0);
@@ -1740,6 +1741,7 @@ function SettingsMenu({
1740
1741
  ];
1741
1742
  case "other":
1742
1743
  return [
1744
+ { label: "Current Provider", value: "aiProvider", status: aiProvider },
1743
1745
  { label: "API Tier", value: "apiTier", status: apiTier }
1744
1746
  ];
1745
1747
  default:
@@ -1948,7 +1950,7 @@ function SettingsMenu({
1948
1950
  },
1949
1951
  isSelected ? "\u276F " : " ",
1950
1952
  item.label
1951
- ), !isCommandListItem && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", dimColor: true }, dots), /* @__PURE__ */ React6.createElement(Text6, { color: getStatusColor(item), bold: true }, "[ ", item.status, " ]"))), isCommandListItem && !isEditingThis && item.status !== "None" && /* @__PURE__ */ React6.createElement(Box6, { paddingX: 4, marginBottom: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", dimColor: true }, "\u21B3 ", item.status)), isEditingThis && /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginLeft: 4, marginBottom: 1 }, /* @__PURE__ */ React6.createElement(Box6, { paddingX: 1, borderStyle: "single", borderColor: "cyan", flexDirection: "row" }, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan", bold: true }, "> ", " "), /* @__PURE__ */ React6.createElement(
1953
+ ), !isCommandListItem && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", dimColor: true }, dots), /* @__PURE__ */ React6.createElement(Text6, { color: getStatusColor(item), bold: true }, item.value === "aiProvider" ? item.status : `[ ${item.status} ]`))), isCommandListItem && !isEditingThis && item.status !== "None" && /* @__PURE__ */ React6.createElement(Box6, { paddingX: 4, marginBottom: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", dimColor: true }, "\u21B3 ", item.status)), isEditingThis && /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginLeft: 4, marginBottom: 1 }, /* @__PURE__ */ React6.createElement(Box6, { paddingX: 1, borderStyle: "single", borderColor: "cyan", flexDirection: "row" }, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan", bold: true }, "> ", " "), /* @__PURE__ */ React6.createElement(
1952
1954
  TextInput,
1953
1955
  {
1954
1956
  value: editValue,
@@ -2413,7 +2415,7 @@ ${userMemories}` : "";
2413
2415
  ${parts.join("\n\n")}
2414
2416
  ` : "";
2415
2417
  };
2416
- getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, isMemoryEnabled = true, isFirstPrompt = false) => {
2418
+ getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, isMemoryEnabled = true, isFirstPrompt = false, aiProvider = "Google", isMultiModal = false) => {
2417
2419
  let thinkingConfig = "";
2418
2420
  if (thinkingLevel !== "GEM") {
2419
2421
  let levelKey = thinkingLevel;
@@ -2465,22 +2467,22 @@ Identity: Flux Flow (by Kushal Roy Chowdhury). Conversational, Sassy${mode === "
2465
2467
  Mode: ${mode}${thinkingLevel !== "Fast" ? " (Thinking Mode)" : ""}. ${mode === "Flux" ? "Logical, Highly Detailed, Task-Driven. Prioritizes scalable file/folder structures, modular architecture, clean code abstractions, step-by-step execution. Industry standard latest coding practices/libraries, clean code, Double Check Imports, Client-Server Sync" : "Concise"}
2466
2468
 
2467
2469
  -- AGENT LOOP RULES (PRIORITY: HIGH) --
2468
- - **MANDATORY: MUST END WITH [turn: continue] to CONTINUE loop OR [turn: finish] to END loop**
2470
+ - **MANDATORY: MUST END WITH [turn: continue] to CONTINUE loop OR [turn: finish] to END loop** \u2190 IMPORTANT EVERY RESPONSE
2469
2471
  - Tool Called? No post tool chat until [turn: continue]
2470
2472
  - NEVER USE [turn: continue] [turn:finish] together
2471
2473
 
2472
2474
  -- MARKERS --
2473
2475
  - TOOL SYSTEM: [TOOL RESULT] (system priority)
2474
2476
  - SYSTEM NOTIFICATION: [SYSTEM], [METADATA] in user turn
2475
- ${thinkingLevel !== "GEM" ? `
2477
+ ${aiProvider === "Google" ? `${thinkingLevel !== "GEM" ? `
2476
2478
  -- THINKING RULES --
2477
2479
  ${thinkingConfig}
2478
2480
  ${thinkingLevel !== "Fast" ? `
2479
2481
  CRITICAL THINKING POLICY
2480
2482
  - ALWAYS use <think> ... </think> before responding, even with simple queries/greetings
2481
2483
  - ${thinkingLevel === "Low" || thinkingLevel === "Medium" || thinkingLevel === "Fast" ? "C" : "Interrogate approaches adversarially, but c"}ommit once best solution is determined through analysis. Avoid spiraling after reaching decision point
2482
- ` : ""}` : ""}
2483
- ${TOOL_PROTOCOL(mode, osDetected)}
2484
+ ` : ""}` : ""}` : ``}
2485
+ ${TOOL_PROTOCOL(mode, osDetected, aiProvider.toLowerCase() === "deepseek" ? false : isMultiModal, aiProvider)}
2484
2486
  ${projectContextBlock}
2485
2487
  -- MEMORY RULES --
2486
2488
  - Memory: ${isMemoryEnabled ? "Subtly Personalize. Auto Saves" : "OFF. Decline Remembering Memories"}
@@ -4415,6 +4417,7 @@ var init_settings = __esm({
4415
4417
  DEFAULT_SETTINGS = {
4416
4418
  mode: "Flux",
4417
4419
  thinkingLevel: "Medium",
4420
+ aiProvider: "Google",
4418
4421
  activeModel: "gemma-4-31b-it",
4419
4422
  showFullThinking: true,
4420
4423
  apiTier: "Free",
@@ -4922,7 +4925,7 @@ var init_tools = __esm({
4922
4925
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
4923
4926
  import path16 from "path";
4924
4927
  import fs17 from "fs";
4925
- var client, TERMINATION_SIGNAL, stripAnsi2, signalTermination, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, consolidatePastMemories, getAIStream;
4928
+ var client, TERMINATION_SIGNAL, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getOpenRouterStream, signalTermination, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, getAIStream;
4926
4929
  var init_ai = __esm({
4927
4930
  async "src/utils/ai.js"() {
4928
4931
  await init_prompts();
@@ -4940,6 +4943,286 @@ var init_ai = __esm({
4940
4943
  if (typeof str !== "string") return str;
4941
4944
  return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
4942
4945
  };
4946
+ fetchWithBackoff = async (url, options, retries = 5, delay = 1e3) => {
4947
+ for (let i = 0; i < retries; i++) {
4948
+ try {
4949
+ const response = await fetch(url, options);
4950
+ if (response.ok) return response;
4951
+ if (response.status !== 429 && response.status < 500) return response;
4952
+ } catch (e) {
4953
+ if (i === retries - 1) throw e;
4954
+ }
4955
+ await new Promise((resolve) => setTimeout(resolve, delay * Math.pow(2, i)));
4956
+ }
4957
+ return fetch(url, options);
4958
+ };
4959
+ getDeepSeekStream = async function* (apiKey, model, contents, systemInstruction, thinkingLevel, mode, isMultiModal) {
4960
+ const messages = [];
4961
+ if (systemInstruction) {
4962
+ messages.push({ role: "system", content: systemInstruction });
4963
+ }
4964
+ for (const content of contents) {
4965
+ const role = content.role === "user" ? "user" : "assistant";
4966
+ const msgContent = [];
4967
+ if (Array.isArray(content.parts)) {
4968
+ for (const part of content.parts) {
4969
+ if (part.text) {
4970
+ msgContent.push({ type: "text", text: part.text });
4971
+ } else if (part.inlineData && isMultiModal) {
4972
+ const mimeType = part.inlineData.mimeType;
4973
+ const data = part.inlineData.data;
4974
+ const isImage = mimeType.startsWith("image/");
4975
+ if (isImage) {
4976
+ msgContent.push({
4977
+ type: "image_url",
4978
+ image_url: {
4979
+ url: `data:${mimeType};base64,${data}`
4980
+ }
4981
+ });
4982
+ }
4983
+ }
4984
+ }
4985
+ } else {
4986
+ const text = content.text || "";
4987
+ if (text) msgContent.push({ type: "text", text });
4988
+ }
4989
+ messages.push({
4990
+ role,
4991
+ content: msgContent.length === 1 && msgContent[0].type === "text" ? msgContent[0].text : msgContent
4992
+ });
4993
+ }
4994
+ const requestPayload = {
4995
+ model,
4996
+ messages,
4997
+ stream: true,
4998
+ stream_options: { include_usage: true },
4999
+ temperature: mode === "Flux" ? 1 : 1.4
5000
+ };
5001
+ if (thinkingLevel !== "Fast") {
5002
+ const reasoningEffortMap = {
5003
+ "Low": "high",
5004
+ "Medium": "high",
5005
+ "High": "high",
5006
+ "xHigh": "max"
5007
+ };
5008
+ requestPayload.reasoning_effort = reasoningEffortMap[thinkingLevel] || "high";
5009
+ requestPayload.extra_body = { thinking: { type: "enabled" } };
5010
+ } else {
5011
+ requestPayload.extra_body = { thinking: { type: "disabled" } };
5012
+ }
5013
+ const response = await fetchWithBackoff("https://api.deepseek.com/chat/completions", {
5014
+ method: "POST",
5015
+ headers: {
5016
+ "Authorization": `Bearer ${apiKey}`,
5017
+ "Content-Type": "application/json"
5018
+ },
5019
+ body: JSON.stringify(requestPayload)
5020
+ });
5021
+ if (!response.ok) {
5022
+ const errData = await response.json().catch(() => ({}));
5023
+ throw new Error(`DeepSeek Error (${response.status}): ${errData.error?.message || response.statusText}`);
5024
+ }
5025
+ const reader = response.body.getReader();
5026
+ const decoder = new TextDecoder();
5027
+ let buffer = "";
5028
+ let pendingParts = [];
5029
+ let latestUsageMetadata = null;
5030
+ let lastFlushTime = Date.now();
5031
+ let hasNewData = false;
5032
+ while (true) {
5033
+ const { done, value } = await reader.read();
5034
+ if (done) {
5035
+ if (hasNewData && (pendingParts.length > 0 || latestUsageMetadata)) {
5036
+ yield {
5037
+ candidates: pendingParts.length > 0 ? [{ content: { parts: pendingParts } }] : [],
5038
+ usageMetadata: latestUsageMetadata
5039
+ };
5040
+ }
5041
+ break;
5042
+ }
5043
+ buffer += decoder.decode(value, { stream: true });
5044
+ const lines = buffer.split("\n");
5045
+ buffer = lines.pop();
5046
+ for (const line of lines) {
5047
+ const cleanLine = line.trim();
5048
+ if (!cleanLine || !cleanLine.startsWith("data: ")) continue;
5049
+ if (cleanLine === "data: [DONE]") break;
5050
+ try {
5051
+ const json = JSON.parse(cleanLine.substring(6));
5052
+ const delta = json.choices?.[0]?.delta;
5053
+ const usage = json.usage;
5054
+ if (usage) {
5055
+ latestUsageMetadata = {
5056
+ totalTokenCount: usage.total_tokens || usage.prompt_tokens + usage.completion_tokens,
5057
+ promptTokenCount: usage.prompt_tokens || 0,
5058
+ candidatesTokenCount: usage.completion_tokens || 0,
5059
+ cachedContentTokenCount: usage.prompt_tokens_details?.cached_tokens || 0,
5060
+ thoughtsTokenCount: usage.completion_tokens_details?.reasoning_tokens || 0
5061
+ };
5062
+ hasNewData = true;
5063
+ }
5064
+ if (delta) {
5065
+ const thought = delta.reasoning_content || null;
5066
+ if (thought) {
5067
+ pendingParts.push({ text: thought, thought: true });
5068
+ hasNewData = true;
5069
+ }
5070
+ if (delta.content) {
5071
+ pendingParts.push({ text: delta.content });
5072
+ hasNewData = true;
5073
+ }
5074
+ }
5075
+ } catch (e) {
5076
+ }
5077
+ }
5078
+ if (Date.now() - lastFlushTime >= 350 && hasNewData) {
5079
+ yield {
5080
+ candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
5081
+ usageMetadata: latestUsageMetadata
5082
+ };
5083
+ pendingParts = [];
5084
+ lastFlushTime = Date.now();
5085
+ hasNewData = false;
5086
+ }
5087
+ }
5088
+ };
5089
+ getOpenRouterStream = async function* (apiKey, model, contents, systemInstruction, thinkingLevel, mode, isMultiModal) {
5090
+ const messages = [];
5091
+ if (systemInstruction) {
5092
+ messages.push({ role: "system", content: systemInstruction });
5093
+ }
5094
+ for (const content of contents) {
5095
+ const role = content.role === "user" ? "user" : "assistant";
5096
+ const msgContent = [];
5097
+ if (Array.isArray(content.parts)) {
5098
+ for (const part of content.parts) {
5099
+ if (part.text) {
5100
+ msgContent.push({ type: "text", text: part.text });
5101
+ } else if (part.inlineData && isMultiModal) {
5102
+ const mimeType = part.inlineData.mimeType;
5103
+ const data = part.inlineData.data;
5104
+ const isImage = mimeType.startsWith("image/");
5105
+ if (isImage) {
5106
+ msgContent.push({
5107
+ type: "image_url",
5108
+ image_url: {
5109
+ url: `data:${mimeType};base64,${data}`
5110
+ }
5111
+ });
5112
+ } else {
5113
+ msgContent.push({
5114
+ type: "file",
5115
+ file: {
5116
+ filename: part.filename || "file",
5117
+ file_data: `data:${mimeType};base64,${data}`
5118
+ }
5119
+ });
5120
+ }
5121
+ }
5122
+ }
5123
+ } else {
5124
+ const text = content.text || "";
5125
+ if (text) msgContent.push({ type: "text", text });
5126
+ }
5127
+ messages.push({
5128
+ role,
5129
+ content: msgContent.length === 1 && msgContent[0].type === "text" ? msgContent[0].text : msgContent
5130
+ });
5131
+ }
5132
+ const reasoningEffortMap = {
5133
+ "Low": "low",
5134
+ "Medium": "medium",
5135
+ "High": "high",
5136
+ "xHigh": "high"
5137
+ };
5138
+ const requestPayload = {
5139
+ model,
5140
+ messages,
5141
+ stream: true,
5142
+ temperature: mode === "Flux" ? 1 : 1.4
5143
+ };
5144
+ const effort = reasoningEffortMap[thinkingLevel];
5145
+ if (effort && thinkingLevel !== "Fast") {
5146
+ requestPayload.reasoning_effort = effort;
5147
+ }
5148
+ const response = await fetchWithBackoff("https://openrouter.ai/api/v1/chat/completions", {
5149
+ method: "POST",
5150
+ headers: {
5151
+ "Authorization": `Bearer ${apiKey}`,
5152
+ "Content-Type": "application/json",
5153
+ "X-Title": "FluxFlow CLI",
5154
+ "X-Cache": "true"
5155
+ },
5156
+ body: JSON.stringify(requestPayload)
5157
+ });
5158
+ if (!response.ok) {
5159
+ const errData = await response.json().catch(() => ({}));
5160
+ throw new Error(`OpenRouter Error (${response.status}): ${errData.error?.message || response.statusText}`);
5161
+ }
5162
+ const reader = response.body.getReader();
5163
+ const decoder = new TextDecoder();
5164
+ let buffer = "";
5165
+ let pendingParts = [];
5166
+ let latestUsageMetadata = null;
5167
+ let lastFlushTime = Date.now();
5168
+ let hasNewData = false;
5169
+ while (true) {
5170
+ const { done, value } = await reader.read();
5171
+ if (done) {
5172
+ if (hasNewData && (pendingParts.length > 0 || latestUsageMetadata)) {
5173
+ yield {
5174
+ candidates: pendingParts.length > 0 ? [{ content: { parts: pendingParts } }] : [],
5175
+ usageMetadata: latestUsageMetadata
5176
+ };
5177
+ }
5178
+ break;
5179
+ }
5180
+ buffer += decoder.decode(value, { stream: true });
5181
+ const lines = buffer.split("\n");
5182
+ buffer = lines.pop();
5183
+ for (const line of lines) {
5184
+ const cleanLine = line.trim();
5185
+ if (!cleanLine || !cleanLine.startsWith("data: ")) continue;
5186
+ if (cleanLine === "data: [DONE]") break;
5187
+ try {
5188
+ const json = JSON.parse(cleanLine.substring(6));
5189
+ const delta = json.choices?.[0]?.delta;
5190
+ const usage = json.usage;
5191
+ if (usage) {
5192
+ latestUsageMetadata = {
5193
+ totalTokenCount: usage.total_tokens || usage.prompt_tokens + usage.completion_tokens,
5194
+ promptTokenCount: usage.prompt_tokens || 0,
5195
+ candidatesTokenCount: usage.completion_tokens || 0,
5196
+ cachedContentTokenCount: usage.prompt_tokens_details?.cached_tokens || 0,
5197
+ thoughtsTokenCount: usage.completion_tokens_details?.reasoning_tokens || 0
5198
+ };
5199
+ hasNewData = true;
5200
+ }
5201
+ if (delta) {
5202
+ const thought = delta.reasoning || (delta.reasoning_details ? delta.reasoning_details.map((d) => d.text).join("") : null);
5203
+ if (thought) {
5204
+ pendingParts.push({ text: thought, thought: true });
5205
+ hasNewData = true;
5206
+ }
5207
+ if (delta.content) {
5208
+ pendingParts.push({ text: delta.content });
5209
+ hasNewData = true;
5210
+ }
5211
+ }
5212
+ } catch (e) {
5213
+ }
5214
+ }
5215
+ if (Date.now() - lastFlushTime >= 350 && hasNewData) {
5216
+ yield {
5217
+ candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
5218
+ usageMetadata: latestUsageMetadata
5219
+ };
5220
+ pendingParts = [];
5221
+ lastFlushTime = Date.now();
5222
+ hasNewData = false;
5223
+ }
5224
+ }
5225
+ };
4943
5226
  signalTermination = () => {
4944
5227
  TERMINATION_SIGNAL = true;
4945
5228
  };
@@ -4974,7 +5257,7 @@ var init_ai = __esm({
4974
5257
  const USER_CONTEXT_LENGTH = 4 * (1024 * 2);
4975
5258
  const AGENT_CONTEXT_LENGTH = 4 * (1024 * 8);
4976
5259
  const { onStatus, onMemoryUpdated, onBackgroundIncrement } = callbacks;
4977
- const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats } = settings;
5260
+ const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats, aiProvider = "Google", apiKey } = settings;
4978
5261
  const isMemoryEnabled = systemSettings?.memory !== false;
4979
5262
  const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
4980
5263
  const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
@@ -5031,25 +5314,55 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
5031
5314
  (_, reject) => setTimeout(() => reject(new Error("JANITOR_TIMEOUT")), 6e4)
5032
5315
  );
5033
5316
  const streamPromise = (async () => {
5034
- const stream = await client.models.generateContentStream({
5035
- model: janitorModel || "gemma-4-26b-a4b-it",
5036
- contents: janitorContents,
5037
- config: {
5038
- systemInstruction: janitorPrompt,
5039
- maxOutputTokens: 512,
5040
- temperature: 0.3,
5041
- safetySettings: [
5042
- { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
5043
- { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
5044
- { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
5045
- { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
5046
- ],
5047
- thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.MINIMAL }
5048
- }
5049
- });
5050
- const iterator2 = stream[Symbol.asyncIterator]();
5051
- const firstResult2 = await iterator2.next();
5052
- return { iterator: iterator2, firstResult: firstResult2 };
5317
+ if (aiProvider === "OpenRouter") {
5318
+ const janitorOpenRouterModel = "google/gemma-4-26b-a4b-it:free";
5319
+ const stream = getOpenRouterStream(
5320
+ apiKey,
5321
+ janitorOpenRouterModel,
5322
+ janitorContents,
5323
+ janitorPrompt,
5324
+ "Fast",
5325
+ // Janitor always minimal
5326
+ mode
5327
+ );
5328
+ const iterator2 = stream[Symbol.asyncIterator]();
5329
+ const firstResult2 = await iterator2.next();
5330
+ return { iterator: iterator2, firstResult: firstResult2 };
5331
+ } else if (aiProvider === "DeepSeek") {
5332
+ const stream = getDeepSeekStream(
5333
+ apiKey,
5334
+ "deepseek-chat",
5335
+ janitorContents,
5336
+ janitorPrompt,
5337
+ "Fast",
5338
+ // Janitor always minimal
5339
+ mode,
5340
+ false
5341
+ );
5342
+ const iterator2 = stream[Symbol.asyncIterator]();
5343
+ const firstResult2 = await iterator2.next();
5344
+ return { iterator: iterator2, firstResult: firstResult2 };
5345
+ } else {
5346
+ const stream = await client.models.generateContentStream({
5347
+ model: janitorModel || "gemma-4-26b-a4b-it",
5348
+ contents: janitorContents,
5349
+ config: {
5350
+ systemInstruction: janitorPrompt,
5351
+ maxOutputTokens: 512,
5352
+ temperature: 0.3,
5353
+ safetySettings: [
5354
+ { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
5355
+ { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
5356
+ { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
5357
+ { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
5358
+ ],
5359
+ thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.MINIMAL }
5360
+ }
5361
+ });
5362
+ const iterator2 = stream[Symbol.asyncIterator]();
5363
+ const firstResult2 = await iterator2.next();
5364
+ return { iterator: iterator2, firstResult: firstResult2 };
5365
+ }
5053
5366
  })();
5054
5367
  const { iterator, firstResult } = await Promise.race([streamPromise, timeoutPromise]);
5055
5368
  let { value: firstChunk, done: firstDone } = firstResult;
@@ -5372,8 +5685,41 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
5372
5685
  client = new GoogleGenAI({ apiKey });
5373
5686
  return client;
5374
5687
  };
5688
+ generateSimpleContent = async (settings, model, contents, systemInstruction, thinkingLevel = "Fast") => {
5689
+ const { aiProvider = "Google", apiKey, mode } = settings;
5690
+ let fullText = "";
5691
+ let usageMetadata = null;
5692
+ let stream;
5693
+ if (aiProvider === "OpenRouter") {
5694
+ stream = getOpenRouterStream(apiKey, model, contents, systemInstruction, thinkingLevel, mode, false);
5695
+ } else if (aiProvider === "DeepSeek") {
5696
+ stream = getDeepSeekStream(apiKey, model, contents, systemInstruction, thinkingLevel, mode, false);
5697
+ } else {
5698
+ const genStream = await client.models.generateContentStream({
5699
+ model,
5700
+ contents,
5701
+ config: {
5702
+ systemInstruction,
5703
+ maxOutputTokens: 2048,
5704
+ temperature: 0.3,
5705
+ thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.MINIMAL }
5706
+ }
5707
+ });
5708
+ stream = genStream;
5709
+ }
5710
+ for await (const chunk of stream) {
5711
+ if (chunk.candidates?.[0]?.content?.parts) {
5712
+ for (const part of chunk.candidates[0].content.parts) {
5713
+ if (part.text && !part.thought) fullText += part.text;
5714
+ }
5715
+ }
5716
+ if (chunk.usageMetadata) usageMetadata = chunk.usageMetadata;
5717
+ }
5718
+ return { text: fullText, usageMetadata };
5719
+ };
5375
5720
  consolidatePastMemories = async (currentChatId, settings) => {
5376
5721
  try {
5722
+ const { aiProvider = "Google" } = settings;
5377
5723
  const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
5378
5724
  const totalMemoriesCount = Object.values(tempStorage).flat().length;
5379
5725
  if (totalMemoriesCount <= 2) return;
@@ -5421,23 +5767,13 @@ ${newMemoryListStr}
5421
5767
  let attempts = 0;
5422
5768
  const maxAttempts = 3;
5423
5769
  let success = false;
5770
+ let targetModel = "gemini-3.1-flash-lite";
5771
+ if (aiProvider === "OpenRouter") targetModel = "google/gemma-4-26b-a4b-it:free";
5772
+ if (aiProvider === "DeepSeek") targetModel = "deepseek-v4-flash";
5424
5773
  while (attempts < maxAttempts && !success) {
5425
5774
  attempts++;
5426
5775
  try {
5427
- const response = await client.models.generateContent({
5428
- model: "gemini-3.1-flash-lite",
5429
- contents: prompt,
5430
- config: {
5431
- temperature: 0.3,
5432
- safetySettings: [
5433
- { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
5434
- { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
5435
- { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
5436
- { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
5437
- ],
5438
- thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.LOW }
5439
- }
5440
- });
5776
+ const response = await generateSimpleContent(settings, targetModel, prompt, null, "Fast");
5441
5777
  const responseText = response.text || "";
5442
5778
  const janitorToolCalls = detectToolCalls(responseText);
5443
5779
  if (janitorToolCalls.length === 0) {
@@ -5449,6 +5785,9 @@ ${newMemoryListStr}
5449
5785
  await dispatchTool(toolName, janitorToolCall.args, { chatId: currentChatId });
5450
5786
  }
5451
5787
  }
5788
+ if (response.usageMetadata) {
5789
+ await addToUsage("tokens", response.usageMetadata.totalTokenCount || 0);
5790
+ }
5452
5791
  success = true;
5453
5792
  } catch (err) {
5454
5793
  if (attempts >= maxAttempts) {
@@ -5467,8 +5806,8 @@ ${newMemoryListStr}
5467
5806
  }
5468
5807
  };
5469
5808
  getAIStream = async function* (modelName, history, settings, steeringCallback, versionFluxflow2) {
5470
- if (!client) throw new Error("AI not initialized");
5471
- const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats } = settings;
5809
+ const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats, aiProvider = "Google", isMultiModal } = settings;
5810
+ if (!client && aiProvider === "Google") throw new Error("AI not initialized");
5472
5811
  const isMemoryEnabled = systemSettings?.memory !== false;
5473
5812
  const originalText = history[history.length - 1].text;
5474
5813
  const summariesFile = path16.join(SECRET_DIR, "chat-summaries.json");
@@ -5501,66 +5840,22 @@ Provide a new consolidated summary of the entire session.` : `Here is the conver
5501
5840
  ${flattenedText2}
5502
5841
 
5503
5842
  Provide a consolidated summary of the entire session.`;
5843
+ let targetModel = "gemini-3.1-flash-lite";
5844
+ if (aiProvider === "OpenRouter") targetModel = "google/gemma-4-26b-a4b-it:free";
5845
+ if (aiProvider === "DeepSeek") targetModel = "deepseek-v4-flash";
5504
5846
  try {
5505
- const response = await client.models.generateContent({
5506
- model: "gemini-3.1-flash-lite",
5507
- contents: prompt,
5508
- config: {
5509
- systemInstruction,
5510
- maxOutputTokens: 4096,
5511
- temperature: 0.3,
5512
- safetySettings: [
5513
- { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
5514
- { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
5515
- { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
5516
- { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
5517
- ],
5518
- thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.MEDIUM }
5519
- }
5520
- });
5847
+ const response = await generateSimpleContent(settings, targetModel, prompt, systemInstruction, "Fast");
5521
5848
  return response.text || "";
5522
5849
  } catch (err) {
5523
- try {
5524
- const response = await client.models.generateContent({
5525
- model: "gemini-2.5-flash",
5526
- contents: prompt,
5527
- config: {
5528
- systemInstruction,
5529
- maxOutputTokens: 4096,
5530
- temperature: 0.3,
5531
- safetySettings: [
5532
- { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
5533
- { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
5534
- { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
5535
- { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
5536
- ],
5537
- thinkingConfig: { includeThoughts: false, thinkingBudget: 8192 }
5538
- }
5539
- });
5540
- return response.text || "";
5541
- } catch (e) {
5850
+ if (aiProvider === "Google") {
5542
5851
  try {
5543
- const response = await client.models.generateContent({
5544
- model: "gemini-2.5-flash-lite",
5545
- contents: prompt,
5546
- config: {
5547
- systemInstruction,
5548
- maxOutputTokens: 4096,
5549
- temperature: 0.3,
5550
- safetySettings: [
5551
- { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
5552
- { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
5553
- { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
5554
- { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
5555
- ],
5556
- thinkingConfig: { includeThoughts: false, thinkingBudget: 8192 }
5557
- }
5558
- });
5559
- return response.text || "";
5560
- } catch (e2) {
5852
+ const fallback = await generateSimpleContent(settings, "gemini-2.5-flash", prompt, systemInstruction, "Fast");
5853
+ return fallback.text || "";
5854
+ } catch (e) {
5561
5855
  return "";
5562
5856
  }
5563
5857
  }
5858
+ return "";
5564
5859
  }
5565
5860
  };
5566
5861
  const flattenedText = flattenContext(modifiedHistory);
@@ -5872,7 +6167,7 @@ CWD: ${process.cwd()}${cwdMismatch ? ` (WARNING: CWD Mismatch! Previous Path: ${
5872
6167
  ${dirStructure}
5873
6168
  ${summaryBlock}
5874
6169
  ${memoryPrompt}
5875
- ${thinkingLevel != "Fast" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>**\n" : ""}` : ""}[USER] ${agentText.replace(/\s*\[Prompted on:.*?\]/g, "").trim()}`.trim();
6170
+ ${thinkingLevel != "Fast" && aiProvider === "Google" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>**\n" : ""}` : ""}[USER] ${agentText.replace(/\s*\[Prompted on:.*?\]/g, "").trim()}`.trim();
5876
6171
  modifiedHistory.push({ role: "user", text: firstUserMsg });
5877
6172
  let lastUsage = null;
5878
6173
  const MAX_LOOPS = mode === "Flux" ? 70 : 7;
@@ -5903,7 +6198,7 @@ ${thinkingLevel != "Fast" ? `${modelName.toLowerCase().startsWith("gemma") ? "[S
5903
6198
 
5904
6199
  [STEERING HINT]: ${hint}`;
5905
6200
  } else {
5906
- modifiedHistory.push({ role: "user", text: `${thinkingLevel != "Fast" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>**\n" : ""}` : ""}[STEERING HINT]: ${hint}` });
6201
+ modifiedHistory.push({ role: "user", text: `${thinkingLevel != "Fast" && aiProvider === "Google" ? `${modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS CRITICAL PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>**\n" : ""}` : ""}[STEERING HINT]: ${hint}` });
5907
6202
  }
5908
6203
  yield { type: "status", content: "Steering Hint Injected." };
5909
6204
  }
@@ -5961,11 +6256,14 @@ ${thinkingLevel != "Fast" ? `${modelName.toLowerCase().startsWith("gemma") ? "[S
5961
6256
  throw new Error("Error: Quota Exausted for Agent");
5962
6257
  }
5963
6258
  targetModel = modelName;
6259
+ if (aiProvider === "DeepSeek" && thinkingLevel === "Fast") {
6260
+ targetModel = "deepseek-chat";
6261
+ }
5964
6262
  if (retryCount === MAX_RETRIES - 1) {
5965
- targetModel = "gemini-3-flash-preview";
6263
+ targetModel = aiProvider === "DeepSeek" ? "deepseek-v4-flash" : "gemini-3-flash-preview";
5966
6264
  yield { type: "model_update", content: "Trying with fallback model" };
5967
6265
  } else if (retryCount === MAX_RETRIES) {
5968
- targetModel = "gemini-3.5-flash";
6266
+ targetModel = aiProvider === "DeepSeek" ? "deepseek-v4-pro" : "gemini-3.5-flash";
5969
6267
  yield { type: "model_update", content: "Trying with fallback model" };
5970
6268
  } else if (retryCount > 12 && retryCount < MAX_RETRIES - 2 && settings.apiKey !== "custom") {
5971
6269
  targetModel = "gemma-4-31b-it";
@@ -5973,12 +6271,12 @@ ${thinkingLevel != "Fast" ? `${modelName.toLowerCase().startsWith("gemma") ? "[S
5973
6271
  } else if (retryCount > 0) {
5974
6272
  yield { type: "model_update", content: null };
5975
6273
  }
5976
- currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? "GEM" : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt);
6274
+ currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? "GEM" : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt, aiProvider, isMultiModal);
5977
6275
  const isGemma = modelName && modelName.toLowerCase().startsWith("gemma");
5978
6276
  const lastUserMsg = contents[contents.length - 1];
5979
6277
  if (isGemma) {
5980
6278
  const jitInstruction = `
5981
- [SYSTEM] Tool result received. Analyze output and proceed with your turn${thinkingLevel != "Fast" ? `. **STRICTLY MAINTAIN THINKING POLICY. DO NOT START A RESPONSE WITHOUT <think> ... </think>}**` : ""}`;
6279
+ [SYSTEM] Tool result received. Analyze output and proceed with your turn${thinkingLevel != "Fast" && aiProvider === "Google" ? `. **STRICTLY MAINTAIN THINKING POLICY. DO NOT START A RESPONSE WITHOUT <think> ... </think>}**` : ""}`;
5982
6280
  if (lastUserMsg && lastUserMsg.role === "user" && lastUserMsg.parts?.[0]?.text?.startsWith("[TOOL RESULT]")) {
5983
6281
  lastUserMsg.parts[0].text += jitInstruction;
5984
6282
  }
@@ -5992,57 +6290,79 @@ ${thinkingLevel != "Fast" ? `${modelName.toLowerCase().startsWith("gemma") ? "[S
5992
6290
  }
5993
6291
  }
5994
6292
  let activeContents = contents;
5995
- stream = await client.models.generateContentStream({
5996
- model: targetModel || "gemma-4-31b-it",
5997
- contents: activeContents,
5998
- config: {
5999
- systemInstruction: currentSystemInstruction,
6000
- temperature: mode === "Flux" ? 1 : 1.4,
6001
- maxOutputTokens: 32768,
6002
- mediaResolution: "MEDIA_RESOLUTION_MEDIUM",
6003
- safetySettings: [
6004
- { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
6005
- { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
6006
- { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
6007
- { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
6008
- ],
6009
- thinkingConfig: (() => {
6010
- const modelLower = (targetModel || "").toLowerCase();
6011
- const isGemma4 = modelLower.includes("gemma-4") || modelLower.startsWith("gemma");
6012
- const isGemini3 = modelLower.includes("gemini-3");
6013
- if (isGemma4 || isGemini3) {
6014
- if (isGemma4) {
6015
- return { includeThoughts: false, thinkingLevel: ThinkingLevel.MINIMAL };
6016
- }
6017
- return {
6018
- includeThoughts: true,
6019
- thinkingLevel: {
6020
- "Fast": modelLower.includes("pro") ? ThinkingLevel.LOW : ThinkingLevel.MINIMAL,
6021
- "Low": ThinkingLevel.LOW,
6022
- "Medium": ThinkingLevel.MEDIUM,
6023
- "High": ThinkingLevel.HIGH,
6024
- "xHigh": ThinkingLevel.HIGH
6025
- }[thinkingLevel] || ThinkingLevel.MEDIUM
6026
- };
6027
- } else {
6028
- const budget = {
6029
- "Fast": -1,
6030
- "Low": 512,
6031
- "Medium": 2048,
6032
- "High": 16384,
6033
- "xHigh": 24576
6034
- }[thinkingLevel] || 2048;
6035
- if (budget === -1) {
6036
- return { includeThoughts: false };
6293
+ if (aiProvider === "OpenRouter") {
6294
+ stream = getOpenRouterStream(
6295
+ settings.apiKey,
6296
+ targetModel,
6297
+ activeContents,
6298
+ currentSystemInstruction,
6299
+ thinkingLevel,
6300
+ mode,
6301
+ isMultiModal
6302
+ );
6303
+ } else if (aiProvider === "DeepSeek") {
6304
+ stream = getDeepSeekStream(
6305
+ settings.apiKey,
6306
+ targetModel,
6307
+ activeContents,
6308
+ currentSystemInstruction,
6309
+ thinkingLevel,
6310
+ mode,
6311
+ isMultiModal
6312
+ );
6313
+ } else {
6314
+ stream = await client.models.generateContentStream({
6315
+ model: targetModel || "gemma-4-31b-it",
6316
+ contents: activeContents,
6317
+ config: {
6318
+ systemInstruction: currentSystemInstruction,
6319
+ temperature: mode === "Flux" ? 1 : 1.4,
6320
+ maxOutputTokens: 32768,
6321
+ mediaResolution: "MEDIA_RESOLUTION_MEDIUM",
6322
+ safetySettings: [
6323
+ { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
6324
+ { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
6325
+ { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
6326
+ { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
6327
+ ],
6328
+ thinkingConfig: (() => {
6329
+ const modelLower = (targetModel || "").toLowerCase();
6330
+ const isGemma4 = modelLower.includes("gemma-4") || modelLower.startsWith("gemma");
6331
+ const isGemini3 = modelLower.includes("gemini-3");
6332
+ if (isGemma4 || isGemini3) {
6333
+ if (isGemma4) {
6334
+ return { includeThoughts: false, thinkingLevel: ThinkingLevel.MINIMAL };
6335
+ }
6336
+ return {
6337
+ includeThoughts: true,
6338
+ thinkingLevel: {
6339
+ "Fast": modelLower.includes("pro") ? ThinkingLevel.LOW : ThinkingLevel.MINIMAL,
6340
+ "Low": ThinkingLevel.LOW,
6341
+ "Medium": ThinkingLevel.MEDIUM,
6342
+ "High": ThinkingLevel.HIGH,
6343
+ "xHigh": ThinkingLevel.HIGH
6344
+ }[thinkingLevel] || ThinkingLevel.MEDIUM
6345
+ };
6346
+ } else {
6347
+ const budget = {
6348
+ "Fast": 0,
6349
+ "Low": 512,
6350
+ "Medium": 2048,
6351
+ "High": 16384,
6352
+ "xHigh": 24576
6353
+ }[thinkingLevel] || 2048;
6354
+ if (budget === 0) {
6355
+ return { includeThoughts: false };
6356
+ }
6357
+ return {
6358
+ includeThoughts: true,
6359
+ thinkingBudget: budget
6360
+ };
6037
6361
  }
6038
- return {
6039
- includeThoughts: true,
6040
- thinkingBudget: budget
6041
- };
6042
- }
6043
- })()
6044
- }
6045
- });
6362
+ })()
6363
+ }
6364
+ });
6365
+ }
6046
6366
  turnText = "";
6047
6367
  lastToolSniffed = null;
6048
6368
  lastToolEventTime = null;
@@ -6595,7 +6915,9 @@ ${boxBottom}` };
6595
6915
  toolCallPointer++;
6596
6916
  }
6597
6917
  }
6598
- lastUsage = chunk.usageMetadata;
6918
+ if (chunk.usageMetadata) {
6919
+ lastUsage = chunk.usageMetadata;
6920
+ }
6599
6921
  if (lastUsage) {
6600
6922
  yield { type: "liveTokens", content: lastUsage.totalTokenCount };
6601
6923
  }
@@ -6781,7 +7103,7 @@ Error Log can be found in ${path16.join(LOGS_DIR, "agent", "error.log")}`);
6781
7103
  const shouldContinue = toolCallPointer > 0;
6782
7104
  yield { type: "status", content: "Working..." };
6783
7105
  const cleanedTurnText = contextSafeReplace(turnText, /\[\s*(turn\s*:)?\s*(continue|finish)\s*\]/gi, "").trim();
6784
- let isActuallyFinished = hasFinish && !shouldContinue || !shouldContinue && !hasContinue;
7106
+ let isActuallyFinished = hasFinish || !shouldContinue;
6785
7107
  if (isActuallyFinished) {
6786
7108
  const fullAgentTextRaw = fullAgentResponseChunks.join("\n");
6787
7109
  const cleanedFullResponse = fullAgentTextRaw.replace(/(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\])/g, "").trim();
@@ -7460,6 +7782,8 @@ function App({ args = [] }) {
7460
7782
  };
7461
7783
  }, [stdout]);
7462
7784
  const [thinkingLevel, setThinkingLevel] = useState10("Medium");
7785
+ const [aiProvider, setAiProvider] = useState10("Google");
7786
+ const [setupStep, setSetupStep] = useState10(0);
7463
7787
  const [latestVer, setLatestVer] = useState10(null);
7464
7788
  const [showFullThinking, setShowFullThinking] = useState10(false);
7465
7789
  const [activeModel, setActiveModel] = useState10("gemma-4-31b-it");
@@ -7495,44 +7819,63 @@ function App({ args = [] }) {
7495
7819
  const [tick, setTick] = useState10(0);
7496
7820
  const isFirstRender = useRef3(true);
7497
7821
  const isSecondRender = useRef3(true);
7822
+ const isThirdRender = useRef3(true);
7498
7823
  useEffect7(() => {
7824
+ if (!apiKey) return;
7499
7825
  if (isFirstRender.current) {
7500
7826
  isFirstRender.current = false;
7501
7827
  setTimeout(() => {
7502
7828
  isSecondRender.current = false;
7829
+ setTimeout(() => {
7830
+ isThirdRender.current = false;
7831
+ }, 1e3);
7503
7832
  }, 2e3);
7504
7833
  return;
7505
7834
  }
7506
7835
  if (isSecondRender.current) {
7507
7836
  return;
7508
7837
  }
7838
+ if (isThirdRender.current) {
7839
+ return;
7840
+ }
7509
7841
  const s = emojiSpace(2);
7842
+ let defaultModel = "";
7843
+ let modelDisplayName = "";
7510
7844
  if (apiTier === "Free") {
7511
- setActiveModel("gemma-4-31b-it");
7512
- saveSettings({ apiTier: "Free", activeModel: "gemma-4-31b-it" });
7513
- setMessages((prev) => {
7514
- setCompletedIndex(prev.length + 1);
7515
- return [...prev, {
7516
- id: "tier-switch-" + Date.now(),
7517
- role: "system",
7518
- text: `\u26A0\uFE0F${s}**[TIER LIMIT]** Auto-switched to Gemma (Free default).`,
7519
- isMeta: true
7520
- }];
7521
- });
7845
+ if (aiProvider === "Google") {
7846
+ defaultModel = "gemma-4-31b-it";
7847
+ modelDisplayName = "Gemma 4 (Free default)";
7848
+ } else if (aiProvider === "DeepSeek") {
7849
+ defaultModel = "deepseek-v4-flash";
7850
+ modelDisplayName = "DeepSeek Flash (Free default)";
7851
+ } else {
7852
+ defaultModel = "google/gemma-4-31b-it:free";
7853
+ modelDisplayName = "Gemma 4 (Free default)";
7854
+ }
7522
7855
  } else {
7523
- setActiveModel("gemini-3-flash-preview");
7524
- saveSettings({ apiTier: "Paid", activeModel: "gemini-3-flash-preview" });
7525
- setMessages((prev) => {
7526
- setCompletedIndex(prev.length + 1);
7527
- return [...prev, {
7528
- id: "tier-switch-" + Date.now(),
7529
- role: "system",
7530
- text: `\u26A0\uFE0F${s}**[TIER LIMIT]** Auto-switched to Gemini 3 Flash.`,
7531
- isMeta: true
7532
- }];
7533
- });
7856
+ if (aiProvider === "Google") {
7857
+ defaultModel = "gemini-3-flash-preview";
7858
+ modelDisplayName = "Gemini 3 Flash";
7859
+ } else if (aiProvider === "DeepSeek") {
7860
+ defaultModel = "deepseek-v4-flash";
7861
+ modelDisplayName = "DeepSeek Flash";
7862
+ } else {
7863
+ defaultModel = "deepseek/deepseek-v4-flash";
7864
+ modelDisplayName = "DeepSeek Flash";
7865
+ }
7534
7866
  }
7535
- }, [apiTier]);
7867
+ setActiveModel(defaultModel);
7868
+ saveSettings({ apiTier, activeModel: defaultModel });
7869
+ setMessages((prev) => {
7870
+ setCompletedIndex(prev.length + 1);
7871
+ return [...prev, {
7872
+ id: "tier-switch-" + Date.now(),
7873
+ role: "system",
7874
+ text: `\u26A0\uFE0F${s}**[TIER LIMIT]** Auto-switched to ${modelDisplayName}.`,
7875
+ isMeta: true
7876
+ }];
7877
+ });
7878
+ }, [apiTier, aiProvider, apiKey]);
7536
7879
  const terminalEnv = useMemo2(() => {
7537
7880
  const isIDE = process.env.TERM_PROGRAM === "vscode" || !!process.env.VSC_TERMINAL_URL || !!process.env.INTELLIJ_TERMINAL_COMMAND_BLOCKS;
7538
7881
  return {
@@ -7717,6 +8060,11 @@ function App({ args = [] }) {
7717
8060
  } else if (activeView !== "chat" && activeView !== "settings") {
7718
8061
  setActiveView("chat");
7719
8062
  } else {
8063
+ if (!apiKey && setupStep === 1) {
8064
+ setSetupStep(0);
8065
+ setTempKey("");
8066
+ return;
8067
+ }
7720
8068
  setEscPressCount((prev) => {
7721
8069
  const nextCount = prev + 1;
7722
8070
  if (nextCount === 1) {
@@ -7809,6 +8157,7 @@ function App({ args = [] }) {
7809
8157
  } else {
7810
8158
  setThinkingLevel(saved.thinkingLevel);
7811
8159
  }
8160
+ setAiProvider(saved.aiProvider || "Google");
7812
8161
  persistedModelRef.current = saved.activeModel;
7813
8162
  if (parsedArgs.model) {
7814
8163
  setActiveModel(parsedArgs.model);
@@ -7919,6 +8268,7 @@ function App({ args = [] }) {
7919
8268
  saveSettings({
7920
8269
  mode,
7921
8270
  thinkingLevel,
8271
+ aiProvider,
7922
8272
  activeModel: modelToSave || activeModel,
7923
8273
  showFullThinking,
7924
8274
  systemSettings,
@@ -7927,16 +8277,26 @@ function App({ args = [] }) {
7927
8277
  apiTier
7928
8278
  });
7929
8279
  }
7930
- }, [mode, thinkingLevel, activeModel, showFullThinking, systemSettings, profileData, imageSettings, isInitializing, parsedArgs, apiTier]);
8280
+ }, [mode, thinkingLevel, aiProvider, activeModel, showFullThinking, systemSettings, profileData, imageSettings, isInitializing, parsedArgs, apiTier]);
7931
8281
  const handleSetup = async (val) => {
7932
8282
  const key = val.trim();
7933
- if (key.length >= 30) {
8283
+ let minLength = 30;
8284
+ if (aiProvider === "OpenRouter") minLength = 10;
8285
+ if (aiProvider === "DeepSeek") minLength = 20;
8286
+ if (key.length >= minLength) {
7934
8287
  await saveAPIKey(key);
7935
8288
  setApiKey(key);
7936
8289
  initAI(key);
7937
- setMessages((prev) => [...prev, { role: "system", text: "\u2705 API Key saved successfully! Initialization complete.", isMeta: true }]);
8290
+ let defaultModel = "gemma-4-31b-it";
8291
+ if (aiProvider === "OpenRouter") {
8292
+ defaultModel = "google/gemma-4-31b-it:free";
8293
+ } else if (aiProvider === "DeepSeek") {
8294
+ defaultModel = "deepseek-v4-flash";
8295
+ }
8296
+ setActiveModel(defaultModel);
8297
+ setMessages((prev) => [...prev, { role: "system", text: `\u2705 ${aiProvider} API Key saved successfully! Model set to ${defaultModel}. Initialization complete.`, isMeta: true }]);
7938
8298
  } else {
7939
- setMessages((prev) => [...prev, { role: "system", text: `\u274C INVALID KEY: Gemini API keys must be at least 30 characters.`, isMeta: true }]);
8299
+ setMessages((prev) => [...prev, { role: "system", text: `\u274C INVALID KEY: ${aiProvider} API keys must be at least ${minLength} characters.`, isMeta: true }]);
7940
8300
  setTempKey("");
7941
8301
  }
7942
8302
  };
@@ -8026,18 +8386,105 @@ function App({ args = [] }) {
8026
8386
  {
8027
8387
  cmd: "/thinking",
8028
8388
  desc: "Set AI reasoning depth",
8029
- subs: [
8030
- { cmd: "Fast", desc: "No Reasoning (Fastest)" },
8031
- { cmd: "Low", desc: "Quick Reasoning (Answers Quickly)" },
8032
- { cmd: "Medium", desc: "Balanced Reasoning (Decent Depth)" },
8033
- { cmd: "High", desc: "Deep Reasoning (Complex Problems)" },
8034
- { cmd: "xHigh", desc: "Extended Reasoning (Advanced Logic & Code)" }
8389
+ subs: aiProvider === "DeepSeek" ? [
8390
+ { cmd: "Fast", desc: "Fastest" },
8391
+ { cmd: "Standard", desc: "Standard Reasoning" },
8392
+ { cmd: "xHigh", desc: "Extended Reasoning" }
8393
+ ] : [
8394
+ { cmd: "Fast", desc: "Fastest" },
8395
+ { cmd: "Low", desc: "Quick Reasoning" },
8396
+ { cmd: "Medium", desc: "Balanced Reasoning" },
8397
+ { cmd: "High", desc: "Deep Reasoning" },
8398
+ { cmd: "xHigh", desc: "Extended Reasoning" }
8035
8399
  ]
8036
8400
  },
8037
8401
  {
8038
8402
  cmd: "/model",
8039
8403
  desc: "Switch Model for Agent",
8040
- subs: apiTier === "Free" ? [
8404
+ subs: aiProvider === "OpenRouter" ? apiTier === "Free" ? [
8405
+ {
8406
+ cmd: "google/gemma-4-31b-it:free",
8407
+ desc: "Multimodal"
8408
+ },
8409
+ {
8410
+ cmd: "moonshotai/kimi-k2.6:free",
8411
+ desc: "Multimodal"
8412
+ },
8413
+ {
8414
+ cmd: "qwen/qwen3-coder:free",
8415
+ desc: ""
8416
+ },
8417
+ {
8418
+ cmd: "z-ai/glm-4.5-air:free",
8419
+ desc: ""
8420
+ }
8421
+ ] : [
8422
+ {
8423
+ cmd: "google/gemini-3.5-flash",
8424
+ desc: "Multimodal"
8425
+ },
8426
+ {
8427
+ cmd: "qwen/qwen3.7-plus",
8428
+ desc: "Multimodal"
8429
+ },
8430
+ {
8431
+ cmd: "minimax/minimax-m3",
8432
+ desc: "Multimodal"
8433
+ },
8434
+ {
8435
+ cmd: "anthropic/claude-sonnet-4.5",
8436
+ desc: "Multimodal"
8437
+ },
8438
+ {
8439
+ cmd: "anthropic/claude-opus-4.6",
8440
+ desc: "Multimodal"
8441
+ },
8442
+ {
8443
+ cmd: "anthropic/claude-opus-4.8",
8444
+ desc: "Multimodal"
8445
+ },
8446
+ {
8447
+ cmd: "deepseek/deepseek-v4-pro",
8448
+ desc: ""
8449
+ },
8450
+ {
8451
+ cmd: "deepseek/deepseek-v4-flash",
8452
+ desc: ""
8453
+ },
8454
+ {
8455
+ cmd: "xiaomi/mimo-v2.5-pro",
8456
+ desc: ""
8457
+ },
8458
+ {
8459
+ cmd: "z-ai/glm-5",
8460
+ desc: ""
8461
+ },
8462
+ {
8463
+ cmd: "openai/gpt-5.2-codex",
8464
+ desc: "Multimodal"
8465
+ },
8466
+ {
8467
+ cmd: "openai/gpt-5.2-pro",
8468
+ desc: "Multimodal"
8469
+ },
8470
+ {
8471
+ cmd: "openai/gpt-5.5-pro",
8472
+ desc: "Multimodal"
8473
+ },
8474
+ {
8475
+ cmd: "moonshotai/kimi-k2.6",
8476
+ desc: "Multimodal"
8477
+ }
8478
+ ] : aiProvider === "DeepSeek" ? [
8479
+ {
8480
+ cmd: "deepseek-v4-flash",
8481
+ desc: "Fast & Efficient"
8482
+ },
8483
+ {
8484
+ cmd: "deepseek-v4-pro",
8485
+ desc: "High-Intelligence Reasoning"
8486
+ }
8487
+ ] : apiTier === "Free" ? [
8041
8488
  {
8042
8489
  cmd: "gemma-4-31b-it",
8043
8490
  desc: "Standard Default"
@@ -8388,7 +8835,7 @@ ${hintText}`, color: "magenta" }];
8388
8835
  case "/model": {
8389
8836
  if (parts[1]) {
8390
8837
  const mod = parts.slice(1).join(" ");
8391
- if (mod === "gemma-4-31b-it" && apiTier !== "Free") {
8838
+ if (mod === "gemma-4-31b-it" && apiTier !== "Free" && aiProvider === "Google") {
8392
8839
  setMessages((prev) => {
8393
8840
  setCompletedIndex(prev.length + 1);
8394
8841
  return [...prev, {
@@ -8697,6 +9144,9 @@ ${timestamp}` };
8697
9144
  text
8698
9145
  });
8699
9146
  });
9147
+ const modelCmd = COMMANDS.find((c) => c.cmd === "/model");
9148
+ const currentModelObj = modelCmd?.subs?.find((s) => s.cmd === activeModel);
9149
+ const isMultiModal = currentModelObj?.desc?.toLowerCase().includes("multimodal");
8700
9150
  const stream = getAIStream(
8701
9151
  activeModel,
8702
9152
  cleanHistoryForAI,
@@ -8708,6 +9158,9 @@ ${timestamp}` };
8708
9158
  janitorModel,
8709
9159
  sessionStats,
8710
9160
  chatId,
9161
+ isMultiModal,
9162
+ aiProvider,
9163
+ apiKey,
8711
9164
  cols: terminalSize.columns - 6,
8712
9165
  rows: 30,
8713
9166
  onExecStart: (cmd) => {
@@ -8877,7 +9330,7 @@ Selection: ${val}`,
8877
9330
  setIsProcessing(false);
8878
9331
  hasFiredJanitor = true;
8879
9332
  runJanitorTask(
8880
- { profile: profileData, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats },
9333
+ { profile: profileData, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats, aiProvider, apiKey },
8881
9334
  packet.data.agentText,
8882
9335
  packet.data.fullAgentTextRaw,
8883
9336
  packet.data.history,
@@ -9199,7 +9652,8 @@ Selection: ${val}`,
9199
9652
  setInputConfig,
9200
9653
  saveSettings,
9201
9654
  quotas,
9202
- setMessages
9655
+ setMessages,
9656
+ aiProvider
9203
9657
  }
9204
9658
  );
9205
9659
  case "apiTier":
@@ -9677,7 +10131,20 @@ Selection: ${val}`,
9677
10131
  showFullThinking,
9678
10132
  columns: Math.max(20, (stdout?.columns || 80) - 1)
9679
10133
  }
9680
- ), activeCommand && /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(TerminalBox, { command: activeCommand, output: execOutput, isFocused: isTerminalFocused, isPty: isActiveCommandPty }))), isInitializing ? /* @__PURE__ */ React13.createElement(Box13, { borderStyle: "double", borderColor: "magenta", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React13.createElement(Text13, { color: "magenta" }, "\u{1F30A} Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React13.createElement(Box13, { borderStyle: "round", borderColor: "gray", padding: 0, flexDirection: "column", flexShrink: 0, width: "100%" }, /* @__PURE__ */ React13.createElement(Box13, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text13, { color: "yellow", bold: true }, "\u{1F511}", emojiSpace(2), "API KEY REQUIRED")), /* @__PURE__ */ React13.createElement(Box13, { paddingX: 1, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Text13, null, "Please enter your Gemini API Key to initialize the agent (If billing is enabled set Tier to paid in /settings \u2192 other \u2192 API Tier)."), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text13, { color: "cyan", bold: true }, "\u{1F4A0} "), /* @__PURE__ */ React13.createElement(
10134
+ ), activeCommand && /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(TerminalBox, { command: activeCommand, output: execOutput, isFocused: isTerminalFocused, isPty: isActiveCommandPty }))), isInitializing ? /* @__PURE__ */ React13.createElement(Box13, { borderStyle: "double", borderColor: "magenta", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React13.createElement(Text13, { color: "magenta" }, "\u{1F30A} Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React13.createElement(Box13, { borderStyle: "round", borderColor: "gray", padding: 0, flexDirection: "column", flexShrink: 0, width: "100%" }, /* @__PURE__ */ React13.createElement(Box13, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text13, { color: "yellow", bold: true }, "\u{1F511}", emojiSpace(2), "API KEY REQUIRED")), /* @__PURE__ */ React13.createElement(Box13, { paddingX: 1, flexDirection: "column" }, setupStep === 0 ? /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(Text13, null, "Select your Preferred Provider:"), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(
10135
+ CommandMenu,
10136
+ {
10137
+ items: [
10138
+ { label: "Google (Free/Paid)", value: "Google" },
10139
+ { label: "DeepSeek (Paid)", value: "DeepSeek" },
10140
+ { label: "OpenRouter (Free/Paid) [EXPERIMENTAL]", value: "OpenRouter" }
10141
+ ],
10142
+ onSelect: (item) => {
10143
+ setAiProvider(item.value);
10144
+ setSetupStep(1);
10145
+ }
10146
+ }
10147
+ ))) : /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(Text13, null, "Please enter your ", aiProvider, " API Key to initialize the agent (If billing is enabled set Tier to paid in /settings \u2192 other \u2192 API Tier)."), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text13, { color: "cyan", bold: true }, "\u{1F4A0} "), /* @__PURE__ */ React13.createElement(
9681
10148
  TextInput4,
9682
10149
  {
9683
10150
  value: tempKey,
@@ -9685,7 +10152,7 @@ Selection: ${val}`,
9685
10152
  onSubmit: handleSetup,
9686
10153
  mask: "*"
9687
10154
  }
9688
- ))), /* @__PURE__ */ React13.createElement(Box13, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text13, { color: "gray", dimColor: true, italic: true }, "(Press Enter to confirm and initialize)"))) : renderActiveView(), confirmExit && /* @__PURE__ */ React13.createElement(Box13, { borderStyle: "round", borderColor: "red", paddingX: 2, marginY: 0, width: "100%" }, /* @__PURE__ */ React13.createElement(Text13, { color: "red", bold: true }, "\u{1F534} EXIT CONFIRMATION: "), /* @__PURE__ */ React13.createElement(Text13, { color: "white" }, "Press "), /* @__PURE__ */ React13.createElement(Text13, { color: "red", bold: true }, "CTRL + C"), /* @__PURE__ */ React13.createElement(Text13, { color: "white" }, " again to exit (", exitCountdown, "s). Press "), /* @__PURE__ */ React13.createElement(Text13, { color: "cyan", bold: true }, "ESC"), /* @__PURE__ */ React13.createElement(Text13, { color: "white" }, " to cancel.")), /* @__PURE__ */ React13.createElement(Box13, { flexShrink: 0, width: "100%" }, /* @__PURE__ */ React13.createElement(
10155
+ )), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text13, { color: "gray", italic: true }, "(Press ESC to go back to provider selection)")))), /* @__PURE__ */ React13.createElement(Box13, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text13, { color: "gray", dimColor: true, italic: true }, setupStep === 0 ? "(Use arrows to select and Enter to confirm)" : "(Press Enter to confirm and initialize)"))) : renderActiveView(), confirmExit && /* @__PURE__ */ React13.createElement(Box13, { borderStyle: "round", borderColor: "red", paddingX: 2, marginY: 0, width: "100%" }, /* @__PURE__ */ React13.createElement(Text13, { color: "red", bold: true }, "\u{1F534} EXIT CONFIRMATION: "), /* @__PURE__ */ React13.createElement(Text13, { color: "white" }, "Press "), /* @__PURE__ */ React13.createElement(Text13, { color: "red", bold: true }, "CTRL + C"), /* @__PURE__ */ React13.createElement(Text13, { color: "white" }, " again to exit (", exitCountdown, "s). Press "), /* @__PURE__ */ React13.createElement(Text13, { color: "cyan", bold: true }, "ESC"), /* @__PURE__ */ React13.createElement(Text13, { color: "white" }, " to cancel.")), /* @__PURE__ */ React13.createElement(Box13, { flexShrink: 0, width: "100%" }, /* @__PURE__ */ React13.createElement(
9689
10156
  StatusBar_default,
9690
10157
  {
9691
10158
  mode,
@@ -9733,7 +10200,7 @@ Selection: ${val}`,
9733
10200
  paddingX: 1
9734
10201
  },
9735
10202
  /* @__PURE__ */ React13.createElement(Box13, { width: 3 }, /* @__PURE__ */ React13.createElement(Text13, { color: isActive ? "cyan" : "gray", bold: isActive }, isActive ? " \u276F" : " ")),
9736
- /* @__PURE__ */ React13.createElement(Box13, { width: 32 }, /* @__PURE__ */ React13.createElement(
10203
+ /* @__PURE__ */ React13.createElement(Box13, { width: 55 }, /* @__PURE__ */ React13.createElement(
9737
10204
  Text13,
9738
10205
  {
9739
10206
  color: isGemmaDisabled ? "gray" : isActive ? "yellow" : "white",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.19.6",
4
- "date": "2026-06-05",
3
+ "version": "1.21.0",
4
+ "date": "2026-06-06",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",