fluxflow-cli 3.2.4 → 3.2.5

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 +373 -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,159 @@ ${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 = 1;
18656
+ if (queue.length > 35) batchSize = 5;
18657
+ else if (queue.length > 15) batchSize = 3;
18658
+ else if (queue.length > 5) batchSize = 2;
18659
+ let changed = false;
18660
+ const nextMsgs = [...activeStreamMessagesRef.current];
18661
+ const clonedIndices = /* @__PURE__ */ new Set();
18662
+ for (let i = 0; i < batchSize; i++) {
18663
+ if (queue.length === 0) break;
18664
+ const task = queue.shift();
18665
+ if (task.type === "create") {
18666
+ nextMsgs.push(task.msg);
18667
+ changed = true;
18668
+ } else if (task.type === "text") {
18669
+ const idx = nextMsgs.findIndex((m) => m.id === task.id);
18670
+ if (idx !== -1) {
18671
+ if (!clonedIndices.has(idx)) {
18672
+ nextMsgs[idx] = { ...nextMsgs[idx] };
18673
+ clonedIndices.add(idx);
18674
+ }
18675
+ nextMsgs[idx].text += task.text;
18676
+ changed = true;
18677
+ }
18678
+ } else if (task.type === "finalize-think") {
18679
+ const idx = nextMsgs.findIndex((m) => m.id === task.id);
18680
+ if (idx !== -1) {
18681
+ if (!clonedIndices.has(idx)) {
18682
+ nextMsgs[idx] = { ...nextMsgs[idx] };
18683
+ clonedIndices.add(idx);
18684
+ }
18685
+ nextMsgs[idx].isStreaming = false;
18686
+ nextMsgs[idx].duration = task.duration;
18687
+ changed = true;
18688
+ }
18689
+ }
18690
+ }
18691
+ if (changed) {
18692
+ activeStreamMessagesRef.current = nextMsgs;
18693
+ setActiveStreamMessages(nextMsgs);
18694
+ }
18695
+ } else if (streamFinishedRef.current) {
18696
+ clearInterval(typewriterIntervalRef.current);
18697
+ typewriterIntervalRef.current = null;
18698
+ finalizeTurn(apiStartVal);
18699
+ }
18700
+ }, 35);
18701
+ };
18702
+ const awaitTypewriter = async () => {
18703
+ while (systemSettings.progressiveRendering && typewriterQueueRef.current.length > 0) {
18704
+ await new Promise((resolve) => setTimeout(resolve, 10));
18705
+ }
18706
+ };
18522
18707
  let hasFiredJanitor = false;
18523
18708
  setIsProcessing(true);
18524
18709
  setLastChunkTime(Date.now());
@@ -18740,27 +18925,29 @@ Selection: ${val}`,
18740
18925
  let inToolCallString = null;
18741
18926
  const signalRegex = /\[?\s*turn\s*:\s*.*?\s*\]?/gi;
18742
18927
  for await (const packet of stream) {
18743
- await new Promise((resolve) => setTimeout(resolve, 3));
18744
18928
  if (packet.type === "text") {
18745
18929
  setLastChunkTime(Date.now());
18746
18930
  }
18747
18931
  if (isFirstPacket && packet.type === "text") {
18748
18932
  apiStart = Date.now();
18749
18933
  isFirstPacket = false;
18934
+ if (systemSettings.progressiveRendering) {
18935
+ startTypewriter(apiStart);
18936
+ }
18750
18937
  }
18751
18938
  if (packet.type === "status") {
18752
18939
  if (!packet.content?.includes("[start]")) {
18753
18940
  setStatusText(packet.content);
18754
18941
  }
18755
18942
  if (packet.content?.includes("[start]")) {
18756
- clearInterval(interval_for_timer);
18943
+ clearInterval(activeTimeIntervalRef.current);
18757
18944
  setActiveTime(0);
18758
- interval_for_timer = setInterval(() => {
18945
+ activeTimeIntervalRef.current = setInterval(() => {
18759
18946
  setActiveTime((prev) => prev + 1);
18760
18947
  }, 1e3);
18761
18948
  } else if (packet.content?.includes("[end]")) {
18762
18949
  setActiveTime(0);
18763
- clearInterval(interval_for_timer);
18950
+ clearInterval(activeTimeIntervalRef.current);
18764
18951
  }
18765
18952
  if (isBridgeConnected()) {
18766
18953
  sendStatus(packet.content);
@@ -18793,6 +18980,9 @@ Selection: ${val}`,
18793
18980
  continue;
