fluxflow-cli 3.19.2 → 3.19.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 (3) hide show
  1. package/README.md +2 -4
  2. package/dist/fluxflow.js +320 -194
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -74,7 +74,7 @@ Zero setup means zero setup. On first run, Flux Flow performs an integrity check
74
74
  - **Rich Aesthetics**: High-contrast, sleek design with smooth transitions and micro-animations.
75
75
 
76
76
  ### 🧠 **The Dual-Intelligence System**
77
- - **Flux Mode (Dev)**: High-speed, agentic problem solving with a 70-turn persistent loop for massive coding tasks.
77
+ - **Flux Mode (Dev)**: High-speed, agentic problem solving for massive coding tasks.
78
78
  - **Flow Mode (Chat)**: Optimized for high-quality conversation and web-assisted reasoning.
79
79
 
80
80
  ### 🛡️ **Digital Fortress Governance**
@@ -83,8 +83,6 @@ Security isn't an afterthought; it's a boundary.
83
83
  - **Granular Command Policies**: Configure Auto-Approve (`Auto` / `Read-Only` / `None`), Auto-Disallow (`Auto` / `Destructive` / `None`), Network Access toggle, and Auto-Approve Git Commits independently.
84
84
  - **External Path Hardlock**: Restricts the agent to your Current Working Directory (CWD) unless you explicitly unlock it.
85
85
  - **Human-in-the-Loop (HITL)**: Every file write and terminal command requires your high-fidelity approval.
86
- - **XOR Vaulting**: All local session histories, memories, and API keys are obfuscated and encrypted at rest.
87
- - **Adaptive Failover**: Automatic multi-stage retry logic with high-concurrency fallback model switching (Gemini 3.1 Flash Lite) during peak API congestion.
88
86
 
89
87
  ### 🧹 **The Background Janitor**
90
88
  While you move at high speed, the Janitor follows behind—refining session titles, compressing data, and ensuring your context window remains at absolute peak performance.
@@ -98,7 +96,7 @@ Delegate complex tasks to subagents. Spawns blocking subagents (`invokeSync`) or
98
96
  - **Deep File-System Interaction**: Edit, move, and refactor code across multiple files with atomic precision.
99
97
  - **Real-Time Web Intelligence**: Autonomous web-searching via DuckDuckGo for live news and technical research.
100
98
  - **Autonomous Project Alignment**: Automatically detects and adheres to project-specific instructions in `Agent.md`, `Skills.md`, and `Fluxflow.md` for high-fidelity alignment with your coding standards and custom workflows.
101
- - **High-Reliability Fallback**: Automatic failover to a lighter, high-concurrency model during peak traffic to ensure zero session loss.
99
+
102
100
  - **Persistent Memory**: The agent learns from your preferences and project requirements across sessions.
103
101
 
104
102
  ---
