fluxflow-cli 3.19.1 → 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.
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:")) {
@@ -6977,7 +6999,8 @@ TOOL RULES:
6977
6999
  ${mode === "Flux" ? `- JSON ESCAPE ALL LITERAL ESCAPE SEQUENCES IN TOOL ARGUMENTS
6978
7000
  - SAME file, MULTIPLE edits? ONE PatchFile (\u226415 blocks) \u2190 PRIORITY
6979
7001
  - Tool denied? Ask for guidance \u2190 MANDATORY
6980
- - Need text or huge files? SearchKeyword > Full Read
7002
+ - Need text or HUGE file? SearchKeyword > Full Read
7003
+ - MUST AVOID UNNECESSARY LARGE-FILE CHUNK READS
6981
7004
  ` : ""}
6982
7005
  - COMMUNICATION WITH USER -
6983
7006
  - [tool:functions.Ask(question="...", optionA="title::description", ...MAX4)]. Ambiguity: MUST for path divergence, security risk. Ask, don't finish/guess. Keep titles short
@@ -6989,7 +7012,7 @@ ${mode === "Flux" ? `- JSON ESCAPE ALL LITERAL ESCAPE SEQUENCES IN TOOL ARGUMENT
6989
7012
  ${mode === "Flux" ? `- WORKSPACE TOOLS (path = relative; FIRST ARGUMENT, path separator: '/') -
6990
7013
  - [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. ${aiProvider !== "Google" ? `${isMultiModal ? `Supports images/docs` : ""}` : `Supports images/docs`}
6991
7014
  - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
6992
- - [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
6993
7016
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile
6994
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
6995
7018
  - [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `POWERSHELL` : `WINDOWS CMD` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user
@@ -9863,7 +9886,7 @@ ${projectContextBlock}${isMemoryEnabled ? `
9863
9886
  -- MEMORY RULES --
9864
9887
  - Subtly Personalize with RELEVENT CONTEXTUAL MEMORIES. Auto Saves
9865
9888
  ` : ""}
9866
- -- SECURITY RULES --
9889
+ -- SECURITY POLICIES --
9867
9890
  - Sensitive files? Ask before Read${isSystemDir ? "\n- PROTECTED DIRECTORY" : ""}
9868
9891
 
9869
9892
  -- CHAT FORMATTING --
@@ -10494,11 +10517,12 @@ var init_history = __esm({
10494
10517
  // src/utils/usage.js
10495
10518
  import fs11 from "fs-extra";
10496
10519
  import path9 from "path";
10497
- 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;
10498
10521
  var init_usage = __esm({
10499
10522
  "src/utils/usage.js"() {
10500
10523
  init_paths();
10501
10524
  init_crypto();
10525
+ init_settings();
10502
10526
  generateSaveId = () => Math.random().toString(36).substring(2) + Date.now().toString(36);
10503
10527
  cachedUsage = null;
10504
10528
  writeTimeout = null;
@@ -10935,14 +10959,16 @@ var init_usage = __esm({
10935
10959
  }
10936
10960
  return summed;
10937
10961
  };
10938
- checkQuota = async (key, settings) => {
10939
- const tier = settings.apiTier || "Free";
10940
- 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];
10941
10970
  const resolveAgentLimits = () => {
10942
- const providerBudgets = quotas.providerBudgets || {};
10943
- const useProvider = !!providerBudgets.__useProvider;
10944
- const currentProvider = settings.aiProvider || "Google";
10945
- if (useProvider && providerBudgets[currentProvider]) {
10971
+ if (isPerProvider) {
10946
10972
  const pb = providerBudgets[currentProvider];
10947
10973
  return {
10948
10974
  agentLimit: typeof pb.agentLimit === "number" && pb.agentLimit > 0 ? pb.agentLimit : 99999999,
@@ -10956,55 +10982,74 @@ var init_usage = __esm({
10956
10982
  monthlyTokenLimit: quotas.monthlyTokenLimit || 99999999999999
10957
10983
  };
10958
10984
  };
10959
- if (tier === "Free") {
10960
- if (key === "agent") {
10961
- const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
10962
- const dailyUsage = await getDailyUsage();
10963
- if (dailyUsage.agent + dailyUsage.background >= 999999) return false;
10964
- const dailyOk = dailyUsage.agent < agentLimit && (dailyUsage.tokens || 0) < tokenLimit;
10965
- if (!dailyOk) return false;
10966
- let monthlyUsage;
10967
- if (quotas.resetMode === "Custom") {
10968
- monthlyUsage = await getCustomPeriodUsage(quotas.resetDay || 1);
10969
- } else {
10970
- 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;
11002
+ }
11003
+ const monthlyModels = monthlyUsage.models?.[currentProvider] || {};
11004
+ for (const m in monthlyModels) {
11005
+ monthlyTokenCount += monthlyModels[m]?.tokens || 0;
10971
11006
  }
10972
- return (monthlyUsage.tokens || 0) < monthlyTokenLimit;
11007
+ } else {
11008
+ dailyAgentCount = dailyUsage.agent || 0;
11009
+ dailyTokenCount = dailyUsage.tokens || 0;
11010
+ monthlyTokenCount = monthlyUsage.tokens || 0;
10973
11011
  }
10974
- if (key === "background") {
10975
- const dailyUsage = await getDailyUsage();
10976
- if (dailyUsage.agent + dailyUsage.background >= 999999) return false;
10977
- 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" };
10978
11014
  }
10979
- if (key === "search") {
10980
- const dailyUsage = await getDailyUsage();
10981
- 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
+ };
10982
11020
  }
10983
- }
10984
- if (tier === "Paid" || tier === "Custom") {
10985
- if (key === "agent") {
10986
- const { agentLimit, tokenLimit, monthlyTokenLimit } = resolveAgentLimits();
10987
- const dailyUsage = await getDailyUsage();
10988
- const dailyOk = dailyUsage.agent < agentLimit && (dailyUsage.tokens || 0) < tokenLimit;
10989
- if (!dailyOk) return false;
10990
- let monthlyUsage;
10991
- if (quotas.resetMode === "Custom") {
10992
- monthlyUsage = await getCustomPeriodUsage(quotas.resetDay || 1);
10993
- } else {
10994
- monthlyUsage = await getMonthlyUsage();
10995
- }
10996
- 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
+ };
10997
11026
  }
10998
- if (key === "background") {
10999
- const dailyUsage = await getDailyUsage();
11000
- 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
+ };
11001
11032
  }
11002
- if (key === "search") {
11003
- const dailyUsage = await getDailyUsage();
11004
- 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" };
11005
11039
  }
11040
+ const ok = dailyUsage.background < (quotas.backgroundLimit || 999999);
11041
+ return { allowed: ok, reason: ok ? void 0 : "Background Request Limit Exceeded" };
11006
11042
  }
11007
- 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;
11008
11053
  };
11009
11054
  getImageQuotaBuckets = (imageCalls) => {
11010
11055
  const hourMs = 60 * 60 * 1e3;
@@ -11321,7 +11366,7 @@ var init_web_search = __esm({
11321
11366
  init_paths();
11322
11367
  init_puppeteer_helper();
11323
11368
  web_search = async (argsString) => {
11324
- const { query, limit = 10, aiMode = false } = parseArgs(argsString);
11369
+ const { query, limit = 5, aiMode = false } = parseArgs(argsString);
11325
11370
  if (!query) return 'ERROR: Missing "query" argument for web_search.';
11326
11371
  const maxRetries = 3;
11327
11372
  let lastError = null;
@@ -11894,22 +11939,22 @@ ${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
11894
11939
  return `ERROR: CRITICAL FAILURE: Verification failed. File [${targetPath}] is empty on disk despite success report!`;
11895
11940
  }
11896
11941
  let snippet = "";
11897
- if (verifiedLineCount <= 200) {
11942
+ if (verifiedLineCount <= 100) {
11898
11943
  snippet = verifiedLines.join("\n");
11899
11944
  } else {
11900
- const head = verifiedLines.slice(0, 100).join("\n");
11901
- const tail = verifiedLines.slice(-100).join("\n");
11945
+ const head = verifiedLines.slice(0, 50).join("\n");
11946
+ const tail = verifiedLines.slice(-50).join("\n");
11902
11947
  snippet = `${head}
11903
11948
 
11904
- ... [${verifiedLineCount - 200} lines truncated] ...
11949
+ ... [${verifiedLineCount - 100} lines truncated] ...
11905
11950
 
11906
11951
  ${tail}`;
11907
11952
  }
11908
11953
  verifiedContent = null;
11909
11954
  return `SUCCESS: File [${targetPath}] saved.
11910
-
11911
11955
  - Stats: [${verifiedLineCount} lines, ${(verifiedSize / 1024).toFixed(1)} KB]
11912
11956
  ${ancestry}- Content Preview:
11957
+
11913
11958
  ${snippet}`;
11914
11959
  } catch (err) {
11915
11960
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -15372,34 +15417,51 @@ var init_ai = __esm({
15372
15417
  for (const line of lines) {
15373
15418
  const cleanLine = line.trim();
15374
15419
  if (!cleanLine || !cleanLine.startsWith("data: ")) continue;
15375
- if (cleanLine === "data: [DONE]") break;
15376
- try {
15377
- const json = JSON.parse(cleanLine.substring(6));
15378
- const delta = json.choices?.[0]?.delta;
15379
- const usage = json.usage;
15380
- if (usage) {
15381
- latestUsageMetadata = {
15382
- totalTokenCount: usage.total_tokens || usage.prompt_tokens + usage.completion_tokens,
15383
- promptTokenCount: usage.prompt_tokens || 0,
15384
- candidatesTokenCount: usage.completion_tokens || 0,
15385
- cachedContentTokenCount: usage.prompt_tokens_details?.cached_tokens || 0,
15386
- thoughtsTokenCount: usage.completion_tokens_details?.reasoning_tokens || 0
15387
- };
15388
- hasNewData = true;
15389
- }
15390
- if (delta) {
15391
- const thought = delta.reasoning_content || null;
15392
- if (thought) {
15393
- pendingParts.push({ text: thought, thought: true });
15394
- hasNewData = true;
15420
+ let isDone = false;
15421
+ if (cleanLine === "data: [DONE]") {
15422
+ isDone = true;
15423
+ } else {
15424
+ try {
15425
+ const json = JSON.parse(cleanLine.substring(6));
15426
+ const delta = json.choices?.[0]?.delta;
15427
+ const usage = json.usage;
15428
+ if (json.choices?.[0]?.finish_reason) {
15429
+ isDone = true;
15395
15430
  }
15396
- if (delta.content) {
15397
- pendingParts.push({ text: delta.content });
15431
+ if (usage) {
15432
+ latestUsageMetadata = {
15433
+ totalTokenCount: usage.total_tokens || usage.prompt_tokens + usage.completion_tokens,
15434
+ promptTokenCount: usage.prompt_tokens || 0,
15435
+ candidatesTokenCount: usage.completion_tokens || 0,
15436
+ cachedContentTokenCount: usage.prompt_tokens_details?.cached_tokens || 0,
15437
+ thoughtsTokenCount: usage.completion_tokens_details?.reasoning_tokens || 0
15438
+ };
15398
15439
  hasNewData = true;
15399
15440
  }
15441
+ if (delta) {
15442
+ const thought = delta.reasoning_content || null;
15443
+ if (thought) {
15444
+ pendingParts.push({ text: thought, thought: true });
15445
+ hasNewData = true;
15446
+ }
15447
+ if (delta.content) {
15448
+ pendingParts.push({ text: delta.content });
15449
+ hasNewData = true;
15450
+ }
15451
+ }
15452
+ } catch (e) {
15400
15453
  }
15401
- } catch (e) {
15402
15454
  }
15455
+ if ((isDone || Date.now() - lastFlushTime >= 150) && hasNewData) {
15456
+ yield {
15457
+ candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
15458
+ usageMetadata: latestUsageMetadata
15459
+ };
15460
+ pendingParts = [];
15461
+ lastFlushTime = Date.now();
15462
+ hasNewData = false;
15463
+ }
15464
+ if (isDone) break;
15403
15465
  }
15404
15466
  if (Date.now() - lastFlushTime >= 150 && hasNewData) {
15405
15467
  yield {
@@ -15497,33 +15559,50 @@ var init_ai = __esm({
15497
15559
  for (const line of lines) {
15498
15560
  const cleanLine = line.trim();
15499
15561
  if (!cleanLine || !cleanLine.startsWith("data: ")) continue;
15500
- if (cleanLine === "data: [DONE]") break;
15501
- try {
15502
- const json = JSON.parse(cleanLine.substring(6));
15503
- const delta = json.choices?.[0]?.delta;
15504
- const usage = json.usage;
15505
- if (usage) {
15506
- latestUsageMetadata = {
15507
- totalTokenCount: usage.total_tokens || (usage.prompt_tokens || 0) + (usage.completion_tokens || 0),
15508
- promptTokenCount: usage.prompt_tokens || 0,
15509
- candidatesTokenCount: usage.completion_tokens || 0,
15510
- cachedContentTokenCount: usage.prompt_tokens_details?.cached_tokens || 0,
15511
- thoughtsTokenCount: 0
15512
- };
15513
- hasNewData = true;
15514
- }
15515
- if (delta) {
15516
- if (delta.thinking) {
15517
- pendingParts.push({ text: delta.thinking, thought: true });
15518
- hasNewData = true;
15562
+ let isDone = false;
15563
+ if (cleanLine === "data: [DONE]") {
15564
+ isDone = true;
15565
+ } else {
15566
+ try {
15567
+ const json = JSON.parse(cleanLine.substring(6));
15568
+ const delta = json.choices?.[0]?.delta;
15569
+ const usage = json.usage;
15570
+ if (json.choices?.[0]?.finish_reason) {
15571
+ isDone = true;
15519
15572
  }
15520
- if (delta.content) {
15521
- pendingParts.push({ text: delta.content });
15573
+ if (usage) {
15574
+ latestUsageMetadata = {
15575
+ totalTokenCount: usage.total_tokens || (usage.prompt_tokens || 0) + (usage.completion_tokens || 0),
15576
+ promptTokenCount: usage.prompt_tokens || 0,
15577
+ candidatesTokenCount: usage.completion_tokens || 0,
15578
+ cachedContentTokenCount: usage.prompt_tokens_details?.cached_tokens || 0,
15579
+ thoughtsTokenCount: 0
15580
+ };
15522
15581
  hasNewData = true;
15523
15582
  }
15583
+ if (delta) {
15584
+ if (delta.thinking) {
15585
+ pendingParts.push({ text: delta.thinking, thought: true });
15586
+ hasNewData = true;
15587
+ }
15588
+ if (delta.content) {
15589
+ pendingParts.push({ text: delta.content });
15590
+ hasNewData = true;
15591
+ }
15592
+ }
15593
+ } catch (e) {
15524
15594
  }
15525
- } catch (e) {
15526
15595
  }
15596
+ if ((isDone || Date.now() - lastFlushTime >= 150) && hasNewData) {
15597
+ yield {
15598
+ candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
15599
+ usageMetadata: latestUsageMetadata
15600
+ };
15601
+ pendingParts = [];
15602
+ lastFlushTime = Date.now();
15603
+ hasNewData = false;
15604
+ }
15605
+ if (isDone) break;
15527
15606
  }
15528
15607
  if (Date.now() - lastFlushTime >= 150 && hasNewData) {
15529
15608
  yield {
@@ -15689,8 +15768,15 @@ var init_ai = __esm({
15689
15768
  signal
15690
15769
  });
15691
15770
  if (!response.ok) {
15692
- const err = await response.json();
15693
- 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}`);
15694
15780
  error.status = response.status;
15695
15781
  throw error;
15696
15782
  }
@@ -15716,9 +15802,14 @@ var init_ai = __esm({
15716
15802
  buffer += decoder.decode(value, { stream: true });
15717
15803
  const lines = buffer.split("\n");
15718
15804
  buffer = lines.pop();
15805
+ let isDone = false;
15719
15806
  for (const line of lines) {
15720
15807
  const trimmed = line.trim();
15721
- if (!trimmed || trimmed === "data: [DONE]") continue;
15808
+ if (!trimmed) continue;
15809
+ if (trimmed === "data: [DONE]") {
15810
+ isDone = true;
15811
+ break;
15812
+ }
15722
15813
  if (trimmed.startsWith("data: ")) {
15723
15814
  let json;
15724
15815
  try {
@@ -15729,6 +15820,9 @@ var init_ai = __esm({
15729
15820
  if (json.error) {
15730
15821
  throw new Error(`NVIDIA Stream Error: ${json.error.message || JSON.stringify(json.error)}`);
15731
15822
  }
15823
+ if (json.choices?.[0]?.finish_reason) {
15824
+ isDone = true;
15825
+ }
15732
15826
  try {
15733
15827
  const usage = json.usage;
15734
15828
  if (usage) {
@@ -15755,7 +15849,7 @@ var init_ai = __esm({
15755
15849
  }
15756
15850
  }
15757
15851
  }
15758
- if (Date.now() - lastFlushTime >= 350 && hasNewData) {
15852
+ if ((isDone || Date.now() - lastFlushTime >= 350) && hasNewData) {
15759
15853
  yield {
15760
15854
  candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
15761
15855
  usageMetadata: latestUsageMetadata
@@ -15765,6 +15859,7 @@ var init_ai = __esm({
15765
15859
  lastFlushTime = Date.now();
15766
15860
  hasNewData = false;
15767
15861
  }
15862
+ if (isDone) break;
15768
15863
  }
15769
15864
  break;
15770
15865
  } catch (error) {
@@ -15982,43 +16077,51 @@ var init_ai = __esm({
15982
16077
  for (const line of lines) {
15983
16078
  const cleanLine = line.trim();
15984
16079
  if (!cleanLine || !cleanLine.startsWith("data: ")) continue;
15985
- if (cleanLine === "data: [DONE]") break;
15986
- try {
15987
- const json = JSON.parse(cleanLine.substring(6));
15988
- const delta = json.choices?.[0]?.delta;
15989
- const usage = json.usage;
15990
- if (usage) {
15991
- latestUsageMetadata = {
15992
- totalTokenCount: usage.total_tokens || (usage.prompt_tokens || 0) + (usage.completion_tokens || 0),
15993
- promptTokenCount: usage.prompt_tokens || 0,
15994
- candidatesTokenCount: usage.completion_tokens || 0,
15995
- cachedContentTokenCount: usage.prompt_tokens_details?.cached_tokens || usage.prompt_tokens_details?.cache_read_input_tokens || usage.cache_read_input_tokens || 0,
15996
- thoughtsTokenCount: usage.completion_tokens_details?.reasoning_tokens || 0
15997
- };
15998
- hasNewData = true;
15999
- }
16000
- if (delta) {
16001
- const thought = delta.reasoning || (delta.reasoning_details ? delta.reasoning_details.map((d) => d.text).join("") : null);
16002
- if (thought) {
16003
- pendingParts.push({ text: thought, thought: true });
16004
- hasNewData = true;
16080
+ let isDone = false;
16081
+ if (cleanLine === "data: [DONE]") {
16082
+ isDone = true;
16083
+ } else {
16084
+ try {
16085
+ const json = JSON.parse(cleanLine.substring(6));
16086
+ const delta = json.choices?.[0]?.delta;
16087
+ const usage = json.usage;
16088
+ if (json.choices?.[0]?.finish_reason) {
16089
+ isDone = true;
16005
16090
  }
16006
- if (delta.content) {
16007
- pendingParts.push({ text: delta.content });
16091
+ if (usage) {
16092
+ latestUsageMetadata = {
16093
+ totalTokenCount: usage.total_tokens || (usage.prompt_tokens || 0) + (usage.completion_tokens || 0),
16094
+ promptTokenCount: usage.prompt_tokens || 0,
16095
+ candidatesTokenCount: usage.completion_tokens || 0,
16096
+ cachedContentTokenCount: usage.prompt_tokens_details?.cached_tokens || usage.prompt_tokens_details?.cache_read_input_tokens || usage.cache_read_input_tokens || 0,
16097
+ thoughtsTokenCount: usage.completion_tokens_details?.reasoning_tokens || 0
16098
+ };
16008
16099
  hasNewData = true;
16009
16100
  }
16101
+ if (delta) {
16102
+ const thought = delta.reasoning || (delta.reasoning_details ? delta.reasoning_details.map((d) => d.text).join("") : null);
16103
+ if (thought) {
16104
+ pendingParts.push({ text: thought, thought: true });
16105
+ hasNewData = true;
16106
+ }
16107
+ if (delta.content) {
16108
+ pendingParts.push({ text: delta.content });
16109
+ hasNewData = true;
16110
+ }
16111
+ }
16112
+ } catch (e) {
16010
16113
  }
16011
- } catch (e) {
16012
16114
  }
16013
- }
16014
- if (Date.now() - lastFlushTime >= 150 && hasNewData) {
16015
- yield {
16016
- candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
16017
- usageMetadata: latestUsageMetadata
16018
- };
16019
- pendingParts = [];
16020
- lastFlushTime = Date.now();
16021
- hasNewData = false;
16115
+ if ((isDone || Date.now() - lastFlushTime >= 150) && hasNewData) {
16116
+ yield {
16117
+ candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
16118
+ usageMetadata: latestUsageMetadata
16119
+ };
16120
+ pendingParts = [];
16121
+ lastFlushTime = Date.now();
16122
+ hasNewData = false;
16123
+ }
16124
+ if (isDone) break;
16022
16125
  }
16023
16126
  }
16024
16127
  };
@@ -16091,18 +16194,24 @@ var init_ai = __esm({
16091
16194
  hasNewData = true;
16092
16195
  }
16093
16196
  if (chunk.done) {
16094
- const evalNs = chunk.prompt_eval_duration || 0;
16095
- const isCached = evalNs > 0 && evalNs < 75e6;
16197
+ let cachedCount = chunk.prompt_cached_count || 0;
16198
+ if (!cachedCount && chunk.prompt_eval_count) {
16199
+ const evalNs = chunk.prompt_eval_duration || 0;
16200
+ if (evalNs > 0 && evalNs < 75e6) {
16201
+ cachedCount = chunk.prompt_eval_count;
16202
+ }
16203
+ }
16096
16204
  latestUsageMetadata = {
16097
16205
  totalTokenCount: (chunk.prompt_eval_count || 0) + (chunk.eval_count || 0),
16098
16206
  promptTokenCount: chunk.prompt_eval_count || 0,
16099
16207
  candidatesTokenCount: chunk.eval_count || 0,
16100
- cachedContentTokenCount: chunk.prompt_eval_count && isCached ? chunk.prompt_eval_count : 0,
16208
+ cachedContentTokenCount: cachedCount,
16101
16209
  thoughtsTokenCount: 0
16210
+ // Note: Ollama does not natively return a separate sub-count for thoughts yet
16102
16211
  };
16103
16212
  hasNewData = true;
16104
16213
  }
16105
- if (Date.now() - lastFlushTime >= 150 && hasNewData) {
16214
+ if (chunk.done || Date.now() - lastFlushTime >= 150 && hasNewData) {
16106
16215
  yield {
16107
16216
  candidates: pendingParts.length > 0 ? [{ content: { parts: [...pendingParts] } }] : [],
16108
16217
  usageMetadata: latestUsageMetadata
@@ -17750,6 +17859,10 @@ ${wildcardToolingPrompt}${taggedContextStr}[USER PROMPT] ${cleanPromptForModel.t
17750
17859
  let fullAgentResponseChunks = [];
17751
17860
  let wasToolCalledInLastLoop = false;
17752
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
+ }
17753
17866
  const currentTurnTools = [];
17754
17867
  wasToolCalledInLastLoop = false;
17755
17868
  if (systemSettings2?.compression === 0 && (sessionStats?.tokens || 0) > contextTruncationCount) {
@@ -17850,16 +17963,30 @@ ${combinedNudge}`;
17850
17963
  if (!text || !text.includes("[tool:")) return text;
17851
17964
  const THINK_OPEN_PH = "___THINK_OPEN_TAG___";
17852
17965
  const THINK_CLOSE_PH = "___THINK_CLOSE_TAG___";
17853
- text = text.replaceAll("<think>", THINK_OPEN_PH).replaceAll("</think>", THINK_CLOSE_PH);
17854
- text = text.replace(/<(\w+)(?:[^>]*)>\s*([\s\S]*?\[tool:[^\]]*\][\s\S]*?)\s*<\/\1>/gi, (match2, tagName, innerContent) => {
17855
- if (innerContent && innerContent.includes("[tool:")) return innerContent.trim();
17856
- return match2;
17857
- });
17966
+ text = text.replace("<think>", THINK_OPEN_PH).replace("</think>", THINK_CLOSE_PH);
17858
17967
  text = text.replace(/```(?:tool|yaml|function|json)?\s*\n?([\s\S]*?)\n?\```/gi, (match2, inner) => {
17859
17968
  if (inner.includes("[tool:")) return inner.trim();
17860
17969
  return match2;
17861
17970
  });
17862
- 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;
17863
17990
  text = text.replaceAll(THINK_OPEN_PH, "<think>").replaceAll(THINK_CLOSE_PH, "</think>");
17864
17991
  return text;
17865
17992
  };
@@ -17933,9 +18060,6 @@ ${combinedNudge}`;
17933
18060
  }
17934
18061
  contents.length = 0;
17935
18062
  contents.push(...finalContents);
17936
- if (!await checkQuota("agent", settings)) {
17937
- throw new Error("Error: Quota Exausted for Agent");
17938
- }
17939
18063
  targetModel = modelName;
17940
18064
  const sysInstructionCacheKey2 = `${chatId}|${aiProvider}|${thinkingLevel}|${targetModel}|${JSON.stringify(profile)}|${!!systemSettings2?.dynamicDirAwareness}|${!!systemSettings2?.subAgents}`;
17941
18065
  let isCacheHit = systemInstructionCache.key === sysInstructionCacheKey2 && systemInstructionCache.value;
@@ -19274,14 +19398,14 @@ ${oldLines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
19274
19398
  `;
19275
19399
  }
19276
19400
  let snippet = "";
19277
- if (verifiedLineCount <= 200) {
19401
+ if (verifiedLineCount <= 100) {
19278
19402
  snippet = verifiedLines.join("\n");
19279
19403
  } else {
19280
- const head = verifiedLines.slice(0, 100).join("\n");
19281
- const tail = verifiedLines.slice(-100).join("\n");
19404
+ const head = verifiedLines.slice(0, 50).join("\n");
19405
+ const tail = verifiedLines.slice(-50).join("\n");
19282
19406
  snippet = `${head}
19283
19407
 
19284
- ... [${verifiedLineCount - 200} lines truncated for history stability] ...
19408
+ ... [${verifiedLineCount - 100} lines truncated for history stability] ...
19285
19409
 
19286
19410
  ${tail}`;
19287
19411
  }
@@ -19304,21 +19428,21 @@ ${oldLines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
19304
19428
  `;
19305
19429
  }
19306
19430
  let snippet2 = "";
19307
- if (verifiedLineCount2 <= 200) {
19431
+ if (verifiedLineCount2 <= 100) {
19308
19432
  snippet2 = verifiedLines2.join("\n");
19309
19433
  } else {
19310
- const head = verifiedLines2.slice(0, 100).join("\n");
19311
- const tail = verifiedLines2.slice(-100).join("\n");
19434
+ const head = verifiedLines2.slice(0, 50).join("\n");
19435
+ const tail = verifiedLines2.slice(-50).join("\n");
19312
19436
  snippet2 = `${head}
19313
19437
 
19314
- ... [${verifiedLineCount2 - 200} lines truncated] ...
19438
+ ... [${verifiedLineCount2 - 100} lines truncated] ...
19315
19439
 
19316
19440
  ${tail}`;
19317
19441
  }
19318
19442
  result2 = `SUCCESS: File [${filePath}] saved via IDE Companion (May have user edits).
19319
-
19320
19443
  - Stats: [${verifiedLineCount2} lines, ${(verifiedSize2 / 1024).toFixed(1)} KB]
19321
19444
  ${ancestry2}- Content Preview:
19445
+
19322
19446
  ${snippet2}`;
19323
19447
  }
19324
19448
  const action = normToolName === "write_file" ? "Created" : "Edited";
@@ -19565,7 +19689,7 @@ ${snippet2}`;
19565
19689
  await incrementUsage("toolFailure");
19566
19690
  if (settings.onToolResult) settings.onToolResult("failure", normToolName);
19567
19691
  }
19568
- const aiContent = `[TOOL RESULT]: ${(result || "").toString().replaceAll("[UI_CONTEXT]", "[CONTEXT]")}`;
19692
+ const aiContent = `[TOOL RESULT]: ${(result || "").toString().replaceAll("[UI_CONTEXT]", "")}`;
19569
19693
  toolResults.push({ role: "user", text: aiContent, binaryPart });
19570
19694
  anyToolExecutedInThisTurn = true;
19571
19695
  let uiContent = `[TOOL RESULT]: ${result || ""}`;
@@ -19881,13 +20005,14 @@ Error Log can be found in ${path26.join(LOGS_DIR, "agent", "error.log")}`);
19881
20005
  wasToolCalledInLastLoop = toolCallPointer > 0 || anyToolExecutedInThisTurn;
19882
20006
  }
19883
20007
  } catch (err) {
19884
- const errLog = err instanceof Error ? (() => {
20008
+ const rawErrStr = err instanceof Error ? (() => {
19885
20009
  try {
19886
20010
  return JSON.parse(JSON.parse(err.message).error.message).error.message;
19887
20011
  } catch {
19888
- return String(err);
20012
+ return err.message || String(err);
19889
20013
  }
19890
20014
  })() : String(err);
20015
+ const errLog = rawErrStr.replace(/^(Error:\s*)+/i, "");
19891
20016
  const date = (/* @__PURE__ */ new Date()).toLocaleString();
19892
20017
  const agentErrDir = path26.join(LOGS_DIR, "agent");
19893
20018
  yield { type: "text", content: `\u274C CRITICAL ERROR: ${errLog.includes("fetch failed") ? "Failed to Connect. Check your Internet Connection or Wait a moment" : errLog}` };
@@ -19933,6 +20058,7 @@ Error Log can be found in ${path26.join(LOGS_DIR, "agent", "error.log")}`);
19933
20058
  if (lower === "openrouter") return "OpenRouter";
19934
20059
  if (lower === "nvidia") return "NVIDIA";
19935
20060
  if (lower === "mistral") return "Mistral";
20061
+ if (lower === "ollama") return "Ollama";
19936
20062
  return null;
19937
20063
  };
19938
20064
  const envSubagentProvider = normalizeProvider(envSubagentProviderRaw);
@@ -19997,6 +20123,7 @@ TOOL RULES:
19997
20123
  - JSON ESCAPE ALL LITERAL ESCAPE SEQUENCES IN TOOL ARGUMENTS
19998
20124
  - SAME file, MULTIPLE edits? ONE PatchFile (\u226415 blocks) \u2190 PRIORITY
19999
20125
  - Need text or huge files? SearchKeyword > Full Read
20126
+ - MUST AVOID UNNECESSARY LARGE-FILE CHUNK READS
20000
20127
  - Restricted Shell Access, No Deletion
20001
20128
  - ONLY valid tools and syntax defined below are allowed
20002
20129
 
@@ -20013,7 +20140,7 @@ ${isAsync ? `- [tool:functions.AskMain(question="...")]. Communicate with PARENT
20013
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
20014
20141
  - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
20015
20142
  - [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. View files
20016
- - [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
20017
20144
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. VERIFY IMPORTS
20018
20145
  - [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `WINDOWS POWERSHELL` : `WINDOWS CMD` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user`.trim();
20019
20146
  const systemInstructionSubAgent = `=== START SYSTEM PROMPT ===
@@ -21325,9 +21452,13 @@ function App({ args = [] }) {
21325
21452
  const commitActiveStreamingMessage = () => {
21326
21453
  flushTypewriterNow();
21327
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
+ }
21328
21459
  const msg = {
21329
21460
  ...activeStreamingMsgRef.current,
21330
- text: flattenString(activeStreamingMsgRef.current.text),
21461
+ text: msgText,
21331
21462
  isStreaming: false
21332
21463
  };
21333
21464
  setMessages((prev) => {
@@ -21439,7 +21570,7 @@ function App({ args = [] }) {
21439
21570
  }
21440
21571
  const envModel = process.env.SUBAGENT_MODEL ? process.env.SUBAGENT_MODEL.trim() : null;
21441
21572
  const envProviderRaw = process.env.SUBAGENT_PROVIDER ? process.env.SUBAGENT_PROVIDER.trim() : null;
21442
- const ALL_PROVIDERS = ["Google", "DeepSeek", "OpenRouter", "NVIDIA", "Mistral"];
21573
+ const ALL_PROVIDERS = ["Google", "DeepSeek", "OpenRouter", "NVIDIA", "Mistral", "Ollama"];
21443
21574
  const normalizeProvider = (pStr) => {
21444
21575
  if (!pStr) return null;
21445
21576
  const lower = pStr.toLowerCase();
@@ -21448,6 +21579,7 @@ function App({ args = [] }) {
21448
21579
  if (lower === "openrouter") return "OpenRouter";
21449
21580
  if (lower === "nvidia") return "NVIDIA";
21450
21581
  if (lower === "mistral") return "Mistral";
21582
+ if (lower === "ollama") return "Ollama";
21451
21583
  return null;
21452
21584
  };
21453
21585
  const envProvider = normalizeProvider(envProviderRaw);
@@ -21774,6 +21906,8 @@ function App({ args = [] }) {
21774
21906
  const [providerBudgetCursor, setProviderBudgetCursor] = useState15(0);
21775
21907
  const [pbsCursor, setPbsCursor] = useState15(0);
21776
21908
  const [pbsSelected, setPbsSelected] = useState15({});
21909
+ const [pbfFormState, setPbfFormState] = useState15({});
21910
+ const [pbfFieldIndex, setPbfFieldIndex] = useState15(0);
21777
21911
  const [systemSettings2, setSystemSettings] = useState15({ memory: true, theme: "Dark", compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
21778
21912
  const colors = useMemo2(() => getThemeColors(systemSettings2.theme), [systemSettings2.theme]);
21779
21913
  const [profileData, setProfileData] = useState15({ name: null, nickname: null, instructions: null });
@@ -21897,7 +22031,7 @@ function App({ args = [] }) {
21897
22031
  return [...prev, {
21898
22032
  id: "tier-switch-" + Date.now(),
21899
22033
  role: "system",
21900
- text: `**[TIER LIMIT]** Auto-switched to ${modelDisplayName}.`,
22034
+ text: `**Switched to ${modelDisplayName}.`,
21901
22035
  isMeta: true
21902
22036
  }];
21903
22037
  });
@@ -22272,7 +22406,7 @@ function App({ args = [] }) {
22272
22406
  return;
22273
22407
  }
22274
22408
  if (activeView === "providerBudgetSelect") {
22275
- const PBS_PROVIDERS = ["Google", "DeepSeek", "Mistral", "NVIDIA", "OpenRouter"];
22409
+ const PBS_PROVIDERS = ["Google", "DeepSeek", "Mistral", "NVIDIA", "OpenRouter", "Ollama"];
22276
22410
  if (key.upArrow) {
22277
22411
  setPbsCursor((c) => (c - 1 + PBS_PROVIDERS.length) % PBS_PROVIDERS.length);
22278
22412
  return;
@@ -22299,6 +22433,41 @@ function App({ args = [] }) {
22299
22433
  }
22300
22434
  return;
22301
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
+ }
22302
22471
  if (key.escape) {
22303
22472
  if (showBtwBox) {
22304
22473
  setShowBtwBox(false);
@@ -24469,7 +24638,7 @@ Selection: ${val}`,
24469
24638
  if (afterText.match(/<\/(think|thought)>/i)) {
24470
24639
  const parts = afterText.split(/<\/(think|thought)>/i);
24471
24640
  const rawThinkContent = parts[0] || "";
24472
- const thinkContent = rawThinkContent.replace(/^<(think|thought)>/i, "");
24641
+ const thinkContent = rawThinkContent.replace(/^<(think|thought)[^>]*>\r?\n?/i, "").replace(/\r?\n?$/g, "");
24473
24642
  const agentContent = parts.slice(2).join("").replace(/<\/?(think|thought)>/gi, "");
24474
24643
  activeStreamingMsgRef.current.text = flattenString(thinkContent);
24475
24644
  const startTime = activeStreamingMsgRef.current.startTime || Date.now();
@@ -24482,7 +24651,7 @@ Selection: ${val}`,
24482
24651
  appendStreamText(agentContent);
24483
24652
  }
24484
24653
  } else {
24485
- let thinkStartText = afterText.replace(/^<(think|thought)>/gi, "");
24654
+ let thinkStartText = afterText.replace(/^<(think|thought)[^>]*>\r?\n?/gi, "");
24486
24655
  appendStreamText(thinkStartText);
24487
24656
  }
24488
24657
  continue;
@@ -24719,7 +24888,7 @@ Selection: ${val}`,
24719
24888
  }, [suggestionVisibleCount, suggestions.length]);
24720
24889
  useEffect12(() => {
24721
24890
  if (activeView !== "providerBudgetSelect") return;
24722
- const PBS_PROVIDERS = ["Google", "DeepSeek", "Mistral", "NVIDIA", "OpenRouter"];
24891
+ const PBS_PROVIDERS = ["Google", "DeepSeek", "Mistral", "NVIDIA", "OpenRouter", "Ollama"];
24723
24892
  const existingBudgets = quotas.providerBudgets || {};
24724
24893
  const initialSelected = PBS_PROVIDERS.reduce((acc, p) => {
24725
24894
  acc[p] = !!(existingBudgets[p] && (existingBudgets[p].agentLimit || existingBudgets[p].tokenLimit));
@@ -24730,74 +24899,19 @@ Selection: ${val}`,
24730
24899
  }, [activeView]);
24731
24900
  useEffect12(() => {
24732
24901
  if (activeView !== "providerBudgetFlow") return;
24733
- const currentProvider = providerBudgetQueue[providerBudgetCursor];
24734
- if (!currentProvider) {
24735
- const returnMode = budgetReturnView === "settings" ? "resetMode" : "budgetResetMode";
24736
- const rawPB = quotas.providerBudgets || {};
24737
- const cleaned = { __useProvider: true };
24738
- for (const prov of providerBudgetQueue) {
24739
- if (rawPB[prov]) cleaned[prov] = rawPB[prov];
24740
- }
24741
- const finalCleanedQuotas = { ...quotas, providerBudgets: cleaned };
24742
- setQuotas(finalCleanedQuotas);
24743
- saveSettings({ apiTier, quotas: finalCleanedQuotas });
24744
- setActiveView(returnMode);
24745
- 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
+ };
24746
24911
  }
24747
- const existingPB = (quotas.providerBudgets || {})[currentProvider] || {};
24748
- const totalProviders = providerBudgetQueue.length;
24749
- const currentStep = providerBudgetCursor + 1;
24750
- const providerLabel = `[${currentStep}/${totalProviders}] ${currentProvider}`;
24751
- const advanceToNext = (finalQuotas) => {
24752
- if (providerBudgetCursor + 1 < providerBudgetQueue.length) {
24753
- setProviderBudgetCursor((c) => c + 1);
24754
- setActiveView("providerBudgetFlow");
24755
- } else {
24756
- const rawPB = finalQuotas.providerBudgets || {};
24757
- const cleaned = { __useProvider: true };
24758
- for (const prov of providerBudgetQueue) {
24759
- if (rawPB[prov]) cleaned[prov] = rawPB[prov];
24760
- }
24761
- const finalCleanedQuotas = { ...finalQuotas, providerBudgets: cleaned };
24762
- setQuotas(finalCleanedQuotas);
24763
- const rm = budgetReturnView === "settings" ? "resetMode" : "budgetResetMode";
24764
- saveSettings({ apiTier, quotas: finalCleanedQuotas });
24765
- setActiveView(rm);
24766
- }
24767
- };
24768
- setInputConfig({
24769
- label: `${providerLabel} \u2014 Daily budget (requests/day):`,
24770
- key: "providerBudgets",
24771
- providerKey: currentProvider,
24772
- subKey: "agentLimit",
24773
- value: getPrefilledValue(existingPB.agentLimit),
24774
- returnView: "providerBudgetSelect",
24775
- next: (newQuotas) => {
24776
- const updatedPB = (newQuotas.providerBudgets || {})[currentProvider] || {};
24777
- return {
24778
- label: `${providerLabel} \u2014 Daily budget (tokens/day):`,
24779
- key: "providerBudgets",
24780
- providerKey: currentProvider,
24781
- subKey: "tokenLimit",
24782
- value: getPrefilledValue(updatedPB.tokenLimit),
24783
- returnView: "providerBudgetSelect",
24784
- next: (q2) => {
24785
- const pb2 = (q2.providerBudgets || {})[currentProvider] || {};
24786
- return {
24787
- label: `${providerLabel} \u2014 Monthly budget (tokens/month):`,
24788
- key: "providerBudgets",
24789
- providerKey: currentProvider,
24790
- subKey: "monthlyTokenLimit",
24791
- value: getPrefilledValue(pb2.monthlyTokenLimit),
24792
- returnView: "providerBudgetFlow",
24793
- onDone: advanceToNext
24794
- };
24795
- }
24796
- };
24797
- }
24798
- });
24799
- setActiveView("input");
24800
- }, [activeView, providerBudgetCursor]);
24912
+ setPbfFormState(initialForm);
24913
+ setPbfFieldIndex(0);
24914
+ }, [activeView, providerBudgetQueue]);
24801
24915
  const CustomMenuItem = ({ label, isSelected }) => {
24802
24916
  const isCancel = label === "Cancel" || label === "Back" || label.toLowerCase().includes("exit") || label.toLowerCase().includes("back");
24803
24917
  return /* @__PURE__ */ React16.createElement(
@@ -24811,10 +24925,9 @@ Selection: ${val}`,
24811
24925
  /* @__PURE__ */ React16.createElement(Text16, { color: isSelected ? "white" : "gray", bold: isSelected }, isSelected ? "\u276F " : " ", label)
24812
24926
  );
24813
24927
  };
24814
- const renderProgressBar = (label, current, limit) => {
24928
+ const renderProgressBar = (label, current, limit, barWidth = 10, paddingLeft = 2, labelWidth = 9) => {
24815
24929
  const actualPercent = limit > 0 ? Math.min(100, current / limit * 100) : 0;
24816
24930
  const percent = Math.round(actualPercent);
24817
- const barWidth = 15;
24818
24931
  const filledCount = Math.round(percent / 100 * barWidth);
24819
24932
  const barStr = "\u2588".repeat(filledCount) + "\u2591".repeat(Math.max(0, barWidth - filledCount));
24820
24933
  let barColor = colors.success || "green";
@@ -24823,7 +24936,7 @@ Selection: ${val}`,
24823
24936
  } else if (percent > 80) {
24824
24937
  barColor = colors.danger || "red";
24825
24938
  }
24826
- const isTokens = label.toLowerCase().includes("token");
24939
+ const isTokens = label.toLowerCase().includes("token") || label.toLowerCase().includes("daily") || label.toLowerCase().includes("monthly");
24827
24940
  const displayLimit = shouldClearValue(limit) ? "\u221E" : isTokens ? formatTokens(limit) : limit;
24828
24941
  const displayCurrent = isTokens ? formatTokens(current) : current;
24829
24942
  let displayPercent;
@@ -24834,7 +24947,7 @@ Selection: ${val}`,
24834
24947
  } else {
24835
24948
  displayPercent = `${percent}%`;
24836
24949
  }
24837
- 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, ")"));
24838
24951
  };
24839
24952
  const renderActiveView = () => {
24840
24953
  switch (activeView) {
@@ -25075,8 +25188,43 @@ Selection: ${val}`,
25075
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);
25076
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")));
25077
25190
  }
25078
- case "providerBudgetFlow":
25079
- 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
+ }
25080
25228
  case "budgetResetMode":
25081
25229
  return /* @__PURE__ */ React16.createElement(
25082
25230
  CommandMenu,
@@ -25128,33 +25276,72 @@ Selection: ${val}`,
25128
25276
  );
25129
25277
  const limitsNotSet = !usingProviderBudgets && (shouldClearValue(reqLimit) || shouldClearValue(tokenLimit) || shouldClearValue(monthlyLimit));
25130
25278
  let resetInfo = "";
25279
+ let resetCountdown = "";
25131
25280
  if (quotas.resetMode === "Custom") {
25132
25281
  const today2 = /* @__PURE__ */ new Date();
25133
25282
  const resetDay = quotas.resetDay || 1;
25283
+ let resetYear = today2.getFullYear();
25134
25284
  let resetMonth = today2.getMonth();
25135
25285
  if (today2.getDate() >= resetDay) {
25136
25286
  resetMonth += 1;
25287
+ if (resetMonth > 11) {
25288
+ resetMonth = 0;
25289
+ resetYear += 1;
25290
+ }
25137
25291
  }
25138
- const resetDate = new Date(today2.getFullYear(), resetMonth, resetDay);
25139
- 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();
25140
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)`;
25141
25300
  }
25142
- 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) => {
25143
- const pb = providerBudgetsMap[prov];
25144
- const provReqCurrent = dailyUsage?.providerRequests?.[prov] || 0;
25145
- let provTokenCurrent = 0;
25146
- const dailyModels = dailyUsage?.models?.[prov] || {};
25147
- for (const m in dailyModels) {
25148
- provTokenCurrent += dailyModels[m]?.tokens || 0;
25149
- }
25150
- let provMonthlyCurrent = 0;
25151
- const monthlySource = quotas.resetMode === "Custom" ? customPeriodUsage : monthlyUsage;
25152
- const monthlyModels = monthlySource?.models?.[prov] || {};
25153
- for (const m in monthlyModels) {
25154
- 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
+ });
25155
25321
  }
25156
- 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"));
25157
- }), 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"))));
25158
25345
  }
25159
25346
  case "input":
25160
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(
@@ -25849,7 +26036,7 @@ Selection: ${val}`,
25849
26036
  })(), /* @__PURE__ */ React16.createElement(
25850
26037
  GlintText_default,
25851
26038
  {
25852
- 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"),
25853
26040
  baseColor: colors.text,
25854
26041
  glintColor: colors.textMuted,
25855
26042
  glintWidth: 3
@@ -25938,7 +26125,7 @@ Selection: ${val}`,
25938
26125
  onSubmit: handleSetup,
25939
26126
  mask: "*"
25940
26127
  }
25941
- )))), /* @__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 && (() => {
25942
26129
  const windowSize = 5;
25943
26130
  let startIdx = suggestionOffsetRef.current;
25944
26131
  let firstSelectableIndex = 0;