18794
18981
  }
18795
18982
  if (packet.type === "turn_reset") {
18983
+ if (systemSettings.progressiveRendering) {
18984
+ await awaitTypewriter();
18985
+ }
18796
18986
  currentThinkId = null;
18797
18987
  currentAgentId = null;
18798
18988
  inThinkMode = false;
@@ -18800,21 +18990,29 @@ Selection: ${val}`,
18800
18990
  inToolCall = false;
18801
18991
  toolCallEncounteredInTurn = false;
18802
18992
  thinkConsumedInTurn = false;
18993
+ const streamingMsgs = activeStreamMessagesRef.current;
18994
+ activeStreamMessagesRef.current = [];
18995
+ setActiveStreamMessages([]);
18803
18996
  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;
18997
+ const totalDuration = Date.now() - apiStart;
18998
+ const flushed = streamingMsgs.map((m) => {
18999
+ const flatText = m.text ? (" " + m.text).slice(1) : m.text;
19000
+ const flatFullText = m.fullText ? (" " + m.fullText).slice(1) : m.fullText;
19001
+ return {
19002
+ ...m,
19003
+ isStreaming: false,
19004
+ text: flatText,
19005
+ fullText: flatFullText,
19006
+ workedDuration: m.role === "agent" ? totalDuration : m.workedDuration
19007
+ };
18811
19008
  });
19009
+ const newMsgs = [...prev, ...flushed];
18812
19010
  setCompletedIndex(newMsgs.length);
18813
19011
  return newMsgs;
18814
19012
  });
18815
19013
  clearBlocksCache();
18816
19014
  if (global.gc) {
18817
- for (let i = 0; i < 2; i++) {
19015
+ for (let i = 0; i < 1; i++) {
18818
19016
  global.gc();
18819
19017
  await new Promise((resolve) => setImmediate(resolve));
18820
19018
  }
@@ -18825,7 +19023,7 @@ Selection: ${val}`,
18825
19023
  if (packet.type === "interactive_turn_finished") {
18826
19024
  setIsProcessing(false);
18827
19025
  setActiveTime(0);
18828
- clearInterval(interval_for_timer);
19026
+ clearInterval(activeTimeIntervalRef.current);
18829
19027
  if (isBridgeConnected()) {
18830
19028
  sendStatus(null);
18831
19029
  }
@@ -18848,9 +19046,15 @@ Selection: ${val}`,
18848
19046
  continue;
18849
19047
  }
18850
19048
  if (packet.type === "visual_feedback") {
19049
+ if (systemSettings.progressiveRendering) {
19050
+ await awaitTypewriter();
19051
+ }
19052
+ const streamingMsgs = activeStreamMessagesRef.current;
19053
+ activeStreamMessagesRef.current = [];
19054
+ setActiveStreamMessages([]);
18851
19055
  setMessages((prev) => {
18852
- const updatedPrev = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
18853
- const newMsgs = [...updatedPrev, {
19056
+ const flushed = streamingMsgs.map((m) => ({ ...m, isStreaming: false }));
19057
+ const newMsgs = [...prev, ...flushed, {
18854
19058
  id: "feedback-" + Date.now() + "-" + Math.random().toString(36).substring(2, 9),
18855
19059
  role: "system",
18856
19060
  text: packet.content,
@@ -18888,9 +19092,15 @@ Selection: ${val}`,
18888
19092
  continue;
18889
19093
  }
18890
19094
  if (packet.type === "tool_result") {
19095
+ if (systemSettings.progressiveRendering) {
19096
+ await awaitTypewriter();
19097
+ }
19098
+ const streamingMsgs = activeStreamMessagesRef.current;
19099
+ activeStreamMessagesRef.current = [];
19100
+ setActiveStreamMessages([]);
18891
19101
  setMessages((prev) => {
18892
- const updatedPrev = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
18893
- const newMsgs = [...updatedPrev, {
19102
+ const flushed = streamingMsgs.map((m) => ({ ...m, isStreaming: false }));
19103
+ const newMsgs = [...prev, ...flushed, {
18894
19104
  id: "tool-" + Date.now(),
18895
19105
  role: "system",
18896
19106
  text: packet.content,
@@ -18956,7 +19166,7 @@ Selection: ${val}`,
18956
19166
  let chunkText = packet.content;
18957
19167
  if (packet.type === "text" && chunkText.includes("Request Cancelled")) {
18958
19168
  if (global.gc) {
18959
- for (let i = 0; i < 5; i++) {
19169
+ for (let i = 0; i < 3; i++) {
18960
19170
  global.gc();
18961
19171
  await new Promise((resolve) => setImmediate(resolve));
18962
19172
  }
@@ -18999,26 +19209,62 @@ Selection: ${val}`,
18999
19209
  if (beforeText) {
19000
19210
  if (!currentAgentId) {
19001
19211
  currentAgentId = "agent-" + Date.now();
19002
- setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: beforeText, isStreaming: true }]);
19212
+ const newMsg = { id: currentAgentId, role: "agent", text: beforeText, isStreaming: true };
19213
+ if (systemSettings.progressiveRendering) {
19214
+ typewriterQueueRef.current.push({ type: "create", msg: { ...newMsg, text: "" } });
19215
+ pushTokenText(currentAgentId, beforeText);
19216
+ } else {
19217
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, newMsg];
19218
+ }
19003
19219
  } else {
19004
- setMessages((prev) => prev.map(
19005
- (m) => m.id === currentAgentId ? { ...m, text: m.text + beforeText, isStreaming: true } : m
19006
- ));
19220
+ if (systemSettings.progressiveRendering) {
19221
+ pushTokenText(currentAgentId, beforeText);
19222
+ } else {
19223
+ activeStreamMessagesRef.current = activeStreamMessagesRef.current.map(
19224
+ (m) => m.id === currentAgentId ? { ...m, text: m.text + beforeText, isStreaming: true } : m
19225
+ );
19226
+ }
19227
+ }
19228
+ if (!systemSettings.progressiveRendering) {
19229
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19007
19230
  }
19008
19231
  }
19009
19232
  inThinkMode = true;
19010
19233
  thinkConsumedInTurn = true;
19011
19234
  let thinkStartText = afterText.replace(/<(think|thought)>/gi, "");
19012
19235
  currentThinkId = "think-" + Date.now();
19013
- setMessages((prev) => [...prev, { id: currentThinkId, role: "think", text: thinkStartText, isStreaming: true, startTime: Date.now() }]);
19236
+ const thinkMsg = { id: currentThinkId, role: "think", text: thinkStartText, isStreaming: true, startTime: Date.now() };
19237
+ if (systemSettings.progressiveRendering) {
19238
+ typewriterQueueRef.current.push({ type: "create", msg: { ...thinkMsg, text: "", startTime: Date.now() } });
19239
+ if (thinkStartText) {
19240
+ pushTokenText(currentThinkId, thinkStartText);
19241
+ }
19242
+ } else {
19243
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, thinkMsg];
19244
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19245
+ }
19014
19246
  continue;
19015
19247
  }
19016
19248
  if ((chunkLower.includes("</think>") || chunkLower.includes("</thought>")) && currentThinkId) {
19017
19249
  const parts = chunkText.split(/<\/(think|thought)>/gi);
19018
19250
  const thinkPart = parts[0] || "";
19019
19251
  const agentPart = parts.slice(2).join("").replace(/<\/?(think|thought)>/gi, "");
19020
- setMessages((prev) => {
19021
- const newMsgs = prev.map((m) => {
19252
+ inThinkMode = false;
19253
+ currentAgentId = "agent-" + Date.now();
19254
+ if (systemSettings.progressiveRendering) {
19255
+ if (thinkPart) {
19256
+ pushTokenText(currentThinkId, thinkPart);
19257
+ }
19258
+ const startTime = parseInt(currentThinkId.split("-")[1]) || Date.now();
19259
+ const duration = Date.now() - startTime;
19260
+ typewriterQueueRef.current.push({ type: "finalize-think", id: currentThinkId, duration });
19261
+ const newAgentMsg = { id: currentAgentId, role: "agent", text: "", isStreaming: true };
19262
+ typewriterQueueRef.current.push({ type: "create", msg: newAgentMsg });
19263
+ if (agentPart) {
19264
+ pushTokenText(currentAgentId, agentPart);
19265
+ }
19266
+ } else {
19267
+ activeStreamMessagesRef.current = activeStreamMessagesRef.current.map((m) => {
19022
19268
  if (m.id === currentThinkId && typeof m.id === "string") {
19023
19269
  const startTime = m.startTime || parseInt(m.id.split("-")[1]) || Date.now();
19024
19270
  const duration = Date.now() - startTime;
@@ -19026,40 +19272,56 @@ Selection: ${val}`,
19026
19272
  }
19027
19273
  return m;
19028
19274
  });
19029
- inThinkMode = false;
19030
- currentAgentId = "agent-" + Date.now();
19031
- return [...newMsgs, { id: currentAgentId, role: "agent", text: agentPart, isStreaming: true }];
19032
- });
19275
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, { id: currentAgentId, role: "agent", text: agentPart, isStreaming: true }];
19276
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19277
+ }
19033
19278
  continue;
19034
19279
  }
19035
19280
  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;
19281
+ let transitioning = false;
19282
+ let transitionContent = "";
19283
+ if (systemSettings.progressiveRendering) {
19284
+ if (chunkText.toLowerCase().includes("</think>")) {
19285
+ transitioning = true;
19286
+ const parts = chunkText.split(/<\/think>/gi);
19287
+ const thinkPart = parts[0] || "";
19288
+ transitionContent = parts.slice(1).join("</think>") || "";
19289
+ if (thinkPart) pushTokenText(currentThinkId, thinkPart);
19290
+ const startTime = parseInt(currentThinkId.split("-")[1]) || Date.now();
19291
+ const duration = Date.now() - startTime;
19292
+ typewriterQueueRef.current.push({ type: "finalize-think", id: currentThinkId, duration });
19293
+ inThinkMode = false;
19294
+ currentAgentId = "agent-" + Date.now();
19295
+ typewriterQueueRef.current.push({ type: "create", msg: { id: currentAgentId, role: "agent", text: "", isStreaming: true } });
19296
+ if (transitionContent) {
19297
+ pushTokenText(currentAgentId, transitionContent.replace(/<\/?(think|thought)>/gi, ""));
19298
+ }
19299
+ } else {
19300
+ pushTokenText(currentThinkId, chunkText);
19301
+ }
19302
+ } else {
19303
+ activeStreamMessagesRef.current = activeStreamMessagesRef.current.map((m) => {
19304
+ if (m.id === currentThinkId) {
19305
+ const newText = m.text + chunkText;
19043
19306
  if (newText.toLowerCase().includes("</think>")) {
19044
19307
  transitioning = true;
19045
19308
  const parts = newText.split(/<\/think>/gi);
19046
19309
  transitionContent = parts.slice(1).join("</think>") || "";
19047
- const startTime = next[i].startTime || parseInt(String(next[i].id).split("-")[1]) || Date.now();
19310
+ const startTime = m.startTime || parseInt(String(m.id).split("-")[1]) || Date.now();
19048
19311
  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 };
19312
+ return { ...m, text: parts[0], isStreaming: false, duration };
19052
19313
  }
19053
- break;
19314
+ return { ...m, text: newText, isStreaming: true };
19054
19315
  }
19055
- }
19316
+ return m;
19317
+ });
19056
19318
  if (transitioning) {
19057
19319
  inThinkMode = false;
19058
19320
  currentAgentId = "agent-" + Date.now();
19059
- next.push({ id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?(think|thought)>/gi, ""), isStreaming: true });
19321
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, { id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?(think|thought)>/gi, ""), isStreaming: true }];
19060
19322
  }
19061
- return next;
19062
- });
19323
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19324
+ }
19063
19325
  } else if (!inThinkMode) {
19064
19326
  const chunkLower2 = chunkText.toLowerCase();
19065
19327
  if (!toolCallEncounteredInTurn && (chunkLower2.includes("tool:functions.") || chunkLower2.includes("agent:generalist."))) {
@@ -19067,91 +19329,69 @@ Selection: ${val}`,
19067
19329
  }
19068
19330
  if (!currentAgentId) {
19069
19331
  currentAgentId = "agent-" + Date.now();
19070
- setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: chunkText, isStreaming: true }]);
19332
+ const newMsg = { id: currentAgentId, role: "agent", text: chunkText, isStreaming: true };
19333
+ if (systemSettings.progressiveRendering) {
19334
+ typewriterQueueRef.current.push({ type: "create", msg: { ...newMsg, text: "" } });
19335
+ pushTokenText(currentAgentId, chunkText);
19336
+ } else {
19337
+ activeStreamMessagesRef.current = [...activeStreamMessagesRef.current, newMsg];
19338
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19339
+ }
19071
19340
  } else {
19072
- setMessages((prev) => {
19073
- const next = [...prev];
19341
+ if (systemSettings.progressiveRendering) {
19342
+ pushTokenText(currentAgentId, chunkText);
19343
+ } else {
19344
+ const next = [...activeStreamMessagesRef.current];
19074
19345
  for (let i = next.length - 1; i >= 0; i--) {
19075
19346
  if (next[i].id === currentAgentId) {
19076
19347
  next[i] = { ...next[i], text: next[i].text + chunkText, isStreaming: true };
19077
19348
  break;
19078
19349
  }
19079
19350
  }
19080
- return next;
19081
- });
19351
+ activeStreamMessagesRef.current = next;
19352
+ setActiveStreamMessages([...activeStreamMessagesRef.current]);
19353
+ }
19082
19354
  }
19083
19355
  }
19084
19356
  }
19357
+ if (systemSettings.progressiveRendering) {
19358
+ streamFinishedRef.current = true;
19359
+ } else {
19360
+ await finalizeTurn(apiStart);
19361
+ }
19085
19362
  const apiEnd = Date.now();
19086
19363
  setSessionApiTime((prev) => prev + (apiEnd - apiStart));
19087
19364
  } catch (err) {
19365
+ if (typewriterIntervalRef.current) {
19366
+ clearInterval(typewriterIntervalRef.current);
19367
+ typewriterIntervalRef.current = null;
19368
+ }
19369
+ typewriterQueueRef.current = [];
19088
19370
  setMessages((prev) => {
19089
19371
  setCompletedIndex(prev.length + 1);
19090
19372
  return [...prev, { id: "error-" + Date.now(), role: "system", text: `\u274C ERROR: ${err.message}` }];
19091
19373
  });
19092
19374
  } 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");
19375
+ if (!systemSettings.progressiveRendering) {
19376
+ setIsProcessing(false);
19377
+ setStatusText(null);
19378
+ setActiveTime(0);
19379
+ clearInterval(activeTimeIntervalRef.current);
19380
+ if (didSignalTerminationRef.current) {
19381
+ appendCancelMessage();
19115
19382
  }
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]");
19383
+ clearBlocksCache();
19384
+ if (global.gc) {
19385
+ try {
19386
+ for (let i = 0; i < 3; i++) {
19387
+ global.gc();
19388
+ await new Promise((resolve) => setImmediate(resolve));
19130
19389
  }
19390
+ lastGCTime = Date.now();
19391
+ } catch (e) {
19131
19392
  }
19132
- return newMsgs.reverse();
19133
- });
19134
- setActiveView("resolution");
19393
+ }
19135
19394
  }
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
19395
  }