package/dist/fluxflow.js CHANGED
@@ -2662,22 +2662,41 @@ var init_text = __esm({
2662
2662
  const indices = /* @__PURE__ */ new Set();
2663
2663
  const allowMultiple = args.allowMultiple === true || String(args.allowMultiple).toLowerCase() === "true";
2664
2664
  Object.keys(args).forEach((key) => {
2665
- const m = key.match(/^(searchContent|replaceContent|newContent|content_to_replace|content_to_add)(\d+)?$/);
2665
+ const m = key.match(/^(searchContent|search_content|replaceContent|replace_content|newContent|new_content|content_to_replace|content_to_add|searchBlock|search_block|newBlock|new_block)_?(\d+)?$/i);
2666
2666
  if (m) {
2667
- const index2 = m[2] ? parseInt(m[2]) : 1;
2667
+ const index2 = m[2] ? parseInt(m[2], 10) : 1;
2668
2668
  indices.add(index2);
2669
2669
  }
2670
2670
  });
2671
2671
  const sortedIndices = Array.from(indices).sort((a, b) => a - b);
2672
- for (const i of sortedIndices) {
2672
+ const searchNames = ["searchContent", "search_content", "replaceContent", "replace_content", "content_to_replace", "searchBlock", "search_block"];
2673
+ const newNames = ["newContent", "new_content", "content_to_add", "newBlock", "new_block"];
2674
+ const getVal = (index2, searchList, newList) => {
2673
2675
  let r, n;
2674
- if (i === 1) {
2675
- r = args.searchContent1 ?? args.searchContent ?? args.replaceContent1 ?? (args.content_to_replace ?? args.replaceContent);
2676
- n = args.newContent1 ?? (args.content_to_add ?? args.newContent);
2677
- } else {
2678
- r = args[`searchContent${i}`] ?? args[`replaceContent${i}`] ?? args[`content_to_replace${i}`];
2679
- n = args[`newContent${i}`] ?? args[`content_to_add${i}`];
2676
+ for (const name of searchList) {
2677
+ const keysToTry = index2 === 1 ? [`${name}1`, name, `${name}_1`] : [`${name}${index2}`, `${name}_${index2}`];
2678
+ for (const k of keysToTry) {
2679
+ if (args[k] !== void 0) {
2680
+ r = args[k];
2681
+ break;
2682
+ }
2683
+ }
2684
+ if (r !== void 0) break;
2680
2685
  }
2686
+ for (const name of newList) {
2687
+ const keysToTry = index2 === 1 ? [`${name}1`, name, `${name}_1`] : [`${name}${index2}`, `${name}_${index2}`];
2688
+ for (const k of keysToTry) {
2689
+ if (args[k] !== void 0) {
2690
+ n = args[k];
2691
+ break;
2692
+ }
2693
+ }
2694
+ if (n !== void 0) break;
2695
+ }
2696
+ return { r, n };
2697
+ };
2698
+ for (const i of sortedIndices) {
2699
+ const { r, n } = getVal(i, searchNames, newNames);
2681
2700
  if (r !== void 0 && n !== void 0) {
2682
2701
  patchPairs.push({ replace: r, new: n });
2683
2702
  } else if (r !== void 0 || n !== void 0) {
@@ -2921,6 +2940,9 @@ var init_text = __esm({
2921
2940
  if (!patchResults || patchResults.length === 0) return "";
2922
2941
  const allLinesOriginal = originalContent.split(/\r?\n/);
2923
2942
  const allLinesFinal = finalContent.split(/\r?\n/);
2943
+ const maxLineNum = Math.max(allLinesOriginal.length, allLinesFinal.length, 1);
2944
+ const gutterWidth = Math.max(4, String(maxLineNum).length);
2945
+ const fmtNum = (num) => String(num).padStart(gutterWidth, " ");
2924
2946
  let diffText = `[DIFF_START]
2925
2947
  `;
2926
2948
  const separatorLine = "\u2550".repeat(88);
@@ -2933,7 +2955,7 @@ var init_text = __esm({
2933
2955
  const contextStart = Math.max(0, startLineFinal - 4);
2934
2956
  currentFinalLineIdx = contextStart;
2935
2957
  while (currentFinalLineIdx < startLineFinal - 1) {
2936
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2958
+ diffText += `[UI_CONTEXT] ${fmtNum(currentFinalLineIdx + 1)} |${allLinesFinal[currentFinalLineIdx] || ""}
2937
2959
  `;
2938
2960
  currentFinalLineIdx++;
2939
2961
  }
@@ -2944,7 +2966,7 @@ var init_text = __esm({
2944
2966
  if (gap >= threshold) {
2945
2967
  let afterLimit = Math.min(allLinesFinal.length, currentFinalLineIdx + 3);
2946
2968
  while (currentFinalLineIdx < afterLimit) {
2947
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2969
+ diffText += `[UI_CONTEXT] ${fmtNum(currentFinalLineIdx + 1)} |${allLinesFinal[currentFinalLineIdx] || ""}
2948
2970
  `;
2949
2971
  currentFinalLineIdx++;
2950
2972
  }
@@ -2953,13 +2975,13 @@ var init_text = __esm({
2953
2975
  const beforeStart = Math.max(currentFinalLineIdx, startLineFinal - 4);
2954
2976
  currentFinalLineIdx = beforeStart;
2955
2977
  while (currentFinalLineIdx < startLineFinal - 1) {
2956
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2978
+ diffText += `[UI_CONTEXT] ${fmtNum(currentFinalLineIdx + 1)} |${allLinesFinal[currentFinalLineIdx] || ""}
2957
2979
  `;
2958
2980
  currentFinalLineIdx++;
2959
2981
  }
2960
2982
  } else {
2961
2983
  while (currentFinalLineIdx < startLineFinal - 1) {
2962
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2984
+ diffText += `[UI_CONTEXT] ${fmtNum(currentFinalLineIdx + 1)} |${allLinesFinal[currentFinalLineIdx] || ""}
2963
2985
  `;
2964
2986
  currentFinalLineIdx++;
2965
2987
  }
@@ -2981,7 +3003,7 @@ var init_text = __esm({
2981
3003
  lineText = origIndent + line.trimStart();
2982
3004
  }
2983
3005
  }
2984
- diffText += `-${res.originalStartLine + i}|${lineText}
3006
+ diffText += `-${fmtNum(res.originalStartLine + i)} |${lineText}
2985
3007
  `;
2986
3008
  });
2987
3009
  let hunkEndInFinal = currentFinalLineIdx;
@@ -3004,7 +3026,7 @@ var init_text = __esm({
3004
3026
  }
3005
3027
  }
3006
3028
  while (currentFinalLineIdx < hunkEndInFinal) {
3007
- diffText += `+${currentFinalLineIdx + 1}|${allLinesFinal[currentFinalLineIdx] || ""}
3029
+ diffText += `+${fmtNum(currentFinalLineIdx + 1)} |${allLinesFinal[currentFinalLineIdx] || ""}
3008
3030
  `;
3009
3031
  currentFinalLineIdx++;
3010
3032
  }
@@ -3013,7 +3035,7 @@ var init_text = __esm({
3013
3035
  if (lastSuccessfulHunk !== null) {
3014
3036
  let limit = Math.min(allLinesFinal.length, currentFinalLineIdx + 3);
3015
3037
  while (currentFinalLineIdx < limit) {
3016
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
3038
+ diffText += `[UI_CONTEXT] ${fmtNum(currentFinalLineIdx + 1)} |${allLinesFinal[currentFinalLineIdx] || ""}
3017
3039
  `;
3018
3040
  currentFinalLineIdx++;
3019
3041
  }
@@ -3453,7 +3475,7 @@ var init_text = __esm({
3453
3475
  cleanSignals = (text, isThinkRole = false) => {
3454
3476
  if (!text) return text;
3455
3477
  if (isThinkRole) {
3456
- return text.replace(/^<(think|thought)>/gi, "").replace(/<\/(think|thought)>$/gi, "");
3478
+ return text.replace(/^<(think|thought)>/gi, "").replace(/<\/(think|thought)>$/gi, "").replace(/^\r?\n+/, "").replace(/\r?\n+$/, "");
3457
3479
  }
3458
3480
  let result = text.replace(REGEX_INITIAL_THINK, "</think>").replace(REGEX_INITIAL_TOOL, (match, _nl, offset, str) => !bypassBacktick && isInsideBacktick(str, offset) ? match : "");
3459
3481
  if (result && result.includes("[tool:")) {
@@ -6990,7 +7012,7 @@ ${mode === "Flux" ? `- JSON ESCAPE ALL LITERAL ESCAPE SEQUENCES IN TOOL ARGUMENT
6990
7012
  ${mode === "Flux" ? `- WORKSPACE TOOLS (path = relative; FIRST ARGUMENT, path separator: '/') -
6991
7013
  - [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. ${aiProvider !== "Google" ? `${isMultiModal ? `Supports images/docs` : ""}` : `Supports images/docs`}
6992
7014
  - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
6993
- - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", searchContent1="string OR ^LINE:start..end$", newContent1="...", ...MAX15)]. TARGET MINIMAL DIFF. "^LINE:start..end$" line ranges MUST for multi-line selection or escape sequences. Verify diffs
7015
+ - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", searchContent1="string OR ^LINE:start..end$", newContent1="...", ...MAX15)]. TARGET MINIMAL DIFF. Line Ranges "^LINE:start..end$" MUST for multi-line selection or escape sequences. Verify diffs
6994
7016
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile
6995
7017
  - [tool:functions.SearchKeyword(keyword="...", path="optional, dir/file/glob/regex", fuzzy="bool optional, default: false", regex="bool optional, default: auto")]. path scopes search. Find definitions, logic, relevant code
6996
7018
  - [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `POWERSHELL` : `WINDOWS CMD` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user
@@ -10495,11 +10517,12 @@ var init_history = __esm({
10495
10517
  // src/utils/usage.js
10496
10518
  import fs11 from "fs-extra";
10497
10519
  import path9 from "path";
10498
- 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;
10520
+ var generateSaveId, cachedUsage, writeTimeout, lastWriteTime, isDirty, defaultStats, purgeOldHistory, loadUsageFromFile, flushUsage, queueFlush, initUsage, forceFlushUsage, getDailyUsage, getMonthlyUsage, incrementUsage, runtimeSession, addToUsage, getCustomPeriodUsage, checkQuotaDetailed, checkQuota, getImageQuotaBuckets, getImageQuotaLimit, checkImageQuota, getImageQuotaStats, recordImageGeneration;
10499
10521
  var init_usage = __esm({
10500
10522
  "src/utils/usage.js"() {
10501
10523
  init_paths();
10502
10524
  init_crypto();
10525
+ init_settings();
10503
10526
  generateSaveId = () => Math.random().toString(36).substring(2) + Date.now().toString(36);
10504
10527
  cachedUsage = null;
10505
10528
  writeTimeout = null;
@@ -10936,14 +10959,16 @@ var init_usage = __esm({
10936
10959
  }
10937
10960
  return summed;
10938
10961
  };
10939
- checkQuota = async (key, settings) => {
10940
- const tier = settings.apiTier || "Free";
10941
- const quotas = settings.quotas || {};
10962
+ checkQuotaDetailed = async (key, settings = {}) => {
10963
+ const loadedSettings = await loadSettings().catch(() => ({}));
10964
+ const tier = settings.apiTier || loadedSettings.apiTier || "Free";
10965
+ const quotas = settings.quotas || settings.systemSettings?.quotas || loadedSettings.quotas || {};
10966
+ const providerBudgets = quotas.providerBudgets || {};
10967
+ const useProvider = !!providerBudgets.__useProvider;
10968
+ const currentProvider = settings.aiProvider || loadedSettings.aiProvider || "Google";
10969
+ const isPerProvider = useProvider && !!providerBudgets[currentProvider];
10942
10970
  const resolveAgentLimits = () => {
10943
- const providerBudgets = quotas.providerBudgets || {};
10944
- const useProvider = !!providerBudgets.__useProvider;
10945
- const currentProvider = settings.aiProvider || "Google";
10946
- if (useProvider && providerBudgets[currentProvider]) {
10971
+ if (isPerProvider) {
10947
10972
  const pb = providerBudgets[currentProvider];
10948
10973
  return {
10949
10974
  agentLimit: typeof pb.agentLimit === "number" && pb.agentLimit > 0 ? pb.agentLimit : 99999999,
@@ -10957,55 +10982,74 @@ var init_usage = __esm({
10957
10982
  monthlyTokenLimit: quotas.monthlyTokenLimit || 99999999999999
10958
10983
  };
10959
10984
  };
10960
- if (tier === "Free") {
10961
- if (key === "agent") {
10962
- const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
10963
- const dailyUsage = await getDailyUsage();
10964
- if (dailyUsage.agent + dailyUsage.background >= 999999) return false;
10965
- const dailyOk = dailyUsage.agent < agentLimit && (dailyUsage.tokens || 0) < tokenLimit;
10966
- if (!dailyOk) return false;
10967
- let monthlyUsage;
10968
- if (quotas.resetMode === "Custom") {
10969
- monthlyUsage = await getCustomPeriodUsage(quotas.resetDay || 1);
10970
- } else {
10971
- monthlyUsage = await getMonthlyUsage();
10985
+ if (key === "agent") {
10986
+ const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
10987
+ const dailyUsage = await getDailyUsage();
10988
+ let monthlyUsage;
10989
+ if (quotas.resetMode === "Custom") {
10990
+ monthlyUsage = await getCustomPeriodUsage(quotas.resetDay || 1);
10991
+ } else {
10992
+ monthlyUsage = await getMonthlyUsage();
10993
+ }
10994
+ let dailyAgentCount = 0;
10995
+ let dailyTokenCount = 0;
10996
+ let monthlyTokenCount = 0;
10997
+ if (isPerProvider) {
10998
+ dailyAgentCount = dailyUsage.providerRequests?.[currentProvider] || 0;
10999
+ const dailyModels = dailyUsage.models?.[currentProvider] || {};
11000
+ for (const m in dailyModels) {
11001
+ dailyTokenCount += dailyModels[m]?.tokens || 0;
10972
11002
  }
10973
- return (monthlyUsage.tokens || 0) < monthlyTokenLimit;
11003
+ const monthlyModels = monthlyUsage.models?.[currentProvider] || {};
11004
+ for (const m in monthlyModels) {
11005
+ monthlyTokenCount += monthlyModels[m]?.tokens || 0;
11006
+ }
11007
+ } else {
11008
+ dailyAgentCount = dailyUsage.agent || 0;
11009
+ dailyTokenCount = dailyUsage.tokens || 0;
11010
+ monthlyTokenCount = monthlyUsage.tokens || 0;
10974
11011
  }
10975
- if (key === "background") {
10976
- const dailyUsage = await getDailyUsage();
10977
- if (dailyUsage.agent + dailyUsage.background >= 999999) return false;
10978
- return dailyUsage.background < (quotas.backgroundLimit || 999999);
11012
+ if (tier === "Free" && dailyUsage.agent + dailyUsage.background >= 999999) {
11013
+ return { allowed: false, reason: "Free Tier Daily Usage Limit Exceeded" };
10979
11014
  }
10980
- if (key === "search") {
10981
- const dailyUsage = await getDailyUsage();
10982
- return dailyUsage.search < (quotas.searchLimit || 100);
11015
+ if (dailyAgentCount >= agentLimit) {
11016
+ return {
11017
+ allowed: false,
11018
+ reason: isPerProvider ? `Daily Request Limit Reached for ${currentProvider}` : `Daily Agent Request Limit Reached`
11019
+ };
10983
11020
  }
10984
- }
10985
- if (tier === "Paid" || tier === "Custom") {
10986
- if (key === "agent") {
10987
- const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
10988
- const dailyUsage = await getDailyUsage();
10989
- const dailyOk = dailyUsage.agent < agentLimit && (dailyUsage.tokens || 0) < tokenLimit;
10990
- if (!dailyOk) return false;
10991
- let monthlyUsage;
10992
- if (quotas.resetMode === "Custom") {
10993
- monthlyUsage = await getCustomPeriodUsage(quotas.resetDay || 1);
10994
- } else {
10995
- monthlyUsage = await getMonthlyUsage();
10996
- }
10997
- return (monthlyUsage.tokens || 0) < monthlyTokenLimit;
11021
+ if (dailyTokenCount >= tokenLimit) {
11022
+ return {
11023
+ allowed: false,
11024
+ reason: isPerProvider ? `Daily Token Budget Exhausted for ${currentProvider}` : `Daily Token Budget Exhausted`
11025
+ };
10998
11026
  }
10999
- if (key === "background") {
11000
- const dailyUsage = await getDailyUsage();
11001
- return dailyUsage.background < (quotas.backgroundLimit || 999999);
11027
+ if (monthlyTokenCount >= monthlyTokenLimit) {
11028
+ return {
11029
+ allowed: false,
11030
+ reason: isPerProvider ? `Monthly Token Budget Exhausted for ${currentProvider}` : `Monthly Token Budget Exhausted`
11031
+ };
11002
11032
  }
11003
- if (key === "search") {
11004
- const dailyUsage = await getDailyUsage();
11005
- return dailyUsage.search < (quotas.searchLimit || 100);
11033
+ return { allowed: true };
11034
+ }
11035
+ if (key === "background") {
11036
+ const dailyUsage = await getDailyUsage();
11037
+ if (tier === "Free" && dailyUsage.agent + dailyUsage.background >= 999999) {
11038
+ return { allowed: false, reason: "Free Tier Background Limit Exceeded" };
11006
11039
  }
11040
+ const ok = dailyUsage.background < (quotas.backgroundLimit || 999999);
11041
+ return { allowed: ok, reason: ok ? void 0 : "Background Request Limit Exceeded" };
11007
11042
  }
11008
- return true;
11043
+ if (key === "search") {
11044
+ const dailyUsage = await getDailyUsage();
11045
+ const ok = dailyUsage.search < (quotas.searchLimit || 100);
11046
+ return { allowed: ok, reason: ok ? void 0 : "Search Quota Exceeded" };
11047
+ }
11048
+ return { allowed: true };
11049
+ };
11050
+ checkQuota = async (key, settings = {}) => {
11051
+ const res = await checkQuotaDetailed(key, settings);
11052
+ return res.allowed;
11009
11053
  };
11010
11054
  getImageQuotaBuckets = (imageCalls) => {
11011
11055
  const hourMs = 60 * 60 * 1e3;
@@ -11322,7 +11366,7 @@ var init_web_search = __esm({
11322
11366
  init_paths();
11323
11367
  init_puppeteer_helper();
11324
11368
  web_search = async (argsString) => {
11325
- const { query, limit = 10, aiMode = false } = parseArgs(argsString);
11369
+ const { query, limit = 5, aiMode = false } = parseArgs(argsString);
11326
11370
  if (!query) return 'ERROR: Missing "query" argument for web_search.';
11327
11371
  const maxRetries = 3;
11328
11372
  let lastError = null;
@@ -11895,22 +11939,22 @@ ${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
11895
11939
  return `ERROR: CRITICAL FAILURE: Verification failed. File [${targetPath}] is empty on disk despite success report!`;
11896
11940
  }
11897
11941
  let snippet = "";
11898
- if (verifiedLineCount <= 200) {
11942
+ if (verifiedLineCount <= 100) {
11899
11943
  snippet = verifiedLines.join("\n");
11900
11944
  } else {
11901
- const head = verifiedLines.slice(0, 100).join("\n");
11902
- const tail = verifiedLines.slice(-100).join("\n");
11945
+ const head = verifiedLines.slice(0, 50).join("\n");
11946
+ const tail = verifiedLines.slice(-50).join("\n");
11903
11947
  snippet = `${head}
11904
11948
 
11905
- ... [${verifiedLineCount - 200} lines truncated] ...
11949
+ ... [${verifiedLineCount - 100} lines truncated] ...
11906
11950
 
11907
11951
  ${tail}`;
11908
11952
  }
11909
11953
  verifiedContent = null;
11910
11954
  return `SUCCESS: File [${targetPath}] saved.
11911
-
11912
11955
  - Stats: [${verifiedLineCount} lines, ${(verifiedSize / 1024).toFixed(1)} KB]
11913
11956
  ${ancestry}- Content Preview:
11957
+
11914
11958
  ${snippet}`;
11915
11959
  } catch (err) {
11916
11960
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -15724,8 +15768,15 @@ var init_ai = __esm({
15724
15768
  signal
15725
15769
  });
15726
15770
  if (!response.ok) {
15727
- const err = await response.json();
15728
- const error = new Error(`NVIDIA API Error: ${err.error?.message || response.statusText}`);
15771
+ const errText = await response.text().catch(() => "");
15772
+ let errMsg = response.statusText;
15773
+ try {
15774
+ const errData = JSON.parse(errText);
15775
+ errMsg = errData.error?.message || errData.message || JSON.stringify(errData.detail || errData);
15776
+ } catch {
15777
+ if (errText) errMsg = errText;
15778
+ }
15779
+ const error = new Error(`NVIDIA API Error (${response.status}): ${errMsg}`);
15729
15780
  error.status = response.status;
15730
15781
  throw error;
15731
15782
  }
@@ -17808,6 +17859,10 @@ ${wildcardToolingPrompt}${taggedContextStr}[USER PROMPT] ${cleanPromptForModel.t
17808
17859
  let fullAgentResponseChunks = [];
17809
17860
  let wasToolCalledInLastLoop = false;
17810
17861
  for (let loop = 0; loop <= MAX_LOOPS; loop++) {
17862
+ const quotaCheck = await checkQuotaDetailed("agent", settings);
17863
+ if (!quotaCheck.allowed) {
17864
+ throw new Error(quotaCheck.reason || `Budget Exhausted for Provider (${aiProvider || "Agent"})`);
17865
+ }
17811
17866
  const currentTurnTools = [];
17812
17867
  wasToolCalledInLastLoop = false;
17813
17868
  if (systemSettings2?.compression === 0 && (sessionStats?.tokens || 0) > contextTruncationCount) {
@@ -17909,15 +17964,29 @@ ${combinedNudge}`;
17909
17964
  const THINK_OPEN_PH = "___THINK_OPEN_TAG___";
17910
17965
  const THINK_CLOSE_PH = "___THINK_CLOSE_TAG___";
17911
17966
  text = text.replace("<think>", THINK_OPEN_PH).replace("</think>", THINK_CLOSE_PH);
17912
- text = text.replace(/<(\w+)(?:[^>]*)>\s*([\s\S]*?\[tool:[^\]]*\][\s\S]*?)\s*<\/\1>/gi, (match2, tagName, innerContent) => {
17913
- if (innerContent && innerContent.includes("[tool:")) return innerContent.trim();
17914
- return match2;
17915
- });
17916
17967
  text = text.replace(/```(?:tool|yaml|function|json)?\s*\n?([\s\S]*?)\n?\```/gi, (match2, inner) => {
17917
17968
  if (inner.includes("[tool:")) return inner.trim();
17918
17969
  return match2;
17919
17970
  });
17920
- text = text.replace(/<(\w+)(?:[^>]*)>\r?\n?/gi, "").replace(/\r?\n?<\/\w+(?:[^>]*)>/gi, "");
17971
+ let result = "";
17972
+ let i = 0;
17973
+ while (i < text.length) {
17974
+ const toolIdx = text.indexOf("[tool:", i);
17975
+ if (toolIdx === -1) {
17976
+ result += text.substring(i).replace(/<(\w+)(?:[^>]*)>\r?\n?/gi, "").replace(/\r?\n?<\/\w+(?:[^>]*)>/gi, "");
17977
+ break;
17978
+ }
17979
+ const beforeTool = text.substring(i, toolIdx);
17980
+ result += beforeTool.replace(/<(\w+)(?:[^>]*)>\r?\n?/gi, "").replace(/\r?\n?<\/\w+(?:[^>]*)>/gi, "");
17981
+ const endToolIdx = text.indexOf("]", toolIdx);
17982
+ if (endToolIdx === -1) {
17983
+ result += text.substring(toolIdx);
17984
+ break;
17985
+ }
17986
+ result += text.substring(toolIdx, endToolIdx + 1);
17987
+ i = endToolIdx + 1;
17988
+ }
17989
+ text = result;
17921
17990
  text = text.replaceAll(THINK_OPEN_PH, "<think>").replaceAll(THINK_CLOSE_PH, "</think>");
17922
17991
  return text;
17923
17992
  };
@@ -17991,9 +18060,6 @@ ${combinedNudge}`;
17991
18060
  }
17992
18061
  contents.length = 0;
17993
18062
  contents.push(...finalContents);
17994
- if (!await checkQuota("agent", settings)) {
17995
- throw new Error("Error: Quota Exausted for Agent");
17996
- }
17997
18063
  targetModel = modelName;
17998
18064
  const sysInstructionCacheKey2 = `${chatId}|${aiProvider}|${thinkingLevel}|${targetModel}|${JSON.stringify(profile)}|${!!systemSettings2?.dynamicDirAwareness}|${!!systemSettings2?.subAgents}`;
17999
18065
  let isCacheHit = systemInstructionCache.key === sysInstructionCacheKey2 && systemInstructionCache.value;
@@ -19332,14 +19398,14 @@ ${oldLines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
19332
19398
  `;
19333
19399
  }
19334
19400
  let snippet = "";
19335
- if (verifiedLineCount <= 200) {
19401
+ if (verifiedLineCount <= 100) {
19336
19402
  snippet = verifiedLines.join("\n");
19337
19403
  } else {
19338
- const head = verifiedLines.slice(0, 100).join("\n");
19339
- const tail = verifiedLines.slice(-100).join("\n");
19404
+ const head = verifiedLines.slice(0, 50).join("\n");
19405
+ const tail = verifiedLines.slice(-50).join("\n");
19340
19406
  snippet = `${head}
19341
19407
 
19342
- ... [${verifiedLineCount - 200} lines truncated for history stability] ...
19408
+ ... [${verifiedLineCount - 100} lines truncated for history stability] ...
19343
19409
 
19344
19410
  ${tail}`;
19345
19411
  }
@@ -19362,21 +19428,21 @@ ${oldLines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
19362
19428
  `;
19363
19429
  }
19364
19430
  let snippet2 = "";
19365
- if (verifiedLineCount2 <= 200) {
19431
+ if (verifiedLineCount2 <= 100) {
19366
19432
  snippet2 = verifiedLines2.join("\n");
19367
19433
  } else {
19368
- const head = verifiedLines2.slice(0, 100).join("\n");
19369
- const tail = verifiedLines2.slice(-100).join("\n");
19434
+ const head = verifiedLines2.slice(0, 50).join("\n");
19435
+ const tail = verifiedLines2.slice(-50).join("\n");
19370
19436
  snippet2 = `${head}
19371
19437
 
19372
- ... [${verifiedLineCount2 - 200} lines truncated] ...
19438
+ ... [${verifiedLineCount2 - 100} lines truncated] ...
19373
19439
 
19374
19440
  ${tail}`;
19375
19441
  }
19376
19442
  result2 = `SUCCESS: File [${filePath}] saved via IDE Companion (May have user edits).
19377
-
19378
19443
  - Stats: [${verifiedLineCount2} lines, ${(verifiedSize2 / 1024).toFixed(1)} KB]
19379
19444
  ${ancestry2}- Content Preview:
19445
+
19380
19446
  ${snippet2}`;
19381
19447
  }
19382
19448
  const action = normToolName === "write_file" ? "Created" : "Edited";
@@ -19623,7 +19689,7 @@ ${snippet2}`;
19623
19689
  await incrementUsage("toolFailure");
19624
19690
  if (settings.onToolResult) settings.onToolResult("failure", normToolName);
19625
19691
  }
19626
- const aiContent = `[TOOL RESULT]: ${(result || "").toString().replaceAll("[UI_CONTEXT]", "[CONTEXT]")}`;
19692
+ const aiContent = `[TOOL RESULT]: ${(result || "").toString().replaceAll("[UI_CONTEXT]", "")}`;
19627
19693
  toolResults.push({ role: "user", text: aiContent, binaryPart });
19628
19694
  anyToolExecutedInThisTurn = true;
19629
19695
  let uiContent = `[TOOL RESULT]: ${result || ""}`;
@@ -19939,13 +20005,14 @@ Error Log can be found in ${path26.join(LOGS_DIR, "agent", "error.log")}`);
19939
20005
  wasToolCalledInLastLoop = toolCallPointer > 0 || anyToolExecutedInThisTurn;
19940
20006
  }
19941
20007
  } catch (err) {
19942
- const errLog = err instanceof Error ? (() => {
20008
+ const rawErrStr = err instanceof Error ? (() => {
19943
20009
  try {
19944
20010
  return JSON.parse(JSON.parse(err.message).error.message).error.message;
19945
20011
  } catch {
19946
- return String(err);
20012
+ return err.message || String(err);
19947
20013
  }
19948
20014
  })() : String(err);
20015
+ const errLog = rawErrStr.replace(/^(Error:\s*)+/i, "");
19949
20016
  const date = (/* @__PURE__ */ new Date()).toLocaleString();
19950
20017
  const agentErrDir = path26.join(LOGS_DIR, "agent");
19951
20018
  yield { type: "text", content: `\u274C CRITICAL ERROR: ${errLog.includes("fetch failed") ? "Failed to Connect. Check your Internet Connection or Wait a moment" : errLog}` };
@@ -20073,7 +20140,7 @@ ${isAsync ? `- [tool:functions.AskMain(question="...")]. Communicate with PARENT
20073
20140
  - [tool:functions.SearchKeyword(keyword="...", path="optional, dir/file/glob/regex", fuzzy="bool optional, default: false", regex="bool optional, default: auto")]. path scopes search. Find definitions, logic, relevant code
20074
20141
  - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
20075
20142
  - [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. View files
20076
- - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", searchContent1="string OR ^LINE:start..end$", newContent1="...", ...MAX15)]. TARGET MINIMAL DIFF. "^LINE:start..end$" line ranges MUST for multi-line selection or escape sequences. Verify diffs
20143
+ - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", searchContent1="string OR ^LINE:start..end$", newContent1="...", ...MAX15)]. TARGET MINIMAL DIFF. Line Ranges "^LINE:start..end$" MUST for multi-line selection or escape sequences. Verify diffs
20077
20144
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. VERIFY IMPORTS
20078
20145
  - [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `WINDOWS POWERSHELL` : `WINDOWS CMD` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user`.trim();
20079
20146
  const systemInstructionSubAgent = `=== START SYSTEM PROMPT ===
@@ -21385,9 +21452,13 @@ function App({ args = [] }) {
21385
21452
  const commitActiveStreamingMessage = () => {
21386
21453
  flushTypewriterNow();
21387
21454
  if (activeStreamingMsgRef.current) {
21455
+ let msgText = flattenString(activeStreamingMsgRef.current.text);
21456
+ if (activeStreamingMsgRef.current.role === "think") {
21457
+ msgText = msgText.replace(/^\r?\n+/, "").replace(/\r?\n+$/, "");
21458
+ }
21388
21459
  const msg = {
21389
21460
  ...activeStreamingMsgRef.current,
21390
- text: flattenString(activeStreamingMsgRef.current.text),
21461
+ text: msgText,
21391
21462
  isStreaming: false
21392
21463
  };
21393
21464
  setMessages((prev) => {
@@ -21835,6 +21906,8 @@ function App({ args = [] }) {
21835
21906
  const [providerBudgetCursor, setProviderBudgetCursor] = useState15(0);
21836
21907
  const [pbsCursor, setPbsCursor] = useState15(0);
21837
21908
  const [pbsSelected, setPbsSelected] = useState15({});
21909
+ const [pbfFormState, setPbfFormState] = useState15({});
21910
+ const [pbfFieldIndex, setPbfFieldIndex] = useState15(0);
21838
21911
  const [systemSettings2, setSystemSettings] = useState15({ memory: true, theme: "Dark", compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
21839
21912
  const colors = useMemo2(() => getThemeColors(systemSettings2.theme), [systemSettings2.theme]);
21840
21913
  const [profileData, setProfileData] = useState15({ name: null, nickname: null, instructions: null });
@@ -21958,7 +22031,7 @@ function App({ args = [] }) {
21958
22031
  return [...prev, {
21959
22032
  id: "tier-switch-" + Date.now(),
21960
22033
  role: "system",
21961
- text: `**[TIER LIMIT]** Auto-switched to ${modelDisplayName}.`,
22034
+ text: `**Switched to ${modelDisplayName}.`,
21962
22035
  isMeta: true
21963
22036
  }];
21964
22037
  });
@@ -22333,7 +22406,7 @@ function App({ args = [] }) {
22333
22406
  return;
22334
22407
  }
22335
22408
  if (activeView === "providerBudgetSelect") {
22336
- const PBS_PROVIDERS = ["Google", "DeepSeek", "Mistral", "NVIDIA", "OpenRouter"];
22409
+ const PBS_PROVIDERS = ["Google", "DeepSeek", "Mistral", "NVIDIA", "OpenRouter", "Ollama"];
22337
22410
  if (key.upArrow) {
22338
22411
  setPbsCursor((c) => (c - 1 + PBS_PROVIDERS.length) % PBS_PROVIDERS.length);
22339
22412
  return;
@@ -22360,6 +22433,41 @@ function App({ args = [] }) {
22360
22433
  }
22361
22434
  return;
22362
22435
  }
22436
+ if (activeView === "providerBudgetFlow") {
22437
+ const totalFields = providerBudgetQueue.length * 2 + 1;
22438
+ if (key.upArrow) {
22439
+ setPbfFieldIndex((i) => Math.max(0, i - 1));
22440
+ return;
22441
+ } else if (key.downArrow) {
22442
+ setPbfFieldIndex((i) => Math.min(totalFields - 1, i + 1));
22443
+ return;
22444
+ } else if (key.return) {
22445
+ if (pbfFieldIndex === totalFields - 1) {
22446
+ const rawPB = quotas.providerBudgets || {};
22447
+ const cleaned = { __useProvider: true };
22448
+ for (const prov of providerBudgetQueue) {
22449
+ const formProv = pbfFormState[prov] || {};
22450
+ cleaned[prov] = {
22451
+ agentLimit: 9999999999,
22452
+ tokenLimit: parseInt(formProv.tokenLimit, 10) || 0,
22453
+ monthlyTokenLimit: parseInt(formProv.monthlyTokenLimit, 10) || 0
22454
+ };
22455
+ }
22456
+ const finalCleanedQuotas = { ...quotas, providerBudgets: cleaned };
22457
+ setQuotas(finalCleanedQuotas);
22458
+ saveSettings({ apiTier, quotas: finalCleanedQuotas });
22459
+ const returnMode = budgetReturnView === "settings" ? "resetMode" : "budgetResetMode";
22460
+ setActiveView(returnMode);
22461
+ } else {
22462
+ setPbfFieldIndex((i) => Math.min(totalFields - 1, i + 1));
22463
+ }
22464
+ return;
22465
+ } else if (key.escape) {
22466
+ setActiveView("providerBudgetSelect");
22467
+ return;
22468
+ }
22469
+ return;
22470
+ }
22363
22471
  if (key.escape) {
22364
22472
  if (showBtwBox) {
22365
22473
  setShowBtwBox(false);
@@ -24530,7 +24638,7 @@ Selection: ${val}`,
24530
24638
  if (afterText.match(/<\/(think|thought)>/i)) {
24531
24639
  const parts = afterText.split(/<\/(think|thought)>/i);
24532
24640
  const rawThinkContent = parts[0] || "";
24533
- const thinkContent = rawThinkContent.replace(/^<(think|thought)>/i, "");
24641
+ const thinkContent = rawThinkContent.replace(/^<(think|thought)[^>]*>\r?\n?/i, "").replace(/\r?\n?$/g, "");
24534
24642
  const agentContent = parts.slice(2).join("").replace(/<\/?(think|thought)>/gi, "");
24535
24643
  activeStreamingMsgRef.current.text = flattenString(thinkContent);
24536
24644
  const startTime = activeStreamingMsgRef.current.startTime || Date.now();
@@ -24543,7 +24651,7 @@ Selection: ${val}`,
24543
24651
  appendStreamText(agentContent);
24544
24652
  }
24545
24653
  } else {
24546
- let thinkStartText = afterText.replace(/^<(think|thought)>/gi, "");
24654
+ let thinkStartText = afterText.replace(/^<(think|thought)[^>]*>\r?\n?/gi, "");
24547
24655
  appendStreamText(thinkStartText);
24548
24656
  }
24549
24657
  continue;
@@ -24780,7 +24888,7 @@ Selection: ${val}`,
24780
24888
  }, [suggestionVisibleCount, suggestions.length]);
24781
24889
  useEffect12(() => {
24782
24890
  if (activeView !== "providerBudgetSelect") return;
24783
- const PBS_PROVIDERS = ["Google", "DeepSeek", "Mistral", "NVIDIA", "OpenRouter"];
24891
+ const PBS_PROVIDERS = ["Google", "DeepSeek", "Mistral", "NVIDIA", "OpenRouter", "Ollama"];
24784
24892
  const existingBudgets = quotas.providerBudgets || {};
24785
24893
  const initialSelected = PBS_PROVIDERS.reduce((acc, p) => {
24786
24894
  acc[p] = !!(existingBudgets[p] && (existingBudgets[p].agentLimit || existingBudgets[p].tokenLimit));
@@ -24791,74 +24899,19 @@ Selection: ${val}`,
24791
24899
  }, [activeView]);
24792
24900
  useEffect12(() => {
24793
24901
  if (activeView !== "providerBudgetFlow") return;
24794
- const currentProvider = providerBudgetQueue[providerBudgetCursor];
24795
- if (!currentProvider) {
24796
- const returnMode = budgetReturnView === "settings" ? "resetMode" : "budgetResetMode";
24797
- const rawPB = quotas.providerBudgets || {};
24798
- const cleaned = { __useProvider: true };
24799
- for (const prov of providerBudgetQueue) {
24800
- if (rawPB[prov]) cleaned[prov] = rawPB[prov];
24801
- }
24802
- const finalCleanedQuotas = { ...quotas, providerBudgets: cleaned };
24803
- setQuotas(finalCleanedQuotas);
24804
- saveSettings({ apiTier, quotas: finalCleanedQuotas });
24805
- setActiveView(returnMode);
24806
- return;
24902
+ const initialForm = {};
24903
+ const existingPBs = quotas.providerBudgets || {};
24904
+ for (const prov of providerBudgetQueue) {
24905
+ const pb = existingPBs[prov] || {};
24906
+ initialForm[prov] = {
24907
+ agentLimit: getPrefilledValue(pb.agentLimit),
24908
+ tokenLimit: getPrefilledValue(pb.tokenLimit),
24909
+ monthlyTokenLimit: getPrefilledValue(pb.monthlyTokenLimit)
24910
+ };
24807
24911
  }
24808
- const existingPB = (quotas.providerBudgets || {})[currentProvider] || {};
24809
- const totalProviders = providerBudgetQueue.length;
24810
- const currentStep = providerBudgetCursor + 1;
24811
- const providerLabel = `[${currentStep}/${totalProviders}] ${currentProvider}`;
24812
- const advanceToNext = (finalQuotas) => {
24813
- if (providerBudgetCursor + 1 < providerBudgetQueue.length) {
24814
- setProviderBudgetCursor((c) => c + 1);
24815
- setActiveView("providerBudgetFlow");
24816
- } else {
24817
- const rawPB = finalQuotas.providerBudgets || {};
24818
- const cleaned = { __useProvider: true };
24819
- for (const prov of providerBudgetQueue) {
24820
- if (rawPB[prov]) cleaned[prov] = rawPB[prov];
24821
- }
24822
- const finalCleanedQuotas = { ...finalQuotas, providerBudgets: cleaned };
24823
- setQuotas(finalCleanedQuotas);
24824
- const rm = budgetReturnView === "settings" ? "resetMode" : "budgetResetMode";
24825
- saveSettings({ apiTier, quotas: finalCleanedQuotas });
24826
- setActiveView(rm);
24827
- }
24828
- };
24829
- setInputConfig({
24830
- label: `${providerLabel} \u2014 Daily budget (requests/day):`,
24831
- key: "providerBudgets",
24832
- providerKey: currentProvider,
24833
- subKey: "agentLimit",
24834
- value: getPrefilledValue(existingPB.agentLimit),
24835
- returnView: "providerBudgetSelect",
24836
- next: (newQuotas) => {
24837
- const updatedPB = (newQuotas.providerBudgets || {})[currentProvider] || {};
24838
- return {
24839
- label: `${providerLabel} \u2014 Daily budget (tokens/day):`,
24840
- key: "providerBudgets",
24841
- providerKey: currentProvider,
24842
- subKey: "tokenLimit",
24843
- value: getPrefilledValue(updatedPB.tokenLimit),
24844
- returnView: "providerBudgetSelect",
24845
- next: (q2) => {
24846
- const pb2 = (q2.providerBudgets || {})[currentProvider] || {};
24847
- return {
24848
- label: `${providerLabel} \u2014 Monthly budget (tokens/month):`,
24849
- key: "providerBudgets",
24850
- providerKey: currentProvider,
24851
- subKey: "monthlyTokenLimit",
24852
- value: getPrefilledValue(pb2.monthlyTokenLimit),
24853
- returnView: "providerBudgetFlow",
24854
- onDone: advanceToNext
24855
- };
24856
- }
24857
- };
24858
- }
24859
- });
24860
- setActiveView("input");
24861
- }, [activeView, providerBudgetCursor]);
24912
+ setPbfFormState(initialForm);
24913
+ setPbfFieldIndex(0);
24914
+ }, [activeView, providerBudgetQueue]);
24862
24915
  const CustomMenuItem = ({ label, isSelected }) => {
24863
24916
  const isCancel = label === "Cancel" || label === "Back" || label.toLowerCase().includes("exit") || label.toLowerCase().includes("back");
24864
24917
  return /* @__PURE__ */ React16.createElement(
@@ -24872,10 +24925,9 @@ Selection: ${val}`,
24872
24925
  /* @__PURE__ */ React16.createElement(Text16, { color: isSelected ? "white" : "gray", bold: isSelected }, isSelected ? "\u276F " : " ", label)
24873
24926
  );
24874
24927
  };
24875
- const renderProgressBar = (label, current, limit) => {
24928
+ const renderProgressBar = (label, current, limit, barWidth = 10, paddingLeft = 2, labelWidth = 9) => {
24876
24929
  const actualPercent = limit > 0 ? Math.min(100, current / limit * 100) : 0;
24877
24930
  const percent = Math.round(actualPercent);
24878
- const barWidth = 15;
24879
24931
  const filledCount = Math.round(percent / 100 * barWidth);
24880
24932
  const barStr = "\u2588".repeat(filledCount) + "\u2591".repeat(Math.max(0, barWidth - filledCount));
24881
24933
  let barColor = colors.success || "green";
@@ -24884,7 +24936,7 @@ Selection: ${val}`,
24884
24936
  } else if (percent > 80) {
24885
24937
  barColor = colors.danger || "red";
24886
24938
  }
24887
- const isTokens = label.toLowerCase().includes("token");
24939
+ const isTokens = label.toLowerCase().includes("token") || label.toLowerCase().includes("daily") || label.toLowerCase().includes("monthly");
24888
24940
  const displayLimit = shouldClearValue(limit) ? "\u221E" : isTokens ? formatTokens(limit) : limit;
24889
24941
  const displayCurrent = isTokens ? formatTokens(current) : current;
24890
24942
  let displayPercent;
@@ -24895,7 +24947,7 @@ Selection: ${val}`,
24895
24947
  } else {
24896
24948
  displayPercent = `${percent}%`;
24897
24949
  }
24898
- return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "row", paddingLeft: 4, key: label }, /* @__PURE__ */ React16.createElement(Box14, { width: 18 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, label, ": ")), /* @__PURE__ */ React16.createElement(Text16, { color: barColor }, barStr), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, " ", displayPercent, " (", displayCurrent, "/", displayLimit, ")"));
24950
+ return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "row", paddingLeft, key: label }, /* @__PURE__ */ React16.createElement(Box14, { width: labelWidth }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, label, ": ")), /* @__PURE__ */ React16.createElement(Text16, { color: barColor }, barStr), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, " ", displayPercent, " (", displayCurrent, "/", displayLimit, ")"));
24899
24951
  };
24900
24952
  const renderActiveView = () => {
24901
24953
  switch (activeView) {
@@ -25136,8 +25188,43 @@ Selection: ${val}`,
25136
25188
  return /* @__PURE__ */ React16.createElement(Box14, { key: prov, backgroundColor: isActive ? colors.highlightBg || "#2a2a2a" : void 0, paddingX: 1, width: "100%", flexDirection: "row" }, /* @__PURE__ */ React16.createElement(Text16, { color: isActive ? colors.text : colors.textMuted, bold: isActive }, isActive ? "\u276F " : " "), /* @__PURE__ */ React16.createElement(Text16, { color: isChecked ? colors.success || "green" : colors.textMuted }, isChecked ? "\u2611" : "\u2610"), /* @__PURE__ */ React16.createElement(Text16, { color: isActive ? colors.text : colors.textMuted, bold: isActive }, " ", prov), isChecked && quotas.providerBudgets?.[prov]?.agentLimit ? /* @__PURE__ */ React16.createElement(Text16, { color: colors.primary || "cyan" }, " (budget set)") : null);
25137
25189
  }), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, italic: true }, "\u2191\u2193 Navigate \u2022 Space to toggle \u2022 Enter to confirm \u2022 ESC to go back"), !anySelected && /* @__PURE__ */ React16.createElement(Text16, { color: colors.warning || "yellow", italic: true }, " Select at least one provider to continue")));
25138
25190
  }
25139
- case "providerBudgetFlow":
25140
- return null;
25191
+ case "providerBudgetFlow": {
25192
+ const fields = [];
25193
+ for (const prov of providerBudgetQueue) {
25194
+ fields.push({ provider: prov, subKey: "tokenLimit", label: "Daily Tokens (tokens/day)" });
25195
+ fields.push({ provider: prov, subKey: "monthlyTokenLimit", label: "Monthly Tokens (tokens/month)" });
25196
+ }
25197
+ const saveButtonIndex = fields.length;
25198
+ return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: colors.borderMuted, padding: 0, width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true }, "PROVIDER BUDGET CONFIGURATION")), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, paddingBottom: 0, marginBottom: 0 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, italic: true }, "Set limits for selected providers (leave blank or 0 for no limit)")), providerBudgetQueue.map((prov) => {
25199
+ const provFields = fields.filter((f) => f.provider === prov);
25200
+ return /* @__PURE__ */ React16.createElement(Box14, { key: prov, flexDirection: "column", marginY: 0, paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.primary || "cyan", bold: true }, `
25201
+ \u2500\u2500 ${prov} \u2500\u2500`), provFields.map((field) => {
25202
+ const fieldIdx = fields.findIndex((f) => f.provider === field.provider && f.subKey === field.subKey);
25203
+ const isFocused = pbfFieldIndex === fieldIdx;
25204
+ const currentVal = pbfFormState[field.provider]?.[field.subKey] ?? "";
25205
+ return /* @__PURE__ */ React16.createElement(Box14, { key: field.subKey, paddingLeft: 2, flexDirection: "row" }, /* @__PURE__ */ React16.createElement(Text16, { color: isFocused ? colors.text : colors.textMuted, bold: isFocused }, isFocused ? "\u276F " : " ", field.label.padEnd(30, " "), ": ", " "), isFocused ? /* @__PURE__ */ React16.createElement(
25206
+ TextInput4,
25207
+ {
25208
+ value: currentVal,
25209
+ onChange: (val) => {
25210
+ setPbfFormState((prev) => ({
25211
+ ...prev,
25212
+ [prov]: {
25213
+ ...prev[prov] || {},
25214
+ [field.subKey]: val
25215
+ }
25216
+ }));
25217
+ },
25218
+ onSubmit: () => {
25219
+ if (fieldIdx + 1 <= saveButtonIndex) {
25220
+ setPbfFieldIndex(fieldIdx + 1);
25221
+ }
25222
+ }
25223
+ }
25224
+ ) : /* @__PURE__ */ React16.createElement(Text16, { color: currentVal ? colors.text : colors.textMuted }, currentVal ? currentVal : "0 (Unlimited)"));
25225
+ }));
25226
+ }), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 2, marginTop: 1 }, pbfFieldIndex === saveButtonIndex ? /* @__PURE__ */ React16.createElement(Box14, { backgroundColor: colors.highlightBg || "#2a2a2a", paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.success || "green", bold: true }, "\u276F [ Save & Apply Budgets ]")) : /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, " [ Save & Apply Budgets ]")), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, italic: true }, "\u2191\u2193 Navigate fields \u2022 Enter next / save \u2022 ESC to go back")));
25227
+ }
25141
25228
  case "budgetResetMode":
25142
25229
  return /* @__PURE__ */ React16.createElement(
25143
25230
  CommandMenu,
@@ -25189,33 +25276,72 @@ Selection: ${val}`,
25189
25276
  );
25190
25277
  const limitsNotSet = !usingProviderBudgets && (shouldClearValue(reqLimit) || shouldClearValue(tokenLimit) || shouldClearValue(monthlyLimit));
25191
25278
  let resetInfo = "";
25279
+ let resetCountdown = "";
25192
25280
  if (quotas.resetMode === "Custom") {
25193
25281
  const today2 = /* @__PURE__ */ new Date();
25194
25282
  const resetDay = quotas.resetDay || 1;
25283
+ let resetYear = today2.getFullYear();
25195
25284
  let resetMonth = today2.getMonth();
25196
25285
  if (today2.getDate() >= resetDay) {
25197
25286
  resetMonth += 1;
25287
+ if (resetMonth > 11) {
25288
+ resetMonth = 0;
25289
+ resetYear += 1;
25290
+ }
25198
25291
  }
25199
- const resetDate = new Date(today2.getFullYear(), resetMonth, resetDay);
25200
- const monthName = resetDate.toLocaleString("default", { month: "short" });
25292
+ const targetResetDate = new Date(resetYear, resetMonth, resetDay, 0, 0, 0);
25293
+ const monthName = targetResetDate.toLocaleString("default", { month: "short" }).toUpperCase();
25201
25294
  resetInfo = `${monthName}-${resetDay}`;
25295
+ const diffMs = Math.max(0, targetResetDate.getTime() - today2.getTime());
25296
+ const totalHours = Math.floor(diffMs / (1e3 * 60 * 60));
25297
+ const daysLeft = Math.floor(totalHours / 24);
25298
+ const hoursLeft = totalHours % 24;
25299
+ resetCountdown = `(${daysLeft} days: ${hoursLeft} hrs left)`;
25202
25300
  }
25203
- return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: colors.borderMuted, padding: 1, width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 1, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, "BUDGET LIMIT STATUS"), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "[ ESC to Close ]")), limitsNotSet ? /* @__PURE__ */ React16.createElement(Box14, { padding: 1, justifyContent: "center", alignItems: "center", width: "100%" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true }, "LIMITS NOT SET")) : usingProviderBudgets && configuredProviders.length > 0 ? /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", gap: 1, width: "100%" }, configuredProviders.map((prov) => {
25204
- const pb = providerBudgetsMap[prov];
25205
- const provReqCurrent = dailyUsage?.providerRequests?.[prov] || 0;
25206
- let provTokenCurrent = 0;
25207
- const dailyModels = dailyUsage?.models?.[prov] || {};
25208
- for (const m in dailyModels) {
25209
- provTokenCurrent += dailyModels[m]?.tokens || 0;
25210
- }
25211
- let provMonthlyCurrent = 0;
25212
- const monthlySource = quotas.resetMode === "Custom" ? customPeriodUsage : monthlyUsage;
25213
- const monthlyModels = monthlySource?.models?.[prov] || {};
25214
- for (const m in monthlyModels) {
25215
- provMonthlyCurrent += monthlyModels[m]?.tokens || 0;
25301
+ return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: colors.borderMuted, padding: 1, paddingBottom: 0, width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 1, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true, underline: true }, "BUDGET LIMIT STATUS"), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "[ ESC to Close ]")), limitsNotSet ? /* @__PURE__ */ React16.createElement(Box14, { padding: 1, justifyContent: "center", alignItems: "center", width: "100%" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true }, "LIMITS NOT SET")) : usingProviderBudgets && configuredProviders.length > 0 ? /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", gap: 0, width: "100%" }, (() => {
25302
+ const cols = stdout?.columns || terminalSize?.columns || 80;
25303
+ const isNarrow = cols < 115;
25304
+ if (isNarrow) {
25305
+ const barW = Math.max(5, Math.min(30, cols - 50));
25306
+ return configuredProviders.map((prov) => {
25307
+ const pb = providerBudgetsMap[prov];
25308
+ let provTokenCurrent = 0;
25309
+ const dailyModels = dailyUsage?.models?.[prov] || {};
25310
+ for (const m in dailyModels) {
25311
+ provTokenCurrent += dailyModels[m]?.tokens || 0;
25312
+ }
25313
+ let provMonthlyCurrent = 0;
25314
+ const monthlySource = quotas.resetMode === "Custom" ? customPeriodUsage : monthlyUsage;
25315
+ const monthlyModels = monthlySource?.models?.[prov] || {};
25316
+ for (const m in monthlyModels) {
25317
+ provMonthlyCurrent += monthlyModels[m]?.tokens || 0;
25318
+ }
25319
+ return /* @__PURE__ */ React16.createElement(Box14, { key: prov, flexDirection: "column", borderStyle: "single", borderColor: colors.borderMuted, paddingX: 1, width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 0 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.primary, bold: true }, "\u25C6 ", prov)), renderProgressBar("Daily", provTokenCurrent, pb.tokenLimit || 99999999999999, barW, 2, 9), renderProgressBar("Monthly", provMonthlyCurrent, pb.monthlyTokenLimit || 99999999999999, barW, 2, 9));
25320
+ });
25216
25321
  }
25217
- return /* @__PURE__ */ React16.createElement(Box14, { key: prov, flexDirection: "column", borderStyle: "single", borderColor: colors.borderMuted, paddingX: 1, width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 0 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.primary, bold: true }, "\u25C6 ", prov)), renderProgressBar("Daily Requests", provReqCurrent, pb.agentLimit || 99999999, "cyan"), renderProgressBar("Daily Tokens", provTokenCurrent, pb.tokenLimit || 99999999999999, "green"), renderProgressBar("Monthly Tokens", provMonthlyCurrent, pb.monthlyTokenLimit || 99999999999999, "yellow"));
25218
- }), resetInfo ? /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "Monthly Reset: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.accent || "magenta", bold: true }, resetInfo)) : /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "Monthly Reset: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary || "blue", bold: true }, "Rolling 30-Day Window"))) : /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "single", borderColor: colors.borderMuted, paddingX: 1, width: "100%" }, renderProgressBar("Daily Requests", reqCurrent, reqLimit, "cyan"), renderProgressBar("Daily Tokens", tokenCurrent, tokenLimit, "green"), renderProgressBar("Monthly Tokens", monthlyCurrent, monthlyLimit, "yellow"), resetInfo ? /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "Monthly Reset: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.accent || "magenta", bold: true }, resetInfo)) : /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "Monthly Reset: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary || "blue", bold: true }, "Rolling 30-Day Window"))));
25322
+ const rows = [];
25323
+ for (let i = 0; i < configuredProviders.length; i += 2) {
25324
+ rows.push(configuredProviders.slice(i, i + 2));
25325
+ }
25326
+ return rows.map((row, rIdx) => /* @__PURE__ */ React16.createElement(Box14, { key: rIdx, flexDirection: "row", width: "100%" }, row.map((prov) => {
25327
+ const pb = providerBudgetsMap[prov];
25328
+ let provTokenCurrent = 0;
25329
+ const dailyModels = dailyUsage?.models?.[prov] || {};
25330
+ for (const m in dailyModels) {
25331
+ provTokenCurrent += dailyModels[m]?.tokens || 0;
25332
+ }
25333
+ let provMonthlyCurrent = 0;
25334
+ const monthlySource = quotas.resetMode === "Custom" ? customPeriodUsage : monthlyUsage;
25335
+ const monthlyModels = monthlySource?.models?.[prov] || {};
25336
+ for (const m in monthlyModels) {
25337
+ provMonthlyCurrent += monthlyModels[m]?.tokens || 0;
25338
+ }
25339
+ const isFullWidth = row.length === 1;
25340
+ const targetCardCols = isFullWidth ? cols : Math.floor(cols / 2);
25341
+ const barW = Math.max(5, Math.min(25, targetCardCols - 36));
25342
+ return /* @__PURE__ */ React16.createElement(Box14, { key: prov, flexDirection: "column", borderStyle: "single", borderColor: colors.borderMuted, paddingX: 1, width: isFullWidth ? "100%" : "50%" }, /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 0 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.primary, bold: true }, "\u25C6 ", prov)), renderProgressBar("Daily", provTokenCurrent, pb.tokenLimit || 99999999999999, barW, 2, 9), renderProgressBar("Monthly", provMonthlyCurrent, pb.monthlyTokenLimit || 99999999999999, barW, 2, 9));
25343
+ })));
25344
+ })(), resetInfo ? /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "Monthly Reset: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.accent || "magenta", bold: true }, resetInfo), resetCountdown ? /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, ` ${resetCountdown}`) : null) : /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "Monthly Reset: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary || "blue", bold: true }, "Rolling 30-Day Window"))) : /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "single", borderColor: colors.borderMuted, paddingX: 1, width: "100%" }, renderProgressBar("Daily Tokens", tokenCurrent, tokenLimit, "green"), renderProgressBar("Monthly Tokens", monthlyCurrent, monthlyLimit, "yellow"), resetInfo ? /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "Monthly Reset: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.accent || "magenta", bold: true }, resetInfo), resetCountdown ? /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, ` ${resetCountdown}`) : null) : /* @__PURE__ */ React16.createElement(Box14, { marginLeft: 4, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted }, "Monthly Reset: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.secondary || "blue", bold: true }, "Rolling 30-Day Window"))));
25219
25345
  }
25220
25346
  case "input":
25221
25347
  return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: colors.borderMuted, padding: 0, width: "100%" }, /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true }, "DATA CONFIGURATION")), inputConfig?.note && /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, italic: true }, inputConfig.note)), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, flexDirection: "row" }, /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true }, inputConfig?.label, " "), /* @__PURE__ */ React16.createElement(
@@ -25910,7 +26036,7 @@ Selection: ${val}`,
25910
26036
  })(), /* @__PURE__ */ React16.createElement(
25911
26037
  GlintText_default,
25912
26038
  {
25913
- text: tempModelOverride || activeModel.split("/")[1] || activeModel.length > 1 ? activeModel : "Use '/model model-id' to select model",
26039
+ text: activeModel.split("/")[1] || (activeModel.length > 1 ? activeModel : "Use '/model model-id' to select model"),
25914
26040
  baseColor: colors.text,
25915
26041
  glintColor: colors.textMuted,
25916
26042
  glintWidth: 3
@@ -25999,7 +26125,7 @@ Selection: ${val}`,
25999
26125
  onSubmit: handleSetup,
26000
26126
  mask: "*"
26001
26127
  }
