fluxflow-cli 1.21.1 → 1.21.3

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 CHANGED
@@ -1,65 +1,65 @@
1
- # 🏛️ Architecture & Design
2
-
3
- Flux Flow is built on a modern, reactive stack that brings web-like development paradigms to the terminal. It utilizes a custom agentic loop for reasoning and a unique dual-model system for background processing.
4
-
5
- ## UI Layer: React & Ink
6
-
7
- The entire terminal interface is built using **React** via the [Ink](https://github.com/vadimdemedes/ink) renderer.
8
- - **Component-Based**: The UI is composed of isolated, reusable React components (`ChatLayout`, `StatusBar`, `CommandMenu`, `TerminalBox`, `ProfileForm`).
9
- - **Reactive State**: The application uses React hooks (`useState`, `useEffect`) to manage user input, application mode, model selection, and the terminal's resizing events.
10
- - **Zero-Render Overheads**: Critical performance trackers, like the session start time, are kept outside the React render cycle to maintain terminal responsiveness during high-speed AI text streaming.
11
-
12
- ## The Agentic Loop
13
-
14
- The core intelligence of Flux Flow resides in `src/utils/ai.js`. It does not rely on opaque third-party agent frameworks; instead, it uses a custom, highly transparent string-based protocol powered by an asynchronous generator (`async function*`). This approach allows for real-time UI updates while managing complex multi-step reasoning.
15
-
16
- The execution flow of a single user prompt follows this loop:
17
-
18
- 1. **Context Assembly**: The user's prompt is combined with the system instructions, temporary session context, persistent user memories, and the current chat history. If the history gets too large (e.g., >254k tokens) and compression is disabled, it is gracefully truncated.
19
- 2. **Stream Processing**: The main loop initiates a streaming request to the Gemini API (`client.models.generateContentStream`). It yields chunks of text and status updates directly back to the React UI as they arrive.
20
- 3. **Detection & Tool Execution**: Once the stream completes for a given turn, the entire response is scanned for tool calls using a custom regex and bracket-balancing parser (looking for `tool:functions.tool_name(args...)`).
21
- - If tools are found, the loop pauses.
22
- - Each tool is dispatched to its respective handler in `src/tools/`.
23
- - Tool outputs are collected and appended to the context as `[TOOL RESULT]: ...`.
24
- 4. **Security Governance**: During tool execution, the loop enforces security checks (e.g., blocking `exec_command` from accessing system root drives if "External Workspace Access" is off) and pauses for Human-in-the-Loop (HITL) approval if necessary.
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
- - 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
- - 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 70 iterations per user prompt, while **Flow mode** is capped at 7.
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 13 consecutive times, the agent surgically pivots to a lighter, high-concurrency fallback model (`gemini-3.1-flash-lite`) for the final 3 attempts to ensure session navigation through API congestion.
31
-
32
- ## Multimodal Pipeline
33
-
34
- Flux Flow implements a native multimodal processing engine in `src/tools/view_file.js`. This allows the agent to move beyond text-based reasoning and analyze visual assets directly.
35
-
36
- - **Binary Detection**: The pipeline uses `is-binary-path` to distinguish between text and binary files.
37
- - **Visual Encoding**: If an image or PDF is detected, the engine reads the raw bytes and converts them into base64-encoded `InlineData` objects.
38
- - **PDF Extraction**: For PDF documents, the engine extracts visual representation of pages to provide the model with high-fidelity spatial and textual context simultaneously.
39
- - **Context Injection**: These multimodal assets are injected directly into the Gemini model's multimodal part array, allowing the model to "see" the file as if it were looking at a screenshot.
40
-
41
- ## The Dual-Model System
42
-
43
- To maintain a fast, snappy UI while still performing complex data management, Flux Flow employs two separate AI models for every interaction:
44
-
45
- ### 1. The Main Agent
46
- - **Responsibility**: Direct user interaction, reasoning, and tool execution.
47
- - **Behavior**: Streams text directly to the UI. It focuses entirely on solving the user's immediate problem or answering their question.
48
-
49
- ### 2. The Janitor (Background Process)
50
- - **Responsibility**: System maintenance, long-term memory extraction, and chat summarization.
51
- - **Behavior**: After the Main Agent finishes its loop, the entire context (User Prompt + Agent Raws) is sent to the Janitor model.
52
- - **Headless Operation**: The Janitor is explicitly instructed to be a "silent background system process" with "no mouth." It *only* outputs valid tool calls (e.g., updating the chat title or saving a new user preference to the persistent memory vault).
53
-
54
- ## Data Persistence & Safety
55
-
56
- - **High-Fidelity Lock**: Because both the UI and the Janitor model may attempt to write to the `history.json` file simultaneously, a Promise-based `WRITE_LOCK` (`src/utils/history.js`) is utilized. This prevents race conditions and ensures data integrity.
57
- - **Encryption**: User secrets and persistent memories (`secret/memories.json`) are handled by `src/utils/crypto.js` to ensure local privacy.
58
-
59
- ## Redirection & The Anchor Strategy
60
-
61
- To support data portability (e.g., storing all app data on an external encrypted drive), Flux Flow utilizes a synchronous "Anchor" strategy in `src/utils/paths.js`.
62
-
63
- - **Synchronous Pivot**: Because many core modules (History, Secrets, Usage) initialize their file paths as constants during module loading, the application must determine the "Actual" data root before anything else.
64
- - **Boot-Sequence Priority**: On every launch, `paths.js` performs a synchronous file system check for `~/.fluxflow/settings.json`. If a redirection path is found (`useExternalData: true`), it immediately overrides the global `DATA_DIR` constant for the entire process.
65
- - **Sub-Coordinate Resolution**: All secondary directories (`LOGS_DIR`, `SECRET_DIR`) are derived dynamically from the redirected `DATA_DIR`, ensuring that all session data flows to the external sanctuary without requiring individual configuration updates across the codebase.
1
+ # 🏛️ Architecture & Design
2
+
3
+ Flux Flow is built on a modern, reactive stack that brings web-like development paradigms to the terminal. It utilizes a custom agentic loop for reasoning and a unique dual-model system for background processing.
4
+
5
+ ## UI Layer: React & Ink
6
+
7
+ The entire terminal interface is built using **React** via the [Ink](https://github.com/vadimdemedes/ink) renderer.
8
+ - **Component-Based**: The UI is composed of isolated, reusable React components (`ChatLayout`, `StatusBar`, `CommandMenu`, `TerminalBox`, `ProfileForm`).
9
+ - **Reactive State**: The application uses React hooks (`useState`, `useEffect`) to manage user input, application mode, model selection, and the terminal's resizing events.
10
+ - **Zero-Render Overheads**: Critical performance trackers, like the session start time, are kept outside the React render cycle to maintain terminal responsiveness during high-speed AI text streaming.
11
+
12
+ ## The Agentic Loop
13
+
14
+ The core intelligence of Flux Flow resides in `src/utils/ai.js`. It does not rely on opaque third-party agent frameworks; instead, it uses a custom, highly transparent string-based protocol powered by an asynchronous generator (`async function*`). This approach allows for real-time UI updates while managing complex multi-step reasoning.
15
+
16
+ The execution flow of a single user prompt follows this loop:
17
+
18
+ 1. **Context Assembly**: The user's prompt is combined with the system instructions, temporary session context, persistent user memories, and the current chat history. If the history gets too large (e.g., >256k tokens) and compression is disabled, it is gracefully truncated. Some models/modes can go upto 400k context.
19
+ 2. **Stream Processing**: The main loop initiates a streaming request to the Gemini API (`client.models.generateContentStream`). It yields chunks of text and status updates directly back to the React UI as they arrive.
20
+ 3. **Detection & Tool Execution**: Once the stream completes for a given turn, the entire response is scanned for tool calls using a custom regex and bracket-balancing parser (looking for `tool:functions.tool_name(args...)`).
21
+ - If tools are found, the loop pauses.
22
+ - Each tool is dispatched to its respective handler in `src/tools/`.
23
+ - Tool outputs are collected and appended to the context as `[TOOL RESULT]: ...`.
24
+ 4. **Security Governance**: During tool execution, the loop enforces security checks (e.g., blocking `exec_command` from accessing system root drives if "External Workspace Access" is off) and pauses for Human-in-the-Loop (HITL) approval if necessary.
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
+ - 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
+ - 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 70 iterations per user prompt, while **Flow mode** is capped at 7.
29
+ - **Multi-Stage Failover**: The loop features a sophisticated 16-attempt retry engine with exponential backoff (1s - 32s).
30
+ - **Critical Fallback Pivot**: If the primary model fails 14 consecutive times, the agent surgically pivots to a lighter, high-concurrency fallback model (`gemini-3.1-flash-lite`) for the final 3 attempts to ensure session navigation through API congestion.
31
+
32
+ ## Multimodal Pipeline
33
+
34
+ Flux Flow implements a native multimodal processing engine in `src/tools/view_file.js`. This allows the agent to move beyond text-based reasoning and analyze visual assets directly (Only on supported models).
35
+
36
+ - **Binary Detection**: The pipeline uses `is-binary-path` to distinguish between text and binary files.
37
+ - **Visual Encoding**: If an image or PDF is detected, the engine reads the raw bytes and converts them into base64-encoded `InlineData` objects.
38
+ - **PDF Extraction**: For PDF documents, the engine extracts visual representation of pages to provide the model with high-fidelity spatial and textual context simultaneously.
39
+ - **Context Injection**: These multimodal assets are injected directly into the Gemini model's multimodal part array, allowing the model to "see" the file as if it were looking at a screenshot.
40
+
41
+ ## The Dual-Model System
42
+
43
+ To maintain a fast, snappy UI while still performing complex data management, Flux Flow employs two separate AI models for every interaction:
44
+
45
+ ### 1. The Main Agent
46
+ - **Responsibility**: Direct user interaction, reasoning, and tool execution.
47
+ - **Behavior**: Streams text directly to the UI. It focuses entirely on solving the user's immediate problem or answering their question.
48
+
49
+ ### 2. The Janitor (Background Process)
50
+ - **Responsibility**: System maintenance, long-term memory extraction, and chat summarization.
51
+ - **Behavior**: After the Main Agent finishes its loop, the entire context (User Prompt + Agent Raws) is sent to the Janitor model.
52
+ - **Headless Operation**: The Janitor is explicitly instructed to be a "silent background system process" with "no mouth." It *only* outputs valid tool calls (e.g., updating the chat title or saving a new user preference to the persistent memory vault).
53
+
54
+ ## Data Persistence & Safety
55
+
56
+ - **High-Fidelity Lock**: Because both the UI and the Janitor model may attempt to write to the `history.json` file simultaneously, a Promise-based `WRITE_LOCK` (`src/utils/history.js`) is utilized. This prevents race conditions and ensures data integrity.
57
+ - **Encryption**: User secrets and persistent memories (`secret/memories.json`) are handled by `src/utils/crypto.js` to ensure local privacy.
58
+
59
+ ## Redirection & The Anchor Strategy
60
+
61
+ To support data portability (e.g., storing all app data on an external encrypted drive), Flux Flow utilizes a synchronous "Anchor" strategy in `src/utils/paths.js`.
62
+
63
+ - **Synchronous Pivot**: Because many core modules (History, Secrets, Usage) initialize their file paths as constants during module loading, the application must determine the "Actual" data root before anything else.
64
+ - **Boot-Sequence Priority**: On every launch, `paths.js` performs a synchronous file system check for `~/.fluxflow/settings.json`. If a redirection path is found (`useExternalData: true`), it immediately overrides the global `DATA_DIR` constant for the entire process.
65
+ - **Sub-Coordinate Resolution**: All secondary directories (`LOGS_DIR`, `SECRET_DIR`) are derived dynamically from the redirected `DATA_DIR`, ensuring that all session data flows to the external sanctuary without requiring individual configuration updates across the codebase.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  ![Flux Flow Logo](https://github.com/KushalRoyChowdhury/fluxflow-cli/blob/main/fluxflow.png)
3
3
 
4
4
  <p align="left">
5
- <a href="https://github.com/KushalRoyChowdhury/fluxflow-cli"><img src="https://img.shields.io/badge/FluxFlow-v1.19.0-blue?style=plastic" alt="FluxFlow Version"></a>
5
+ <a href="https://github.com/KushalRoyChowdhury/fluxflow-cli"><img src="https://img.shields.io/badge/FluxFlow-v1.21-blue?style=plastic" alt="FluxFlow Version"></a>
6
6
  <a href="https://deepmind.google"><img src="https://img.shields.io/badge/Engine-Gemma%204-red?style=plastic" alt="Engine Gemma 4"></a>
7
7
  <a href="https://pollinations.ai"><img src="https://img.shields.io/badge/Built%20With-pollinations.ai-cyan?style=plastic" alt="Built With pollinations.ai"></a>
8
8
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=plastic" alt="License MIT"></a>
@@ -53,7 +53,6 @@ Flux Flow can now see! Use the `view_file` tool to analyze images (JPG, PNG) or
53
53
  Need a report or a presentation? Just ask. Flux Flow features a high-fidelity "Printing Press" that generates professional, branded documents natively:
54
54
  - **PDF**: Branded documents from HTML/CSS with automatic watermarking.
55
55
  - **DOCX**: Native Word documents with multi-page support and automatic numbering.
56
- - **PPTX**: High-fidelity PowerPoint presentations using native elements (selectable text, shapes) translated directly from HTML.
57
56
 
58
57
  ### ⏱️ **Codebase Time Travel (Git-less Reversion)**
59
58
  Refactor and build with absolute fearlessness. Flux Flow maintains transaction-based secure snapshots of files before they are generated or edited:
@@ -68,8 +67,8 @@ Zero setup means zero setup. On first run, Flux Flow performs an integrity check
68
67
  - **Rich Aesthetics**: High-contrast, sleek design with smooth transitions and micro-animations.
69
68
 
70
69
  ### 🧠 **The Dual-Intelligence System**
71
- - **Flux Mode (Dev)**: High-speed, agentic problem solving with a 50-turn persistent loop for massive coding tasks.
72
- - **Flow Mode (Chat)**: Optimized for deep research, high-quality conversation, and web-assisted reasoning.
70
+ - **Flux Mode (Dev)**: High-speed, agentic problem solving with a 70-turn persistent loop for massive coding tasks.
71
+ - **Flow Mode (Chat)**: Optimized for high-quality conversation and web-assisted reasoning.
73
72
 
74
73
  ### 🛡️ **Digital Fortress Governance**
75
74
  Security isn't an afterthought; it's a boundary.
package/TOOLS.md CHANGED
@@ -11,7 +11,6 @@ Flux Flow provides a robust set of tools that allow the AI to interact with the
11
11
  | **Generate Image** | ✅ | ❌ |
12
12
  | **Write PDF** | ✅ | ❌ |
13
13
  | **Write DOCX** | ✅ | ❌ |
14
- | **Write PPTX** | ✅ | ❌ |
15
14
  | **View/Read Files** | ✅ | ❌ |
16
15
  | **Write/Update Files** | ✅ | ❌ |
17
16
  | **Execute Commands** | ✅ | ❌ |
@@ -22,37 +21,33 @@ Flux Flow provides a robust set of tools that allow the AI to interact with the
22
21
  ## Core Tools
23
22
 
24
23
  ### 🌐 Web & Research
25
- - **`web_search`**: Uses DuckDuckGo to find up-to-date information on the internet. Crucial for answering questions about recent events or unlearned documentation.
26
- - **`web_scrape`**: Extracts the detailed text content from a specific URL, allowing the agent to read documentation or articles.
24
+ - **`WebSearch`**: Uses DuckDuckGo to find up-to-date information on the internet. Crucial for answering questions about recent events or unlearned documentation.
25
+ - **`WebScrape`**: Extracts the detailed text content from a specific URL, allowing the agent to read documentation or articles.
27
26
 
28
27
  ### 📄 Document Engineering (The Office Suite)
29
- - **`write_pdf`**: Generates high-fidelity, branded PDF documents from HTML/CSS. Features automatic watermarking and page-aware layout management.
30
- - **`write_docx`**: Generates professional Word documents (.docx) from HTML. Supports multi-page layouts, automatic page numbering, and native styling.
31
- - **`write_pptx`**: Generates high-fidelity, native PowerPoint presentations (.pptx) from HTML.
32
- - **Native Translation**: Uses `html2pptxgenjs` to translate HTML tags (`<h1>`, `<ul>`, `<b>`) directly into selectable PowerPoint text objects.
33
- - **Rich Styling**: Supports CSS-like properties (color, font-size, text-align) for professional slide design.
34
- - **Flat Protocol**: Optimized for AI stability using a flat HTML string interface.
28
+ - **`WritePDF`**: Generates high-fidelity, branded PDF documents from HTML/CSS. Features automatic watermarking and page-aware layout management.
29
+ - **`WriteDOCX`**: Generates professional Word documents (.docx) from HTML. Supports multi-page layouts, automatic page numbering, and native styling.
35
30
 
36
31
  ### 🎨 Creative & Visual
37
- - **`generate_image`**: Generates high-fidelity images using Pollinations AI.
32
+ - **`GenerateImage`**: Generates high-fidelity images using Pollinations AI.
38
33
  - **Customization**: Supports customizable models (Flux, ZImage, Qwen, Nanobanana-Pro, etc.), aspect ratios, custom prompt generation, and random seeds.
39
34
  - **Telemetry**: Tracks hourly credit usage (Low, Medium, Ultra, Premium tiers) with built-in daily limit checks and interactive dashboard displays.
40
35
 
41
36
  ### 📁 File System Operations
42
- - **`list_files`**: Lists the contents of a directory to help the agent understand the project structure.
43
- - **`read_folder`**: Provides detailed statistics and metadata about a directory's contents.
44
- - **`view_file`**: Reads the content of a file.
37
+ - **`ListFiles`**: Lists the contents of a directory to help the agent understand the project structure.
38
+ - **`ReadFolder`**: Provides detailed statistics and metadata about a directory's contents.
39
+ - **`ViewFile`**: Reads the content of a file.
45
40
  - **Native Multimodality**: Supports analyzing images (JPG, PNG, WEBP) and PDF documents. The tool automatically detects binary formats and encodes them for AI analysis.
46
41
  - **Text Reading**: Supports specific line ranges (`start_line`, `end_line`) to manage context size efficiently.
47
- - **`search_keyword`**: Performs a global project search for a specific string or keyword. Returns file paths and line numbers where matches are found, making it essential for navigation and impact analysis.
42
+ - **`SearchKeyword`**: Performs a global project search for a specific string or keyword. Returns file paths and line numbers where matches are found, making it essential for navigation and impact analysis.
48
43
 
49
44
  ### ✍️ Code Editing
50
- - **`write_file`**: Creates a new file or completely overwrites an existing one with new content.
51
- - **`update_file` (Smart Patching)**: Surgically replaces a specific block of text within a file.
45
+ - **`WriteFile`**: Creates a new file or completely overwrites an existing one with new content.
46
+ - **`UpdateFile` (Smart Patching)**: Surgically replaces a specific block of text within a file.
52
47
  - *Diff Generation*: It returns a high-fidelity visual diff (Red/Green changes with context lines) to the UI, allowing the user to see exactly what the agent modified.
53
48
 
54
49
  ### 💻 Terminal Execution
55
- - **`exec_command`**: Runs a shell command directly in the terminal using Node's `child_process.spawn`.
50
+ - **`Run`**: Runs a shell command directly in the terminal using Node's `child_process.spawn` or `node-pty` when available.
56
51
  - *Context Aware*: Runs in the current working directory.
57
52
  - *Cross-Platform*: Uses `shell: true` to handle Windows `.cmd`/`.bat` files natively.
58
53
 
package/UI_FEATURES.md CHANGED
@@ -6,8 +6,8 @@ Flux Flow is designed to be a high-performance terminal application. Beyond basi
6
6
 
7
7
  You can control the application using `/` commands directly in the chat input:
8
8
 
9
- - **/mode [flux|flow]**: Quickly switch between **Flux** (Dev) and **Flow** (Chat) modes. Using it without arguments opens the selection menu.
10
- - **/thinking [low|medium|high|max|show|hide]**: Adjust reasoning depth or toggle visibility of the thinking process. Using it without arguments opens the selection menu.
9
+ - **/mode [flux|flow]**: Quickly switch between **Flux** (Dev) and **Flow** (Chat) modes.
10
+ - **/thinking [fast|low|medium|high|max]**: Adjust reasoning depth.
11
11
  - **/model [name]**: Choose which AI model to use for the main interaction.
12
12
  - **/key**: Open the API Key management view to update or remove your credentials.
13
13
  - **/settings**: Access the system configuration menu.
@@ -26,23 +26,11 @@ Flux Flow allows you to "Anchor" your data outside of the default user directory
26
26
  - **Portability**: Once set, the application will synchronously "pivot" all data operations to your specified `externalDataPath` upon startup.
27
27
  - **Privacy**: Keeps sensitive data off your primary system drive.
28
28
 
29
-
30
- ### Command Shortcuts
31
-
32
- For power users, several commands support direct arguments to skip the menus:
33
- - ` /mode flux ` or ` /mode flow `
34
- - ` /thinking low ` / ` /thinking medium ` / ` /thinking high ` / ` /thinking max `
35
- - ` /thinking show ` / ` /thinking hide ` (Toggles thinking process visibility)
36
- - ` /model gemini-3.1-pro-preview ` (Switches model directly)
37
- - ` /update check ` (Checks for updates)
38
-
39
29
  ## 🧠 Thinking Levels & Visualization
40
30
 
41
31
  Flux Flow separates the model's "internal monologue" (reasoning) from its final response using `<think>` tags.
42
32
 
43
33
  - **Thinking Levels**: Depending on the mode, you can choose from **Fast**, **Low**, **Medium**, **High**, or **xHigh**. Higher levels allow the agent more "space" to reason through complex architecture or debugging problems.
44
- - **Show/Hide Thinking**: You can toggle the visibility of the thinking process using `/thinking show/hide`.
45
- - When **Hidden**, the agent doesn't just disappear; it provides a "minimalist" view showing only the core **Headings** and **Action Steps** (bolded lines) from its reasoning. This keeps you informed of its current "step" without cluttering the screen with detailed internal monologue.
46
34
 
47
35
  ## ⚡ Interactive Sub-Terminal
48
36
 
@@ -73,6 +61,8 @@ By default, the agent **cannot** execute dangerous actions without your consent.
73
61
  For power users, **Auto-Exec** can be enabled in `/settings`.
74
62
  - **⚠️ Warning**: This allows the agent to run any tool and execute any command autonomously.
75
63
  - **External Access**: You can also toggle whether the agent is allowed to access files outside of its current working directory.
64
+ - **Manual Commad Control**: Specify which commands you want the agent to auto-execute, manually approve or auto-deny.
65
+ - **Network Sandboxing**: Turn off network access in integrated terminal.
76
66
 
77
67
  ## 🔄 Steering & Resolution
78
68
 
package/dist/fluxflow.js CHANGED
@@ -940,9 +940,13 @@ var StatusBar, StatusBar_default;
940
940
  var init_StatusBar = __esm({
941
941
  "src/components/StatusBar.jsx"() {
942
942
  init_text();
943
- StatusBar = React4.memo(({ mode, thinkingLevel, tokens = "0.0k", tokensTotal = "0.0k", chatId = "NEW-SESSION", isMemoryEnabled = true, apiTier = "Free" }) => {
943
+ StatusBar = React4.memo(({ mode, thinkingLevel, tokens = "0.0k", tokensTotal = "0.0k", chatId = "NEW-SESSION", isMemoryEnabled = true, apiTier = "Free", aiProvider = "Google" }) => {
944
944
  const modeColor = mode === "Flux" ? "yellow" : "cyan";
945
945
  const modeIcon = mode === "Flux" ? "\u26A1" : "\u{1F30A}";
946
+ let maxLimit = 256e3;
947
+ if (aiProvider === "DeepSeek" || aiProvider === "Google" && apiTier === "Paid") {
948
+ maxLimit = 4e5;
949
+ }
946
950
  return /* @__PURE__ */ React4.createElement(
947
951
  Box4,
948
952
  {
@@ -955,7 +959,7 @@ var init_StatusBar = __esm({
955
959
  },
956
960
  /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Box4, { marginRight: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: modeColor, bold: true }, modeIcon, " ", mode.toUpperCase())), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React4.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "magenta" }, "\u{1F9E0} ", thinkingLevel)), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React4.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "MEM: "), /* @__PURE__ */ React4.createElement(Text4, { color: isMemoryEnabled ? "green" : "red", bold: true }, isMemoryEnabled ? "ON" : "OFF"))),
957
961
  /* @__PURE__ */ React4.createElement(Box4, { flexGrow: 1, justifyContent: "center", paddingX: 2 }, /* @__PURE__ */ React4.createElement(Text4, null, "\u{1F4C1}"), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", italic: true }, " ", truncatePath(process.cwd(), 35))),
958
- /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React4.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, "\u2728"), /* @__PURE__ */ React4.createElement(Text4, { color: "blue" }, " ", formatTokens(tokensTotal), " ", /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "(", (tokens / 254e3 * 100).toFixed(0), "%)"))), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React4.createElement(Box4, { marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, "\u{1F194}"), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true, italic: true }, " ", chatId), (apiTier === "Custom" || apiTier === "Paid") && /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, " | ", /* @__PURE__ */ React4.createElement(Text4, { color: "green", bold: true }, "PAID"))))
962
+ /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React4.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, "\u2728"), /* @__PURE__ */ React4.createElement(Text4, { color: "blue" }, " ", formatTokens(tokensTotal), " ", /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "(", (tokens / maxLimit * 100).toFixed(0), "%)"))), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React4.createElement(Box4, { marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, "\u{1F194}"), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true, italic: true }, " ", chatId), (apiTier === "Custom" || apiTier === "Paid") && /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true }, " | ", /* @__PURE__ */ React4.createElement(Text4, { color: "green", bold: true }, "PAID"))))
959
963
  );