19156
19396
  };
19157
19397
  streamChat();
@@ -19385,7 +19625,7 @@ Selection: ${val}`,
19385
19625
  } else if (selectedProvider === "DeepSeek") {
19386
19626
  defaultModel = "deepseek-v4-flash";
19387
19627
  } else if (selectedProvider === "NVIDIA") {
19388
- defaultModel = "moonshotai/kimi-k2.6";
19628
+ defaultModel = "stepfun-ai/step-3.7-flash";
19389
19629
  }
19390
19630
  setActiveModel(defaultModel);
19391
19631
  const targetTier = (quotas.providerTiers || {})[selectedProvider] || "Free";
@@ -19713,7 +19953,7 @@ Selection: ${val}`,
19713
19953
  } else if (prov === "DeepSeek") {
19714
19954
  defaultModel = "deepseek-v4-flash";
19715
19955
  } else if (prov === "NVIDIA") {
19716
- defaultModel = "moonshotai/kimi-k2.6";
19956
+ defaultModel = "stepfun-ai/step-3.7-flash";
19717
19957
  }
19718
19958
  setActiveModel(defaultModel);
19719
19959
  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.5",
4
4
  "date": "2026-07-08",
5
5
  "description": "A High-Fidelity Agentic CLI with Sub-Agents for the Flux Era.",
6
6
  "keywords": [