26002
- )))), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: "gray", italic: true }, setupStep === 0 ? "(Use arrows to select and Enter to confirm, ESC to go back)" : "(Press Enter to confirm and initialize, ESC to go back)"))) : renderActiveView(), confirmExit && /* @__PURE__ */ React16.createElement(Box14, { borderStyle: "round", borderColor: colors.borderMuted, paddingX: 2, marginY: 0, width: "100%" }, /* @__PURE__ */ React16.createElement(Text16, { color: "red", bold: true }, "\u{1F534} EXIT CONFIRMATION: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, "Press "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true }, "CTRL + C"), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " again to exit (", exitCountdown, "s). Press "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, bold: true }, "ESC"), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " to cancel.")), suggestions.length > 0 && (() => {
26128
+ )))), /* @__PURE__ */ React16.createElement(Box14, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: "gray", italic: true }, setupStep === 0 ? "(Use arrows to select and Enter to confirm, ESC to go back)" : "(Press Enter to confirm and initialize, ESC to go back)"))) : renderActiveView(), confirmExit && /* @__PURE__ */ React16.createElement(Box14, { borderStyle: "round", borderColor: colors.borderMuted, paddingX: 1, marginY: 0, width: "100%" }, /* @__PURE__ */ React16.createElement(Text16, null, /* @__PURE__ */ React16.createElement(Text16, { color: "red", bold: true }, "\u{1F534} EXIT: "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, "Press "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text, bold: true }, "CTRL+C"), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " again to exit (", exitCountdown, "s) \u2022 Press "), /* @__PURE__ */ React16.createElement(Text16, { color: colors.textMuted, bold: true }, "ESC"), /* @__PURE__ */ React16.createElement(Text16, { color: colors.text }, " to cancel"))), suggestions.length > 0 && (() => {
26003
26129
  const windowSize = 5;
26004
26130
  let startIdx = suggestionOffsetRef.current;
26005
26131
  let firstSelectableIndex = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "3.19.2",
3
+ "version": "3.19.3",
4
4
  "date": "2026-08-06",
5
5
  "description": "A High-Fidelity Agentic CLI with Sub-Agents for the Flux Era.",
6
6
  "keywords": [