fluxflow-cli 3.10.0 → 3.11.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.
package/dist/fluxflow.js CHANGED
@@ -462,7 +462,8 @@ var init_settings = __esm({
462
462
  externalDataPath: "",
463
463
  preserveThinking: true,
464
464
  loadingPhrases: true,
465
- progressiveRendering: false
465
+ progressiveRendering: true,
466
+ showTPMEstimate: false
466
467
  },
467
468
  profileData: {
468
469
  name: null,
@@ -5671,15 +5672,58 @@ var init_StatusBar = __esm({
5671
5672
  }
5672
5673
  return "#ff0000";
5673
5674
  };
5674
- StatusBar = React5.memo(({ mode, thinkingLevel, tokens = "0.0k", tokensTotal = "0.0k", chatId = "NEW-SESSION", isMemoryEnabled = true, apiTier = "Free", aiProvider = "Google", activeModel = "", isProcessing = false, lastChunkTime = 0, theme = "Dark" }) => {
5675
+ StatusBar = React5.memo(({ mode, thinkingLevel, tokens = "0.0k", tokensTotal = "0.0k", chatId = "NEW-SESSION", isMemoryEnabled = true, apiTier = "Free", aiProvider = "Google", activeModel = "", isProcessing = false, lastChunkTime = 0, theme = "Dark", wps = 0, showTPMEstimate = false }) => {
5675
5676
  const colors = getThemeColors(theme);
5676
5677
  const modeIcon = mode === "Flux" ? "" : "";
5677
5678
  const [memoryUsage, setMemoryUsage] = useState5(0);
5678
5679
  const [memoryLimit, setMemoryLimit] = useState5(0);
5679
5680
  const [memoryUnit, setMemoryUnit] = useState5("MB");
5680
5681
  const [dotColor, setDotColor] = useState5("green");
5682
+ const [displayedWps, setDisplayedWps] = useState5(0);
5681
5683
  const chunkTimesRef = useRef3([]);
5682
5684
  const smoothedDelayRef = useRef3(370);
5685
+ const wpsHistoryRef = useRef3([]);
5686
+ const lastChunkTimeRef = useRef3(lastChunkTime);
5687
+ useEffect4(() => {
5688
+ lastChunkTimeRef.current = lastChunkTime;
5689
+ }, [lastChunkTime]);
5690
+ useEffect4(() => {
5691
+ if (!isProcessing) {
5692
+ wpsHistoryRef.current = [];
5693
+ return;
5694
+ }
5695
+ if (wps > 0) {
5696
+ const history = wpsHistoryRef.current;
5697
+ history.push(wps);
5698
+ if (history.length > 3) {
5699
+ history.shift();
5700
+ }
5701
+ }
5702
+ }, [isProcessing, wps, lastChunkTime]);
5703
+ useEffect4(() => {
5704
+ if (!isProcessing) {
5705
+ setDisplayedWps(0);
5706
+ return;
5707
+ }
5708
+ const timer = setInterval(() => {
5709
+ const lastTime = lastChunkTimeRef.current;
5710
+ const timeSinceLast = lastTime > 0 ? Date.now() - lastTime : 0;
5711
+ const isStalled = lastTime > 0 && timeSinceLast > 2500;
5712
+ if (isStalled) {
5713
+ setDisplayedWps(0);
5714
+ } else {
5715
+ const history = wpsHistoryRef.current;
5716
+ if (history.length > 0) {
5717
+ const sum = history.reduce((acc, val) => acc + val, 0);
5718
+ const avg = Math.round(sum / history.length * 10) / 10;
5719
+ setDisplayedWps(avg);
5720
+ } else if (wps > 0) {
5721
+ setDisplayedWps(wps);
5722
+ }
5723
+ }
5724
+ }, 1350);
5725
+ return () => clearInterval(timer);
5726
+ }, [isProcessing]);
5683
5727
  useEffect4(() => {
5684
5728
  if (!isProcessing) {
5685
5729
  chunkTimesRef.current = [];
@@ -5760,13 +5804,12 @@ var init_StatusBar = __esm({
5760
5804
  paddingX: 1,
5761
5805
  width: "100%"
5762
5806
  },
5763
- /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Box4, { marginRight: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text, bold: true }, mode.toUpperCase())), /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text, bold: true }, thinkingLevel.toUpperCase())), isMemoryEnabled && /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text, dimColor: true, bold: true }, "MEMORY")))),
5764
- /* @__PURE__ */ React5.createElement(Box4, { flexGrow: 1, justifyContent: "center", paddingX: 2 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text, italic: true }, truncatePath(process.cwd(), 35))),
5765
- /* @__PURE__ */ React5.createElement(Box4, null, isProcessing ? /* @__PURE__ */ React5.createElement(Box4, { marginRight: 0 }, /* @__PURE__ */ React5.createElement(Text5, { color: dotColor }, "\u25CF")) : /* @__PURE__ */ React5.createElement(Text5, null, " "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text }, formatTokens(tokensTotal), " ", (() => {
5807
+ /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Box4, { marginRight: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text, bold: true }, mode.toUpperCase())), /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text, italic: true }, truncatePath(process.cwd(), 35))), isMemoryEnabled && /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "row" }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text, dimColor: true, bold: true }, "MEMORY")))),
5808
+ /* @__PURE__ */ React5.createElement(Box4, null, isProcessing ? /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: dotColor }, "\u25CF"), showTPMEstimate && /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, bold: true }, " ", displayedWps, " tps"), /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, " \u2503"))) : null, /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.text }, formatTokens(tokensTotal), " ", (() => {
5766
5809
  const pct = tokens / maxLimit * 100;
5767
5810
  const color = pct < 60 ? colors.text : pct < 80 ? colors.warning : colors.danger;
5768
5811
  return /* @__PURE__ */ React5.createElement(Text5, { color, dimColor: true }, pct.toFixed(0), "%");
5769
- })())), /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, bold: true }, memoryUsage, "/", memoryLimit, " ", memoryUnit)), /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginLeft: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, bold: true }, chatId), (apiTier === "Custom" || apiTier === "Paid") && /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, " \u2503 "), /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, bold: true }, "PAID"))))
5812
+ })())), /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, "\u2503"), /* @__PURE__ */ React5.createElement(Box4, { marginLeft: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, bold: true }, memoryUsage, "/", memoryLimit, " ", memoryUnit), (apiTier === "Custom" || apiTier === "Paid") && /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, dimColor: true }, " \u2503 "), /* @__PURE__ */ React5.createElement(Text5, { color: colors.textMuted, bold: true }, "PAID"))))
5770
5813
  );