960
964
  });
961
965
  StatusBar_default = StatusBar;
@@ -3648,7 +3652,7 @@ var view_file;
3648
3652
  var init_view_file = __esm({
3649
3653
  "src/tools/view_file.js"() {
3650
3654
  init_arg_parser();
3651
- view_file = async (args) => {
3655
+ view_file = async (args, context = {}) => {
3652
3656
  let { path: targetPath, StartLine, EndLine, start_line, end_line, startLine, endLine } = parseArgs(args);
3653
3657
  const sLine = parseInt(StartLine || start_line || startLine);
3654
3658
  const eLine = parseInt(EndLine || end_line || endLine);
@@ -3680,11 +3684,15 @@ var init_view_file = __esm({
3680
3684
  ".doc": "application/msword"
3681
3685
  };
3682
3686
  if (mimeMap[ext]) {
3687
+ const isMultiModal = context.isMultiModal !== false;
3688
+ if (!isMultiModal) {
3689
+ return `ERROR: Multimodality is not supported for the current model. Unable to load [${targetPath}].`;
3690
+ }
3683
3691
  const buffer = fs8.readFileSync(absolutePath);
3684
3692
  const base64 = buffer.toString("base64");
3685
3693
  const mimeType = mimeMap[ext];
3686
3694
  return {
3687
- text: `[BINARY_FILE]: ${targetPath} (${mimeType}) - Loaded as multimodal part.`,
3695
+ text: `[BINARY FILE]: ${targetPath} (${mimeType}) - Loaded as multimodal part.`,
3688
3696
  binaryPart: {
3689
3697
  inlineData: {
3690
3698
  data: base64,
@@ -4637,7 +4645,7 @@ var init_generate_image = __esm({
4637
4645
  return buffer;
4638
4646
  }
4639
4647
  };
4640
- generate_image = async (args) => {
4648
+ generate_image = async (args, context = {}) => {
4641
4649
  const parsed = parseArgs(args);
4642
4650
  const prompt = parsed.prompt || parsed.text;
4643
4651
  const outputPath = parsed.path || parsed.outputPath || parsed.output || "generated_image.png";
@@ -4765,6 +4773,10 @@ var init_generate_image = __esm({
4765
4773
  ".webp": "image/webp"
4766
4774
  };
4767
4775
  const mimeType = mimeMap[ext] || "image/png";
4776
+ const isMultiModal = context.isMultiModal !== false;
4777
+ if (!isMultiModal) {
4778
+ return `SUCCESS: Image successfully generated from prompt [${prompt}] and saved to [${outputPath}].`;
4779
+ }
4768
4780
  return {
4769
4781
  text: `SUCCESS: Image successfully generated from prompt [${prompt}] and saved to [${outputPath}]. Output attached to multimodal part`,
4770
4782
  binaryPart: {
@@ -4949,7 +4961,7 @@ var init_tools = __esm({
4949
4961
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
4950
4962
  import path16 from "path";
4951
4963
  import fs17 from "fs";
4952
- var client, TERMINATION_SIGNAL, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getOpenRouterStream, signalTermination, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, getAIStream;
4964
+ var client, TERMINATION_SIGNAL, MULTIMODAL_MODELS, isModelMultimodal, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getOpenRouterStream, signalTermination, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, getAIStream;
4953
4965
  var init_ai = __esm({
4954
4966
  async "src/utils/ai.js"() {
4955
4967
  await init_prompts();
@@ -4963,6 +4975,34 @@ var init_ai = __esm({
4963
4975
  init_revert();
4964
4976
  client = null;
4965
4977
  TERMINATION_SIGNAL = false;
4978
+ MULTIMODAL_MODELS = [
4979
+ // OpenRouter models
4980
+ "google/gemma-4-31b-it:free",
4981
+ "moonshotai/kimi-k2.6:free",
4982
+ "google/gemini-3.5-flash",
4983
+ "qwen/qwen3.7-plus",
4984
+ "minimax/minimax-m3",
4985
+ "anthropic/claude-sonnet-4.5",
4986
+ "anthropic/claude-opus-4.6",
4987
+ "anthropic/claude-opus-4.8",
4988
+ "openai/gpt-5.2-codex",
4989
+ "openai/gpt-5.2-pro",
4990
+ "openai/gpt-5.5-pro",
4991
+ "moonshotai/kimi-k2.6",
4992
+ // Google models
4993
+ "gemma-4-31b-it",
4994
+ "gemini-2.5-flash",
4995
+ "gemini-3-flash-preview",
4996
+ "gemini-3.5-flash",
4997
+ "gemini-3.1-flash-lite",
4998
+ "gemini-3.1-pro-preview"
4999
+ ];
5000
+ isModelMultimodal = (model) => {
5001
+ if (!model) return false;
5002
+ const lower = model.toLowerCase();
5003
+ if (lower.startsWith("gemini-") || lower.startsWith("gemma-")) return true;
5004
+ return MULTIMODAL_MODELS.some((m) => m.toLowerCase() === lower);
5005
+ };
4966
5006
  stripAnsi2 = (str) => {
4967
5007
  if (typeof str !== "string") return str;
4968
5008
  return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
@@ -5099,7 +5139,7 @@ var init_ai = __esm({
5099
5139
  } catch (e) {
5100
5140
  }
5101
5141
  }
5102
- if (Date.now() - lastFlushTime >= 100 && hasNewData) {
5142
+ if (Date.now() - lastFlushTime >= 150 && hasNewData) {
5103
5143
  yield {
5104
5144
  candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
5105
5145
  usageMetadata: latestUsageMetadata
@@ -5236,7 +5276,7 @@ var init_ai = __esm({
5236
5276
  } catch (e) {
5237
5277
  }
5238
5278
  }
5239
- if (Date.now() - lastFlushTime >= 100 && hasNewData) {
5279
+ if (Date.now() - lastFlushTime >= 150 && hasNewData) {
5240
5280
  yield {
5241
5281
  candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
5242
5282
  usageMetadata: latestUsageMetadata
@@ -5789,12 +5829,12 @@ ${newMemoryListStr}
5789
5829
  }
5790
5830
  }
5791
5831
  let attempts = 0;
5792
- const maxAttempts = 3;
5832
+ const maxAttempts = 5;
5793
5833
  let success = false;
5794
- let targetModel = "gemini-3.1-flash-lite";
5834
+ let targetModel = "gemma-4-26b-a4b-it";
5795
5835
  if (aiProvider === "OpenRouter") targetModel = "google/gemma-4-26b-a4b-it:free";
5796
5836
  if (aiProvider === "DeepSeek") targetModel = "deepseek-v4-flash";
5797
- while (attempts < maxAttempts && !success) {
5837
+ while (attempts <= maxAttempts && !success) {
5798
5838
  attempts++;
5799
5839
  try {
5800
5840
  const response = await generateSimpleContent(settings, targetModel, prompt, null, "Fast");
@@ -5830,7 +5870,8 @@ ${newMemoryListStr}
5830
5870
  }
5831
5871
  };
5832
5872
  getAIStream = async function* (modelName, history, settings, steeringCallback, versionFluxflow2) {
5833
- const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats, aiProvider = "Google", isMultiModal } = settings;
5873
+ const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats, aiProvider = "Google", apiTier } = settings;
5874
+ const isMultiModal = isModelMultimodal(modelName);
5834
5875
  if (!client && aiProvider === "Google") throw new Error("AI not initialized");
5835
5876
  const isMemoryEnabled = systemSettings?.memory !== false;
5836
5877
  const originalText = history[history.length - 1].text;
@@ -5844,8 +5885,14 @@ ${newMemoryListStr}
5844
5885
  await RevertManager.startTransaction(chatId, agentText);
5845
5886
  try {
5846
5887
  let modifiedHistory = [...history.slice(0, -1)];
5847
- if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > 244e3) {
5848
- yield { type: "status", content: "Condensing session context..." };
5888
+ let contextCompressionCount = 252e3;
5889
+ let contextTruncationCount = 254e3;
5890
+ if (aiProvider === "DeepSeek" || aiProvider === "Google" && apiTier === "Paid") {
5891
+ contextCompressionCount = 396e3;
5892
+ contextTruncationCount = 4e5;
5893
+ }
5894
+ if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > contextCompressionCount) {
5895
+ yield { type: "status_history", content: "Context Limit Reached. Condensing session history..." };
5849
5896
  const flattenContext = (hist) => {
5850
5897
  return hist.filter((m) => (m.role === "user" || m.role === "agent" || m.role === "system") && !String(m.id).startsWith("welcome") && !m.isMeta).map((m) => {
5851
5898
  const role = m.text?.startsWith("[TOOL RESULT]") ? "TOOL" : m.role === "agent" ? "AGENT" : "USER";
@@ -5864,23 +5911,32 @@ Provide a new consolidated summary of the entire session.` : `Here is the conver
5864
5911
  ${flattenedText2}
5865
5912
 
5866
5913
  Provide a consolidated summary of the entire session.`;
5867
- let targetModel = "gemini-3.1-flash-lite";
5914
+ let targetModel = "gemma-4-26b-a4b-it";
5868
5915
  if (aiProvider === "OpenRouter") targetModel = "google/gemma-4-26b-a4b-it:free";
5869
5916
  if (aiProvider === "DeepSeek") targetModel = "deepseek-v4-flash";
5870
- try {
5871
- const response = await generateSimpleContent(settings, targetModel, prompt, systemInstruction, "Fast");
5872
- return response.text || "";
5873
- } catch (err) {
5874
- if (aiProvider === "Google") {
5875
- try {
5876
- const fallback = await generateSimpleContent(settings, "gemini-2.5-flash", prompt, systemInstruction, "Fast");
5877
- return fallback.text || "";
5878
- } catch (e) {
5917
+ let attempts = 0;
5918
+ let success = false;
5919
+ let response = null;
5920
+ while (attempts <= 3 && !success) {
5921
+ attempts++;
5922
+ try {
5923
+ response = await generateSimpleContent(settings, targetModel, prompt, systemInstruction, "Fast");
5924
+ success = true;
5925
+ } catch (err) {
5926
+ if (attempts > 3) {
5927
+ if (aiProvider === "Google") {
5928
+ try {
5929
+ const fallback = await generateSimpleContent(settings, "gemini-3.1-flash-lite", prompt, systemInstruction, "Fast");
5930
+ return fallback.text || "";
5931
+ } catch (e) {
5932
+ return "";
5933
+ }
5934
+ }
5879
5935
  return "";
5880
5936
  }
5881
5937
  }
5882
- return "";
5883
5938
  }
5939
+ return response ? response.text || "" : "";
5884
5940
  };
5885
5941
  const flattenedText = flattenContext(modifiedHistory);
5886
5942
  const summaries2 = readEncryptedJson(summariesFile, {});
@@ -5905,9 +5961,6 @@ Provide a consolidated summary of the entire session.`;
5905
5961
  wasCompressedInStream = true;
5906
5962
  }
5907
5963
  }
5908
- if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > 254e3) {
5909
- modifiedHistory = getTruncatedHistory(modifiedHistory, 6);
5910
- }
5911
5964
  if (isFirstPrompt && isMemoryEnabled) {
5912
5965
  yield { type: "status", content: "Condensing past chat memories..." };
5913
5966
  await consolidatePastMemories(chatId, settings);
@@ -6206,6 +6259,9 @@ ${thinkingLevel != "Fast" && aiProvider === "Google" ? `${modelName.toLowerCase(
6206
6259
  }
6207
6260
  });
6208
6261
  for (let loop = 0; loop <= MAX_LOOPS; loop++) {
6262
+ if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > contextTruncationCount) {
6263
+ modifiedHistory = getTruncatedHistory(modifiedHistory, 6);
6264
+ }
6209
6265
  if (loop > 0) {
6210
6266
  yield { type: "status", content: "Processed. Reconnecting..." };
6211
6267
  }
@@ -6265,7 +6321,7 @@ ${thinkingLevel != "Fast" && aiProvider === "Google" ? `${modelName.toLowerCase(
6265
6321
  }
6266
6322
  const contents = modifiedHistory.filter((msg) => (msg.role === "user" || msg.role === "agent" || msg.role === "system") && !String(msg.id).startsWith("welcome") && !msg.isMeta && !msg.isTerminalRecord && !(msg.text && msg.text.startsWith("[TERMINAL_RECORD]"))).map((msg, idx, arr) => {
6267
6323
  const parts = [{ text: msg.text }];
6268
- if (msg.binaryPart) {
6324
+ if (msg.binaryPart && isModelMultimodal(targetModel)) {
6269
6325
  const physicalUserTurnsAfter = arr.slice(idx + 1).filter((m) => m.role === "user" && !m.text?.startsWith("[TOOL RESULT]")).length;
6270
6326
  if (physicalUserTurnsAfter <= 2) {
6271
6327
  parts.push(msg.binaryPart);
@@ -6628,7 +6684,7 @@ ${thinkingLevel != "Fast" && aiProvider === "Google" ? `${modelName.toLowerCase(
6628
6684
  await new Promise((resolve) => setTimeout(resolve, 3e3));
6629
6685
  break;
6630
6686
  }
6631
- const toolActionableText = turnText.replace(/(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\]|$)/gi, "");
6687
+ const toolActionableText = turnText.replace(/(?:<(think|thought|thoughts)>|\[(think|thought|thoughts)\])[\s\S]*?(?:<\/(think|thought|thoughts)>|\[\/(think|thought|thoughts)\]|$)/gi, "");
6632
6688
  const allToolsFound = detectToolCalls(toolActionableText);
6633
6689
  while (allToolsFound.length > toolCallPointer) {
6634
6690
  const toolCall = allToolsFound[toolCallPointer];
@@ -6890,7 +6946,8 @@ ${boxBottom}` };
6890
6946
  onChunk: (chunk2) => settings.onExecChunk ? settings.onExecChunk(chunk2) : null,
6891
6947
  onAskUser: settings.onAskUser,
6892
6948
  systemSettings: settings.systemSettings,
6893
- mode
6949
+ mode,
6950
+ isMultiModal: isModelMultimodal(targetModel)
6894
6951
  });
6895
6952
  yield { type: "spinner", content: true };
6896
6953
  if (process.stdout.isTTY) {
@@ -6948,7 +7005,7 @@ ${boxBottom}` };
6948
7005
  if (normToolName === "memory" && result.includes("SUCCESS")) yield { type: "memory_updated" };
6949
7006
  toolCallPointer++;
6950
7007
  }
6951
- if (aiProvider === "Google" && pendingGoogleText && Date.now() - lastGoogleFlushTime >= 100) {
7008
+ if (aiProvider === "Google" && pendingGoogleText && Date.now() - lastGoogleFlushTime >= 150) {
6952
7009
  yield { type: "text", content: pendingGoogleText };
6953
7010
  pendingGoogleText = "";
6954
7011
  lastGoogleFlushTime = Date.now();
@@ -9185,7 +9242,7 @@ ${timestamp}` };
9185
9242
  let isFirstPacket = true;
9186
9243
  try {
9187
9244
  const rawHistory = [...messages, userMessage].filter(
9188
- (m) => m.role !== "think" && !m.isVisualFeedback && !String(m.id).startsWith("welcome")
9245
+ (m) => m.role !== "think" && !m.isVisualFeedback && !m.isMeta && !String(m.id).startsWith("welcome")
9189
9246
  );
9190
9247
  const cleanHistoryForAI = [];
9191
9248
  rawHistory.forEach((m, idx) => {
@@ -9208,9 +9265,6 @@ ${timestamp}` };
9208
9265
  text
9209
9266
  });
9210
9267
  });
9211
- const modelCmd = COMMANDS.find((c) => c.cmd === "/model");
9212
- const currentModelObj = modelCmd?.subs?.find((s) => s.cmd === activeModel);
9213
- const isMultiModal = currentModelObj?.desc?.toLowerCase().includes("multimodal");
9214
9268
  const stream = getAIStream(
9215
9269
  activeModel,
9216
9270
  cleanHistoryForAI,
@@ -9222,9 +9276,9 @@ ${timestamp}` };
9222
9276
  janitorModel,
9223
9277
  sessionStats,
9224
9278
  chatId,
9225
- isMultiModal,
9226
9279
  aiProvider,
9227
9280
  apiKey,
9281
+ apiTier,
9228
9282
  cols: terminalSize.columns - 6,
9229
9283
  rows: 30,
9230
9284
  onExecStart: (cmd) => {
@@ -9372,6 +9426,11 @@ Selection: ${val}`,
9372
9426
  setStatusText(packet.content);
9373
9427
  continue;
9374
9428
  }
9429
+ if (packet.type === "status_history") {
9430
+ setStatusText(packet.content);
9431
+ setMessages((prev) => [...prev, { id: "condense-" + Date.now(), role: "system", text: `\u2699\uFE0F [SYSTEM] ${packet.content}`, isMeta: true }]);
9432
+ continue;
9433
+ }
9375
9434
  if (packet.type === "spinner") {
9376
9435
  setIsSpinnerActive(packet.content);
9377
9436
  continue;
@@ -10298,7 +10357,8 @@ Selection: ${val}`,
10298
10357
  tokensTotal: sessionStats.tokens,
10299
10358
  chatId,
10300
10359
  isMemoryEnabled: systemSettings.memory,
10301
- apiTier
10360
+ apiTier,
10361
+ aiProvider
10302
10362
  }
10303
10363
  )), activeView === "exit" && (() => {
10304
10364
  const wallTimeMs = Date.now() - SESSION_START_TIME;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.21.1",
3
+ "version": "1.21.3",
4
4
  "date": "2026-06-06",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [