fluxflow-cli 3.2.4 → 3.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/fluxflow.js +375 -133
  2. package/package.json +1 -1
package/dist/fluxflow.js CHANGED
@@ -326,7 +326,8 @@ var init_settings = __esm({
326
326
  useExternalData: false,
327
327
  externalDataPath: "",
328
328
  preserveThinking: true,
329
- loadingPhrases: true
329
+ loadingPhrases: true,
330
+ progressiveRendering: false
330
331
  },
331
332
  profileData: {
332
333
  name: null,
@@ -2544,7 +2545,7 @@ var init_text = __esm({
2544
2545
  };
2545
2546
  parseMessageToBlocks = (msg, columns) => {
2546
2547
  if (!msg) return { completed: [], active: [] };
2547
- const cacheKey = `${msg.id}-${msg.text?.length || 0}-${columns}-${msg.isStreaming}`;
2548
+ const cacheKey = `${msg.id}-${msg.text?.length || 0}-${columns}-${msg.isStreaming}-${msg.workedDuration || 0}-${msg.memoryUpdated ? 1 : 0}-${msg.color || ""}`;
2548
2549
  if (!msg.isStreaming && blocksCache.has(cacheKey)) {
2549
2550
  return blocksCache.get(cacheKey);
2550
2551
  }
@@ -4002,7 +4003,7 @@ ${coloredArt[7]}`;
4002
4003
  import React4, { useState as useState4, useEffect as useEffect3, useRef as useRef2 } from "react";
4003
4004
  import { Box as Box3, Text as Text4 } from "ink";
4004
4005
  import { diffWordsWithSpace } from "diff";
4005
- var useStreamingText, formatThinkText, REGEX_MD_TOKENS, REGEX_LATEX_FRAC, REGEX_LATEX_STYLE, parseMathSymbols, SYNTAX_KEYWORDS, SYNTAX_RULES, REGEX_SYNTAX, tokenCache, MAX_TOKEN_CACHE_SIZE, tokenizeLine, renderHighlightedLine, renderLatexText, InlineMarkdown, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, formatThinkingDuration, MessageItem, BlockItem, ChatLayout;
4006
+ var useStreamingText, formatThinkText, REGEX_MD_TOKENS, REGEX_LATEX_FRAC, REGEX_LATEX_STYLE, parseMathSymbols, SYNTAX_KEYWORDS, SYNTAX_RULES, tokenCache, MAX_TOKEN_CACHE_SIZE, tokenizeLine, renderHighlightedLine, renderLatexText, InlineMarkdown, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, formatThinkingDuration, MessageItem, BlockItem, ChatLayout;
4006
4007
  var init_ChatLayout = __esm({
4007
4008
  "src/components/ChatLayout.jsx"() {
4008
4009
  init_TerminalBox();
@@ -4058,7 +4059,6 @@ var init_ChatLayout = __esm({
4058
4059
  /\b(true|false|null|undefined|nil|None)\b/.source,
4059
4060
  /\b(\d+(?:\.\d+)?|0x[0-9a-fA-F]+)\b/.source
4060
4061
  ];
4061
- REGEX_SYNTAX = new RegExp(SYNTAX_RULES.join("|"), "g");
4062
4062
  tokenCache = /* @__PURE__ */ new Map();
4063
4063
  MAX_TOKEN_CACHE_SIZE = 1e3;
4064
4064
  tokenizeLine = (line, lang) => {
@@ -4070,8 +4070,8 @@ var init_ChatLayout = __esm({
4070
4070
  let lastIndex = 0;
4071
4071
  const tokens = [];
4072
4072
  let match;
4073
- REGEX_SYNTAX.lastIndex = 0;
4074
- while ((match = REGEX_SYNTAX.exec(line)) !== null) {
4073
+ const localRegex = new RegExp(SYNTAX_RULES.join("|"), "g");
4074
+ while ((match = localRegex.exec(line)) !== null) {
4075
4075
  const matchText = match[0];
4076
4076
  const matchIndex = match.index;
4077
4077
  if (matchIndex > lastIndex) {
@@ -4094,7 +4094,7 @@ var init_ChatLayout = __esm({
4094
4094
  color = "#ff9e64";
4095
4095
  }
4096
4096
  tokens.push({ text: matchText, color, bold });
4097
- lastIndex = REGEX_SYNTAX.lastIndex;
4097
+ lastIndex = localRegex.lastIndex;
4098
4098
  }
4099
4099
  if (lastIndex < line.length) {
4100
4100
  tokens.push({ text: line.substring(lastIndex) });
@@ -5966,6 +5966,7 @@ function SettingsMenu({
5966
5966
  { label: "Key Strategy", value: "apiTier", status: apiTier === "Free" ? "Free" : quotas?.providerBudgets?.__useProvider ? "Paid" : "Paid" },
5967
5967
  { label: "Preserve Thinking", value: "preserveThinking", status: systemSettings.preserveThinking !== false ? "ON" : "OFF" },
5968
5968
  { label: "Loading Phrases", value: "loadingPhrases", status: systemSettings.loadingPhrases !== false ? "ON" : "OFF" },
5969
+ { label: "Progressive Rendering [EXPERIMENTAL]", value: "progressiveRendering", status: systemSettings.progressiveRendering ? "ON" : "OFF" },
5969
5970
  { label: "Download Language Parsers", value: "parserDownload", status: "ACTION" }
5970
5971
  ];
5971
5972
  default:
@@ -6134,6 +6135,12 @@ function SettingsMenu({
6134
6135
  saveSettings2({ systemSettings: newSysSettings, apiTier, quotas });
6135
6136
  return newSysSettings;
6136
6137
  });
6138
+ } else if (item.value === "progressiveRendering") {
6139
+ setSystemSettings((s) => {
6140
+ const newSysSettings = { ...s, progressiveRendering: !s.progressiveRendering };
6141
+ saveSettings2({ systemSettings: newSysSettings, apiTier, quotas });
6142
+ return newSysSettings;
6143
+ });
6137
6144
  }
6138
6145
  };
6139
6146
  return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "white", padding: 0, width: "100%", minHeight: 32 }, /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, paddingY: 0, marginBottom: 0, borderStyle: "single", borderColor: "gray", width: "100%" }, /* @__PURE__ */ React7.createElement(Text7, { color: "white", bold: true }, "SYSTEM CONFIGURATION")), /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "row", width: "100%", minHeight: 26 }, /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", width: "30%", borderStyle: "round", borderColor: activeColumn === "categories" ? "white" : "grey", padding: 1, paddingY: 0 }, /* @__PURE__ */ React7.createElement(Box6, { marginBottom: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: activeColumn === "categories" ? "white" : "grey", bold: true, underline: true }, "CATEGORIES")), CATEGORIES.map((cat, index) => {
@@ -6170,7 +6177,7 @@ function SettingsMenu({
6170
6177
  currentItems.forEach((item, index) => {
6171
6178
  const isSelected = activeColumn === "items" && selectedItemIndex === index;
6172
6179
  const labelLength = item.label.length;
6173
- const dotsCount = Math.max(2, 35 - labelLength);
6180
+ const dotsCount = Math.max(2, 38 - labelLength);
6174
6181
  const dots = ".".repeat(dotsCount);
6175
6182
  const getStatusColor = (item2) => {
6176
6183
  if (currentCatId === "security") {
@@ -6218,7 +6225,7 @@ function SettingsMenu({
6218
6225
  });
6219
6226
  if (currentCatId === "other") {
6220
6227
  elements.push(
6221
- /* @__PURE__ */ React7.createElement(Box6, { key: "pty-notice", marginTop: 15, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "white" }, isPtyAvailable ? "\u2713 Advance Interactive Terminal Supported" : "\u26A0 Interactive Terminal is Limited"))
6228
+ /* @__PURE__ */ React7.createElement(Box6, { key: "pty-notice", marginTop: 14, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "white" }, isPtyAvailable ? "\u2713 Advance Interactive Terminal Supported" : "\u26A0 Interactive Terminal is Limited"))
6222
6229
  );
6223
6230
  elements.push(
6224
6231
  /* @__PURE__ */ React7.createElement(Box6, { key: "memory-load-2026", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "Memory Load: ", currentMemory, "/", maxMemory, " ", memoryUnit))
@@ -16473,8 +16480,8 @@ function App({ args = [] }) {
16473
16480
  defaultModel = "deepseek-v4-flash";
16474
16481
  modelDisplayName = "DeepSeek Flash (Free default)";
16475
16482
  } else if (aiProvider === "NVIDIA") {
16476
- defaultModel = "moonshotai/kimi-k2.6";
16477
- modelDisplayName = "Moonshot Kimi (NVIDIA)";
16483
+ defaultModel = "stepfun-ai/step-3.7-flash";
16484
+ modelDisplayName = "Step 3.7 Flash (NVIDIA)";
16478
16485
  } else {
16479
16486
  defaultModel = "google/gemma-4-31b-it:free";
16480
16487
  modelDisplayName = "Gemma 4 (Free default)";
@@ -16487,8 +16494,8 @@ function App({ args = [] }) {
16487
16494
  defaultModel = "deepseek-v4-flash";
16488
16495
  modelDisplayName = "DeepSeek Flash";
16489
16496
  } else if (aiProvider === "NVIDIA") {
16490
- defaultModel = "moonshotai/kimi-k2.6";
16491
- modelDisplayName = "Moonshot Kimi (NVIDIA)";
16497
+ defaultModel = "stepfun-ai/step-3.7-flash";
16498
+ modelDisplayName = "Step 3.7 Flash (NVIDIA)";
16492
16499
  } else {
16493
16500
  defaultModel = "deepseek/deepseek-v4-flash";
16494
16501
  modelDisplayName = "DeepSeek Flash";
@@ -16549,7 +16556,7 @@ function App({ args = [] }) {
16549
16556
  const [wittyPhrase, setWittyPhrase] = useState15("");
16550
16557
  const [hasPasteBlock, setHasPasteBlock] = useState15(false);
16551
16558
  const [activeTime, setActiveTime] = useState15(0);
16552
- let interval_for_timer;
16559
+ const activeTimeIntervalRef = useRef4(null);
16553
16560
  useEffect12(() => {
16554
16561
  let interval;
16555
16562
  if (statusText && systemSettings.loadingPhrases !== false) {
@@ -16660,6 +16667,11 @@ function App({ args = [] }) {
16660
16667
  }, [messages]);
16661
16668
  const [completedIndex, setCompletedIndex] = useState15(messages.length);
16662
16669
  const [clearKey, setClearKey] = useState15(0);
16670
+ const [activeStreamMessages, setActiveStreamMessages] = useState15([]);
16671
+ const activeStreamMessagesRef = useRef4([]);
16672
+ const typewriterQueueRef = useRef4([]);
16673
+ const typewriterIntervalRef = useRef4(null);
16674
+ const streamFinishedRef = useRef4(false);
16663
16675
  const lastCompletedBlocksRef = useRef4([]);
16664
16676
  const cachedHistoryRef = useRef4({
16665
16677
  completedIndex: 0,
@@ -16733,7 +16745,7 @@ function App({ args = [] }) {
16733
16745
  };
16734
16746
  }
16735
16747
  }
16736
- const activeMsgs = messages.slice(completedIndex);
16748
+ const activeMsgs = activeStreamMessages;
16737
16749
  const streamingCompletedBlocks = [];
16738
16750
  const activeBlocks = [];
16739
16751
  for (let i = 0; i < activeMsgs.length; i++) {
@@ -16749,7 +16761,15 @@ function App({ args = [] }) {
16749
16761
  for (let j = 0; j < parsed.completed.length; j++) streamingCompletedBlocks.push(parsed.completed[j]);
16750
16762
  for (let j = 0; j < parsed.active.length; j++) activeBlocks.push(parsed.active[j]);
16751
16763
  }
16752
- const finalCompleted = [...historicalBlocks];
16764
+ const finalCompleted = historicalBlocks.map((block) => {
16765
+ if (block.type === "full-message" && block.msg) {
16766
+ const latestMsg = messages.find((m) => m.id === block.msg.id);
16767
+ if (latestMsg && latestMsg !== block.msg) {
16768
+ return { ...block, msg: latestMsg };
16769
+ }
16770
+ }
16771
+ return block;
16772
+ });
16753
16773
  for (let j = 0; j < streamingCompletedBlocks.length; j++) {
16754
16774
  finalCompleted.push(streamingCompletedBlocks[j]);
16755
16775
  }
@@ -16769,7 +16789,7 @@ function App({ args = [] }) {
16769
16789
  completed: finalCompleted,
16770
16790
  active: activeBlocks
16771
16791
  };
16772
- }, [messages, completedIndex, terminalSize.columns, clearKey, chatId]);
16792
+ }, [messages, activeStreamMessages, completedIndex, terminalSize.columns, clearKey, chatId]);
16773
16793
  const isTerminalWaitingForInput = useMemo2(() => {
16774
16794
  if (!activeCommand || !execOutput) return false;
16775
16795
  const lastChunk = execOutput.trim();
@@ -17067,7 +17087,7 @@ function App({ args = [] }) {
17067
17087
  } else if (startupProvider === "OpenRouter") {
17068
17088
  defaultModel = "google/gemma-4-31b-it:free";
17069
17089
  } else if (startupProvider === "NVIDIA") {
17070
- defaultModel = "moonshotai/kimi-k2.6";
17090
+ defaultModel = "stepfun-ai/step-3.7-flash";
17071
17091
  }
17072
17092
  } else {
17073
17093
  if (startupProvider === "Google") {
@@ -17077,7 +17097,7 @@ function App({ args = [] }) {
17077
17097
  } else if (startupProvider === "OpenRouter") {
17078
17098
  defaultModel = "deepseek/deepseek-v4-flash";
17079
17099
  } else if (startupProvider === "NVIDIA") {
17080
- defaultModel = "moonshotai/kimi-k2.6";
17100
+ defaultModel = "stepfun-ai/step-3.7-flash";
17081
17101
  }
17082
17102
  }
17083
17103
  setActiveModel(defaultModel);
@@ -17294,7 +17314,7 @@ function App({ args = [] }) {
17294
17314
  } else if (aiProvider === "DeepSeek") {
17295
17315
  defaultModel = "deepseek-v4-flash";
17296
17316
  } else if (aiProvider === "NVIDIA") {
17297
- defaultModel = "moonshotai/kimi-k2.6";
17317
+ defaultModel = "stepfun-ai/step-3.7-flash";
17298
17318
  }
17299
17319
  setActiveModel(defaultModel);
17300
17320
  setMessages((prev) => [...prev, { role: "system", text: `${aiProvider} API Key saved successfully! Model set to ${defaultModel}. Initialization complete.`, isMeta: true }]);
@@ -17853,7 +17873,7 @@ ${cleanText}`, color: "magenta" }];
17853
17873
  setTimeout(() => {
17854
17874
  if (global.gc) {
17855
17875
  const gCAsync = async () => {
17856
- for (let i = 0; i < 5; i++) {
17876
+ for (let i = 0; i < 3; i++) {
17857
17877
  global.gc();
17858
17878
  await new Promise((resolve) => setImmediate(resolve));
17859
17879
  }
@@ -17880,7 +17900,7 @@ ${cleanText}`, color: "magenta" }];
17880
17900
  setTimeout(() => {
17881
17901
  if (global.gc) {
17882
17902
  const gCAsync = async () => {
17883
- for (let i = 0; i < 5; i++) {
17903
+ for (let i = 0; i < 3; i++) {
17884
17904
  global.gc();
17885
17905
  await new Promise((resolve) => setImmediate(resolve));
17886
17906
  }
@@ -18503,12 +18523,24 @@ ${timestamp}` };
18503
18523
  const appendCancelMessage = () => {
18504
18524
  if (didAppendCancel) return;
18505
18525
  didAppendCancel = true;
18526
+ const dangling = activeStreamMessagesRef.current;
18527
+ activeStreamMessagesRef.current = [];
18528
+ setActiveStreamMessages([]);
18529
+ if (typewriterIntervalRef.current) {
18530
+ clearInterval(typewriterIntervalRef.current);
18531
+ typewriterIntervalRef.current = null;
18532
+ }
18533
+ typewriterQueueRef.current = [];
18506
18534
  setMessages((prev) => {
18507
18535
  const lastMsg = prev[prev.length - 1];
18508
18536
  if (lastMsg && lastMsg.text && lastMsg.text.includes("Request Cancelled")) {
18509
18537
  return prev;
18510
18538
  }
18511
- const updatedPrev = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
18539
+ const flushed = dangling.map((m) => {
18540
+ const flatText = m.text ? (" " + m.text).slice(1) : m.text;
18541
+ return { ...m, isStreaming: false, text: flatText };
18542
+ });
18543
+ const updatedPrev = [...prev, ...flushed].map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
18512
18544
  const newMsgs = [...updatedPrev, {
18513
18545
  id: "cancel-" + Date.now(),
18514
18546
  role: "system",
@@ -18519,6 +18551,161 @@ ${timestamp}` };
18519
18551
  return newMsgs;
18520
18552
  });
18521
18553
  };
18554
+ const finalizeTurn = async (apiStartVal) => {
18555
+ const dangling = activeStreamMessagesRef.current;
18556
+ if (dangling.length > 0) {
18557
+ activeStreamMessagesRef.current = [];
18558
+ setActiveStreamMessages([]);
18559
+ setMessages((prev) => {
18560
+ const totalDuration = Date.now() - apiStartVal;
18561
+ const flushed = dangling.map((m) => {
18562
+ const flatText = m.text ? (" " + m.text).slice(1) : m.text;
18563
+ return {
18564
+ ...m,
18565
+ isStreaming: false,
18566
+ text: flatText,
18567
+ workedDuration: m.role === "agent" ? totalDuration : m.workedDuration
18568
+ };
18569
+ });
18570
+ const newMsgs = [...prev, ...flushed];
18571
+ setCompletedIndex(newMsgs.length);
18572
+ return newMsgs;
18573
+ });
18574
+ }
18575
+ setIsProcessing(false);
18576
+ setStatusText(null);
18577
+ setActiveTime(0);
18578
+ clearInterval(activeTimeIntervalRef.current);
18579
+ if (didSignalTerminationRef.current) {
18580
+ appendCancelMessage();
18581
+ }
18582
+ clearBlocksCache();
18583
+ if (global.gc) {
18584
+ try {
18585
+ for (let i = 0; i < 3; i++) {
18586
+ global.gc();
18587
+ await new Promise((resolve) => setImmediate(resolve));
18588
+ }
18589
+ lastGCTime = Date.now();
18590
+ } catch (e) {
18591
+ }
18592
+ }
18593
+ if (!hasFiredJanitor) {
18594
+ if (process.stdout.isTTY) {
18595
+ process.stdout.write("\x1B]0;FluxFlow | Idle\x07");
18596
+ process.stdout.write("\x1B]633;P;TerminalTitle=FluxFlow | Idle\x07");
18597
+ }
18598
+ }
18599
+ if (queuedPromptRef.current) {
18600
+ setResolutionData(queuedPromptRef.current);
18601
+ setQueuedPrompt(null);
18602
+ const hintToResolve = queuedPromptRef.current;
18603
+ queuedPromptRef.current = null;
18604
+ setMessages((prev) => {
18605
+ const newMsgs = [...prev];
18606
+ const hintMsg = newMsgs.reverse().find((m) => m.text?.includes("[STEERING HINT: QUEUED]") || m.text?.includes("[QUESTION: QUEUED]"));
18607
+ if (hintMsg) {
18608
+ if (hintMsg.text.includes("[STEERING HINT: QUEUED]")) {
18609
+ hintMsg.text = hintMsg.text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: FINISHED_TURN]");
18610
+ } else if (hintMsg.text.includes("[QUESTION: QUEUED]")) {
18611
+ hintMsg.text = hintMsg.text.replace("[QUESTION: QUEUED]", "[QUESTION: FINISHED_TURN]");
18612
+ }
18613
+ }
18614
+ return newMsgs.reverse();
18615
+ });
18616
+ setActiveView("resolution");
18617
+ }
18618
+ setMessages((prev) => {
18619
+ const totalDuration = Date.now() - apiStartVal;
18620
+ let foundLastAgent = false;
18621
+ const newMsgs = [...prev].reverse().map((m) => {
18622
+ let updated = m.isStreaming ? { ...m, isStreaming: false } : m;
18623
+ if (updated.text) {
18624
+ updated.text = (" " + updated.text).slice(1);
18625
+ }
18626
+ if (!foundLastAgent && updated.role === "agent") {
18627
+ foundLastAgent = true;
18628
+ updated = { ...updated, workedDuration: totalDuration };
18629
+ }
18630
+ return updated;
18631
+ }).reverse();
18632
+ const historyToSave = newMsgs.filter((m) => !String(m.id).startsWith("welcome") && (!m.isMeta || m.text && m.text.includes("Request Cancelled")));
18633
+ saveChat(chatId, null, historyToSave);
18634
+ setCompletedIndex(newMsgs.length);
18635
+ return newMsgs;
18636
+ });
18637
+ };
18638
+ const pushTokenText = (id, text) => {
18639
+ if (systemSettings.progressiveRendering) {
18640
+ const tokens = text.split(/(\s+)/).filter(Boolean);
18641
+ for (const tok of tokens) {
18642
+ typewriterQueueRef.current.push({ type: "text", id, text: tok });
18643
+ }
18644
+ }
18645
+ };
18646
+ const startTypewriter = (apiStartVal) => {
18647
+ if (typewriterIntervalRef.current) {
18648
+ clearInterval(typewriterIntervalRef.current);
18649
+ }
18650
+ streamFinishedRef.current = false;
18651
+ typewriterQueueRef.current = [];
18652
+ typewriterIntervalRef.current = setInterval(() => {
18653
+ const queue = typewriterQueueRef.current;
18654
+ if (queue.length > 0) {
18655
+ let batchSize = 2;
18656
+ if (queue.length > 65) batchSize = 16;
18657
+ else if (queue.length > 50) batchSize = 12;
18658
+ else if (queue.length > 35) batchSize = 8;
18659
+ else if (queue.length > 15) batchSize = 6;
18660
+ else if (queue.length > 5) batchSize = 4;
18661
+ let changed = false;
18662
+ const nextMsgs = [...activeStreamMessagesRef.current];
18663
+ const clonedIndices = /* @__PURE__ */ new Set();
18664
+ for (let i = 0; i < batchSize; i++) {
18665
+ if (queue.length === 0) break;
18666
+ const task = queue.shift();
18667
+ if (task.type === "create") {
18668
+ nextMsgs.push(task.msg);
18669
+ changed = true;
18670
+ } else if (task.type === "text") {
18671
+ const idx = nextMsgs.findIndex((m) => m.id === task.id);
18672
+ if (idx !== -1) {
18673
+ if (!clonedIndices.has(idx)) {
18674
+ nextMsgs[idx] = { ...nextMsgs[idx] };
18675
+ clonedIndices.add(idx);
18676
+ }
18677
+ nextMsgs[idx].text += task.text;
18678
+ changed = true;
18679
+ }
18680
+ } else if (task.type === "finalize-think") {
18681
+ const idx = nextMsgs.findIndex((m) => m.id === task.id);
18682
+ if (idx !== -1) {
18683
+ if (!clonedIndices.has(idx)) {
18684
+ nextMsgs[idx] = { ...nextMsgs[idx] };
18685
+ clonedIndices.add(idx);
18686
+ }
18687
+ nextMsgs[idx].isStreaming = false;
18688
+ nextMsgs[idx].duration = task.duration;
18689
+ changed = true;
18690
+ }
18691
+ }
18692
+ }
18693
+ if (changed) {
18694
+ activeStreamMessagesRef.current = nextMsgs;
18695
+ setActiveStreamMessages(nextMsgs);
18696
+ }
18697
+ } else if (streamFinishedRef.current) {
18698
+ clearInterval(typewriterIntervalRef.current);
18699
+ typewriterIntervalRef.current = null;
18700
+ finalizeTurn(apiStartVal);
18701
+ }
18702
+ }, 100);
18703
+ };
18704
+ const awaitTypewriter = async () => {
18705
+ while (systemSettings.progressiveRendering && typewriterQueueRef.current.length > 0) {
18706
+ await new Promise((resolve) => setTimeout(resolve, 10));
18707
+ }
18708
+ };
18522
18709
  let hasFiredJanitor = false;
18523
18710
  setIsProcessing(true);
18524
18711
  setLastChunkTime(Date.now());
@@ -18740,27 +18927,29 @@ Selection: ${val}`,
18740
18927
  let inToolCallString = null;
18741
18928
  const signalRegex = /\[?\s*turn\s*:\s*.*?\s*\]?/gi;
18742
18929
  for await (const packet of stream) {
18743
- await new Promise((resolve) => setTimeout(resolve, 3));
18744
18930
  if (packet.type === "text") {
18745
18931
  setLastChunkTime(Date.now());
18746
18932
  }
18747
18933
  if (isFirstPacket && packet.type === "text") {
18748
18934
  apiStart = Date.now();
18749
18935
  isFirstPacket = false;
18936
+ if (systemSettings.progressiveRendering) {
18937
+ startTypewriter(apiStart);
18938
+ }
18750
18939
  }
18751
18940
  if (packet.type === "status") {
18752
18941
  if (!packet.content?.includes("[start]")) {
18753
18942
  setStatusText(packet.content);
18754
18943
  }
18755
18944
  if (packet.content?.includes("[start]")) {
18756
- clearInterval(interval_for_timer);
18945
+ clearInterval(activeTimeIntervalRef.current);
18757
18946
  setActiveTime(0);
18758
- interval_for_timer = setInterval(() => {
18947
+ activeTimeIntervalRef.current = setInterval(() => {
18759
18948
  setActiveTime((prev) => prev + 1);
18760
18949
  }, 1e3);
18761
18950
  } else if (packet.content?.includes("[end]")) {
18762
18951
  setActiveTime(0);
18763
- clearInterval(interval_for_timer);
18952
+ clearInterval(activeTimeIntervalRef.current);
18764
18953
  }
18765
18954
  if (isBridgeConnected()) {
18766
18955
  sendStatus(packet.content);
@@ -18793,6 +18982,9 @@ Selection: ${val}`,
18793
18982
  continue;
18794
18983
  }
18795
18984
  if (packet.type === "turn_reset") {
18985
+ if (systemSettings.progressiveRendering) {
18986
+ await awaitTypewriter();
18987
+ }
18796
18988
  currentThinkId = null;
18797
18989
  currentAgentId = null;
18798
18990
  inThinkMode = false;
@@ -18800,21 +18992,29 @@ Selection: ${val}`,
18800
18992
  inToolCall = false;
18801
18993
  toolCallEncounteredInTurn = false;
18802
18994
  thinkConsumedInTurn = false;
18995
+ const streamingMsgs = activeStreamMessagesRef.current;
18996
+ activeStreamMessagesRef.current = [];
18997
+ setActiveStreamMessages([]);
18803
18998
  setMessages((prev) => {
18804
- const newMsgs = prev.map((m) => {
18805
- if (m.isStreaming) {
18806
- const flatText = m.text ? (" " + m.text).slice(1) : m.text;
18807
- const flatFullText = m.fullText ? (" " + m.fullText).slice(1) : m.fullText;
18808
- return { ...m, isStreaming: false, text: flatText, fullText: flatFullText };
18809
- }
18810
- return m;
18999
+ const totalDuration = Date.now() - apiStart;
19000
+ const flushed = streamingMsgs.map((m) => {
19001
+ const flatText = m.text ? (" " + m.text).slice(1) : m.text;
19002
+ const flatFullText = m.fullText ? (" " + m.fullText).slice(1) : m.fullText;
19003
+ return {
19004
+ ...m,
19005
+ isStreaming: false,
19006
+ text: flatText,
19007
+ fullText: flatFullText,
19008
+ workedDuration: m.role === "agent" ? totalDuration : m.workedDuration
19009
+ };
18811
19010
  });
19011
+ const newMsgs = [...prev, ...flushed];
18812
19012
  setCompletedIndex(newMsgs.length);
18813
19013
  return newMsgs;
18814
19014
  });
18815
19015
  clearBlocksCache();
18816
19016
  if (global.gc) {
18817
- for (let i = 0; i < 2; i++) {
19017
+ for (let i = 0; i < 1; i++) {
18818
19018
  global.gc();
18819
19019
  await new Promise((resolve) => setImmediate(resolve));
18820
19020
  }
@@ -18825,7 +19025,7 @@ Selection: ${val}`,
18825
19025
  if (packet.type === "interactive_turn_finished") {
18826
19026
  setIsProcessing(false);
18827
19027
  setActiveTime(0);
18828
- clearInterval(interval_for_timer);
19028
+ clearInterval(activeTimeIntervalRef.current);
18829
19029
  if (isBridgeConnected()) {
18830
19030
  sendStatus(null);
18831
19031
  }
@@ -18848,9 +19048,15 @@ Selection: ${val}`,
18848
19048
  continue;
18849
19049
  }
18850
19050
  if (packet.type === "visual_feedback") {
19051
+ if (systemSettings.progressiveRendering) {
19052
+ await awaitTypewriter();
19053
+ }
19054
+ const streamingMsgs = activeStreamMessagesRef.current;
19055
+ activeStreamMessagesRef.current = [];
19056
+ setActiveStreamMessages([]);
18851
19057
  setMessages((prev) => {
18852
- const updatedPrev = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
18853
- const newMsgs = [...updatedPrev, {
19058
+ const flushed = streamingMsgs.map((m) => ({ ...m, isStreaming: false }));
19059
+ const newMsgs = [...prev, ...flushed, {
18854
19060
  id: "feedback-" + Date.now() + "-" + Math.random().toString(36).substring(2, 9),
18855
19061
  role: "system",
18856
19062
  text: packet.content,
@@ -18888,9 +19094,15 @@ Selection: ${val}`,
18888
19094
  continue;
18889
19095
  }
18890
19096
  if (packet.type === "tool_result") {
19097
+ if (systemSettings.progressiveRendering) {
19098
+ await awaitTypewriter();
19099
+ }
19100
+ const streamingMsgs = activeStreamMessagesRef.current;
19101
+ activeStreamMessagesRef.current = [];
19102
+ setActiveStreamMessages([]);
18891
19103
  setMessages((prev) => {
18892
- const updatedPrev = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
18893
- const newMsgs = [...updatedPrev, {
19104
+ const flushed = streamingMsgs.map((m) => ({ ...m, isStreaming: false }));
19105
+ const newMsgs = [...prev, ...flushed, {
18894
19106
  id: "tool-" + Date.now(),
18895
19107
  role: "system",
18896
19108
  text: packet.content,
@@ -18956,7 +19168,7 @@ Selection: ${val}`,
18956
19168
  let chunkText = packet.content;
18957
19169
  if (packet.type === "text" && chunkText.includes("Request Cancelled")) {
18958
19170
  if (global.gc) {
18959
- for (let i = 0; i < 5; i++) {
19171
+ for (let i = 0; i < 3; i++) {
18960
19172
  global.gc();
18961
19173
  await new Promise((resolve) => setImmediate(resolve));
18962
19174
  }
@@ -18999,26 +19211,62 @@ Selection: ${val}`,
18999
19211
  if (beforeText) {
19000
19212
  if (!currentAgentId) {
19001
19213
  currentAgentId = "agent-" + Date.now();
19002
- setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: beforeText, isStreaming: true }]);
19214
+ const newMsg = { id: currentAgentId, role: "agent", text: beforeText, isStreaming: true };
19215
+ if (systemSettings.progressiveRendering) {
19216
+ typewriterQueueRef.current.push({ type: "create", msg: { ...newMsg, text: "" } });
19217
+ pushTokenText(currentAgentId, beforeText);
19218
+ } else {
19219
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, newMsg];
19220
+ }
19003
19221
  } else {
19004
- setMessages((prev) => prev.map(
19005
- (m) => m.id === currentAgentId ? { ...m, text: m.text + beforeText, isStreaming: true } : m
19006
- ));
19222
+ if (systemSettings.progressiveRendering) {
19223
+ pushTokenText(currentAgentId, beforeText);
19224
+ } else {
19225
+ activeStreamMessagesRef.current = activeStreamMessagesRef.current.map(
19226
+ (m) => m.id === currentAgentId ? { ...m, text: m.text + beforeText, isStreaming: true } : m
19227
+ );
19228
+ }
19229
+ }
19230
+ if (!systemSettings.progressiveRendering) {
19231
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19007
19232
  }
19008
19233
  }
19009
19234
  inThinkMode = true;
19010
19235
  thinkConsumedInTurn = true;
19011
19236
  let thinkStartText = afterText.replace(/<(think|thought)>/gi, "");
19012
19237
  currentThinkId = "think-" + Date.now();
19013
- setMessages((prev) => [...prev, { id: currentThinkId, role: "think", text: thinkStartText, isStreaming: true, startTime: Date.now() }]);
19238
+ const thinkMsg = { id: currentThinkId, role: "think", text: thinkStartText, isStreaming: true, startTime: Date.now() };
19239
+ if (systemSettings.progressiveRendering) {
19240
+ typewriterQueueRef.current.push({ type: "create", msg: { ...thinkMsg, text: "", startTime: Date.now() } });
19241
+ if (thinkStartText) {
19242
+ pushTokenText(currentThinkId, thinkStartText);
19243
+ }
19244
+ } else {
19245
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, thinkMsg];
19246
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19247
+ }
19014
19248
  continue;
19015
19249
  }
19016
19250
  if ((chunkLower.includes("</think>") || chunkLower.includes("</thought>")) && currentThinkId) {
19017
19251
  const parts = chunkText.split(/<\/(think|thought)>/gi);
19018
19252
  const thinkPart = parts[0] || "";
19019
19253
  const agentPart = parts.slice(2).join("").replace(/<\/?(think|thought)>/gi, "");
19020
- setMessages((prev) => {
19021
- const newMsgs = prev.map((m) => {
19254
+ inThinkMode = false;
19255
+ currentAgentId = "agent-" + Date.now();
19256
+ if (systemSettings.progressiveRendering) {
19257
+ if (thinkPart) {
19258
+ pushTokenText(currentThinkId, thinkPart);
19259
+ }
19260
+ const startTime = parseInt(currentThinkId.split("-")[1]) || Date.now();
19261
+ const duration = Date.now() - startTime;
19262
+ typewriterQueueRef.current.push({ type: "finalize-think", id: currentThinkId, duration });
19263
+ const newAgentMsg = { id: currentAgentId, role: "agent", text: "", isStreaming: true };
19264
+ typewriterQueueRef.current.push({ type: "create", msg: newAgentMsg });
19265
+ if (agentPart) {
19266
+ pushTokenText(currentAgentId, agentPart);
19267
+ }
19268
+ } else {
19269
+ activeStreamMessagesRef.current = activeStreamMessagesRef.current.map((m) => {
19022
19270
  if (m.id === currentThinkId && typeof m.id === "string") {
19023
19271
  const startTime = m.startTime || parseInt(m.id.split("-")[1]) || Date.now();
19024
19272
  const duration = Date.now() - startTime;
@@ -19026,40 +19274,56 @@ Selection: ${val}`,
19026
19274
  }
19027
19275
  return m;
19028
19276
  });
19029
- inThinkMode = false;
19030
- currentAgentId = "agent-" + Date.now();
19031
- return [...newMsgs, { id: currentAgentId, role: "agent", text: agentPart, isStreaming: true }];
19032
- });
19277
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, { id: currentAgentId, role: "agent", text: agentPart, isStreaming: true }];
19278
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19279
+ }
19033
19280
  continue;
19034
19281
  }
19035
19282
  if (inThinkMode && currentThinkId) {
19036
- setMessages((prev) => {
19037
- const next = [...prev];
19038
- let transitioning = false;
19039
- let transitionContent = "";
19040
- for (let i = next.length - 1; i >= 0; i--) {
19041
- if (next[i].id === currentThinkId) {
19042
- const newText = next[i].text + chunkText;
19283
+ let transitioning = false;
19284
+ let transitionContent = "";
19285
+ if (systemSettings.progressiveRendering) {
19286
+ if (chunkText.toLowerCase().includes("</think>")) {
19287
+ transitioning = true;
19288
+ const parts = chunkText.split(/<\/think>/gi);
19289
+ const thinkPart = parts[0] || "";
19290
+ transitionContent = parts.slice(1).join("</think>") || "";
19291
+ if (thinkPart) pushTokenText(currentThinkId, thinkPart);
19292
+ const startTime = parseInt(currentThinkId.split("-")[1]) || Date.now();
19293
+ const duration = Date.now() - startTime;
19294
+ typewriterQueueRef.current.push({ type: "finalize-think", id: currentThinkId, duration });
19295
+ inThinkMode = false;
19296
+ currentAgentId = "agent-" + Date.now();
19297
+ typewriterQueueRef.current.push({ type: "create", msg: { id: currentAgentId, role: "agent", text: "", isStreaming: true } });
19298
+ if (transitionContent) {
19299
+ pushTokenText(currentAgentId, transitionContent.replace(/<\/?(think|thought)>/gi, ""));
19300
+ }
19301
+ } else {
19302
+ pushTokenText(currentThinkId, chunkText);
19303
+ }
19304
+ } else {
19305
+ activeStreamMessagesRef.current = activeStreamMessagesRef.current.map((m) => {
19306
+ if (m.id === currentThinkId) {
19307
+ const newText = m.text + chunkText;
19043
19308
  if (newText.toLowerCase().includes("</think>")) {
19044
19309
  transitioning = true;
19045
19310
  const parts = newText.split(/<\/think>/gi);
19046
19311
  transitionContent = parts.slice(1).join("</think>") || "";
19047
- const startTime = next[i].startTime || parseInt(String(next[i].id).split("-")[1]) || Date.now();
19312
+ const startTime = m.startTime || parseInt(String(m.id).split("-")[1]) || Date.now();
19048
19313
  const duration = Date.now() - startTime;
19049
- next[i] = { ...next[i], text: parts[0], isStreaming: false, duration };
19050
- } else {
19051
- next[i] = { ...next[i], text: newText, isStreaming: true };
19314
+ return { ...m, text: parts[0], isStreaming: false, duration };
19052
19315
  }
19053
- break;
19316
+ return { ...m, text: newText, isStreaming: true };
19054
19317
  }
19055
- }
19318
+ return m;
19319
+ });
19056
19320
  if (transitioning) {
19057
19321
  inThinkMode = false;
19058
19322
  currentAgentId = "agent-" + Date.now();
19059
- next.push({ id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?(think|thought)>/gi, ""), isStreaming: true });
19323
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, { id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?(think|thought)>/gi, ""), isStreaming: true }];
19060
19324
  }
19061
- return next;
19062
- });
19325
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19326
+ }
19063
19327
  } else if (!inThinkMode) {
19064
19328
  const chunkLower2 = chunkText.toLowerCase();
19065
19329
  if (!toolCallEncounteredInTurn && (chunkLower2.includes("tool:functions.") || chunkLower2.includes("agent:generalist."))) {
@@ -19067,91 +19331,69 @@ Selection: ${val}`,
19067
19331
  }
19068
19332
  if (!currentAgentId) {
19069
19333
  currentAgentId = "agent-" + Date.now();
19070
- setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: chunkText, isStreaming: true }]);
19334
+ const newMsg = { id: currentAgentId, role: "agent", text: chunkText, isStreaming: true };
19335
+ if (systemSettings.progressiveRendering) {
19336
+ typewriterQueueRef.current.push({ type: "create", msg: { ...newMsg, text: "" } });
19337
+ pushTokenText(currentAgentId, chunkText);
19338
+ } else {
19339
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, newMsg];
19340
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19341
+ }
19071
19342
  } else {
19072
- setMessages((prev) => {
19073
- const next = [...prev];
19343
+ if (systemSettings.progressiveRendering) {
19344
+ pushTokenText(currentAgentId, chunkText);
19345
+ } else {
19346
+ const next = [...activeStreamMessagesRef.current];
19074
19347
  for (let i = next.length - 1; i >= 0; i--) {
19075
19348
  if (next[i].id === currentAgentId) {
19076
19349
  next[i] = { ...next[i], text: next[i].text + chunkText, isStreaming: true };
19077
19350
  break;
19078
19351
  }
19079
19352
  }
19080
- return next;
19081
- });
19353
+ activeStreamMessagesRef.current = next;
19354
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19355
+ }
19082
19356
  }
19083
19357
  }
19084
19358
  }
19359
+ if (systemSettings.progressiveRendering) {
19360
+ streamFinishedRef.current = true;
19361
+ } else {
19362
+ await finalizeTurn(apiStart);
19363
+ }
19085
19364
  const apiEnd = Date.now();
19086
19365
  setSessionApiTime((prev) => prev + (apiEnd - apiStart));
19087
19366
  } catch (err) {
19367
+ if (typewriterIntervalRef.current) {
19368
+ clearInterval(typewriterIntervalRef.current);
19369
+ typewriterIntervalRef.current = null;
19370
+ }
19371
+ typewriterQueueRef.current = [];
19088
19372
  setMessages((prev) => {
19089
19373
  setCompletedIndex(prev.length + 1);
19090
19374
  return [...prev, { id: "error-" + Date.now(), role: "system", text: `\u274C ERROR: ${err.message}` }];
19091
19375
  });
19092
19376
  } finally {
19093
- setIsProcessing(false);
19094
- setStatusText(null);
19095
- setActiveTime(0);
19096
- clearInterval(interval_for_timer);
19097
- if (didSignalTerminationRef.current) {
19098
- appendCancelMessage();
19099
- }
19100
- clearBlocksCache();
19101
- if (global.gc) {
19102
- try {
19103
- for (let i = 0; i < 5; i++) {
19104
- global.gc();
19105
- await new Promise((resolve) => setImmediate(resolve));
19106
- }
19107
- lastGCTime = Date.now();
19108
- } catch (e) {
19109
- }
19110
- }
19111
- if (!hasFiredJanitor) {
19112
- if (process.stdout.isTTY) {
19113
- process.stdout.write("\x1B]0;FluxFlow | Idle\x07");
19114
- process.stdout.write("\x1B]633;P;TerminalTitle=FluxFlow | Idle\x07");
19377
+ if (!systemSettings.progressiveRendering) {
19378
+ setIsProcessing(false);
19379
+ setStatusText(null);
19380
+ setActiveTime(0);
19381
+ clearInterval(activeTimeIntervalRef.current);
19382
+ if (didSignalTerminationRef.current) {
19383
+ appendCancelMessage();
19115
19384
  }
19116
- }
19117
- if (queuedPromptRef.current) {
19118
- setResolutionData(queuedPromptRef.current);
19119
- setQueuedPrompt(null);
19120
- const hintToResolve = queuedPromptRef.current;
19121
- queuedPromptRef.current = null;
19122
- setMessages((prev) => {
19123
- const newMsgs = [...prev];
19124
- const hintMsg = newMsgs.reverse().find((m) => m.text?.includes("[STEERING HINT: QUEUED]") || m.text?.includes("[QUESTION: QUEUED]"));
19125
- if (hintMsg) {
19126
- if (hintMsg.text.includes("[STEERING HINT: QUEUED]")) {
19127
- hintMsg.text = hintMsg.text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: FINISHED_TURN]");
19128
- } else if (hintMsg.text.includes("[QUESTION: QUEUED]")) {
19129
- hintMsg.text = hintMsg.text.replace("[QUESTION: QUEUED]", "[QUESTION: FINISHED_TURN]");
19385
+ clearBlocksCache();
19386
+ if (global.gc) {
19387
+ try {
19388
+ for (let i = 0; i < 3; i++) {
19389
+ global.gc();
19390
+ await new Promise((resolve) => setImmediate(resolve));
19130
19391
  }
19392
+ lastGCTime = Date.now();
19393
+ } catch (e) {
19131
19394
  }
19132
- return newMsgs.reverse();
19133
- });
19134
- setActiveView("resolution");
19395
+ }
19135
19396
  }
19136
- setMessages((prev) => {
19137
- const totalDuration = Date.now() - apiStart;
19138
- let foundLastAgent = false;
19139
- const newMsgs = [...prev].reverse().map((m) => {
19140
- let updated = m.isStreaming ? { ...m, isStreaming: false } : m;
19141
- if (updated.text) {
19142
- updated.text = (" " + updated.text).slice(1);
19143
- }
19144
- if (!foundLastAgent && updated.role === "agent") {
19145
- foundLastAgent = true;
19146
- updated = { ...updated, workedDuration: totalDuration };
19147
- }
19148
- return updated;
19149
- }).reverse();
19150
- const historyToSave = newMsgs.filter((m) => !String(m.id).startsWith("welcome") && (!m.isMeta || m.text && m.text.includes("Request Cancelled")));
19151
- saveChat(chatId, null, historyToSave);
19152
- setCompletedIndex(newMsgs.length);
19153
- return newMsgs;
19154
- });
19155
19397
  }
19156
19398
  };
19157
19399
  streamChat();
@@ -19385,7 +19627,7 @@ Selection: ${val}`,
19385
19627
  } else if (selectedProvider === "DeepSeek") {
19386
19628
  defaultModel = "deepseek-v4-flash";
19387
19629
  } else if (selectedProvider === "NVIDIA") {
19388
- defaultModel = "moonshotai/kimi-k2.6";
19630
+ defaultModel = "stepfun-ai/step-3.7-flash";
19389
19631
  }
19390
19632
  setActiveModel(defaultModel);
19391
19633
  const targetTier = (quotas.providerTiers || {})[selectedProvider] || "Free";
@@ -19713,7 +19955,7 @@ Selection: ${val}`,
19713
19955
  } else if (prov === "DeepSeek") {
19714
19956
  defaultModel = "deepseek-v4-flash";
19715
19957
  } else if (prov === "NVIDIA") {
19716
- defaultModel = "moonshotai/kimi-k2.6";
19958
+ defaultModel = "stepfun-ai/step-3.7-flash";
19717
19959
  }
19718
19960
  setActiveModel(defaultModel);
19719
19961
  const targetTier = (quotas.providerTiers || {})[prov] || "Free";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "3.2.4",
3
+ "version": "3.2.6",
4
4
  "date": "2026-07-08",
5
5
  "description": "A High-Fidelity Agentic CLI with Sub-Agents for the Flux Era.",
6
6
  "keywords": [