5771
5814
  });
5772
5815
  StatusBar_default = StatusBar;
@@ -6765,7 +6808,8 @@ function SettingsMenu({
6765
6808
  return [
6766
6809
  { label: "Theme", value: "theme", status: systemSettings.theme || "Dark" },
6767
6810
  { label: "Loading Phrases", value: "loadingPhrases", status: systemSettings.loadingPhrases !== false ? "ON" : "OFF" },
6768
- { label: "Progressive Rendering [EXPERIMENTAL]", value: "progressiveRendering", status: systemSettings.progressiveRendering ? "ON" : "OFF" }
6811
+ { label: "Progressive Rendering [EXPERIMENTAL]", value: "progressiveRendering", status: systemSettings.progressiveRendering ? "ON" : "OFF" },
6812
+ { label: "Show TPM Estimate", value: "showTPMEstimate", status: systemSettings.showTPMEstimate ? "ON" : "OFF" }
6769
6813
  ];
6770
6814
  case "memory":
6771
6815
  return [
@@ -6968,6 +7012,12 @@ function SettingsMenu({
6968
7012
  saveSettings2({ systemSettings: newSysSettings, apiTier, quotas });
6969
7013
  return newSysSettings;
6970
7014
  });
7015
+ } else if (item.value === "showTPMEstimate") {
7016
+ setSystemSettings((s) => {
7017
+ const newSysSettings = { ...s, showTPMEstimate: !s.showTPMEstimate };
7018
+ saveSettings2({ systemSettings: newSysSettings, apiTier, quotas });
7019
+ return newSysSettings;
7020
+ });
6971
7021
  } else if (item.value === "theme") {
6972
7022
  const options = ["Dark", "Light", "GitHub Dark", "GitHub Light", "Transparent Dark", "Transparent Light", "Chaos"];
6973
7023
  const activeTheme = systemSettings.theme === "Mystery" ? "Chaos" : systemSettings.theme || "Dark";
@@ -7481,7 +7531,7 @@ ${projectContextBlock}
7481
7531
  - Language: Same as User Query
7482
7532
  - NO CHAT **AFTER** FIRING TOOLS IN CURRENT TURN
7483
7533
  - Short headsup summary of actions before firing tools
7484
- - Task Complete? End response with summary of changes made (with reason) and files edited
7534
+ - Task Complete? End response with summary of changes made (with reason) and files edited (if any)
7485
7535
  - Basic LaTeX${mode === "Flux" ? "" : ".\nUse Kaomojis HEAVILY"}
7486
7536
  === END SYSTEM PROMPT ===`.trim();
7487
7537
  };
@@ -13311,14 +13361,19 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
13311
13361
  throw new Error("Subagent task was cancelled.");
13312
13362
  }
13313
13363
  }
13314
- if (settings && typeof settings.onTokenChunk === "function") {
13315
- settings.onTokenChunk();
13316
- }
13364
+ let chunkText = "";
13317
13365
  if (chunk.candidates?.[0]?.content?.parts) {
13318
13366
  for (const part of chunk.candidates[0].content.parts) {
13319
- if (part.text && !part.thought) fullText += part.text;
13367
+ if (part.text && !part.thought) {
13368
+ fullText += part.text;
13369
+ chunkText += part.text;
13370
+ }
13320
13371
  }
13321
13372
  }
13373
+ const chunkWordCount = chunkText ? chunkText.trim().split(/\s+/).filter(Boolean).length : 0;
13374
+ if (settings && typeof settings.onTokenChunk === "function") {
13375
+ settings.onTokenChunk(chunkText, chunkWordCount);
13376
+ }
13322
13377
  if (chunk.usageMetadata) usageMetadata = chunk.usageMetadata;
13323
13378
  }
13324
13379
  } finally {
@@ -14445,7 +14500,6 @@ ${ideErr} [/ERROR]`;
14445
14500
  config: {
14446
14501
  systemInstruction: currentSystemInstruction,
14447
14502
  mediaResolution: "MEDIA_RESOLUTION_MEDIUM",
14448
- temperature: 1.05,
14449
14503
  safetySettings: [
14450
14504
  { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
14451
14505
  { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
@@ -14669,18 +14723,6 @@ ${ideErr} [/ERROR]`;
14669
14723
  yield chunk;
14670
14724
  continue;
14671
14725
  }
14672
- if (settings && typeof settings.onTokenChunk === "function") {
14673
- settings.onTokenChunk();
14674
- }
14675
- if (isFirstChunk) {
14676
- yield { type: "status", content: "Thinking" };
14677
- isFirstChunk = false;
14678
- }
14679
- if (TERMINATION_SIGNAL) {
14680
- yield { type: "status", content: "Request Cancelled" };
14681
- yield { type: "text", content: "\n\n\x1B[33m\u24D8 Request Cancelled\x1B[0m" };
14682
- break;
14683
- }
14684
14726
  let chunkText = "";
14685
14727
  const parts = chunk.candidates?.[0]?.content?.parts;
14686
14728
  if (parts && parts.length > 0) {
@@ -14709,6 +14751,19 @@ ${ideErr} [/ERROR]`;
14709
14751
  }
14710
14752
  chunkText += t;
14711
14753
  }
14754
+ const chunkWordCount = chunkText ? chunkText.trim().split(/\s+/).filter(Boolean).length : 0;
14755
+ if (settings && typeof settings.onTokenChunk === "function") {
14756
+ settings.onTokenChunk(chunkText, chunkWordCount);
14757
+ }
14758
+ if (isFirstChunk) {
14759
+ yield { type: "status", content: "Thinking" };
14760
+ isFirstChunk = false;
14761
+ }
14762
+ if (TERMINATION_SIGNAL) {
14763
+ yield { type: "status", content: "Request Cancelled" };
14764
+ yield { type: "text", content: "\n\n\x1B[33m\u24D8 Request Cancelled\x1B[0m" };
14765
+ break;
14766
+ }
14712
14767
  if (chunkText) {
14713
14768
  if (isDedupeActive) {
14714
14769
  dedupeBuffer += chunkText;
@@ -17507,6 +17562,8 @@ function App({ args = [] }) {
17507
17562
  });
17508
17563
  activeStreamingMsgRef.current = null;
17509
17564
  }
17565
+ chunkWordCountRef.current = 0;
17566
+ streamingWordStatsRef.current = { totalWords: 0, startTime: 0, wps: 0 };
17510
17567
  };
17511
17568
  const startTypewriter = () => {
17512
17569
  if (typewriterTickRef.current) {
@@ -17516,12 +17573,16 @@ function App({ args = [] }) {
17516
17573
  typewriterTickRef.current = setInterval(() => {
17517
17574
  const queue = typewriterQueueRef.current;
17518
17575
  if (queue.length > 0 && activeStreamingMsgRef.current) {
17519
- let batchSize = 2;
17520
- if (queue.length > 65) batchSize = 16;
17576
+ let batchSize = 1;
17577
+ if (queue.length > 85) batchSize = 30;
17578
+ else if (queue.length > 80) batchSize = 25;
17579
+ else if (queue.length > 75) batchSize = 20;
17580
+ else if (queue.length > 65) batchSize = 16;
17521
17581
  else if (queue.length > 50) batchSize = 12;
17522
17582
  else if (queue.length > 35) batchSize = 8;
17523
17583
  else if (queue.length > 15) batchSize = 6;
17524
17584
  else if (queue.length > 5) batchSize = 4;
17585
+ else if (queue.length > 3) batchSize = 2;
17525
17586
  let batchedText = "";
17526
17587
  for (let i = 0; i < batchSize && queue.length > 0; i++) {
17527
17588
  batchedText += queue.shift();
@@ -17529,7 +17590,7 @@ function App({ args = [] }) {
17529
17590
  activeStreamingMsgRef.current.text = flattenString(activeStreamingMsgRef.current.text + batchedText);
17530
17591
  forceRender();
17531
17592
  }
17532
- }, 100);
17593
+ }, 80);
17533
17594
  };
17534
17595
  const awaitTypewriter = async () => {
17535
17596
  while (systemSettings.progressiveRendering && typewriterQueueRef.current.length > 0) {
@@ -17844,6 +17905,8 @@ function App({ args = [] }) {
17844
17905
  const [imageSettings, setImageSettings] = useState15({ keyType: "Default", quality: "Low-High", apiKey: "" });
17845
17906
  const [sessionStats, setSessionStats] = useState15({ tokens: 0 });
17846
17907
  const [lastChunkTime, setLastChunkTime] = useState15(0);
17908
+ const chunkWordCountRef = useRef4(0);
17909
+ const streamingWordStatsRef = useRef4({ totalWords: 0, startTime: 0, wps: 0 });
17847
17910
  const [sessionAgentCalls, setSessionAgentCalls] = useState15(0);
17848
17911
  const [sessionBackgroundCalls, setSessionBackgroundCalls] = useState15(0);
17849
17912
  const [sessionTotalTokens, setSessionTotalTokens] = useState15(0);
@@ -19901,8 +19964,22 @@ ${timestamp}` };
19901
19964
  apiTier,
19902
19965
  cols: terminalSize.columns - 6,
19903
19966
  rows: 30,
19904
- onTokenChunk: () => {
19905
- setLastChunkTime(Date.now());
19967
+ onTokenChunk: (chunkText, wordCount) => {
19968
+ const now = Date.now();
19969
+ setLastChunkTime(now);
19970
+ if (typeof wordCount === "number") {
19971
+ chunkWordCountRef.current = wordCount;
19972
+ const stats = streamingWordStatsRef.current;
19973
+ if (!stats.startTime) {
19974
+ stats.startTime = now;
19975
+ stats.totalWords = 0;
19976
+ }
19977
+ stats.totalWords += wordCount;
19978
+ const elapsedSec = (now - stats.startTime) / 1e3;
19979
+ if (elapsedSec > 0) {
19980
+ stats.wps = Math.round(stats.totalWords / elapsedSec * 10) / 10;
19981
+ }
19982
+ }
19906
19983
  },
19907
19984
  onVisualFeedback: (content) => {
19908
19985
  setMessages((prev) => {
@@ -21586,7 +21663,7 @@ Selection: ${val}`,
21586
21663
  glintColor: colors.textMuted,
21587
21664
  glintWidth: 3
21588
21665
  }
21589
- ))), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { width: "100%", height: 1, overflow: "hidden" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.inputBorder }, "\u2584".repeat(Math.max(1, terminalSize.columns)))), /* @__PURE__ */ React16.createElement(
21666
+ ), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, dimColor: true }, " (", thinkingLevel, ")"))), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { width: "100%", height: 1, overflow: "hidden" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.inputBorder }, "\u2584".repeat(Math.max(1, terminalSize.columns)))), /* @__PURE__ */ React16.createElement(
21590
21667
  Box14,
21591
21668
  {
21592
21669
  backgroundColor: colors.inputBg,
@@ -21759,7 +21836,9 @@ Selection: ${val}`,
21759
21836
  activeModel,
21760
21837
  isProcessing,
21761
21838
  lastChunkTime,
21762
- theme: systemSettings.theme
21839
+ theme: systemSettings.theme,
21840
+ wps: streamingWordStatsRef.current.wps,
21841
+ showTPMEstimate: systemSettings.showTPMEstimate
21763
21842
  }
21764
21843
  )), activeView === "exit" && (() => {
21765
21844
  const wallTimeMs = Date.now() - SESSION_START_TIME;
package/model_config.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 0,
3
- "release": 20260723,
3
+ "release": 20260725,
4
4
  "fallbacks": {
5
5
  "janitor_default": "gemini-3.1-flash-lite",
6
6
  "janitor_attempts_fallback": "gemma-4-26b-a4b-it",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "3.10.0",
4
- "date": "2026-07-24",
3
+ "version": "3.11.0",
4
+ "date": "2026-07-25",
5
5
  "description": "A High-Fidelity Agentic CLI with Sub-Agents for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",