fluxflow-cli 1.11.1 → 1.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/fluxflow.js +128 -29
  2. package/package.json +1 -1
package/dist/fluxflow.js CHANGED
@@ -173,7 +173,7 @@ var init_ChatLayout = __esm({
173
173
  };
174
174
  cleanSignals = (text) => {
175
175
  if (!text) return text;
176
- let result = text;
176
+ let result = text.replace(/<\/think>(\r?\n){2}/gi, "</think>").replace(/(\r?\n){2}(?=\[?(?:tool:functions|tool\.functions|\s*turn\s*:))/gi, "");
177
177
  const trigger = "tool:functions.";
178
178
  while (true) {
179
179
  const lowerResult = result.toLowerCase();
@@ -1095,7 +1095,7 @@ ${TOOL_PROTOCOL(mode)}
1095
1095
  ${projectContextBlock}
1096
1096
 
1097
1097
  -- MEMORY RULES --
1098
- - Memory: ${isMemoryEnabled ? "Use to subtly personalize. Auto Saves" : "OFF. Decline Saves"}
1098
+ - Memory: ${isMemoryEnabled ? "Use to subtly personalize. Auto Saves" : "OFF. Decline saving new memories"}
1099
1099
  - Time: Logs are timestamped. RELATIVE TIME REFERENCE e.g. few mins ago
1100
1100
 
1101
1101
  -- SECURITY RULES --
@@ -1109,7 +1109,7 @@ ${projectContextBlock}
1109
1109
 
1110
1110
  -- RESPONSE RULES --
1111
1111
  - End with [turn: continue] for more steps or [turn: finish] when done
1112
- - Always end with [turn: continue] if called any tools
1112
+ - Tool Called? No post tool chat until [turn: continue]
1113
1113
  - Task Complete? End loop with [turn: finish]
1114
1114
  [/SYSTEM]`.trim();
1115
1115
  };
@@ -1368,11 +1368,25 @@ var init_history = __esm({
1368
1368
  // src/utils/usage.js
1369
1369
  import fs6 from "fs-extra";
1370
1370
  import path5 from "path";
1371
- var cachedUsage, writeTimeout, lastWriteTime, isDirty, defaultStats, loadUsageFromFile, flushUsage, queueFlush, initUsage, forceFlushUsage, getDailyUsage, incrementUsage, addToUsage, checkQuota, getImageQuotaBuckets, getImageQuotaLimit, checkImageQuota, getImageQuotaStats, recordImageGeneration;
1371
+ import os3 from "os";
1372
+ var getLocalBackupPath, BACKUP_FILE, generateSaveId, cachedUsage, writeTimeout, lastWriteTime, isDirty, defaultStats, loadUsageFromFile, flushUsage, queueFlush, initUsage, forceFlushUsage, getDailyUsage, incrementUsage, addToUsage, checkQuota, getImageQuotaBuckets, getImageQuotaLimit, checkImageQuota, getImageQuotaStats, recordImageGeneration;
1372
1373
  var init_usage = __esm({
1373
1374
  "src/utils/usage.js"() {
1374
1375
  init_paths();
1375
1376
  init_crypto();
1377
+ getLocalBackupPath = () => {
1378
+ if (process.platform === "win32") {
1379
+ const localAppData = process.env.LOCALAPPDATA || path5.join(os3.homedir(), "AppData", "Local");
1380
+ return path5.join(localAppData, "FxFl", "backups", "backup.json");
1381
+ }
1382
+ if (process.platform === "darwin") {
1383
+ return path5.join(os3.homedir(), "Library", "Application Support", "FxFl", "backups", "backup.json");
1384
+ }
1385
+ const xdgDataHome = process.env.XDG_DATA_HOME || path5.join(os3.homedir(), ".local", "share");
1386
+ return path5.join(xdgDataHome, "fxfl", "backups", "backup.json");
1387
+ };
1388
+ BACKUP_FILE = getLocalBackupPath();
1389
+ generateSaveId = () => Math.random().toString(36).substring(2) + Date.now().toString(36);
1376
1390
  cachedUsage = null;
1377
1391
  writeTimeout = null;
1378
1392
  lastWriteTime = 0;
@@ -1390,28 +1404,67 @@ var init_usage = __esm({
1390
1404
  };
1391
1405
  loadUsageFromFile = async () => {
1392
1406
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1407
+ let primaryData = null;
1408
+ let backupData = null;
1393
1409
  try {
1394
1410
  if (await fs6.exists(USAGE_FILE)) {
1395
1411
  const rawContent = (await fs6.readFile(USAGE_FILE, "utf8")).trim();
1396
- let data;
1397
1412
  if (rawContent.startsWith("{") || rawContent.startsWith("[")) {
1398
- data = JSON.parse(rawContent);
1413
+ primaryData = JSON.parse(rawContent);
1399
1414
  } else {
1400
- data = JSON.parse(decryptAes(rawContent));
1415
+ primaryData = JSON.parse(decryptAes(rawContent));
1401
1416
  }
1402
- if (data && data.date === today && data.stats) {
1403
- const mergedStats = { ...defaultStats, ...data.stats };
1404
- if (!Array.isArray(mergedStats.imageCalls)) {
1405
- mergedStats.imageCalls = [];
1406
- }
1407
- return {
1408
- ...data,
1409
- stats: mergedStats
1410
- };
1417
+ }
1418
+ } catch (err) {
1419
+ }
1420
+ try {
1421
+ if (await fs6.exists(BACKUP_FILE)) {
1422
+ const rawContent = (await fs6.readFile(BACKUP_FILE, "utf8")).trim();
1423
+ if (rawContent.startsWith("{") || rawContent.startsWith("[")) {
1424
+ backupData = JSON.parse(rawContent);
1425
+ } else {
1426
+ backupData = JSON.parse(decryptAes(rawContent));
1411
1427
  }
1412
1428
  }
1413
1429
  } catch (err) {
1414
1430
  }
1431
+ let resolvedData = null;
1432
+ if (primaryData && backupData) {
1433
+ if (primaryData.saveId !== backupData.saveId) {
1434
+ resolvedData = primaryData;
1435
+ try {
1436
+ await fs6.ensureDir(path5.dirname(BACKUP_FILE));
1437
+ await fs6.copy(USAGE_FILE, BACKUP_FILE);
1438
+ } catch (e) {
1439
+ }
1440
+ } else {
1441
+ resolvedData = primaryData;
1442
+ }
1443
+ } else if (primaryData && !backupData) {
1444
+ resolvedData = primaryData;
1445
+ try {
1446
+ await fs6.ensureDir(path5.dirname(BACKUP_FILE));
1447
+ await fs6.copy(USAGE_FILE, BACKUP_FILE);
1448
+ } catch (e) {
1449
+ }
1450
+ } else if (!primaryData && backupData) {
1451
+ resolvedData = backupData;
1452
+ try {
1453
+ await fs6.ensureDir(path5.dirname(USAGE_FILE));
1454
+ await fs6.copy(BACKUP_FILE, USAGE_FILE);
1455
+ } catch (e) {
1456
+ }
1457
+ }
1458
+ if (resolvedData && resolvedData.date === today && resolvedData.stats) {
1459
+ const mergedStats = { ...defaultStats, ...resolvedData.stats };
1460
+ if (!Array.isArray(mergedStats.imageCalls)) {
1461
+ mergedStats.imageCalls = [];
1462
+ }
1463
+ return {
1464
+ ...resolvedData,
1465
+ stats: mergedStats
1466
+ };
1467
+ }
1415
1468
  return { date: today, stats: { ...defaultStats } };
1416
1469
  };
1417
1470
  flushUsage = async () => {
@@ -1449,6 +1502,7 @@ var init_usage = __esm({
1449
1502
  }
1450
1503
  }
1451
1504
  }
1505
+ cachedUsage.saveId = generateSaveId();
1452
1506
  const tempFile = USAGE_FILE + ".tmp";
1453
1507
  const encryptedStr = encryptAes(JSON.stringify(cachedUsage, null, 2));
1454
1508
  await fs6.writeFile(tempFile, encryptedStr, "utf8");
@@ -1456,6 +1510,11 @@ var init_usage = __esm({
1456
1510
  await fs6.fsync(fd);
1457
1511
  await fs6.close(fd);
1458
1512
  await fs6.rename(tempFile, USAGE_FILE);
1513
+ try {
1514
+ await fs6.ensureDir(path5.dirname(BACKUP_FILE));
1515
+ await fs6.copy(USAGE_FILE, BACKUP_FILE);
1516
+ } catch (backupErr) {
1517
+ }
1459
1518
  isDirty = false;
1460
1519
  lastWriteTime = Date.now();
1461
1520
  } catch (e) {
@@ -2790,6 +2849,26 @@ var init_write_pdf = __esm({
2790
2849
  ]
2791
2850
  });
2792
2851
  const page = await browser.newPage();
2852
+ let resolvedContent = content;
2853
+ const imgRegex = /<img[^>]+src=["']([^"']+)["']/gi;
2854
+ let match;
2855
+ while ((match = imgRegex.exec(content)) !== null) {
2856
+ const originalSrc = match[1];
2857
+ if (!originalSrc || originalSrc.startsWith("http://") || originalSrc.startsWith("https://") || originalSrc.startsWith("data:")) {
2858
+ continue;
2859
+ }
2860
+ try {
2861
+ const imgPath = path11.resolve(process.cwd(), originalSrc);
2862
+ if (await fs12.pathExists(imgPath)) {
2863
+ const ext = path11.extname(imgPath).toLowerCase().replace(".", "") || "png";
2864
+ const mime = ext === "jpg" ? "jpeg" : ext === "svg" ? "svg+xml" : ext;
2865
+ const base64 = await fs12.readFile(imgPath, "base64");
2866
+ const dataUri = `data:image/${mime};base64,${base64}`;
2867
+ resolvedContent = resolvedContent.split(originalSrc).join(dataUri);
2868
+ }
2869
+ } catch (e) {
2870
+ }
2871
+ }
2793
2872
  const styledContent = `
2794
2873
  <style>
2795
2874
  @page {
@@ -2819,7 +2898,7 @@ var init_write_pdf = __esm({
2819
2898
  }
2820
2899
  </style>
2821
2900
  <div class="watermark">Generated by FluxFlow CLI (AI)</div>
2822
- ${content}
2901
+ ${resolvedContent}
2823
2902
  `;
2824
2903
  await page.setContent(styledContent, { waitUntil: "networkidle0", timeout: 18e4 });
2825
2904
  const pdfBytes = await page.pdf({
@@ -3795,10 +3874,12 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
3795
3874
  let lastToolSniffed = null;
3796
3875
  let lastToolDetail = null;
3797
3876
  let lastToolEventTime = null;
3877
+ let lastToolFinishedAt = 0;
3798
3878
  let toolResults = [];
3799
3879
  let toolCallPointer = 0;
3800
3880
  let isThinkingLoop = false;
3801
3881
  let isStutteringLoop = false;
3882
+ let isGeneralLoop = false;
3802
3883
  let isInitialAttempt = true;
3803
3884
  let accumulatedContext = "";
3804
3885
  let dedupeBuffer = "";
@@ -4023,6 +4104,7 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
4023
4104
  if (respRepetitionRatio > repetitionThresholdResponse) {
4024
4105
  yield { type: "status", content: `Response Loop Detected. Re-centering...` };
4025
4106
  isThinkingLoop = false;
4107
+ isGeneralLoop = true;
4026
4108
  await new Promise((resolve) => setTimeout(resolve, 3e3));
4027
4109
  break;
4028
4110
  }
@@ -4221,6 +4303,12 @@ ${boxBottom}` };
4221
4303
  }
4222
4304
  }
4223
4305
  }
4306
+ if (lastToolFinishedAt > 0) {
4307
+ const timeSinceLastTool = Date.now() - lastToolFinishedAt;
4308
+ if (timeSinceLastTool < 1e3) {
4309
+ await new Promise((resolve) => setTimeout(resolve, 1e3 - timeSinceLastTool));
4310
+ }
4311
+ }
4224
4312
  const effectiveStart = lastToolEventTime || Date.now();
4225
4313
  yield { type: "spinner", content: false };
4226
4314
  let result = await dispatchTool(normToolName, toolCall.args, {
@@ -4234,6 +4322,7 @@ ${boxBottom}` };
4234
4322
  process.stdout.write(`\x1B]0;Working...\x07`);
4235
4323
  }
4236
4324
  const toolEnd = Date.now();
4325
+ lastToolFinishedAt = toolEnd;
4237
4326
  yield { type: "tool_time", content: toolEnd - effectiveStart };
4238
4327
  lastToolEventTime = toolEnd;
4239
4328
  let binaryPart = null;
@@ -4296,8 +4385,10 @@ ${boxBottom}` };
4296
4385
  const hasFinish2 = /\[\s*(turn\s*:)?\s*finish\s*\]/i.test(signalSafeText2.toLowerCase());
4297
4386
  const hasContinue = /\[\s*(turn\s*:)?\s*continue\s*\]/i.test(signalSafeText2.toLowerCase());
4298
4387
  const didCallTool = toolResults.length > 0 || lastToolSniffed !== null;
4299
- if (!hasFinish2 && !hasContinue && !didCallTool && signalSafeText2.length > 0) {
4300
- throw new Error("Silent stream cutoff (500): Model stream closed cleanly but missing turn finish/continue signals.");
4388
+ const pureOutputText = signalSafeText2.replace(/<think>[\s\S]*?<\/think>/gi, "").trim();
4389
+ const endsNormally = /[.!?}"'`’“”]$|```$/s.test(pureOutputText);
4390
+ if (!hasFinish2 && !hasContinue && !didCallTool && signalSafeText2.length > 0 && !endsNormally && !isThinkingLoop && !isStutteringLoop && !isGeneralLoop) {
4391
+ throw new Error("Silent stream cutoff (500): Model stream closed cleanly but cut off mid-sentence without signals.");
4301
4392
  }
4302
4393
  success = true;
4303
4394
  await incrementUsage("agent");
@@ -4376,6 +4467,8 @@ Error Log can be found in ${path15.join(LOGS_DIR, "agent", "error.log")}`);
4376
4467
  } else {
4377
4468
  if (retryCount <= MAX_RETRIES) {
4378
4469
  retryCount++;
4470
+ inStreamRetryCount = 1;
4471
+ accumulatedContext = "";
4379
4472
  const waitTime = Math.min(1e3 * Math.pow(2, retryCount - 1), 32e3);
4380
4473
  isInitialAttempt = true;
4381
4474
  yield { type: "status", content: `Retrying Connection (${retryCount}/${MAX_RETRIES}) [${(waitTime / 1e3).toFixed(0)}s]...` };
@@ -4438,6 +4531,7 @@ ${timestamp}`;
4438
4531
  modifiedHistory.push({ role: "user", text: `[SYSTEM] ${isStutteringLoop && !isThinkingLoop ? `STUTTERING DETECTED by Internal System. Re-calibrate your response & proceed.` : `${isThinkingLoop ? " OVER-THINKING" : " LOOP"} DETECTED by Internal System${isThinkingLoop ? " for current EFFORT_LEVEL" : ""}. ${isThinkingLoop ? "If you have planned the task, prioritize the execution/output. " : "If you have finished your task use [turn: finish] else continue."}`}` });
4439
4532
  isThinkingLoop = false;
4440
4533
  isStutteringLoop = false;
4534
+ isGeneralLoop = false;
4441
4535
  }
4442
4536
  }
4443
4537
  yield { type: "status", content: null };
@@ -4686,7 +4780,7 @@ var app_exports = {};
4686
4780
  __export(app_exports, {
4687
4781
  default: () => App
4688
4782
  });
4689
- import os3 from "os";
4783
+ import os4 from "os";
4690
4784
  import React10, { useState as useState7, useEffect as useEffect5, useRef as useRef2, useMemo } from "react";
4691
4785
  import { Box as Box10, Text as Text10, useInput as useInput4, useStdout } from "ink";
4692
4786
  import Spinner2 from "ink-spinner";
@@ -4861,7 +4955,7 @@ function App() {
4861
4955
  const [messages, setMessages] = useState7(() => {
4862
4956
  const logoMsg = { id: "logo-" + Date.now(), role: "system", text: FLUX_LOGO, isLogo: true, isMeta: true };
4863
4957
  const welcomeMsg = { id: "welcome", role: "system", text: "\u{1F30A}\u26A1 Welcome to Flux Flow! Type /help for commands.", isMeta: true };
4864
- const isHomeDir = process.cwd() === os3.homedir();
4958
+ const isHomeDir = process.cwd() === os4.homedir();
4865
4959
  const isSystemDir = (() => {
4866
4960
  const cwd = process.cwd().toLowerCase();
4867
4961
  if (process.platform === "win32") {
@@ -4889,7 +4983,7 @@ function App() {
4889
4983
  id: "home-warning",
4890
4984
  role: "system",
4891
4985
  text: `[SECURITY ALERT] HOME DIRECTORY DETECTED`,
4892
- subText: `You are currently in ${os3.homedir()}. Working here is high-risk as the agent may modify system-sensitive configurations. Please move to a project folder for safety.`,
4986
+ subText: `You are currently in ${os4.homedir()}. Working here is high-risk as the agent may modify system-sensitive configurations. Please move to a project folder for safety.`,
4893
4987
  isHomeWarning: true,
4894
4988
  isMeta: true
4895
4989
  });
@@ -5298,7 +5392,8 @@ ${hintText}`, color: "magenta" }];
5298
5392
  const resumedMsgs = [...target.messages];
5299
5393
  const hasLogo = resumedMsgs[0]?.text?.includes("\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557");
5300
5394
  if (!hasLogo) {
5301
- resumedMsgs.unshift({ id: "welcome-" + Date.now(), role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Resuming Flux Flow Session...\n" });
5395
+ resumedMsgs.unshift({ id: "welcome-" + Date.now(), role: "system", text: "\u{1F30A}\u26A1 Resuming Flux Flow Session...", isMeta: true });
5396
+ resumedMsgs.unshift({ id: "logo-" + Date.now(), role: "system", text: FLUX_LOGO, isLogo: true, isMeta: true });
5302
5397
  }
5303
5398
  setMessages(resumedMsgs);
5304
5399
  setMessages((prev) => [...prev, { id: "sys-" + Date.now(), role: "system", text: `\u{1F4E1} SESSION RESUMED: [${targetId}]`, isMeta: true }]);
@@ -5314,8 +5409,11 @@ ${hintText}`, color: "magenta" }];
5314
5409
  break;
5315
5410
  }
5316
5411
  case "/clear": {
5317
- setMessages([{ id: "welcome-" + Date.now(), role: "system", text: FLUX_LOGO + "\n\n\u{1F30A}\u26A1 Welcome back to Flux Flow! Context cleared.\n", isMeta: true }]);
5318
- setCompletedIndex(1);
5412
+ setMessages([
5413
+ { id: "logo-" + Date.now(), role: "system", text: FLUX_LOGO, isLogo: true, isMeta: true },
5414
+ { id: "welcome-" + Date.now(), role: "system", text: "\u{1F30A}\u26A1 Welcome back to Flux Flow! Context cleared.", isMeta: true }
5415
+ ]);
5416
+ setCompletedIndex(2);
5319
5417
  setChatId(generateChatId());
5320
5418
  setSessionStats({ tokens: 0 });
5321
5419
  setIsExpanded(false);
@@ -5474,21 +5572,22 @@ ${hintText}`, color: "magenta" }];
5474
5572
  let formattedLevel;
5475
5573
  if (parts[1]) {
5476
5574
  let val = parts[1].toLowerCase();
5575
+ const isBypass = parts.includes("--bypass");
5477
5576
  formattedLevel = val.charAt(0).toUpperCase() + val.slice(1);
5478
5577
  if (val === "xhigh") {
5479
5578
  formattedLevel = "xHigh";
5480
5579
  }
5481
- if (mode === "Flow" && (formattedLevel === "Medium" || formattedLevel === "High" || formattedLevel === "xHigh")) {
5580
+ if (!isBypass && mode === "Flow" && (formattedLevel === "Medium" || formattedLevel === "High" || formattedLevel === "xHigh")) {
5482
5581
  setMessages((prev) => {
5483
5582
  setCompletedIndex(prev.length + 1);
5484
5583
  return [...prev, { id: Date.now(), role: "system", text: `\u274C [RESTRICTED] "${formattedLevel}" is restricted in Flow mode. Switch to Flux to enable Higher Thinking Levels.`, isMeta: true }];
5485
5584
  });
5486
5585
  } else {
5487
5586
  setThinkingLevel(formattedLevel);
5488
- const s2 = emojiSpace(2);
5587
+ const s2 = emojiSpace(1);
5489
5588
  setMessages((prev) => {
5490
5589
  setCompletedIndex(prev.length + 1);
5491
- return [...prev, { id: Date.now(), role: "system", text: `\u{1F527}${s2}[SYSTEM] Thinking level set to ${formattedLevel}`, isMeta: true }];
5590
+ return [...prev, { id: Date.now(), role: "system", text: `\u{1F527} [SYSTEM] Thinking level set to ${formattedLevel}${isBypass ? ` (Bypass Activated \u{1F575}\uFE0F${s2})` : ""}`, isMeta: true }];
5492
5591
  });
5493
5592
  }
5494
5593
  } else {
@@ -6671,7 +6770,7 @@ var init_app = __esm({
6671
6770
  packageJsonPath = path16.join(path16.dirname(fileURLToPath(import.meta.url)), "../package.json");
6672
6771
  packageJson = JSON.parse(fs18.readFileSync(packageJsonPath, "utf8"));
6673
6772
  versionFluxflow = packageJson.version;
6674
- updatedOn = "2026-05-17";
6773
+ updatedOn = "2026-05-20";
6675
6774
  ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React10.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "magenta", bold: true, underline: true }, "\u{1F7E3} STEERING HINT RESOLUTION")), /* @__PURE__ */ React10.createElement(Box10, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, null, "The agent already finished the task before your hint was consumed.")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1, backgroundColor: "#222", paddingX: 2, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { italic: true, color: "gray" }, '"', data, '"')), /* @__PURE__ */ React10.createElement(Box10, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, "How would you like to proceed?")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 0 }, /* @__PURE__ */ React10.createElement(
6676
6775
  CommandMenu,
6677
6776
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.11.1",
3
+ "version": "1.11.3",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",