fluxflow-cli 3.16.0 → 3.16.2

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 +191 -112
  2. package/package.json +4 -3
package/dist/fluxflow.js CHANGED
@@ -52,13 +52,14 @@ __export(paths_exports, {
52
52
  SETTINGS_FILE: () => SETTINGS_FILE,
53
53
  TEMP_MEM_CHAT_FILE: () => TEMP_MEM_CHAT_FILE,
54
54
  TEMP_MEM_FILE: () => TEMP_MEM_FILE,
55
- USAGE_FILE: () => USAGE_FILE
55
+ USAGE_FILE: () => USAGE_FILE,
56
+ USAGE_FILE_OLD: () => USAGE_FILE_OLD
56
57
  });
57
58
  import os from "os";
58
59
  import path from "path";
59
60
  import fs from "fs";
60
61
  import crypto from "crypto";
61
- var FLUXFLOW_DIR, SETTINGS_FILE, externalDir, DATA_DIR, LOGS_DIR, SECRET_DIR, HISTORY_FILE, HISTORY_DIR, USAGE_FILE, MEMORIES_FILE, TEMP_MEM_FILE, TEMP_MEM_CHAT_FILE, BACKUPS_DIR, LEDGER_FILE, LEDGER_ADVANCE_FILE, ACTIVE_TX_FILE, PATHS_FILE, CONTEXT_FILE, PARSER_DIR;
62
+ var FLUXFLOW_DIR, SETTINGS_FILE, externalDir, DATA_DIR, LOGS_DIR, SECRET_DIR, HISTORY_FILE, HISTORY_DIR, USAGE_FILE_OLD, USAGE_FILE, MEMORIES_FILE, TEMP_MEM_FILE, TEMP_MEM_CHAT_FILE, BACKUPS_DIR, LEDGER_FILE, LEDGER_ADVANCE_FILE, ACTIVE_TX_FILE, PATHS_FILE, CONTEXT_FILE, PARSER_DIR;
62
63
  var init_paths = __esm({
63
64
  "src/utils/paths.js"() {
64
65
  FLUXFLOW_DIR = path.join(os.homedir(), ".fluxflow");
@@ -96,7 +97,8 @@ var init_paths = __esm({
96
97
  SECRET_DIR = path.join(DATA_DIR, "secret");
97
98
  HISTORY_FILE = path.join(SECRET_DIR, "history.json");
98
99
  HISTORY_DIR = path.join(SECRET_DIR, "history");
99
- USAGE_FILE = path.join(FLUXFLOW_DIR, "usage.json");
100
+ USAGE_FILE_OLD = path.join(FLUXFLOW_DIR, "usage.json");
101
+ USAGE_FILE = path.join(SECRET_DIR, "usage.json");
100
102
  MEMORIES_FILE = path.join(SECRET_DIR, "memories.json");
101
103
  TEMP_MEM_FILE = path.join(SECRET_DIR, "memory-temp.json");
102
104
  TEMP_MEM_CHAT_FILE = path.join(SECRET_DIR, "temp-memory-chat.json");
@@ -6105,6 +6107,7 @@ var init_ChatLayout = __esm({
6105
6107
  { cmd: "/quit", desc: "Exit and shutdown Flux" },
6106
6108
  { cmd: "/help", desc: "Show all available commands" },
6107
6109
  { cmd: "/compress", desc: "Summarize and compress chat history" },
6110
+ { cmd: "/truncate", desc: "Truncate tool results in chat history" },
6108
6111
  { cmd: "/clear", desc: "Clear terminal screen" },
6109
6112
  { cmd: "/resume", desc: "Load previous session" },
6110
6113
  { cmd: "/revert", desc: "Revert codebase to checkpoint" },
@@ -9187,24 +9190,11 @@ var init_history = __esm({
9187
9190
  // src/utils/usage.js
9188
9191
  import fs10 from "fs-extra";
9189
9192
  import path9 from "path";
9190
- import os3 from "os";
9191
- var getLocalBackupPath, BACKUP_FILE, generateSaveId, cachedUsage, writeTimeout, lastWriteTime, isDirty, defaultStats, purgeOldHistory, loadUsageFromFile, flushUsage, queueFlush, initUsage, forceFlushUsage, getDailyUsage, getMonthlyUsage, incrementUsage, runtimeSession, addToUsage, getCustomPeriodUsage, checkQuota, getImageQuotaBuckets, getImageQuotaLimit, checkImageQuota, getImageQuotaStats, recordImageGeneration;
9193
+ var generateSaveId, cachedUsage, writeTimeout, lastWriteTime, isDirty, defaultStats, purgeOldHistory, loadUsageFromFile, flushUsage, queueFlush, initUsage, forceFlushUsage, getDailyUsage, getMonthlyUsage, incrementUsage, runtimeSession, addToUsage, getCustomPeriodUsage, checkQuota, getImageQuotaBuckets, getImageQuotaLimit, checkImageQuota, getImageQuotaStats, recordImageGeneration;
9192
9194
  var init_usage = __esm({
9193
9195
  "src/utils/usage.js"() {
9194
9196
  init_paths();
9195
9197
  init_crypto();
9196
- getLocalBackupPath = () => {
9197
- if (process.platform === "win32") {
9198
- const localAppData = process.env.LOCALAPPDATA || path9.join(os3.homedir(), "AppData", "Local");
9199
- return path9.join(localAppData, "FxFl", "backups", "backup.json");
9200
- }
9201
- if (process.platform === "darwin") {
9202
- return path9.join(os3.homedir(), "Library", "Application Support", "FxFl", "backups", "backup.json");
9203
- }
9204
- const xdgDataHome = process.env.XDG_DATA_HOME || path9.join(os3.homedir(), ".local", "share");
9205
- return path9.join(xdgDataHome, "fxfl", "backups", "backup.json");
9206
- };
9207
- BACKUP_FILE = getLocalBackupPath();
9208
9198
  generateSaveId = () => Math.random().toString(36).substring(2) + Date.now().toString(36);
9209
9199
  cachedUsage = null;
9210
9200
  writeTimeout = null;
@@ -9239,10 +9229,16 @@ var init_usage = __esm({
9239
9229
  return purged;
9240
9230
  };
9241
9231
  loadUsageFromFile = async () => {
9242
- const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9232
+ const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9233
+ try {
9234
+ if (!await fs10.exists(USAGE_FILE) && await fs10.exists(USAGE_FILE_OLD)) {
9235
+ await fs10.ensureDir(path9.dirname(USAGE_FILE));
9236
+ await fs10.move(USAGE_FILE_OLD, USAGE_FILE);
9237
+ }
9238
+ } catch (err) {
9239
+ }
9243
9240
  const tempFile = USAGE_FILE + ".tmp";
9244
9241
  let primaryData = null;
9245
- let backupData = null;
9246
9242
  try {
9247
9243
  if (await fs10.exists(tempFile)) {
9248
9244
  const rawContent = (await fs10.readFile(tempFile, "utf8")).trim();
@@ -9284,44 +9280,7 @@ var init_usage = __esm({
9284
9280
  } catch (err) {
9285
9281
  }
9286
9282
  }
9287
- try {
9288
- if (await fs10.exists(BACKUP_FILE)) {
9289
- const rawContent = (await fs10.readFile(BACKUP_FILE, "utf8")).trim();
9290
- if (rawContent.startsWith("{") || rawContent.startsWith("[")) {
9291
- backupData = JSON.parse(rawContent);
9292
- } else {
9293
- backupData = JSON.parse(decryptAes(rawContent));
9294
- }
9295
- }
9296
- } catch (err) {
9297
- }
9298
- let resolvedData = null;
9299
- if (primaryData && backupData) {
9300
- if (primaryData.saveId !== backupData.saveId) {
9301
- resolvedData = primaryData;
9302
- try {
9303
- await fs10.ensureDir(path9.dirname(BACKUP_FILE));
9304
- await fs10.copy(USAGE_FILE, BACKUP_FILE);
9305
- } catch (e) {
9306
- }
9307
- } else {
9308
- resolvedData = primaryData;
9309
- }
9310
- } else if (primaryData && !backupData) {
9311
- resolvedData = primaryData;
9312
- try {
9313
- await fs10.ensureDir(path9.dirname(BACKUP_FILE));
9314
- await fs10.copy(USAGE_FILE, BACKUP_FILE);
9315
- } catch (e) {
9316
- }
9317
- } else if (!primaryData && backupData) {
9318
- resolvedData = backupData;
9319
- try {
9320
- await fs10.ensureDir(path9.dirname(USAGE_FILE));
9321
- await fs10.copy(BACKUP_FILE, USAGE_FILE);
9322
- } catch (e) {
9323
- }
9324
- }
9283
+ let resolvedData = primaryData;
9325
9284
  if (resolvedData) {
9326
9285
  const stats = resolvedData.stats || { ...defaultStats };
9327
9286
  const mergedStats = { ...defaultStats, ...stats };
@@ -9329,28 +9288,32 @@ var init_usage = __esm({
9329
9288
  mergedStats.imageCalls = [];
9330
9289
  }
9331
9290
  const history = resolvedData.history || {};
9332
- if (resolvedData.date === today) {
9291
+ const purgedHistory = purgeOldHistory(history, today2);
9292
+ if (Object.keys(history).length !== Object.keys(purgedHistory).length) {
9293
+ isDirty = true;
9294
+ }
9295
+ if (resolvedData.date === today2) {
9333
9296
  return {
9334
9297
  ...resolvedData,
9335
9298
  stats: mergedStats,
9336
- history
9299
+ history: purgedHistory
9337
9300
  };
9338
9301
  } else {
9339
9302
  const oldDate = resolvedData.date;
9340
9303
  const oldStats = mergedStats;
9341
- const updatedHistory = { ...history };
9304
+ const updatedHistory = { ...purgedHistory };
9342
9305
  if (oldDate) {
9343
9306
  updatedHistory[oldDate] = oldStats;
9344
9307
  }
9345
9308
  return {
9346
- date: today,
9309
+ date: today2,
9347
9310
  stats: { ...defaultStats },
9348
- history: purgeOldHistory(updatedHistory, today)
9311
+ history: purgeOldHistory(updatedHistory, today2)
9349
9312
  };
9350
9313
  }
9351
9314
  }
9352
9315
  return {
9353
- date: today,
9316
+ date: today2,
9354
9317
  stats: { ...defaultStats },
9355
9318
  history: {}
9356
9319
  };
@@ -9433,7 +9396,10 @@ var init_usage = __esm({
9433
9396
  mergedHistory[dateKey] = diskData.history[dateKey];
9434
9397
  }
9435
9398
  }
9436
- cachedUsage.history = mergedHistory;
9399
+ cachedUsage.history = purgeOldHistory(mergedHistory, cachedUsage.date || today);
9400
+ } else if (cachedUsage && cachedUsage.history) {
9401
+ const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9402
+ cachedUsage.history = purgeOldHistory(cachedUsage.history, today2);
9437
9403
  }
9438
9404
  cachedUsage.saveId = generateSaveId();
9439
9405
  const tempFile = USAGE_FILE + ".tmp";
@@ -9443,11 +9409,6 @@ var init_usage = __esm({
9443
9409
  await fs10.fsync(fd);
9444
9410
  await fs10.close(fd);
9445
9411
  await fs10.rename(tempFile, USAGE_FILE);
9446
- try {
9447
- await fs10.ensureDir(path9.dirname(BACKUP_FILE));
9448
- await fs10.copy(USAGE_FILE, BACKUP_FILE);
9449
- } catch (backupErr) {
9450
- }
9451
9412
  isDirty = false;
9452
9413
  lastWriteTime = Date.now();
9453
9414
  } catch (e) {
@@ -9466,6 +9427,9 @@ var init_usage = __esm({
9466
9427
  };
9467
9428
  initUsage = async () => {
9468
9429
  cachedUsage = await loadUsageFromFile();
9430
+ if (isDirty) {
9431
+ queueFlush();
9432
+ }
9469
9433
  };
9470
9434
  forceFlushUsage = async () => {
9471
9435
  if (writeTimeout) {
@@ -9475,10 +9439,10 @@ var init_usage = __esm({
9475
9439
  await flushUsage();
9476
9440
  };
9477
9441
  getDailyUsage = async () => {
9478
- const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9442
+ const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9479
9443
  if (!cachedUsage) {
9480
9444
  cachedUsage = await loadUsageFromFile();
9481
- } else if (cachedUsage.date !== today) {
9445
+ } else if (cachedUsage.date !== today2) {
9482
9446
  const oldDate = cachedUsage.date;
9483
9447
  const oldStats = cachedUsage.stats;
9484
9448
  const history = cachedUsage.history || {};
@@ -9486,9 +9450,9 @@ var init_usage = __esm({
9486
9450
  history[oldDate] = oldStats;
9487
9451
  }
9488
9452
  cachedUsage = {
9489
- date: today,
9453
+ date: today2,
9490
9454
  stats: { ...defaultStats },
9491
- history: purgeOldHistory(history, today)
9455
+ history: purgeOldHistory(history, today2)
9492
9456
  };
9493
9457
  isDirty = true;
9494
9458
  await flushUsage();
@@ -9499,15 +9463,15 @@ var init_usage = __esm({
9499
9463
  return cachedUsage.stats;
9500
9464
  };
9501
9465
  getMonthlyUsage = async () => {
9502
- const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9466
+ const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9503
9467
  if (!cachedUsage) {
9504
9468
  cachedUsage = await loadUsageFromFile();
9505
9469
  }
9506
- if (cachedUsage.date !== today) {
9470
+ if (cachedUsage.date !== today2) {
9507
9471
  await getDailyUsage();
9508
9472
  }
9509
9473
  const history = cachedUsage.history || {};
9510
- const purgedHistory = purgeOldHistory(history, today);
9474
+ const purgedHistory = purgeOldHistory(history, today2);
9511
9475
  cachedUsage.history = purgedHistory;
9512
9476
  const todayStats = cachedUsage.stats || { ...defaultStats };
9513
9477
  const summed = { ...defaultStats };
@@ -9604,17 +9568,17 @@ var init_usage = __esm({
9604
9568
  queueFlush();
9605
9569
  };
9606
9570
  getCustomPeriodUsage = async (resetDay = 1) => {
9607
- const today = /* @__PURE__ */ new Date();
9608
- const todayStr = today.toISOString().split("T")[0];
9571
+ const today2 = /* @__PURE__ */ new Date();
9572
+ const todayStr = today2.toISOString().split("T")[0];
9609
9573
  if (!cachedUsage) {
9610
9574
  cachedUsage = await loadUsageFromFile();
9611
9575
  }
9612
9576
  if (cachedUsage.date !== todayStr) {
9613
9577
  await getDailyUsage();
9614
9578
  }
9615
- let startYear = today.getFullYear();
9616
- let startMonth = today.getMonth();
9617
- const todayDay = today.getDate();
9579
+ let startYear = today2.getFullYear();
9580
+ let startMonth = today2.getMonth();
9581
+ const todayDay = today2.getDate();
9618
9582
  if (todayDay < resetDay) {
9619
9583
  startMonth -= 1;
9620
9584
  if (startMonth < 0) {
@@ -9928,14 +9892,14 @@ var init_usage = __esm({
9928
9892
  });
9929
9893
 
9930
9894
  // src/utils/puppeteer_helper.js
9931
- import os4 from "os";
9895
+ import os3 from "os";
9932
9896
  import path10 from "path";
9933
9897
  import fs11 from "fs";
9934
9898
  import { createRequire } from "module";
9935
9899
  import { fileURLToPath as fileURLToPath2 } from "url";
9936
9900
  function getPuppeteerConfig() {
9937
- const platform = os4.platform();
9938
- const arch = os4.arch();
9901
+ const platform = os3.platform();
9902
+ const arch = os3.arch();
9939
9903
  let pptrPlatform = "";
9940
9904
  let execName = "";
9941
9905
  let subDir = "";
@@ -10272,6 +10236,7 @@ ${finalResults}`;
10272
10236
  import puppeteer2 from "puppeteer";
10273
10237
  import fs13 from "fs";
10274
10238
  import path12 from "path";
10239
+ import TurndownService from "turndown";
10275
10240
  var web_scrape;
10276
10241
  var init_web_scrape = __esm({
10277
10242
  "src/tools/web_scrape.js"() {
@@ -10329,15 +10294,20 @@ var init_web_scrape = __esm({
10329
10294
  el.removeAttribute(attrName);
10330
10295
  }
10331
10296
  }
10332
- if ((el.tagName === "SPAN" || el.tagName === "DIV" || el.tagName === "SECTION") && el.attributes.length === 0) {
10333
- if (el.tagName === "SPAN" || el.tagName === "DIV" && el.childNodes.length === 1 && el.childNodes[0].nodeType === Node.TEXT_NODE) {
10297
+ });
10298
+ while (document.querySelector("div, span")) {
10299
+ document.querySelectorAll("div, span").forEach((el) => {
10300
+ if (el.parentNode) {
10334
10301
  el.replaceWith(...el.childNodes);
10335
10302
  }
10336
- }
10303
+ });
10304
+ }
10305
+ document.querySelectorAll("br").forEach((br) => {
10306
+ br.replaceWith(document.createTextNode("\n\n"));
10337
10307
  });
10338
10308
  const pruneEmpty = () => {
10339
10309
  let found = false;
10340
- document.querySelectorAll("*:not(br)").forEach((el) => {
10310
+ document.querySelectorAll("*").forEach((el) => {
10341
10311
  if (el.childNodes.length === 0 && !el.innerText.trim()) {
10342
10312
  el.remove();
10343
10313
  found = true;
@@ -10349,11 +10319,17 @@ var init_web_scrape = __esm({
10349
10319
  return document.body.innerHTML;
10350
10320
  });
10351
10321
  if (!htmlContent) throw new Error("EMPTY_RENDER_RESULT");
10352
- const cleanedHtml = htmlContent.replace(/\s+/g, " ").replace(/>\s+</g, "><").trim().substring(0, 5e4);
10322
+ const cleanedHtml = htmlContent.replace(/<br\s*\/?>/gi, "\n\n").replace(/[ \t]+/g, " ").replace(/>[ \t]+</g, "><").replace(/\n\s+/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
10323
+ const turndownService = new TurndownService({
10324
+ headingStyle: "atx",
10325
+ codeBlockStyle: "fenced"
10326
+ });
10327
+ const rawMarkdown = turndownService.turndown(cleanedHtml).replace(/\.\s*\n/g, "\n").replace(/ +/g, " ").replace(/\t/g, " ").replace(/\n\s+/g, "\n").replace(/\n{3,}/g, "\n\n");
10328
+ const markdown = rawMarkdown.substring(0, 5e4);
10353
10329
  await browser.close();
10354
- return `CLEANED HTML FROM [${url}]:
10330
+ return `Markdown parsed from [${url}]:
10355
10331
 
10356
- ${cleanedHtml}${htmlContent.length > 5e4 ? "\n\n[TRUNCATED AT 50K CHARS]" : ""}`;
10332
+ ${markdown}${rawMarkdown.length > 5e4 ? "\n\n[TRUNCATED AT 50K CHARS]" : ""}`;
10357
10333
  } catch (err) {
10358
10334
  lastError = err;
10359
10335
  if (browser) await browser.close();
@@ -10553,7 +10529,7 @@ var init_view_file = __esm({
10553
10529
  const end = Math.min(totalLines, finalEnd);
10554
10530
  const resultLines = lines.slice(start, end);
10555
10531
  const header = `File: [${targetPath}] (Showing lines ${start + 1}-${end} of ${totalLines}).`;
10556
- const code = resultLines.map((line, i) => `${String(start + i + 1).padStart(4)}: ${line}`).join("\n");
10532
+ const code = resultLines.map((line, i) => `${String(start + i + 1).padStart(4)}: ${line.trimEnd()}`).join("\n");
10557
10533
  return `${header}
10558
10534
 
10559
10535
  ${code}`;
@@ -13626,7 +13602,7 @@ var init_ai = __esm({
13626
13602
  TERMINATION_SIGNAL = false;
13627
13603
  getCleanGroupedLength = (rawHistory) => {
13628
13604
  const preprocessed = rawHistory.filter(
13629
- (m) => (m.role === "user" || m.role === "agent" || m.role === "system") && m.role !== "think" && !m.isVisualFeedback && !m.isMeta && !String(m.id).startsWith("welcome")
13605
+ (m) => (m.role === "user" || m.role === "agent" || m.role === "system") && m.role !== "think" && !m.isVisualFeedback && !m.isMeta && !m.isTerminalRecord && !(m.text && m.text.includes("[TERMINAL_RECORD]")) && !String(m.id).startsWith("welcome")
13630
13606
  ).map((m, idx, arr) => {
13631
13607
  let text = m.fullText || m.text || "";
13632
13608
  if (m.role === "user" && idx < arr.length - 1) {
@@ -13672,7 +13648,7 @@ var init_ai = __esm({
13672
13648
  turnMessages.forEach((tm) => {
13673
13649
  const textLower = (tm.text || "").toLowerCase();
13674
13650
  const hasTool = textLower.includes("tool:functions.") || textLower.includes("agent:generalist.");
13675
- const isResult = tm.role === "system" && (tm.text?.startsWith("[TOOL RESULT]") || tm.text?.startsWith("SUCCESS:") || tm.text?.startsWith("ERROR:") || tm.text?.startsWith("[TERMINAL_RECORD]") || tm.isTerminalRecord);
13651
+ const isResult = tm.role === "system" && (tm.text?.startsWith("[TOOL RESULT]") || tm.text?.startsWith("SUCCESS:") || tm.text?.startsWith("ERROR:"));
13676
13652
  if (tm.role === "agent") {
13677
13653
  if (hasTool) {
13678
13654
  toolCalls.push(tm.text);
@@ -15785,7 +15761,7 @@ ${currentSummary}
15785
15761
  const dynamicDirAwareness = !!systemSettings?.dynamicDirAwareness;
15786
15762
  const sysInstructionCacheKey = `${chatId}|${aiProvider}|${thinkingLevel}|${modelName}|${profile}|${dynamicDirAwareness}`;
15787
15763
  const isSysInstructionCached = !dynamicDirAwareness && systemInstructionCache.key === sysInstructionCacheKey && systemInstructionCache.value;
15788
- let dirStructure = isSysInstructionCached ? "" : "\n**DIRECTORY STRUCTURE**\nCWD: " + process.cwd() + `${isPlayground ? " [PLAYGROUND MODE]" : ""}${cwdMismatch ? ` (WARNING: CWD Mismatch! Previous Path: ${lastCwd})` : ""}
15764
+ let dirStructure = isSysInstructionCached ? "" : "\n**DIRECTORY STRUCTURE**\nCWD: " + process.cwd() + `${isPlayground ? " [PLAYGROUND MODE]" : ""}
15789
15765
  ` + getDirTree(process.cwd(), dynamicMaxDepth);
15790
15766
  const ideCtx = await getIDEContext();
15791
15767
  let ideBlock = "";
@@ -16065,7 +16041,8 @@ ${ideCtx.warnings}
16065
16041
  const cleanPromptForModel = cleanAgentText.replace(/\\(@\[[^\]]+\])/g, "$1");
16066
16042
  const firstUserMsg = `[METADATA, Chat Context > Metadata]
16067
16043
  Time: ${dateTimeStr}
16068
- OS: ${osDetected}${systemSettings?.dynamicDirAwareness ? dirStructure : ""}${memoryPrompt}${ideBlock}
16044
+ OS: ${osDetected}${systemSettings?.dynamicDirAwareness ? dirStructure : ""}${cwdMismatch ? `WARNING: CWD Mismatch! Previous Path: "${lastCwd}" WRITE the change in chat to aviod path mismatch later
16045
+ ` : ""}${memoryPrompt}${ideBlock}
16069
16046
  [/METADATA]
16070
16047
  ${activeSummaryBlock}${thinkingLevel !== "Fast" && (aiProvider === "Mistral" || thinkingLevel !== "xHigh" && aiProvider === "Google") ? `${aiProvider === "Mistral" || modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS HIGH PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[SYSTEM Priority: HIGH] ONLY use the system prompt tool schema. eg: [tool:functions.ReadFolder(path=".")] [/SYSTEM]
16071
16048
  ${taggedContextStr}[USER PROMPT]
@@ -19503,7 +19480,7 @@ var app_exports = {};
19503
19480
  __export(app_exports, {
19504
19481
  default: () => App
19505
19482
  });
19506
- import os5 from "os";
19483
+ import os4 from "os";
19507
19484
  import React16, { useState as useState15, useEffect as useEffect12, useRef as useRef4, useMemo as useMemo2 } from "react";
19508
19485
  import { Box as Box14, Text as Text16, useInput as useInput9, useStdout as useStdout2, Static } from "ink";
19509
19486
  import fs30 from "fs-extra";
@@ -19531,6 +19508,7 @@ function App({ args = [] }) {
19531
19508
  const [showBridgePromo, setShowBridgePromo] = useState15(false);
19532
19509
  const [promoSelectedIndex, setPromoSelectedIndex] = useState15(0);
19533
19510
  const suggestionOffsetRef = useRef4(0);
19511
+ const maxScrollRef = useRef4(0);
19534
19512
  const persistedModelRef = useRef4(null);
19535
19513
  const activeStreamingMsgRef = useRef4(null);
19536
19514
  const [renderTick, setRenderTick] = useState15(0);
@@ -20018,6 +19996,7 @@ function App({ args = [] }) {
20018
19996
  const [monthlyUsage, setMonthlyUsage] = useState15(null);
20019
19997
  const [customPeriodUsage, setCustomPeriodUsage] = useState15(null);
20020
19998
  const [statsMode, setStatsMode] = useState15("daily");
19999
+ const [statsScrollOffset, setStatsScrollOffset] = useState15(0);
20021
20000
  const PLAYGROUND_CHAT_ID = "flow-playground";
20022
20001
  const [chatId, setChatId] = useState15(args.includes("--playground") ? PLAYGROUND_CHAT_ID : generateChatId());
20023
20002
  useEffect12(() => {
@@ -20192,7 +20171,7 @@ function App({ args = [] }) {
20192
20171
  useEffect12(() => setEscPressCount(0), [input]);
20193
20172
  const [messages, rawSetMessages] = useState15(() => {
20194
20173
  const logoMsg = { id: "logo-" + Date.now(), role: "system", isLogo: true, isMeta: true };
20195
- const isHomeDir = process.cwd() === os5.homedir();
20174
+ const isHomeDir = process.cwd() === os4.homedir();
20196
20175
  const isSystemDir = (() => {
20197
20176
  const cwd = process.cwd().toLowerCase();
20198
20177
  if (process.platform === "win32") {
@@ -20219,7 +20198,7 @@ function App({ args = [] }) {
20219
20198
  id: "home-warning",
20220
20199
  role: "system",
20221
20200
  text: `[SECURITY ALERT] HOME DIRECTORY DETECTED`,
20222
- subText: `You are currently in ${os5.homedir()}. Working here is high-risk as the agent may modify system-sensitive configurations. Please open FluxFlow in project folder.`,
20201
+ subText: `You are currently in ${os4.homedir()}. Working here is high-risk as the agent may modify system-sensitive configurations. Please open FluxFlow in project folder.`,
20223
20202
  isHomeWarning: true
20224
20203
  });
20225
20204
  }
@@ -20398,10 +20377,20 @@ function App({ args = [] }) {
20398
20377
  if (prev === "modelBreakdown") return "daily";
20399
20378
  return prev === "daily" ? "monthly" : "daily";
20400
20379
  });
20380
+ setStatsScrollOffset(0);
20401
20381
  return;
20402
20382
  }
20403
20383
  if (key.space || inputText === " ") {
20404
20384
  setStatsMode((prev) => prev === "modelBreakdown" ? "daily" : "modelBreakdown");
20385
+ setStatsScrollOffset(0);
20386
+ return;
20387
+ }
20388
+ if (key.upArrow) {
20389
+ setStatsScrollOffset((prev) => Math.max(0, prev - 1));
20390
+ return;
20391
+ }
20392
+ if (key.downArrow) {
20393
+ setStatsScrollOffset((prev) => Math.min(maxScrollRef.current, prev + 1));
20405
20394
  return;
20406
20395
  }
20407
20396
  }
@@ -21028,6 +21017,7 @@ function App({ args = [] }) {
21028
21017
  { cmd: "/resume", desc: "Load previous session" },
21029
21018
  { cmd: "/clear", desc: "Clear terminal screen" },
21030
21019
  { cmd: "/compress", desc: "Summarize and compress chat history" },
21020
+ { cmd: "/truncate", desc: "Truncate tool results in chat history" },
21031
21021
  { cmd: "/revert", desc: "Revert codebase back to a checkpoint" },
21032
21022
  { cmd: "/gemini", desc: "Get a happy message from Gemini CLI" },
21033
21023
  { cmd: "/save", desc: "Force save current chat" },
@@ -21960,6 +21950,42 @@ ${list || "No saved chats found."}`, isMeta: true }];
21960
21950
  runCompress();
21961
21951
  break;
21962
21952
  }
21953
+ case "/truncate": {
21954
+ setInput("");
21955
+ let truncatedCount = 0;
21956
+ setMessages((prev) => {
21957
+ const updatedMessages = prev.map((m) => {
21958
+ const fullTextStr = m.fullText || m.text || "";
21959
+ if (!fullTextStr.startsWith("[TOOL RESULT]:")) {
21960
+ return m;
21961
+ }
21962
+ if (fullTextStr.startsWith("[TOOL RESULT]: ERROR") || fullTextStr.startsWith("[TOOL RESULT]: DENIED") || fullTextStr.includes("...Result Truncated by System on User Request")) {
21963
+ return m;
21964
+ }
21965
+ truncatedCount++;
21966
+ if (fullTextStr.startsWith("[TOOL RESULT]: SUCCESS")) {
21967
+ return {
21968
+ ...m,
21969
+ fullText: "[TOOL RESULT]: SUCCESS: ...Result Truncated by System on User Request"
21970
+ };
21971
+ }
21972
+ return {
21973
+ ...m,
21974
+ fullText: "[TOOL RESULT]: ...Result Truncated by System on User Request"
21975
+ };
21976
+ });
21977
+ const finalMsgs = [...updatedMessages, {
21978
+ id: Date.now(),
21979
+ role: "system",
21980
+ text: `[SYSTEM] Truncated ${truncatedCount} tool result(s) in chat history.`,
21981
+ isMeta: true
21982
+ }];
21983
+ saveChat(chatId, null, finalMsgs);
21984
+ setCompletedIndex(finalMsgs.length);
21985
+ return finalMsgs;
21986
+ });
21987
+ break;
21988
+ }
21963
21989
  case "/help": {
21964
21990
  setMessages((prev) => {
21965
21991
  setCompletedIndex(prev.length + 1);
@@ -22019,7 +22045,7 @@ ${timestamp}` };
22019
22045
  let isFirstPacket = true;
22020
22046
  try {
22021
22047
  const rawHistory = [...messages, userMessage].filter(
22022
- (m) => m.role !== "think" && !m.isVisualFeedback && !m.isMeta && !String(m.id).startsWith("welcome")
22048
+ (m) => m.role !== "think" && !m.isVisualFeedback && !m.isMeta && !m.isTerminalRecord && !(m.text && m.text.includes("[TERMINAL_RECORD]")) && !String(m.id).startsWith("welcome")
22023
22049
  );
22024
22050
  const cleanHistoryForAI = [];
22025
22051
  const preprocessed = rawHistory.map((m, idx) => {
@@ -22061,7 +22087,7 @@ ${timestamp}` };
22061
22087
  i++;
22062
22088
  }
22063
22089
  turnMessages.forEach((tm) => {
22064
- const isResult = tm.role === "system" && (tm.text?.startsWith("[TOOL RESULT]") || tm.text?.startsWith("SUCCESS:") || tm.text?.startsWith("ERROR:") || tm.text?.startsWith("[TERMINAL_RECORD]") || tm.isTerminalRecord);
22090
+ const isResult = tm.role === "system" && (tm.text?.startsWith("[TOOL RESULT]") || tm.text?.startsWith("SUCCESS:") || tm.text?.startsWith("ERROR:"));
22065
22091
  const emitRole = isResult ? "system" : "agent";
22066
22092
  const rawText = (tm.text || "").trim();
22067
22093
  if (!rawText) return;
@@ -23196,13 +23222,13 @@ Selection: ${val}`,
23196
23222
  const limitsNotSet = !usingProviderBudgets && (shouldClearValue(reqLimit) || shouldClearValue(tokenLimit) || shouldClearValue(monthlyLimit));
23197
23223
  let resetInfo = "";
23198
23224
  if (quotas.resetMode === "Custom") {
23199
- const today = /* @__PURE__ */ new Date();
23225
+ const today2 = /* @__PURE__ */ new Date();
23200
23226
  const resetDay = quotas.resetDay || 1;
23201
- let resetMonth = today.getMonth();
23202
- if (today.getDate() >= resetDay) {
23227
+ let resetMonth = today2.getMonth();
23228
+ if (today2.getDate() >= resetDay) {
23203
23229
  resetMonth += 1;
23204
23230
  }
23205
- const resetDate = new Date(today.getFullYear(), resetMonth, resetDay);
23231
+ const resetDate = new Date(today2.getFullYear(), resetMonth, resetDay);
23206
23232
  const monthName = resetDate.toLocaleString("default", { month: "short" });
23207
23233
  resetInfo = `${monthName}-${resetDay}`;
23208
23234
  }
@@ -23334,10 +23360,62 @@ Selection: ${val}`,
23334
23360
  const imageCreditsLabel = statsMode === "monthly" ? "Image Credits:" : "Image Credits:";
23335
23361
  const codeChangesLabel = statsMode === "monthly" ? "Code Changes:" : "Code Changes:";
23336
23362
  const toolCallsLabel = statsMode === "monthly" ? "Tool Calls:" : "Tool Calls:";
23337
- return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: colors.borderMuted, paddingX: 3, paddingY: 1, paddingBottom: 0, width: Math.min(125, (stdout?.columns || 100) - 2) }, statsMode === "modelBreakdown" ? /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, "30-DAY MODEL TOKEN BREAKDOWN"), !monthlyUsage?.models || Object.keys(monthlyUsage.models).length === 0 ? /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, italic: true }, "No model token usage recorded in the last 30 days.")) : Object.entries(monthlyUsage.models).map(([provider, models]) => {
23338
- const providerTotalTokens = Object.values(models).reduce((sum, m) => sum + (m.tokens || 0), 0);
23339
- return /* @__PURE__ */ React16.createElement(Box14, { key: provider, flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 40 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.primary, bold: true }, provider, ":")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true }, formatTokens(providerTotalTokens))), Object.entries(models).map(([modelName, stats]) => /* @__PURE__ */ React16.createElement(Box14, { key: modelName, flexDirection: "column", marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 36 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "\xBB ", modelName, ":")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(stats.tokens || 0))), /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Box14, { width: 32 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Input Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens((stats.tokens || 0) - (stats.candidateTokens || 0)))), (stats.cachedTokens || 0) > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 5 }, /* @__PURE__ */ React16.createElement(Box14, { width: 31 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Cached:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(stats.cachedTokens))), /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Box14, { width: 32 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Output Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(stats.candidateTokens || 0))))));
23340
- })) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, "SESSION TELEMETRY")), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Session Duration:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(Date.now() - SESSION_START_TIME))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Model Requests:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, sessionAgentCalls)), /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB API Time:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(sessionApiTime))), /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Tool Time:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(sessionToolTime))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Memory Agent:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, sessionBackgroundCalls)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Tokens Consumed:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalTokens))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Active Context:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionStats.tokens))), sessionTotalTokens > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Input Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalTokens - sessionTotalCandidateTokens))), sessionTotalCachedTokens > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Box14, { width: 21 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Cached:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalCachedTokens))), sessionTotalCandidateTokens > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Output Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalCandidateTokens)))), sessionImageCount > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Images Made:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, sessionImageCount)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Image Credits:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, Number(((sessionImageCredits || 0) * 1e3).toFixed(0)), " credits"))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Code Changes (Sess):")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green" }, "+", runtimeSession.linesAdded), " ", /* @__PURE__ */ React16.createElement(Text16, { color: colors.danger || "red" }, "-", runtimeSession.linesRemoved))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Tool Calls (Sess):")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, runtimeSession.toolSuccess + runtimeSession.toolFailure + runtimeSession.toolDenied, " ( "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green" }, "\u2714 ", runtimeSession.toolSuccess), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.warning || "yellow" }, "\u{1F6C7} ", runtimeSession.toolDenied), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.danger || "red" }, "\u2718 ", runtimeSession.toolFailure), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " )"))), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, trackerTitle), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, timeLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatDuration(u?.duration || 0))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Model Requests:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, u?.agent || 0)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Memory Agent:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, u?.background || 0)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, tokensLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(u?.tokens || 0))), (u?.tokens || 0) > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Input Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens((u?.tokens || 0) - (u?.candidateTokens || 0)))), (u?.cachedTokens || 0) > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Box14, { width: 21 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Cached:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(u.cachedTokens))), (u?.candidateTokens || 0) > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Output Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(u.candidateTokens)))), (u?.imageCalls?.length || 0) > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, imagesLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, u.imageCalls.length)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, imageCreditsLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, Number(((u.imageCalls.reduce((sum, c) => sum + c.cost, 0) || 0) * 1e3).toFixed(0)), " credits"))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, codeChangesLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green" }, "+", u?.linesAdded || 0), " ", /* @__PURE__ */ React16.createElement(Text16, { color: colors.danger || "red" }, "-", u?.linesRemoved || 0))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, toolCallsLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, (u?.toolSuccess || 0) + (u?.toolFailure || 0) + (u?.toolDenied || 0), " ( "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green" }, "\u2714 ", u?.toolSuccess || 0), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.warning || "yellow" }, "\u{1F6C7} ", u?.toolDenied || 0), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.danger || "red" }, "\u2718 ", u?.toolFailure || 0), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " )")))), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, dimColor: true, marginTop: 1, italic: true }, "(Press TAB to toggle Daily/Monthly views, SPACE for Model Breakdown, ESC to return)"));
23363
+ const maxRows = Math.max(4, (stdout?.rows || terminalSize?.rows || 24) - 15);
23364
+ const renderLeaderRow = (key, leftText, rightText, leftColor, rightColor, indent = 0, isBold = false) => {
23365
+ const cols = stdout?.columns || terminalSize?.columns || 80;
23366
+ const boxWidth = Math.min(125, cols - 2);
23367
+ const lineWidth = Math.max(20, boxWidth - 6);
23368
+ const maxLeftLen = Math.max(5, lineWidth - indent - rightText.length - 5);
23369
+ let cleanLeftText = leftText;
23370
+ if (cleanLeftText.length > maxLeftLen) {
23371
+ cleanLeftText = cleanLeftText.substring(0, maxLeftLen - 1) + "\u2026";
23372
+ }
23373
+ const dotsCount = Math.max(2, lineWidth - indent - cleanLeftText.length - rightText.length - 2);
23374
+ const dotsStr = " " + ".".repeat(dotsCount) + " ";
23375
+ const indentStr = " ".repeat(indent);
23376
+ return /* @__PURE__ */ React16.createElement(Box14, { key, width: lineWidth }, /* @__PURE__ */ React16.createElement(Text16, { wrap: "truncate" }, /* @__PURE__ */ React16.createElement(Text16, null, indentStr), /* @__PURE__ */ React16.createElement(Text16, { color: leftColor, bold: isBold }, cleanLeftText), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, dimColor: true }, dotsStr), /* @__PURE__ */ React16.createElement(Text16, { color: rightColor, bold: isBold }, rightText)));
23377
+ };
23378
+ const breakdownRows = [];
23379
+ if (!monthlyUsage?.models || Object.keys(monthlyUsage.models).length === 0) {
23380
+ breakdownRows.push(
23381
+ /* @__PURE__ */ React16.createElement(Box14, { key: "empty", marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, italic: true }, "No model token usage recorded in the last 30 days."))
23382
+ );
23383
+ } else {
23384
+ Object.entries(monthlyUsage.models).forEach(([provider, models], pIdx) => {
23385
+ const providerTotalTokens = Object.values(models).reduce((sum, m) => sum + (m.tokens || 0), 0);
23386
+ if (pIdx > 0) {
23387
+ breakdownRows.push(/* @__PURE__ */ React16.createElement(Box14, { key: `space-prov-${provider}` }, /* @__PURE__ */ React16.createElement(Text16, null, " ")));
23388
+ }
23389
+ breakdownRows.push(
23390
+ renderLeaderRow(`prov-${provider}`, `${provider}:`, formatTokens(providerTotalTokens), colors.primary, colors.text, 0, true)
23391
+ );
23392
+ Object.entries(models).forEach(([modelName, stats], mIdx) => {
23393
+ if (mIdx > 0) {
23394
+ breakdownRows.push(/* @__PURE__ */ React16.createElement(Box14, { key: `space-mod-${provider}-${modelName}` }, /* @__PURE__ */ React16.createElement(Text16, null, " ")));
23395
+ }
23396
+ breakdownRows.push(
23397
+ renderLeaderRow(`mod-${provider}-${modelName}`, `\xBB ${modelName}:`, formatTokens(stats.tokens || 0), colors.secondary, colors.text, 2, true)
23398
+ );
23399
+ breakdownRows.push(
23400
+ renderLeaderRow(`in-${provider}-${modelName}`, "\xBB Input Tokens:", formatTokens((stats.tokens || 0) - (stats.candidateTokens || 0)), colors.textMuted, colors.text, 5, false)
23401
+ );
23402
+ if ((stats.cachedTokens || 0) > 0) {
23403
+ breakdownRows.push(
23404
+ renderLeaderRow(`cache-${provider}-${modelName}`, "\xBB Cached:", formatTokens(stats.cachedTokens), colors.textMuted, colors.text, 7, false)
23405
+ );
23406
+ }
23407
+ breakdownRows.push(
23408
+ renderLeaderRow(`out-${provider}-${modelName}`, "\xBB Output Tokens:", formatTokens(stats.candidateTokens || 0), colors.textMuted, colors.text, 5, false)
23409
+ );
23410
+ });
23411
+ });
23412
+ }
23413
+ const totalRows = breakdownRows.length;
23414
+ const maxScroll = Math.max(0, totalRows - maxRows);
23415
+ maxScrollRef.current = maxScroll;
23416
+ const effectiveScroll = Math.min(statsScrollOffset, maxScroll);
23417
+ const visibleRows = breakdownRows.slice(effectiveScroll, effectiveScroll + maxRows);
23418
+ return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: colors.borderMuted, paddingX: 3, paddingY: 1, paddingBottom: 0, width: Math.min(125, (stdout?.columns || 100) - 2) }, statsMode === "modelBreakdown" ? /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Box14, { justifyContent: "space-between" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, "30-DAY MODEL TOKEN BREAKDOWN"), totalRows > maxRows && /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, dimColor: true }, "[", effectiveScroll + 1, "-", Math.min(totalRows, effectiveScroll + maxRows), " of ", totalRows, "] \u25B2\u25BC")), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", height: maxRows, marginTop: 1 }, visibleRows)) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, "SESSION TELEMETRY")), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Session Duration:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(Date.now() - SESSION_START_TIME))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Model Requests:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, sessionAgentCalls)), /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB API Time:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(sessionApiTime))), /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Tool Time:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatMsDuration(sessionToolTime))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Memory Agent:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, sessionBackgroundCalls)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Tokens Consumed:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalTokens))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Active Context:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionStats.tokens))), sessionTotalTokens > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Input Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalTokens - sessionTotalCandidateTokens))), sessionTotalCachedTokens > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Box14, { width: 21 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Cached:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalCachedTokens))), sessionTotalCandidateTokens > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Output Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(sessionTotalCandidateTokens)))), sessionImageCount > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Images Made:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, sessionImageCount)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Image Credits:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, Number(((sessionImageCredits || 0) * 1e3).toFixed(0)), " credits"))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Code Changes (Sess):")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green" }, "+", runtimeSession.linesAdded), " ", /* @__PURE__ */ React16.createElement(Text16, { color: colors.danger || "red" }, "-", runtimeSession.linesRemoved))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Tool Calls (Sess):")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, runtimeSession.toolSuccess + runtimeSession.toolFailure + runtimeSession.toolDenied, " ( "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green" }, "\u2714 ", runtimeSession.toolSuccess), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.warning || "yellow" }, "\u{1F6C7} ", runtimeSession.toolDenied), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.danger || "red" }, "\u2718 ", runtimeSession.toolFailure), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " )"))), /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, trackerTitle), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, timeLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatDuration(u?.duration || 0))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Model Requests:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, u?.agent || 0)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, "Memory Agent:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, u?.background || 0)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, tokensLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(u?.tokens || 0))), (u?.tokens || 0) > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Input Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens((u?.tokens || 0) - (u?.candidateTokens || 0)))), (u?.cachedTokens || 0) > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Box14, { width: 21 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Cached:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(u.cachedTokens))), (u?.candidateTokens || 0) > 0 && /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2 }, /* @__PURE__ */ React16.createElement(Box14, { width: 23 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\xBB Output Tokens:")), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, formatTokens(u.candidateTokens)))), (u?.imageCalls?.length || 0) > 0 && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, imagesLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, u.imageCalls.length)), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, imageCreditsLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, Number(((u.imageCalls.reduce((sum, c) => sum + c.cost, 0) || 0) * 1e3).toFixed(0)), " credits"))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, codeChangesLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green" }, "+", u?.linesAdded || 0), " ", /* @__PURE__ */ React16.createElement(Text16, { color: colors.danger || "red" }, "-", u?.linesRemoved || 0))), /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Box14, { width: 25 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary }, toolCallsLabel)), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, (u?.toolSuccess || 0) + (u?.toolFailure || 0) + (u?.toolDenied || 0), " ( "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green" }, "\u2714 ", u?.toolSuccess || 0), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.warning || "yellow" }, "\u{1F6C7} ", u?.toolDenied || 0), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.danger || "red" }, "\u2718 ", u?.toolFailure || 0), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " )")))), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, dimColor: true, italic: true }, "\n", "(Press TAB to toggle Daily/Monthly views, SPACE for Model Breakdown, ESC to return)"));
23341
23419
  }
23342
23420
  case "dynamicDirDanger":
23343
23421
  return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: colors.borderMuted, paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.warning || "yellow", bold: true, underline: true }, "DYNAMIC DIRECTORY AWARENESS"), /* @__PURE__ */ React16.createElement(Text16, { marginTop: 1, color: colors.text }, "Enabling this keeps the agent aware of filesystem state in real time, but may reduce prompt cache efficiency."), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, "\n", "RECOMMENDED SCENARIOS TO TURN ON:"), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\u2022 Repo is small."), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\u2022 The task benefits from real-time filesystem awareness."), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\u2022 Files are often created, renamed, or deleted."), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\u2022 You know exactly what you're signing up for."), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\u2022 You don't have conflicting decisions regarding token bills."), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "\u2022 You want to see your wallet crying at 3am."), /* @__PURE__ */ React16.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(
@@ -24142,7 +24220,7 @@ var init_app = __esm({
24142
24220
  };
24143
24221
  getKeybindingsPath = (ideName) => {
24144
24222
  const dirName = getIDEDirName(ideName);
24145
- const home = os5.homedir();
24223
+ const home = os4.homedir();
24146
24224
  if (process.platform === "win32") {
24147
24225
  const appData = process.env.APPDATA;
24148
24226
  if (!appData) return null;
@@ -24401,10 +24479,10 @@ var init_app = __esm({
24401
24479
  // src/cli.jsx
24402
24480
  import { spawn as spawn3 } from "child_process";
24403
24481
  import { fileURLToPath as fileURLToPath4 } from "url";
24404
- import os6 from "os";
24482
+ import os5 from "os";
24405
24483
  import dotenv2 from "dotenv";
24406
24484
  dotenv2.config({ quiet: true });
24407
- var totalSystemRamBytes = os6.totalmem();
24485
+ var totalSystemRamBytes = os5.totalmem();
24408
24486
  var totalSystemRamMB = totalSystemRamBytes / (1024 * 1024);
24409
24487
  var SAFETY_MARGIN = 0.5;
24410
24488
  var calculatedLimit = Math.floor(totalSystemRamMB * SAFETY_MARGIN);
@@ -24502,6 +24580,7 @@ Usage: fluxflow --export error`);
24502
24580
  /clear Clear terminal screen
24503
24581
  /resume Load previous session
24504
24582
  /compress Summarize and compress chat history
24583
+ /truncate Truncate tool results in chat history
24505
24584
  /revert Revert codebase back to a checkpoint
24506
24585
  /save Force save current chat
24507
24586
  /export [chat|logs] Export chat session or system error logs
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "3.16.0",
4
- "date": "2026-08-01",
3
+ "version": "3.16.2",
4
+ "date": "2026-08-02",
5
5
  "description": "A High-Fidelity Agentic CLI with Sub-Agents for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "scripts": {
43
43
  "start": "tsx ./src/cli.jsx",
44
- "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/fluxflow.js --external:react --external:ink --external:chalk --external:fs-extra --external:gradient-string --external:ink-text-input --external:ink-select-input --external:@google/genai --external:zod --external:nanoid --external:puppeteer --external:pdf-lib --external:node-pty --external:html-to-docx --external:typescript --external:ws --external:web-tree-sitter --external:diff --external:dotenv --external:fast-glob"
44
+ "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/fluxflow.js --external:react --external:ink --external:chalk --external:fs-extra --external:gradient-string --external:ink-text-input --external:ink-select-input --external:@google/genai --external:zod --external:nanoid --external:puppeteer --external:pdf-lib --external:node-pty --external:html-to-docx --external:typescript --external:ws --external:web-tree-sitter --external:diff --external:dotenv --external:fast-glob --external:turndown"
45
45
  },
46
46
  "dependencies": {
47
47
  "@google/genai": "^1.52.0",
@@ -62,6 +62,7 @@
62
62
  "pdf-lib": "^1.17.1",
63
63
  "puppeteer": "24.43.1",
64
64
  "react": "^19.2.5",
65
+ "turndown": "^7.2.4",
65
66
  "web-tree-sitter": "^0.25.10",
66
67
  "ws": "^8.21.0",
67
68
  "zod": "^4.3.6"