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 +65 -65
- package/README.md +3 -4
- package/TOOLS.md +12 -17
- package/UI_FEATURES.md +4 -14
- package/dist/fluxflow.js +98 -38
- package/package.json +1 -1
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., >
|
|
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
|
|
30
|
-
- **Critical Fallback Pivot**: If the primary model fails
|
|
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
|

|
|
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.
|
|
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
|
|
72
|
-
- **Flow Mode (Chat)**: Optimized for
|
|
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
|
-
- **`
|
|
26
|
-
- **`
|
|
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
|
-
- **`
|
|
30
|
-
- **`
|
|
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
|
-
- **`
|
|
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
|
-
- **`
|
|
43
|
-
- **`
|
|
44
|
-
- **`
|
|
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
|
-
- **`
|
|
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
|
-
- **`
|
|
51
|
-
- **`
|
|
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
|
-
- **`
|
|
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.
|
|
10
|
-
- **/thinking [low|medium|high|max
|
|
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 /
|
|
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: `[
|
|
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 >=
|
|
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 >=
|
|
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 =
|
|
5832
|
+
const maxAttempts = 5;
|
|
5793
5833
|
let success = false;
|
|
5794
|
-
let targetModel = "
|
|
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
|
|
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",
|
|
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
|
-
|
|
5848
|
-
|
|
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 = "
|
|
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
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
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 >=
|
|
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;
|