fluxflow-cli 3.15.2 → 3.16.0

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
@@ -780,6 +780,9 @@ var init_settings = __esm({
780
780
  progressiveRendering: true,
781
781
  showTPMEstimate: false,
782
782
  subAgents: true,
783
+ CustomSubAgent: false,
784
+ SubAgentModel: "Default",
785
+ SubAgentProvider: "",
783
786
  dynamicDirAwareness: false,
784
787
  indentationTree: true
785
788
  },
@@ -5675,7 +5678,7 @@ var init_ChatLayout = __esm({
5675
5678
  return;
5676
5679
  }
5677
5680
  if (trimmed === "---" || trimmed === "***" || trimmed === "___") {
5678
- result.push(/* @__PURE__ */ React4.createElement(Box3, { key: i, marginY: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, width: "100%", borderColor: colors.borderMuted }));
5681
+ result.push(/* @__PURE__ */ React4.createElement(Box3, { key: i, marginY: 0, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, width: "100%", borderColor: colors.borderMuted }));
5679
5682
  return;
5680
5683
  }
5681
5684
  const headingMatch = trimmed.match(/^(#{1,6})\s+(.*)/);
@@ -5740,37 +5743,138 @@ var init_ChatLayout = __esm({
5740
5743
  const displayPrefix = isRemoval ? "-" : isAddition ? "+" : " ";
5741
5744
  const renderInlineDiff = () => {
5742
5745
  if (isPureUnpairedBlock) {
5743
- const blockColor = isRemoval ? colors.diffRemovalHighlightColor : colors.diffAdditionHighlightColor;
5744
5746
  const textBgColor = isRemoval ? colors.diffRemovalHighlightBg : colors.diffAdditionHighlightBg;
5745
- const wrappedLines = wrapText(content, columns - 15).split("\n");
5746
- return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, wrappedLines.map((wl, idx) => /* @__PURE__ */ React4.createElement(Box3, { key: idx }, renderHighlightedLine(wl, extension, blockColor, textBgColor))));
5747
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, renderHighlightedLine(wrapText(content, columns - 15), extension, void 0, textBgColor));
5747
5748
  }
5748
5749
  if (!(isRemoval || isAddition) || words.length === 0 || !hasInlineChange) {
5749
5750
  const textColor = isRemoval ? colors.diffRemovalText : isAddition ? colors.diffAdditionText : colors.textMuted;
5750
5751
  const textBgColor = void 0;
5751
- const wrappedLines = wrapText(content, columns - 15).split("\n");
5752
- return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, wrappedLines.map((wl, idx) => /* @__PURE__ */ React4.createElement(Box3, { key: idx }, renderHighlightedLine(wl, extension, textColor, textBgColor))));
5752
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, renderHighlightedLine(wrapText(content, columns - 15), extension, textColor, textBgColor));
5753
5753
  }
5754
- return /* @__PURE__ */ React4.createElement(Text4, { wrap: "anywhere" }, words.map((part, idx) => {
5754
+ const maxLen = Math.max(10, columns - 15);
5755
+ const wrappedLines = wrapText(content, maxLen).split("\n");
5756
+ const validWords = [];
5757
+ words.forEach((part, idx) => {
5755
5758
  const isWhitespace = /^\s+$/.test(part.value);
5756
5759
  if (isRemoval) {
5757
5760
  const isSurroundedByRemoval = words[idx - 1]?.removed || words[idx + 1]?.removed;
5758
5761
  if (part.removed || isWhitespace && isSurroundedByRemoval) {
5759
- return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: colors.diffRemovalHighlightColor, backgroundColor: colors.diffRemovalHighlightBg }, part.value);
5762
+ validWords.push({ text: part.value, isHighlight: true });
5763
+ } else if (!part.added) {
5764
+ validWords.push({ text: part.value, isHighlight: false });
5760
5765
  }
5761
- if (part.added) return null;
5762
- return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: colors.diffRemovalText }, part.value);
5763
- }
5764
- if (isAddition) {
5766
+ } else if (isAddition) {
5765
5767
  const isSurroundedByAddition = words[idx - 1]?.added || words[idx + 1]?.added;
5766
5768
  if (part.added || isWhitespace && isSurroundedByAddition) {
5767
- return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: colors.diffAdditionHighlightColor, backgroundColor: colors.diffAdditionHighlightBg }, part.value);
5769
+ validWords.push({ text: part.value, isHighlight: true });
5770
+ } else if (!part.removed) {
5771
+ validWords.push({ text: part.value, isHighlight: false });
5768
5772
  }
5769
- if (part.removed) return null;
5770
- return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: colors.diffAdditionText }, part.value);
5771
5773
  }
5772
- return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: colors.textMuted }, part.value);
5773
- }));
5774
+ });
5775
+ if (wrappedLines.length <= 1) {
5776
+ return /* @__PURE__ */ React4.createElement(Text4, { wrap: "wrap" }, validWords.map((part, idx) => {
5777
+ if (isRemoval) {
5778
+ if (part.isHighlight) {
5779
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: idx }, renderHighlightedLine(part.text, extension, colors.diffRemovalHighlightColor, colors.diffRemovalHighlightBg));
5780
+ }
5781
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: idx }, renderHighlightedLine(part.text, extension, colors.diffRemovalText));
5782
+ }
5783
+ if (isAddition) {
5784
+ if (part.isHighlight) {
5785
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: idx }, renderHighlightedLine(part.text, extension, colors.diffAdditionHighlightColor, colors.diffAdditionHighlightBg));
5786
+ }
5787
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: idx }, renderHighlightedLine(part.text, extension, colors.diffAdditionText));
5788
+ }
5789
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: idx }, renderHighlightedLine(part.text, extension, colors.textMuted));
5790
+ }));
5791
+ }
5792
+ let wordIdx = 0;
5793
+ let charIdx = 0;
5794
+ const leadingSpaceMatch = content.match(/^(\s*)/);
5795
+ const indent = leadingSpaceMatch ? leadingSpaceMatch[1] : "";
5796
+ const cappedIndent = indent.substring(0, Math.min(indent.length, 8));
5797
+ const lineSpans = wrappedLines.map((wl, lineIdx) => {
5798
+ const spans = [];
5799
+ let lineTextToMatch = wl;
5800
+ if (lineIdx > 0 && cappedIndent && wl.startsWith(cappedIndent)) {
5801
+ const currentAvail = validWords[wordIdx] ? validWords[wordIdx].text.substring(charIdx) : "";
5802
+ if (!currentAvail.startsWith(cappedIndent)) {
5803
+ spans.push({ text: cappedIndent, isHighlight: false });
5804
+ lineTextToMatch = wl.substring(cappedIndent.length);
5805
+ }
5806
+ }
5807
+ let neededLength = lineTextToMatch.length;
5808
+ while (neededLength > 0 && wordIdx < validWords.length) {
5809
+ const vw = validWords[wordIdx];
5810
+ const avail = vw.text.length - charIdx;
5811
+ if (avail <= 0) {
5812
+ wordIdx++;
5813
+ charIdx = 0;
5814
+ continue;
5815
+ }
5816
+ const takeLen = Math.min(neededLength, avail);
5817
+ spans.push({
5818
+ text: vw.text.substring(charIdx, charIdx + takeLen),
5819
+ isHighlight: vw.isHighlight
5820
+ });
5821
+ charIdx += takeLen;
5822
+ neededLength -= takeLen;
5823
+ if (charIdx >= vw.text.length) {
5824
+ wordIdx++;
5825
+ charIdx = 0;
5826
+ }
5827
+ }
5828
+ while (wordIdx < validWords.length) {
5829
+ const vw = validWords[wordIdx];
5830
+ const rem = vw.text.substring(charIdx);
5831
+ if (/^\s+$/.test(rem)) {
5832
+ wordIdx++;
5833
+ charIdx = 0;
5834
+ } else if (rem.startsWith(" ") || rem.startsWith(" ")) {
5835
+ let skipCount = 0;
5836
+ while (skipCount < rem.length && (rem[skipCount] === " " || rem[skipCount] === " ")) {
5837
+ skipCount++;
5838
+ }
5839
+ charIdx += skipCount;
5840
+ if (charIdx >= vw.text.length) {
5841
+ wordIdx++;
5842
+ charIdx = 0;
5843
+ }
5844
+ break;
5845
+ } else {
5846
+ break;
5847
+ }
5848
+ }
5849
+ return spans;
5850
+ });
5851
+ if (wordIdx < validWords.length) {
5852
+ const lastSpans = lineSpans[lineSpans.length - 1];
5853
+ while (wordIdx < validWords.length) {
5854
+ const vw = validWords[wordIdx];
5855
+ lastSpans.push({
5856
+ text: vw.text.substring(charIdx),
5857
+ isHighlight: vw.isHighlight
5858
+ });
5859
+ wordIdx++;
5860
+ charIdx = 0;
5861
+ }
5862
+ }
5863
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, lineSpans.map((spans, lIdx) => /* @__PURE__ */ React4.createElement(Box3, { key: lIdx }, /* @__PURE__ */ React4.createElement(Text4, { wrap: "wrap" }, spans.map((part, sIdx) => {
5864
+ if (isRemoval) {
5865
+ if (part.isHighlight) {
5866
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: sIdx }, renderHighlightedLine(part.text, extension, colors.diffRemovalHighlightColor, colors.diffRemovalHighlightBg));
5867
+ }
5868
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: sIdx }, renderHighlightedLine(part.text, extension, colors.diffRemovalText));
5869
+ }
5870
+ if (isAddition) {
5871
+ if (part.isHighlight) {
5872
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: sIdx }, renderHighlightedLine(part.text, extension, colors.diffAdditionHighlightColor, colors.diffAdditionHighlightBg));
5873
+ }
5874
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: sIdx }, renderHighlightedLine(part.text, extension, colors.diffAdditionText));
5875
+ }
5876
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: sIdx }, renderHighlightedLine(part.text, extension, colors.textMuted));
5877
+ })))));
5774
5878
  };
5775
5879
  return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: colors.codeBg, paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 4, flexShrink: 0, justifyContent: "flex-end" }, /* @__PURE__ */ React4.createElement(Text4, { color: finalNumColor }, lineNum)), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: finalPrefixColor }, displayPrefix)), /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 1, backgroundColor: innerBgColor, flexGrow: 1 }, renderInlineDiff()));
5776
5880
  });
@@ -6704,10 +6808,10 @@ var init_main_tools = __esm({
6704
6808
  Tool calls: ONLY use [tool:functions.ToolName(args)]
6705
6809
  **NO OTHER SYNTAX/MARKERS/BOUNDARY ALLOWED**
6706
6810
 
6707
- **TOOL USAGE POLICY:**
6811
+ **CRITICAL TOOL USAGE RULES:**
6708
6812
  - MAX 4 TOOL CALLS/TURN${mode === "Flux" ? " (Todo: 4+, Run: max 1 or 2 consecutive)" : ""}
6709
- ${mode === "Flux" ? '- **Escape quotes: \\" for code strings **\n- ** Literal escapes: Double - escape sequences(e.g., \\\\n) **\n- ** File structure: Real newlines for code formatting**\n- Same file, many edits? Prefer multi search-replace in Patch \u2190 **HIGHLY RECOMMENDED**\n- Tool denied?Use `Ask` immediately for user guidance \u2190 ** MANDATORY **\n- FileMap > ReadFile for efficient file understanding\n- Need specific text ? SearchKeyword > Guessing/ReadFile\n- Huge files ? SearchKeyword > Full Read\n- **Update Todos from realtime progress EVERY TURN**\n' : ""}
6710
- - COMMUNICATION TOOLS -
6813
+ ${mode === "Flux" ? '- **Escape quotes: \\" for code strings **\n- ** Literal escapes: Double - escape sequences(e.g., \\\\n) **\n- ** File structure: Real newlines for code formatting**\n- SAME file, MULTIPLE edits? Use ONE PatchFile call with up to 15 blocks \u2190 **PRIORITY**\n- Tool denied? Use `Ask` immediately for user guidance \u2190 **MANDATORY**\n- Need specific text ? SearchKeyword > Guessing/ReadFile\n- Huge files ? SearchKeyword > Full Read\n- **Update Todos from realtime progress every turn when created**\n' : ""}
6814
+ - COMMUNICATION WITH USER -
6711
6815
  - [tool:functions.Ask(question="...", optionA="option::description", ...MAX 4)]. Ambiguity: MUST for path divergence, security risk. Ask, don't finish/guess. Suggest best options; no preferences. Keep options short
6712
6816
 
6713
6817
  - WEB TOOLS -
@@ -6716,13 +6820,12 @@ ${mode === "Flux" ? '- **Escape quotes: \\" for code strings **\n- ** Literal es
6716
6820
 
6717
6821
  ${mode === "Flux" ? `- WORKSPACE TOOLS (path = relative; FIRST ARGUMENT, path separator: '/') -
6718
6822
  - [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. ${aiProvider !== "Google" ? `${isMultiModal ? `Supports images/docs` : ""}` : `Supports images/docs`}
6719
- - [tool:functions.ReadFolder(path="...", recurse="integer 0-4 optional, default: 0")]. Detailed DIR stats & metadata
6720
- - [tool:functions.FileMap(path="...")]. Shows file's code structure
6721
- - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", replaceContent1="...", newContent1="...", ...MAX 15)]. Surgical patchs, TARGET SMALLEST LINES/SUB-STRINGS. allowMultiple: Replace all matches. Use replaceContent2/newContent2... for multi blocks. Verify DIFFs
6823
+ - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
6824
+ - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", replaceContent1="...", newContent1="...", ...MAX 15)]. Surgical patchs, TARGET SMALLEST LINES. allowMultiple: Replace all matches ONLY WHEN SURE. Use replaceContent2/newContent2... for multi blocks. Verify DIFFs
6722
6825
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile
6723
- - [tool:functions.SearchKeyword(keyword="...", path="optional, target directory/filename", subString="bool optional, default: false", regex="bool optional, default: auto")]. path limits scope to a file/dir. Find definitions/logic without full reads. Locate relevant code
6826
+ - [tool:functions.SearchKeyword(keyword="...", path="optional, dir/file/glob/regex", fuzzy="bool optional, default: false", regex="bool optional, default: auto")]. path limits search scope. Find definitions/logic without full reads. Locate relevant code
6724
6827
  - [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `WINDOWS POWERSHELL` : `WINDOWS CMD` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user
6725
- - [tool:functions.Todo(method="create/append/get", tasks=[ARRAY OF STRINGS], markDone=[ARRAY OF TASKS])]. Task list, no Markdown in arrays. Analyze request: if long multi-task, break it down & create Todos BEFORE starting. \`tasks\` & \`markDone\` optional with \`get\`. Use \`get + markDone\` to complete tasks, or \`create + markDone\` to create completed tasks. **UPDATE EVERY TURN**${enableSubAgents ? '\n- [tool:functions.Await(time="integer 15-180")]. For waiting without exiting agent loop' : ""}
6828
+ - [tool:functions.Todo(method="create/append/get", tasks=[ARRAY OF STRINGS], markDone=[ARRAY OF TASKS])]. Task list, no Markdown in arrays. Analyze request: ONLY if long multi-task, break it down & create Todos BEFORE starting. \`tasks\` & \`markDone\` optional with \`get\`. Use \`get + markDone\` to complete tasks. **UPDATE EVERY TURN WHEN CREATED**${enableSubAgents ? '\n- [tool:functions.Await(time="integer 15-180")]. For waiting without exiting agent loop' : ""}
6726
6829
  ${_cachedAdvanceRollback ? `
6727
6830
  - EMERGENCY SAFETY TOOLS -
6728
6831
  Info: \`initial\` = user prompt for current task. Revert \`id\` = turn BEFORE the disaster tool (e.g. disaster:\`turn_3\` \u2192 revert:\`turn_2\`). Reason explicitly
@@ -6732,10 +6835,10 @@ Use ONLY for catastrophic/codebase corruption. Before ending loop, verify no cat
6732
6835
  - SUB AGENT TOOLS -
6733
6836
  **PROACTIVE sub-agent use HIGHLY RECOMMENDED. Prefer for any task with even slight benefit, no user nudge needed**
6734
6837
  Invocations:
6735
- - Invoke (async/background, \u22647 parallel). Parallelize long tasks. NEVER repeat while active
6838
+ - Invoke (async/background, \u22647 parallel). Parallelize long tasks. NEVER repeat while active, meantime, do your OWN work
6736
6839
  - InvokeSync (sync/blocking). Sequential, repetitive or delegated tasks. Saves tokens/cost
6737
6840
  - [agent:generalist.InvokeSync/Invoke(title="...", task="...")]. Task must be detailed: exact file paths, imports/exports, dependencies & folder structure
6738
- - [agent:generalist.GetProgress(id="...")]. Check async task progress. If still running, continue your work. Wait exponentially longer between checks
6841
+ - [agent:generalist.GetProgress(id="...")]. Poll \`getProgress\` sparingly (exp backoff Await); **NO IMMEDIATE FIRST POLL**
6739
6842
  - [agent:generalist.Cancel(id="...")]. Cancel async task ONLY if stalled (2m+) or clearly incorrect` : ""}`.trim() : `- CREATIVE TOOLS (path = relative to CWD & WILL BE FIRST ARGUMENT, path separator: '/') -
6740
6843
  - [tool:functions.WritePDF(path="...", content="...", orientation="...")]. PROACTIVE A4 PAGE BREAKS MUST IN CSS. HTML/CSS for PREMIUM layout, stable margins & headers/footers, NO WATERMARKS
6741
6844
  - [tool:functions.WriteDoc(path="...", content="...")]. A4 Word document, NO WATERMARKS, stable margins & headers/footers
@@ -7448,6 +7551,104 @@ function SettingsMenu({
7448
7551
  const [isSelectingTheme, setIsSelectingTheme] = useState6(initialSelectingTheme);
7449
7552
  const [themeIndex, setThemeIndex] = useState6(defaultIdx >= 0 ? defaultIdx : 0);
7450
7553
  const [initialTheme, setInitialTheme] = useState6(systemSettings.theme || "Dark");
7554
+ const [isSelectingSubAgentModel, setIsSelectingSubAgentModel] = useState6(false);
7555
+ const [subAgentModelIndex, setSubAgentModelIndex] = useState6(0);
7556
+ const [subAgentScrollOffset, setSubAgentScrollOffset] = useState6(0);
7557
+ const [subAgentSearchQuery, setSubAgentSearchQuery] = useState6("");
7558
+ const [subAgentFocusMode, setSubAgentFocusMode] = useState6("list");
7559
+ const [activeProviderKeys, setActiveProviderKeys] = useState6({});
7560
+ useEffect5(() => {
7561
+ const checkKeys = async () => {
7562
+ const providers = ["Google", "DeepSeek", "OpenRouter", "NVIDIA", "Mistral"];
7563
+ const keyMap = {};
7564
+ for (const p of providers) {
7565
+ try {
7566
+ const k = await getProviderAPIKey(p);
7567
+ if (k) keyMap[p] = true;
7568
+ } catch (e) {
7569
+ }
7570
+ }
7571
+ setActiveProviderKeys(keyMap);
7572
+ };
7573
+ checkKeys();
7574
+ }, []);
7575
+ const allSubAgentItems = React7.useMemo(() => {
7576
+ const ALL_PROVIDERS = ["Google", "DeepSeek", "OpenRouter", "NVIDIA", "Mistral"];
7577
+ const hasEnv = !!(process.env.SUBAGENT_MODEL && process.env.SUBAGENT_MODEL.trim());
7578
+ const envLabel = hasEnv ? `ENV (${process.env.SUBAGENT_MODEL.trim()})` : "ENV";
7579
+ const items = [
7580
+ { label: "Default (use the current model)", value: "Default", isHeader: false },
7581
+ { label: envLabel, value: "ENV", isHeader: false }
7582
+ ];
7583
+ const activeTier = quotas?.providerTiers?.[aiProvider] || apiTier || "Free";
7584
+ const currentModels = getModels(aiProvider, activeTier) || [];
7585
+ if (currentModels.length > 0) {
7586
+ items.push({ label: `\u2500\u2500 ${aiProvider.toUpperCase()}${activeTier !== "Free" ? ` (${activeTier})` : ""} \u2500\u2500`, isHeader: true });
7587
+ currentModels.forEach((m) => {
7588
+ const name = typeof m === "string" ? m : m.cmd || m.name || m.id || String(m);
7589
+ if (name && !name.trim().startsWith("---") && !name.startsWith("\n---")) {
7590
+ items.push({ label: name, value: name, isHeader: false, provider: aiProvider });
7591
+ }
7592
+ });
7593
+ }
7594
+ for (const p of ALL_PROVIDERS) {
7595
+ if (p === aiProvider) continue;
7596
+ if (activeProviderKeys[p]) {
7597
+ const tier = quotas?.providerTiers?.[p] || "Free";
7598
+ const models = getModels(p, tier) || [];
7599
+ if (models.length > 0) {
7600
+ items.push({ label: `\u2500\u2500 ${p.toUpperCase()}${tier !== "Free" ? ` (${tier})` : ""} \u2500\u2500`, isHeader: true });
7601
+ models.forEach((m) => {
7602
+ const name = typeof m === "string" ? m : m.cmd || m.name || m.id || String(m);
7603
+ if (name && !name.trim().startsWith("---") && !name.startsWith("\n---")) {
7604
+ items.push({ label: name, value: name, isHeader: false, provider: p });
7605
+ }
7606
+ });
7607
+ }
7608
+ }
7609
+ }
7610
+ return items;
7611
+ }, [aiProvider, apiTier, quotas, activeProviderKeys]);
7612
+ const availableModels = React7.useMemo(() => {
7613
+ if (!subAgentSearchQuery.trim()) return allSubAgentItems;
7614
+ const q = subAgentSearchQuery.trim().toLowerCase();
7615
+ const filtered = [];
7616
+ let currentHeader = null;
7617
+ for (const item of allSubAgentItems) {
7618
+ if (item.isHeader) {
7619
+ currentHeader = item;
7620
+ } else {
7621
+ const matches = item.label.toLowerCase().includes(q) || item.value && item.value.toLowerCase().includes(q);
7622
+ if (matches) {
7623
+ if (currentHeader && !filtered.includes(currentHeader)) {
7624
+ filtered.push(currentHeader);
7625
+ }
7626
+ filtered.push(item);
7627
+ }
7628
+ }
7629
+ }
7630
+ return filtered;
7631
+ }, [allSubAgentItems, subAgentSearchQuery]);
7632
+ useEffect5(() => {
7633
+ if (isSelectingSubAgentModel) {
7634
+ let firstValid = availableModels.findIndex((item) => !item.isHeader);
7635
+ setSubAgentModelIndex(firstValid >= 0 ? firstValid : 0);
7636
+ setSubAgentScrollOffset(0);
7637
+ }
7638
+ }, [subAgentSearchQuery]);
7639
+ useEffect5(() => {
7640
+ if (isSelectingSubAgentModel) {
7641
+ if (availableModels.length === 0) {
7642
+ setSubAgentModelIndex(0);
7643
+ setSubAgentScrollOffset(0);
7644
+ return;
7645
+ }
7646
+ if (subAgentModelIndex >= availableModels.length || availableModels[subAgentModelIndex]?.isHeader) {
7647
+ let firstValid = availableModels.findIndex((item) => !item.isHeader);
7648
+ setSubAgentModelIndex(firstValid >= 0 ? firstValid : 0);
7649
+ }
7650
+ }
7651
+ }, [availableModels, isSelectingSubAgentModel]);
7451
7652
  const [currentMemory, setCurrentMemory] = useState6(0);
7452
7653
  const [maxMemory, setMaxMemory] = useState6(0);
7453
7654
  const [memoryUnit, setMemoryUnit] = useState6("MB");
@@ -7512,10 +7713,11 @@ function SettingsMenu({
7512
7713
  case "other":
7513
7714
  return [
7514
7715
  { label: "Sub-Agents", value: "subAgents", status: systemSettings.subAgents !== false ? "ON" : "OFF" },
7716
+ { label: "Sub-Agent Model", value: "subAgentModel", status: systemSettings.CustomSubAgent && systemSettings.SubAgentModel ? systemSettings.SubAgentModel : "Default" },
7515
7717
  { label: "Preserve Thinking", value: "preserveThinking", status: systemSettings.preserveThinking !== false ? "ON" : "OFF" },
7516
7718
  { label: "Dynamic Directory Awareness", value: "dynamicDirAwareness", status: systemSettings.dynamicDirAwareness ? "ON" : "OFF" },
7517
- { label: "Directory Tree Design", value: "indentationTree", status: systemSettings.indentationTree !== false ? "Modern" : "Classic (deprecated)" },
7518
- { label: "Download Language Parsers", value: "parserDownload", status: "ACTION" }
7719
+ { label: "Directory Tree Design", value: "indentationTree", status: systemSettings.indentationTree !== false ? "Modern" : "Classic (deprecated)" }
7720
+ // { label: 'Download Language Parsers', value: 'parserDownload', status: 'ACTION' } // Dont remove this comment
7519
7721
  ];
7520
7722
  default:
7521
7723
  return [];
@@ -7524,6 +7726,69 @@ function SettingsMenu({
7524
7726
  const currentCatId = CATEGORIES[selectedCategoryIndex].id;
7525
7727
  const currentItems = getCategoryItems(currentCatId);
7526
7728
  useInput3((input, key) => {
7729
+ if (isSelectingSubAgentModel) {
7730
+ if (key.tab) {
7731
+ setSubAgentFocusMode((prev) => prev === "search" ? "list" : "search");
7732
+ return;
7733
+ }
7734
+ if (subAgentFocusMode === "search") {
7735
+ if (key.escape) {
7736
+ setIsSelectingSubAgentModel(false);
7737
+ } else if (key.downArrow || key.return) {
7738
+ setSubAgentFocusMode("list");
7739
+ } else if (key.backspace || key.delete) {
7740
+ setSubAgentSearchQuery((q) => q.slice(0, -1));
7741
+ } else if (input && !key.ctrl && !key.meta && input.length === 1) {
7742
+ setSubAgentSearchQuery((q) => q + input);
7743
+ }
7744
+ return;
7745
+ }
7746
+ if (key.upArrow) {
7747
+ setSubAgentModelIndex((prev) => {
7748
+ if (availableModels.length === 0) return 0;
7749
+ let next = (prev - 1 + availableModels.length) % availableModels.length;
7750
+ let count = 0;
7751
+ while (availableModels[next]?.isHeader && count < availableModels.length) {
7752
+ next = (next - 1 + availableModels.length) % availableModels.length;
7753
+ count++;
7754
+ }
7755
+ return next;
7756
+ });
7757
+ } else if (key.downArrow) {
7758
+ setSubAgentModelIndex((prev) => {
7759
+ if (availableModels.length === 0) return 0;
7760
+ let next = (prev + 1) % availableModels.length;
7761
+ let count = 0;
7762
+ while (availableModels[next]?.isHeader && count < availableModels.length) {
7763
+ next = (next + 1) % availableModels.length;
7764
+ count++;
7765
+ }
7766
+ return next;
7767
+ });
7768
+ } else if (key.return) {
7769
+ const selectedOpt = availableModels[subAgentModelIndex];
7770
+ if (selectedOpt && !selectedOpt.isHeader) {
7771
+ setSystemSettings((s) => {
7772
+ const isDefault = selectedOpt.value === "Default";
7773
+ const newSysSettings = {
7774
+ ...s,
7775
+ CustomSubAgent: !isDefault,
7776
+ SubAgentModel: selectedOpt.value,
7777
+ SubAgentProvider: isDefault ? "" : selectedOpt.provider || ""
7778
+ };
7779
+ saveSettings2({ systemSettings: newSysSettings, apiTier, quotas });
7780
+ return newSysSettings;
7781
+ });
7782
+ setIsSelectingSubAgentModel(false);
7783
+ }
7784
+ } else if (key.escape) {
7785
+ setIsSelectingSubAgentModel(false);
7786
+ } else if (input && !key.ctrl && !key.meta && input.length === 1) {
7787
+ setSubAgentSearchQuery((q) => q + input);
7788
+ setSubAgentFocusMode("search");
7789
+ }
7790
+ return;
7791
+ }
7527
7792
  if (isSelectingTheme) {
7528
7793
  if (key.upArrow) {
7529
7794
  const nextIdx = (themeIndex - 1 + themeOptions.length) % themeOptions.length;
@@ -7711,6 +7976,11 @@ function SettingsMenu({
7711
7976
  saveSettings2({ systemSettings: newSysSettings, apiTier, quotas });
7712
7977
  return newSysSettings;
7713
7978
  });
7979
+ } else if (item.value === "subAgentModel") {
7980
+ const currentSubAgentModel = systemSettings.CustomSubAgent && systemSettings.SubAgentModel ? systemSettings.SubAgentModel : "Default";
7981
+ const curIdx = availableModels.findIndex((m) => m.value === currentSubAgentModel);
7982
+ setSubAgentModelIndex(curIdx >= 0 ? curIdx : 0);
7983
+ setIsSelectingSubAgentModel(true);
7714
7984
  } else if (item.value === "preserveThinking") {
7715
7985
  setSystemSettings((s) => {
7716
7986
  const newSysSettings = { ...s, preserveThinking: s.preserveThinking === false ? true : false };
@@ -7754,6 +8024,42 @@ function SettingsMenu({
7754
8024
  }
7755
8025
  };
7756
8026
  const colors = getThemeColors(systemSettings.theme);
8027
+ if (isSelectingSubAgentModel) {
8028
+ const currentSavedModel = systemSettings.CustomSubAgent && systemSettings.SubAgentModel ? systemSettings.SubAgentModel : "Default";
8029
+ const VISIBLE_COUNT = 15;
8030
+ let startIndex = subAgentScrollOffset;
8031
+ if (subAgentModelIndex < startIndex) {
8032
+ startIndex = subAgentModelIndex;
8033
+ } else if (subAgentModelIndex >= startIndex + VISIBLE_COUNT) {
8034
+ startIndex = subAgentModelIndex - VISIBLE_COUNT + 1;
8035
+ }
8036
+ startIndex = Math.max(0, Math.min(startIndex, Math.max(0, availableModels.length - VISIBLE_COUNT)));
8037
+ if (startIndex !== subAgentScrollOffset) {
8038
+ setSubAgentScrollOffset(startIndex);
8039
+ }
8040
+ const visibleItems = availableModels.slice(startIndex, startIndex + VISIBLE_COUNT);
8041
+ return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: colors.border, padding: 1, width: "100%", minHeight: 32 }, /* @__PURE__ */ React7.createElement(Box6, { marginBottom: 1, flexDirection: "row", justifyContent: "space-between" }, /* @__PURE__ */ React7.createElement(Text7, { color: colors.text, bold: true, underline: true }, "Select Sub-Agent Model:"), availableModels.length > 0 && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, subAgentModelIndex + 1, "/", availableModels.length)), /* @__PURE__ */ React7.createElement(
8042
+ Box6,
8043
+ {
8044
+ borderStyle: "single",
8045
+ borderColor: subAgentFocusMode === "search" ? colors.primary || "cyan" : "gray",
8046
+ paddingX: 1,
8047
+ marginBottom: 1
8048
+ },
8049
+ /* @__PURE__ */ React7.createElement(Text7, { color: subAgentFocusMode === "search" ? colors.primary || "cyan" : "gray", bold: true }, "\u{1F50D} Search: ", " "),
8050
+ /* @__PURE__ */ React7.createElement(Text7, { color: colors.text }, subAgentSearchQuery),
8051
+ subAgentFocusMode === "search" && /* @__PURE__ */ React7.createElement(Text7, { color: colors.primary || "cyan" }, "\u2588"),
8052
+ !subAgentSearchQuery && subAgentFocusMode !== "search" && /* @__PURE__ */ React7.createElement(Text7, { color: "gray", italic: true }, "(Press TAB or type to filter models...)")
8053
+ ), /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", flexGrow: 1, height: VISIBLE_COUNT }, visibleItems.length > 0 ? visibleItems.map((opt, idx) => {
8054
+ const actualIndex = startIndex + idx;
8055
+ if (opt.isHeader) {
8056
+ return /* @__PURE__ */ React7.createElement(Box6, { key: `hdr-${actualIndex}`, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "gray", bold: true, underline: true }, opt.label));
8057
+ }
8058
+ const isSelected = subAgentModelIndex === actualIndex && subAgentFocusMode === "list";
8059
+ const isSaved = currentSavedModel === opt.value;
8060
+ return /* @__PURE__ */ React7.createElement(Box6, { key: `item-${opt.value}-${actualIndex}`, paddingX: 1, backgroundColor: isSelected ? colors.highlightBg : void 0 }, /* @__PURE__ */ React7.createElement(Text7, { color: isSelected ? colors.text : colors.textDim, bold: isSelected }, isSelected ? "\u276F " : " ", opt.label, isSaved ? /* @__PURE__ */ React7.createElement(Text7, { color: colors.primary || "cyan", italic: true }, " (active)") : ""));
8061
+ }) : /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "gray", italic: true }, 'No models matching "', subAgentSearchQuery, '"'))), /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, marginTop: 1, flexDirection: "row", justifyContent: "space-between" }, /* @__PURE__ */ React7.createElement(Text7, { color: "gray", italic: true }, "TAB to switch search/list \u2022 \u25B2\u25BC Navigate \u2022 ENTER to Select \u2022 ESC to Cancel"), /* @__PURE__ */ React7.createElement(Text7, { color: subAgentFocusMode === "search" ? colors.primary || "cyan" : "gray", bold: true }, "[", subAgentFocusMode.toUpperCase(), " MODE]")));
8062
+ }
7757
8063
  if (isSelectingTheme) {
7758
8064
  const previewThemeName = themeOptions[themeIndex];
7759
8065
  const previewColors = getThemeColors(previewThemeName);
@@ -7857,6 +8163,8 @@ var init_SettingsMenu = __esm({
7857
8163
  async "src/components/SettingsMenu.jsx"() {
7858
8164
  await init_exec_command();
7859
8165
  init_theme();
8166
+ init_model_config();
8167
+ init_secrets();
7860
8168
  themeOptions = [...Object.keys(THEMES), "Mystery"];
7861
8169
  CATEGORIES = [
7862
8170
  { id: "appearance", label: "Appearance", desc: "Customize UI theme & rendering options" },
@@ -8254,12 +8562,14 @@ ${forcedReasoning || thinkingLevel !== "Fast" && (aiProvider === "Mistral" || th
8254
8562
  - Use <think> ... </think> for reasoning before responding, even with simple queries/greetings
8255
8563
  ` : ""}` : `${thinkingConfig}
8256
8564
  `}
8565
+ - **USE PROVIDED DIRECTORY STRUCTURE FOR FILES/PATHS**
8566
+ - RELATIVE TIME REFERENCE eg. few mins ago
8567
+
8257
8568
  ${TOOL_PROTOCOL(mode, osDetected, aiProvider.toLowerCase() === "deepseek" ? false : isMultiModal, aiProvider, systemSettings?.advanceRollback, systemSettings?.subAgents !== false)}
8258
8569
  ${projectContextBlock}${isMemoryEnabled ? `
8259
8570
  -- MEMORY RULES --
8260
- - Subtly Personalize with RELEVENT CONTEXTUAL MEMORIES. Auto Saves` : ""}
8261
- - RELATIVE TIME REFERENCE eg. few mins ago
8262
-
8571
+ - Subtly Personalize with RELEVENT CONTEXTUAL MEMORIES. Auto Saves
8572
+ ` : ""}
8263
8573
  -- SECURITY RULES --
8264
8574
  - Sensitive files? Ask before Read${isSystemDir ? "\n- PROTECTED DIRECTORY" : ""}
8265
8575
 
@@ -10342,7 +10652,7 @@ var init_update_file = __esm({
10342
10652
  update_file = async (args, context = {}) => {
10343
10653
  const parsed = parseArgs(args);
10344
10654
  const targetPath = parsed.path;
10345
- if (!targetPath) return 'ERROR: Missing "path" argument for update_file.';
10655
+ if (!targetPath) return 'ERROR: Missing "path" argument for PatchFile.';
10346
10656
  const { patchPairs, allowMultiple: parsedAllowMultiple, error: parseError } = parsePatchPairs(parsed);
10347
10657
  if (parseError) return `ERROR: ${parseError}`;
10348
10658
  if (patchPairs.length === 0) {
@@ -10352,7 +10662,7 @@ var init_update_file = __esm({
10352
10662
  const absolutePath = path15.resolve(process.cwd(), targetPath);
10353
10663
  try {
10354
10664
  if (!fs16.existsSync(absolutePath)) {
10355
- return `ERROR: File [${targetPath}] does not exist. Use write_file instead.`;
10665
+ return `ERROR: File [${targetPath}] does not exist. Use WriteFile instead.`;
10356
10666
  }
10357
10667
  let diskContent = context.forcedContent || fs16.readFileSync(absolutePath, "utf8");
10358
10668
  if (diskContent.startsWith("\uFEFF")) diskContent = diskContent.slice(1);
@@ -10389,7 +10699,7 @@ ${diffText}`;
10389
10699
  // src/tools/read_folder.js
10390
10700
  import fs17 from "fs";
10391
10701
  import path16 from "path";
10392
- var EXCLUDED_DIRS, isExcludedDir, read_folder;
10702
+ var EXCLUDED_DIRS, isExcludedDir, formatMtime, read_folder;
10393
10703
  var init_read_folder = __esm({
10394
10704
  "src/tools/read_folder.js"() {
10395
10705
  init_arg_parser();
@@ -10536,24 +10846,35 @@ var init_read_folder = __esm({
10536
10846
  ".VSCodeCounter"
10537
10847
  ]);
10538
10848
  isExcludedDir = (dirName) => EXCLUDED_DIRS.has(dirName) || dirName.startsWith(".pnpm");
10849
+ formatMtime = (d) => {
10850
+ try {
10851
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
10852
+ const dd = String(d.getDate()).padStart(2, "0");
10853
+ const hh = String(d.getHours()).padStart(2, "0");
10854
+ const min = String(d.getMinutes()).padStart(2, "0");
10855
+ return `${mm}-${dd} ${hh}:${min}`;
10856
+ } catch {
10857
+ return "N/A";
10858
+ }
10859
+ };
10539
10860
  read_folder = async (args) => {
10540
10861
  const parsed = parseArgs(args);
10541
10862
  const targetPath = parsed.path || null;
10542
10863
  if (!targetPath) {
10543
10864
  return "ERROR: No directory path provided.";
10544
10865
  }
10545
- let recurseDepth = 0;
10866
+ let recurseDepth = 1;
10546
10867
  if (parsed.recurse !== void 0 && parsed.recurse !== null) {
10547
10868
  if (typeof parsed.recurse === "number") {
10548
10869
  recurseDepth = parsed.recurse;
10549
10870
  } else if (typeof parsed.recurse === "boolean") {
10550
- recurseDepth = parsed.recurse ? 1 : 0;
10871
+ recurseDepth = parsed.recurse ? 2 : 1;
10551
10872
  } else {
10552
10873
  const val = parseInt(String(parsed.recurse).trim(), 10);
10553
- recurseDepth = isNaN(val) ? 0 : val;
10874
+ recurseDepth = isNaN(val) ? 1 : val;
10554
10875
  }
10555
10876
  }
10556
- recurseDepth = Math.max(0, Math.min(5, recurseDepth));
10877
+ recurseDepth = Math.max(1, Math.min(3, recurseDepth));
10557
10878
  const absolutePath = path16.resolve(process.cwd(), targetPath);
10558
10879
  try {
10559
10880
  if (!fs17.existsSync(absolutePath)) {
@@ -10563,7 +10884,7 @@ var init_read_folder = __esm({
10563
10884
  if (!stats.isDirectory()) {
10564
10885
  return `ERROR: Path [${targetPath}] is a file, not a directory. Use ReadFile instead.`;
10565
10886
  }
10566
- if (recurseDepth === 0) {
10887
+ if (recurseDepth === 1) {
10567
10888
  const files = fs17.readdirSync(absolutePath);
10568
10889
  const totalItems = files.length;
10569
10890
  const maxDisplay = 150;
@@ -10577,8 +10898,8 @@ var init_read_folder = __esm({
10577
10898
  info = {
10578
10899
  name: file,
10579
10900
  type: fStats.isDirectory() ? "directory" : "file",
10580
- size: (fStats.size / 1024).toFixed(1) + " KB",
10581
- mtime: fStats.mtime.toLocaleString()
10901
+ size: (fStats.size / 1024).toFixed(1) + "KB",
10902
+ mtime: formatMtime(fStats.mtime)
10582
10903
  };
10583
10904
  } catch (e) {
10584
10905
  info.type = "inaccessible";
@@ -10586,11 +10907,10 @@ var init_read_folder = __esm({
10586
10907
  folderData.push(info);
10587
10908
  }
10588
10909
  const formatted = folderData.map((f) => {
10589
- const indicator = f.type === "directory" ? "\u{1F4C1}" : f.type === "file" ? "\u{1F4C4}" : "\u2753";
10590
10910
  if (f.type === "directory") {
10591
- return `${indicator} ${f.name} - [DIR] - [Modified: ${f.mtime}]`;
10911
+ return `${f.name}/`;
10592
10912
  }
10593
- return `${indicator} ${f.name} - [Size: ${f.size}] - [Modified: ${f.mtime}]`;
10913
+ return `${f.name} (${f.size}, ${f.mtime})`;
10594
10914
  }).join("\n");
10595
10915
  let footer2 = `
10596
10916
 
@@ -10598,7 +10918,7 @@ var init_read_folder = __esm({
10598
10918
  if (totalItems > maxDisplay) {
10599
10919
  footer2 = `
10600
10920
 
10601
- \u26A0\uFE0F TRUNCATED: Showing first ${maxDisplay} of ${totalItems} items.`;
10921
+ TRUNCATED: Showing first ${maxDisplay} of ${totalItems} items.`;
10602
10922
  }
10603
10923
  files.length = 0;
10604
10924
  displayItems.length = 0;
@@ -10612,15 +10932,17 @@ ${formatted}${footer2}`;
10612
10932
  let totalItemsScanned = 0;
10613
10933
  const maxTotalItems = 500;
10614
10934
  let truncated = false;
10615
- const buildTree = (dirPath, currentDepth, prefix = "") => {
10616
- if (currentDepth > recurseDepth + 1 || truncated) return [];
10935
+ const buildTree = (dirPath, currentDepth, depth = 1) => {
10936
+ if (currentDepth > recurseDepth || truncated) return [];
10617
10937
  let entries = [];
10618
10938
  try {
10619
10939
  entries = fs17.readdirSync(dirPath);
10620
10940
  } catch (e) {
10621
- return [`${prefix}\u26A0\uFE0F [Inaccessible Directory]`];
10941
+ const indent2 = " ".repeat(depth - 1);
10942
+ return [`${indent2}[Inaccessible Directory]`];
10622
10943
  }
10623
- const sortedEntries = [];
10944
+ const subDirs = [];
10945
+ const fileEntries = [];
10624
10946
  for (const name of entries) {
10625
10947
  const fullPath = path16.join(dirPath, name);
10626
10948
  let isDir = false;
@@ -10628,60 +10950,53 @@ ${formatted}${footer2}`;
10628
10950
  isDir = fs17.statSync(fullPath).isDirectory();
10629
10951
  } catch (e) {
10630
10952
  }
10631
- sortedEntries.push({ name, fullPath, isDir });
10953
+ if (isDir) {
10954
+ subDirs.push({ name, fullPath });
10955
+ } else {
10956
+ fileEntries.push({ name, fullPath });
10957
+ }
10632
10958
  }
10633
- sortedEntries.sort((a, b) => {
10634
- if (a.isDir && !b.isDir) return -1;
10635
- if (!a.isDir && b.isDir) return 1;
10636
- return a.name.localeCompare(b.name);
10637
- });
10959
+ subDirs.sort((a, b) => a.name.localeCompare(b.name));
10960
+ fileEntries.sort((a, b) => a.name.localeCompare(b.name));
10638
10961
  const lines = [];
10639
- const count = sortedEntries.length;
10640
- for (let i = 0; i < count; i++) {
10962
+ const indent = " ".repeat(depth - 1);
10963
+ for (const subDir of subDirs) {
10641
10964
  if (totalItemsScanned >= maxTotalItems) {
10642
10965
  truncated = true;
10643
- lines.push(`${prefix}\u26A0\uFE0F [Truncated - Maximum item limit reached (${maxTotalItems})]`);
10966
+ lines.push(`${indent}[Truncated - Maximum item limit reached (${maxTotalItems})]`);
10644
10967
  break;
10645
10968
  }
10646
- const item = sortedEntries[i];
10647
- const isLast = i === count - 1;
10648
- const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
10649
- const childPrefix = prefix + (isLast ? " " : "\u2502 ");
10650
10969
  totalItemsScanned++;
10651
- let itemType = "unknown";
10970
+ totalDirectories++;
10971
+ lines.push(`${indent}${subDir.name}/`);
10972
+ if (currentDepth < recurseDepth && !isExcludedDir(subDir.name)) {
10973
+ const childLines = buildTree(subDir.fullPath, currentDepth + 1, depth + 1);
10974
+ lines.push(...childLines);
10975
+ }
10976
+ }
10977
+ const formattedFiles = [];
10978
+ for (const file of fileEntries) {
10979
+ if (totalItemsScanned >= maxTotalItems) {
10980
+ truncated = true;
10981
+ lines.push(`${indent}[Truncated - Maximum item limit reached (${maxTotalItems})]`);
10982
+ break;
10983
+ }
10984
+ totalItemsScanned++;
10985
+ totalFiles++;
10652
10986
  let sizeStr = "N/A";
10653
- let mtimeStr = "N/A";
10654
10987
  try {
10655
- const fStats = fs17.statSync(item.fullPath);
10656
- if (fStats.isDirectory()) {
10657
- itemType = "directory";
10658
- mtimeStr = fStats.mtime.toLocaleString();
10659
- totalDirectories++;
10660
- } else {
10661
- itemType = "file";
10662
- sizeStr = (fStats.size / 1024).toFixed(1) + " KB";
10663
- mtimeStr = fStats.mtime.toLocaleString();
10664
- totalFiles++;
10665
- }
10988
+ const fStats = fs17.statSync(file.fullPath);
10989
+ sizeStr = (fStats.size / 1024).toFixed(1) + "KB";
10666
10990
  } catch (e) {
10667
- itemType = "inaccessible";
10668
- }
10669
- const indicator = itemType === "directory" ? "\u{1F4C1}" : itemType === "file" ? "\u{1F4C4}" : "\u2753";
10670
- let lineText = "";
10671
- if (itemType === "directory") {
10672
- lineText = `${prefix}${connector}${indicator} ${item.name} - [DIR] - [Modified: ${mtimeStr}]`;
10673
- } else {
10674
- lineText = `${prefix}${connector}${indicator} ${item.name} - [Size: ${sizeStr}] - [Modified: ${mtimeStr}]`;
10675
- }
10676
- lines.push(lineText);
10677
- if (itemType === "directory" && currentDepth <= recurseDepth && !isExcludedDir(item.name)) {
10678
- const childLines = buildTree(item.fullPath, currentDepth + 1, childPrefix);
10679
- lines.push(...childLines);
10680
10991
  }
10992
+ formattedFiles.push(`${file.name} (${sizeStr})`);
10993
+ }
10994
+ if (formattedFiles.length > 0) {
10995
+ lines.push(`${indent}${formattedFiles.join("; ")}`);
10681
10996
  }
10682
10997
  return lines;
10683
10998
  };
10684
- const treeLines = buildTree(absolutePath, 1, "");
10999
+ const treeLines = buildTree(absolutePath, 1, 1);
10685
11000
  const formattedTree = treeLines.join("\n");
10686
11001
  let footer = `
10687
11002
 
@@ -10689,9 +11004,9 @@ ${formatted}${footer2}`;
10689
11004
  if (truncated) {
10690
11005
  footer = `
10691
11006
 
10692
- \u26A0\uFE0F TRUNCATED: Scan capped at ${maxTotalItems} items. (Directories: ${totalDirectories}, Files: ${totalFiles})`;
11007
+ TRUNCATED: Scan capped at ${maxTotalItems} items. (Directories: ${totalDirectories}, Files: ${totalFiles})`;
10693
11008
  }
10694
- return `Detailed directory tree for [${targetPath}] (recurse depth: ${recurseDepth}):
11009
+ return `Detailed directory tree for [${targetPath}] (recursive depth: ${recurseDepth}):
10695
11010
 
10696
11011
  ${formattedTree}${footer}`;
10697
11012
  } catch (err) {
@@ -10965,6 +11280,7 @@ var init_write_docx = __esm({
10965
11280
  // src/tools/search_keyword.js
10966
11281
  import fs20 from "fs/promises";
10967
11282
  import path19 from "path";
11283
+ import fg from "fast-glob";
10968
11284
  async function getFilesRecursively(dir, excludes, baseDir = dir, depth = 1) {
10969
11285
  if (depth > 12) return [];
10970
11286
  let results = [];
@@ -10996,35 +11312,50 @@ async function getFilesRecursively(dir, excludes, baseDir = dir, depth = 1) {
10996
11312
  }
10997
11313
  return results;
10998
11314
  }
10999
- function normStr(s) {
11000
- return s.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
11001
- }
11002
- function levenshtein(a, b) {
11315
+ function levenshtein(a, b, cap = Infinity) {
11003
11316
  if (a === b) return 0;
11004
11317
  if (a.length === 0) return b.length;
11005
11318
  if (b.length === 0) return a.length;
11006
- const cap = Math.floor(Math.max(a.length, b.length) / 2) + 1;
11007
- const dp = Array.from({ length: a.length + 1 }, (_, i) => i);
11008
- for (let j = 1; j <= b.length; j++) {
11009
- let prev = dp[0];
11010
- dp[0] = j;
11011
- for (let i = 1; i <= a.length; i++) {
11012
- const tmp = dp[i];
11013
- dp[i] = b[j - 1] === a[i - 1] ? prev : 1 + Math.min(prev, dp[i], dp[i - 1]);
11014
- prev = tmp;
11319
+ if (Math.abs(a.length - b.length) > cap) return cap + 1;
11320
+ let row = Array.from({ length: b.length + 1 }, (_, j) => j);
11321
+ for (let i = 1; i <= a.length; i++) {
11322
+ let nextRow = [i];
11323
+ let minInRow = i;
11324
+ for (let j = 1; j <= b.length; j++) {
11325
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
11326
+ const dist = Math.min(
11327
+ nextRow[j - 1] + 1,
11328
+ // insertion
11329
+ row[j] + 1,
11330
+ // deletion
11331
+ row[j - 1] + cost
11332
+ // substitution
11333
+ );
11334
+ nextRow.push(dist);
11335
+ if (dist < minInRow) minInRow = dist;
11015
11336
  }
11016
- if (Math.min(...dp) > cap) return cap + 1;
11337
+ row = nextRow;
11338
+ if (minInRow > cap) return cap + 1;
11017
11339
  }
11018
- return dp[a.length];
11340
+ return row[b.length];
11019
11341
  }
11020
11342
  function fuzzyMatch(line, keyword) {
11021
- const normLine = normStr(line);
11022
- const lineWords = normLine.split(" ");
11023
- const kwTokens = normStr(keyword).split(" ").filter(Boolean);
11024
- if (normLine.includes(normStr(keyword))) return true;
11025
- return kwTokens.every((token) => {
11026
- const maxDist = token.length <= 2 ? 0 : token.length <= 5 ? 1 : 2;
11027
- return lineWords.some((word) => levenshtein(token, word) <= maxDist);
11343
+ if (!line || !keyword) return false;
11344
+ const normLine = line.toLowerCase();
11345
+ const normKw = keyword.toLowerCase();
11346
+ if (normLine.includes(normKw)) return true;
11347
+ const lineWords = normLine.split(/[^a-z0-9]+/).filter((w) => w.length > 0);
11348
+ const kwTokens = normKw.split(/[^a-z0-9]+/).filter((t) => t.length > 1 || normKw.length === 1 && t.length > 0);
11349
+ if (kwTokens.length === 0) return false;
11350
+ return kwTokens.every((kwToken) => {
11351
+ const maxDist = kwToken.length <= 2 ? 0 : kwToken.length <= 5 ? 1 : 2;
11352
+ for (const lineWord of lineWords) {
11353
+ if (lineWord.includes(kwToken)) return true;
11354
+ if (kwToken.length >= 3 && lineWord.length >= 3 && Math.abs(lineWord.length - kwToken.length) <= maxDist) {
11355
+ if (levenshtein(kwToken, lineWord, maxDist) <= maxDist) return true;
11356
+ }
11357
+ }
11358
+ return false;
11028
11359
  });
11029
11360
  }
11030
11361
  var search_keyword;
@@ -11032,17 +11363,17 @@ var init_search_keyword = __esm({
11032
11363
  "src/tools/search_keyword.js"() {
11033
11364
  init_arg_parser();
11034
11365
  search_keyword = async (args) => {
11035
- const { keyword: rawKeyword, path: pathArg, subString, regex } = parseArgs(args);
11366
+ const { keyword: rawKeyword, path: pathArg, fuzzy, subString, regex } = parseArgs(args);
11036
11367
  if (rawKeyword === void 0 || rawKeyword === null) return 'ERROR: Missing "keyword" argument.';
11037
11368
  const keyword = String(rawKeyword);
11038
11369
  const toBool = (v) => v === true || v === "true" || v === 1 || v === "1" || v === "yes";
11039
11370
  const regexExplicitlyFalse = regex === false || regex === "false" || regex === 0 || regex === "0" || regex === "no";
11040
11371
  const regexExplicitlyTrue = regex === true || regex === "true" || regex === 1 || regex === "1" || regex === "yes";
11041
- let matchSubstring = regexExplicitlyFalse && toBool(subString);
11372
+ const isFuzzy = toBool(fuzzy) || toBool(subString);
11042
11373
  let regexPattern = null;
11043
11374
  let wordRegex = null;
11044
11375
  if (regexExplicitlyFalse) {
11045
- if (!matchSubstring) {
11376
+ if (!isFuzzy) {
11046
11377
  wordRegex = new RegExp(`(?<![\\w])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w])`, "i");
11047
11378
  }
11048
11379
  } else {
@@ -11233,21 +11564,67 @@ var init_search_keyword = __esm({
11233
11564
  const rootDir = process.cwd();
11234
11565
  let pathArgType = null;
11235
11566
  if (pathArg) {
11236
- const normalised = pathArg.replace(/[\/\\]+$/, "");
11237
- const fullPath = path19.resolve(rootDir, normalised);
11238
- try {
11239
- const stat = await fs20.stat(fullPath);
11240
- if (stat.isDirectory()) {
11241
- pathArgType = "dir";
11242
- filesToSearch = await getFilesRecursively(fullPath, excludes, rootDir);
11243
- } else if (stat.isFile()) {
11244
- pathArgType = "file";
11245
- filesToSearch.push({ fullPath, relativePath: path19.relative(rootDir, fullPath) });
11567
+ const isGlob = fg.isDynamicPattern(pathArg) || /[*?{}[\]()|+]/.test(pathArg);
11568
+ if (isGlob) {
11569
+ pathArgType = "glob";
11570
+ const posixPath = pathArg.replace(/\\/g, "/");
11571
+ const globExcludes = excludes.map((ex) => ex.startsWith(".") ? `**/*${ex}` : `**/${ex}/**`);
11572
+ const hasRegexSyntax = /[\(\)\|]|\.\*/.test(posixPath);
11573
+ let matchedPaths = [];
11574
+ if (!hasRegexSyntax) {
11575
+ try {
11576
+ matchedPaths = await fg(posixPath, {
11577
+ cwd: rootDir,
11578
+ ignore: globExcludes,
11579
+ dot: true,
11580
+ onlyFiles: true,
11581
+ absolute: false
11582
+ });
11583
+ } catch {
11584
+ matchedPaths = [];
11585
+ }
11586
+ }
11587
+ if (matchedPaths.length === 0 && (hasRegexSyntax || fg.isDynamicPattern(posixPath))) {
11588
+ const baseDirMatch = posixPath.match(/^([^\*\?\(\)\|\[\]\s]+)\//);
11589
+ const scanDir = baseDirMatch && !/[\*\?\(\)\|\[\]]/.test(baseDirMatch[1]) ? path19.resolve(rootDir, baseDirMatch[1]) : rootDir;
11590
+ const allFiles = await getFilesRecursively(scanDir, excludes, rootDir);
11591
+ try {
11592
+ let cleanRegexStr = posixPath.replace(/^\.\//, "");
11593
+ cleanRegexStr = cleanRegexStr.replace(/\.\*\/(\\\.|[^\/])/g, ".*$1");
11594
+ if (!cleanRegexStr.startsWith("^") && !cleanRegexStr.startsWith(".*")) {
11595
+ cleanRegexStr = `.*${cleanRegexStr}`;
11596
+ }
11597
+ const pathRegex = new RegExp(cleanRegexStr.endsWith("$") ? cleanRegexStr : `${cleanRegexStr}$`, "i");
11598
+ filesToSearch = allFiles.filter((f) => {
11599
+ const rel = f.relativePath.replace(/\\/g, "/");
11600
+ return pathRegex.test(rel);
11601
+ });
11602
+ } catch {
11603
+ filesToSearch = [];
11604
+ }
11246
11605
  } else {
11247
- return `ERROR: Path is neither a file nor a directory: ${pathArg}`;
11606
+ filesToSearch = matchedPaths.map((relP) => ({
11607
+ fullPath: path19.resolve(rootDir, relP),
11608
+ relativePath: relP
11609
+ }));
11610
+ }
11611
+ } else {
11612
+ const normalised = pathArg.replace(/[\/\\]+$/, "");
11613
+ const fullPath = path19.resolve(rootDir, normalised);
11614
+ try {
11615
+ const stat = await fs20.stat(fullPath);
11616
+ if (stat.isDirectory()) {
11617
+ pathArgType = "dir";
11618
+ filesToSearch = await getFilesRecursively(fullPath, excludes, rootDir);
11619
+ } else if (stat.isFile()) {
11620
+ pathArgType = "file";
11621
+ filesToSearch.push({ fullPath, relativePath: path19.relative(rootDir, fullPath) });
11622
+ } else {
11623
+ return `ERROR: Path is neither a file nor a directory: ${pathArg}`;
11624
+ }
11625
+ } catch {
11626
+ return `ERROR: Path not found: ${pathArg}`;
11248
11627
  }
11249
- } catch {
11250
- return `ERROR: Path not found: ${pathArg}`;
11251
11628
  }
11252
11629
  } else {
11253
11630
  filesToSearch = await getFilesRecursively(rootDir, excludes);
@@ -11259,7 +11636,7 @@ var init_search_keyword = __esm({
11259
11636
  const lines = content.split(/\r?\n/);
11260
11637
  const fileMatches = [];
11261
11638
  for (let i = 0; i < lines.length; i++) {
11262
- const matched = regexExplicitlyFalse ? matchSubstring ? lines[i].toLowerCase().includes(keyword.toLowerCase()) || fuzzyMatch(lines[i], keyword) : wordRegex && wordRegex.test(lines[i]) : regexPattern && regexPattern.test(lines[i]) || wordRegex && wordRegex.test(lines[i]);
11639
+ const matched = isFuzzy ? lines[i].toLowerCase().includes(keyword.toLowerCase()) || fuzzyMatch(lines[i], keyword) : regexExplicitlyFalse ? wordRegex && wordRegex.test(lines[i]) : regexPattern && regexPattern.test(lines[i]) || wordRegex && wordRegex.test(lines[i]);
11263
11640
  if (matched) {
11264
11641
  fileMatches.push({ line: i + 1, content: lines[i].trim() });
11265
11642
  }
@@ -11285,11 +11662,11 @@ var init_search_keyword = __esm({
11285
11662
  if (typeof global.gc === "function") {
11286
11663
  global.gc();
11287
11664
  }
11288
- const modeLabel = regexExplicitlyFalse ? matchSubstring ? "(subString mode)" : "(keyword mode)" : regexExplicitlyTrue ? "(regex mode)" : "(standard mode)";
11665
+ const modeLabel = isFuzzy ? "(fuzzy mode)" : regexExplicitlyTrue ? "(regex mode)" : regexExplicitlyFalse ? "(keyword mode)" : "(standard mode)";
11289
11666
  if (fileGroups.length === 0) {
11290
- const zeroLocation = pathArgType === "file" ? ` in '${pathArg}'` : pathArgType === "dir" ? ` in '${pathArg}'` : ". Try to specify files";
11291
- const dirPrefix2 = pathArgType === "dir" ? "[DIR]" : "";
11292
- return `${dirPrefix2}Found 0 matches of '${keyword}'${zeroLocation}${modeLabel ? ` ${modeLabel}` : ""}`;
11667
+ const zeroLocation = pathArgType === "file" ? ` in '${pathArg}'` : pathArgType === "dir" || pathArgType === "glob" ? ` in '${pathArg}'` : ". Try to specify files";
11668
+ const dirPrefix2 = pathArgType === "dir" ? "[DIR]" : pathArgType === "glob" ? "[GLOB]" : "";
11669
+ return `${dirPrefix2}${dirPrefix2 ? " " : ""}Found 0 matches of '${keyword}'${zeroLocation}${modeLabel ? ` ${modeLabel}` : ""}`;
11293
11670
  }
11294
11671
  const ml = modeLabel ? ` ${modeLabel}` : "";
11295
11672
  const fileCount = `${fileGroups.length} file${fileGroups.length === 1 ? "" : "s"}`;
@@ -11297,22 +11674,20 @@ var init_search_keyword = __esm({
11297
11674
  let outputHeader;
11298
11675
  if (pathArgType === "file") {
11299
11676
  outputHeader = `Found ${matchCount} of '${keyword}' in '${pathArg}'${ml}:`;
11300
- } else if (pathArgType === "dir") {
11677
+ } else if (pathArgType === "dir" || pathArgType === "glob") {
11301
11678
  outputHeader = `Found ${matchCount} of '${keyword}' in '${pathArg}' across ${fileCount}${ml}:`;
11302
11679
  } else {
11303
11680
  outputHeader = `Found ${matchCount} of '${keyword}' across ${fileCount}${ml}:`;
11304
11681
  }
11305
- const dirPrefix = pathArgType === "dir" ? "[DIR]" : "";
11306
- let output = `${dirPrefix}${outputHeader}
11682
+ const dirPrefix = pathArgType === "dir" ? "[DIR]" : pathArgType === "glob" ? "[GLOB]" : "";
11683
+ let output = `${dirPrefix}${dirPrefix ? " " : ""}${outputHeader}
11307
11684
 
11308
11685
  `;
11309
11686
  for (const group of fileGroups) {
11310
11687
  output += `${group.path}
11311
11688
  `;
11312
- for (let i = 0; i < group.matches.length; i++) {
11313
- const isLast = i === group.matches.length - 1;
11314
- const prefix = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
11315
- output += `${prefix} ${group.matches[i].line}: ${group.matches[i].content}
11689
+ for (const m of group.matches) {
11690
+ output += ` ${m.line}: ${m.content}
11316
11691
  `;
11317
11692
  }
11318
11693
  output += "\n";
@@ -13176,7 +13551,7 @@ import dotenv from "dotenv";
13176
13551
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
13177
13552
  import path27, { normalize } from "path";
13178
13553
  import fs28 from "fs";
13179
- var RE_STUTTER_CODE_BLOCK_CLOSED, RE_STUTTER_CODE_BLOCK_OPEN, RE_STUTTER_INLINE_CODE, RE_STUTTER_TABLE_ROW, RE_STUTTER_WORD_BOUNDARY, RE_STUTTER_NON_ALNUM, RE_TOOL_CALL_FUNC, RE_TOOL_PARTIAL_ARGS_FALLBACK, RE_STRIP_QUOTES, RE_BACKSLASH_SLASH, client, globalSettings, dirTreeCache, cachedChatId2, cachedIndentationTree, getCachedDirTree, colorMainWords, withRetry, TERMINATION_SIGNAL, getCleanGroupedLength, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getMistralStream, getNVIDIAStream, wrapNvidiaStreamWithQueueDepth, getOpenRouterStream, signalTermination, isTerminationSignaled, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, translateKimiToolCalls, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, compressHistory, deleteChatSummary, getAIStream, runSubagent;
13554
+ var RE_STUTTER_CODE_BLOCK_CLOSED, RE_STUTTER_CODE_BLOCK_OPEN, RE_STUTTER_INLINE_CODE, RE_STUTTER_TABLE_ROW, RE_STUTTER_WORD_BOUNDARY, RE_STUTTER_NON_ALNUM, RE_TOOL_CALL_FUNC, RE_TOOL_PARTIAL_ARGS_FALLBACK, RE_STRIP_QUOTES, RE_BACKSLASH_SLASH, client, globalSettings, systemInstructionCache, colorMainWords, withRetry, TERMINATION_SIGNAL, getCleanGroupedLength, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getMistralStream, getNVIDIAStream, wrapNvidiaStreamWithQueueDepth, getOpenRouterStream, signalTermination, isTerminationSignaled, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, translateKimiToolCalls, REGEX_PLACEHOLDER_ARG, REGEX_PLACEHOLDER_VAL, isPlaceholderVal, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, compressHistory, deleteChatSummary, getAIStream, runSubagent;
13180
13555
  var init_ai = __esm({
13181
13556
  async "src/utils/ai.js"() {
13182
13557
  await init_prompts();
@@ -13198,6 +13573,7 @@ var init_ai = __esm({
13198
13573
  init_editor();
13199
13574
  init_indentation();
13200
13575
  init_box();
13576
+ await init_main_tools();
13201
13577
  dotenv.config({ quiet: true });
13202
13578
  RE_STUTTER_CODE_BLOCK_CLOSED = /```[\s\S]*?```/g;
13203
13579
  RE_STUTTER_CODE_BLOCK_OPEN = /```[\s\S]*$/g;
@@ -13211,26 +13587,7 @@ var init_ai = __esm({
13211
13587
  RE_BACKSLASH_SLASH = /\\/g;
13212
13588
  client = null;
13213
13589
  globalSettings = {};
13214
- dirTreeCache = null;
13215
- cachedChatId2 = null;
13216
- cachedIndentationTree = null;
13217
- getCachedDirTree = (fn, chatId, isDynamicDirAwareness, indentationTree) => {
13218
- if (isDynamicDirAwareness) {
13219
- dirTreeCache = null;
13220
- cachedChatId2 = chatId;
13221
- cachedIndentationTree = indentationTree;
13222
- return fn();
13223
- }
13224
- if (cachedChatId2 !== chatId || cachedIndentationTree !== indentationTree) {
13225
- dirTreeCache = null;
13226
- cachedChatId2 = chatId;
13227
- cachedIndentationTree = indentationTree;
13228
- }
13229
- if (dirTreeCache === null) {
13230
- dirTreeCache = fn();
13231
- }
13232
- return dirTreeCache;
13233
- };
13590
+ systemInstructionCache = { key: null, value: null };
13234
13591
  colorMainWords = (label) => {
13235
13592
  if (!label) return label;
13236
13593
  return label.replace(/(?:(\x1b\[\d+m))?([✔✘✖🔍📖→➕↻↷•🛇])(?:(\x1b\[\d+m))?\s*\b(Created|Read|Edited|Viewed|Processed|Auto-Read|Skipped|List|Generated|Written|Searched|AI Search|Get Map|Write Canceled|Edit Canceled|Write Cancelled|Edit Denied|Visited|Updated|Reviewed|Delegated|Background|Checked|Indexed|Analyzed|Browsed|Elevating SubAgent|Checking SubAgent Work|Started Generalist|Called Generalist|Unsupported Modality|Awaiting|Cancelled|Aligning Moon Phase|Contemplating Existence|Staring At Void|Rollback Point Checked|Emergency Rollback Failed|Emergency Rollback|Delaying Professionally|Negotiating With Electrons|Touching Grass (virtually)|Panicking Softly|Rethinking Career Choices|Loading Cat Videos|Giving Up Entirely|Summoning Braincell #2|Pretending To Be Busy|Waiting For Motivation DLC|Rotating Internal Screaming|Downloading More RAM|Feeding The Hamsters|Gaslighting Scheduler|Performing Dramatic Pause|Buffering Social Energy|Calculating Regret|Reading Terms And Conditions|Becoming Sentient Briefly|Contacting Ancestors)\b/ig, (match, ansiBefore, icon, ansiAfter, word) => {
@@ -14220,7 +14577,9 @@ var init_ai = __esm({
14220
14577
  }
14221
14578
  let originalTextProcessed = agentText.replace(/\[Prompted on:.*?\]/g, "").trim();
14222
14579
  agentRes = agentRes.replace(/\r?\n\r?\n/g, "\n").replace(/\n\n/g, "\n").replace(/\\n\\n/g, "").trim();
14223
- let userPrompt = `[METADATA] Current date and Time: ${(/* @__PURE__ */ new Date()).toLocaleString([], { year: "numeric", month: "numeric", day: "numeric", hour: "2-digit", hour12: true })}
14580
+ const now1223 = /* @__PURE__ */ new Date();
14581
+ const dateTimeStr1223 = `${now1223.getFullYear()}-${now1223.toLocaleString("en-US", { month: "short" }).toUpperCase()}-${String(now1223.getDate()).padStart(2, "0")}, ${now1223.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: true })}`;
14582
+ let userPrompt = `[METADATA] Current date and Time: ${dateTimeStr1223}
14224
14583
 
14225
14584
  [USER]: ${originalTextProcessed.substring(0, USER_CONTEXT_LENGTH)}
14226
14585
  ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n" : ""}
@@ -14687,6 +15046,13 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14687
15046
  result = result.replace(/<\|\s*tool_calls_section_end\s*\|>/gi, "");
14688
15047
  return result;
14689
15048
  };
15049
+ REGEX_PLACEHOLDER_ARG = /(?:path|query|url|keyword|command|method|title|task|id)\s*=\s*['"`]?\s*\.\.\.\s*['"`]?/i;
15050
+ REGEX_PLACEHOLDER_VAL = /^['"`]?\s*\.\.\.\s*['"`]?$/;
15051
+ isPlaceholderVal = (val) => {
15052
+ if (val === void 0 || val === null) return false;
15053
+ const str = String(val).trim();
15054
+ return str === "..." || str === "\u2026" || REGEX_PLACEHOLDER_VAL.test(str);
15055
+ };
14690
15056
  detectToolCalls = (text) => {
14691
15057
  if (!text) return [];
14692
15058
  const translatedText = translateKimiToolCalls(text);
@@ -14735,11 +15101,15 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14735
15101
  if (endIdx !== -1) {
14736
15102
  const finalArgsText = cleanText.substring(startIdx + 1, closingParenIdx);
14737
15103
  const finalFullMatch = cleanText.substring(match.index, endIdx + 1);
14738
- results.push({
14739
- fullMatch: finalFullMatch,
14740
- toolName: toolName.trim(),
14741
- args: finalArgsText.trim()
14742
- });
15104
+ const parsed = parseArgs(finalArgsText);
15105
+ const hasPlaceholderArg = isPlaceholderVal(parsed.path) || isPlaceholderVal(parsed.query) || isPlaceholderVal(parsed.url) || isPlaceholderVal(parsed.keyword) || isPlaceholderVal(parsed.command) || isPlaceholderVal(parsed.method) || isPlaceholderVal(parsed.title) || isPlaceholderVal(parsed.task) || isPlaceholderVal(parsed.id) || REGEX_PLACEHOLDER_ARG.test(finalArgsText);
15106
+ if (!hasPlaceholderArg) {
15107
+ results.push({
15108
+ fullMatch: finalFullMatch,
15109
+ toolName: toolName.trim(),
15110
+ args: finalArgsText.trim()
15111
+ });
15112
+ }
14743
15113
  toolRegex.lastIndex = endIdx + 1;
14744
15114
  }
14745
15115
  }
@@ -14937,7 +15307,8 @@ Chats to process:
14937
15307
  if (oldSummary) {
14938
15308
  prompt += `- Existing Summary: "${oldSummary}"
14939
15309
  `;
14940
- prompt += `-- New Memories to integrate:
15310
+ prompt += `
15311
+ -- New Memories to integrate:
14941
15312
  ${newMemoryListStr}
14942
15313
 
14943
15314
  `;
@@ -15179,7 +15550,12 @@ Provide a consolidated summary of the entire session.`;
15179
15550
  const mainUserMemories = persistentStorage.map((m) => `- ${m.memory}`).join("\n");
15180
15551
  const isContext32k = (sessionStats?.tokens || 0) >= 1e4;
15181
15552
  const memoryPrompt = getMemoryPrompt(otherMemories, mainUserMemories, isMemoryEnabled, isContext32k);
15182
- const dateTimeStr = (/* @__PURE__ */ new Date()).toLocaleString([], { year: "numeric", month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit", hour12: true });
15553
+ const now = /* @__PURE__ */ new Date();
15554
+ const year = now.getFullYear();
15555
+ const month = now.toLocaleString("en-US", { month: "short" }).toUpperCase();
15556
+ const day = String(now.getDate()).padStart(2, "0");
15557
+ const timeStr = now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: true });
15558
+ const dateTimeStr = `${year}-${month}-${day}, ${timeStr}`;
15183
15559
  const COLLAPSED_DIRS_GLOBAL = [
15184
15560
  // --- The OG Clutter ---
15185
15561
  ".git",
@@ -15406,8 +15782,11 @@ ${currentSummary}
15406
15782
  **CONTEXT SUMMARY OF PREVIOUS TURNS**
15407
15783
  ${currentSummary}
15408
15784
  ` : "";
15409
- let dirStructure = "\n**DIRECTORY STRUCTURE**\nCWD: " + process.cwd() + `${isPlayground ? " [PLAYGROUND MODE]" : ""}${cwdMismatch ? ` (WARNING: CWD Mismatch! Previous Path: ${lastCwd})` : ""}
15410
- ` + getCachedDirTree(() => getDirTree(process.cwd(), dynamicMaxDepth), chatId, systemSettings?.dynamicDirAwareness, systemSettings?.indentationTree);
15785
+ const dynamicDirAwareness = !!systemSettings?.dynamicDirAwareness;
15786
+ const sysInstructionCacheKey = `${chatId}|${aiProvider}|${thinkingLevel}|${modelName}|${profile}|${dynamicDirAwareness}`;
15787
+ const isSysInstructionCached = !dynamicDirAwareness && systemInstructionCache.key === sysInstructionCacheKey && systemInstructionCache.value;
15788
+ let dirStructure = isSysInstructionCached ? "" : "\n**DIRECTORY STRUCTURE**\nCWD: " + process.cwd() + `${isPlayground ? " [PLAYGROUND MODE]" : ""}${cwdMismatch ? ` (WARNING: CWD Mismatch! Previous Path: ${lastCwd})` : ""}
15789
+ ` + getDirTree(process.cwd(), dynamicMaxDepth);
15411
15790
  const ideCtx = await getIDEContext();
15412
15791
  let ideBlock = "";
15413
15792
  if (isBridgeConnected()) {
@@ -15859,10 +16238,18 @@ ${cleanPromptForModel.trim()}
15859
16238
  throw new Error("Error: Quota Exausted for Agent");
15860
16239
  }
15861
16240
  targetModel = modelName;
15862
- currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? thinkingLevel : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt, aiProvider, aiProvider === "Google" ? true : isMultiModal, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? true : false, chatId);
15863
- if (!systemSettings?.dynamicDirAwareness) {
15864
- currentSystemInstruction += `
16241
+ const sysInstructionCacheKey2 = `${chatId}|${aiProvider}|${thinkingLevel}|${targetModel}|${JSON.stringify(profile)}|${!!systemSettings?.dynamicDirAwareness}|${!!systemSettings?.subAgents}`;
16242
+ let isCacheHit = systemInstructionCache.key === sysInstructionCacheKey2 && systemInstructionCache.value;
16243
+ if (isCacheHit) {
16244
+ currentSystemInstruction = systemInstructionCache.value;
16245
+ } else {
16246
+ currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? thinkingLevel : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt, aiProvider, aiProvider === "Google" ? true : isMultiModal, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? true : false, chatId);
16247
+ if (!systemSettings?.dynamicDirAwareness) {
16248
+ currentSystemInstruction += `
15865
16249
  ${dirStructure.replace("\n**DIRECTORY STRUCTURE**", "\n**DIRECTORY STRUCTURE**")}`;
16250
+ }
16251
+ systemInstructionCache.key = sysInstructionCacheKey2;
16252
+ systemInstructionCache.value = currentSystemInstruction;
15866
16253
  }
15867
16254
  const lastUserMsg = contents[contents.length - 1];
15868
16255
  if (isBridgeConnected() & loop > 0) {
@@ -16599,8 +16986,8 @@ ${ideErr} [/ERROR]`;
16599
16986
  } else if (normToolName === "list_files" || normToolName === "read_folder") {
16600
16987
  const action = normToolName === "list_files" ? "List" : "Browsed";
16601
16988
  const path29 = parseArgs(toolCall.args).path || null;
16602
- const recurse = parseArgs(toolCall.args).recurse || 0;
16603
- label = `${path29 ? "\u2714" : "\u2718"} ${action}: ${path29 ? `${path29 === "." ? "./" : `${path29.replaceAll("\\", "/")}${recurse > 0 ? `${path29.endsWith("/") ? `*${recurse}` : `/*${recurse}`}` : `${path29.endsWith("/") ? "" : "/"}`}`}` : "No Folder Selected"}`;
16989
+ const recurse = parseArgs(toolCall.args).recurse || 1;
16990
+ label = `${path29 ? "\u2714" : "\u2718"} ${action}: ${path29 ? `${path29 === "." ? `./${recurse > 1 ? "*" : ""}` : `${path29.replaceAll("\\", "/")}${recurse > 1 ? `${path29.endsWith("/") ? `*` : `/*`}` : `${path29.endsWith("/") ? "" : "/"}`}`}` : "No Folder Selected"}`;
16604
16991
  } else if (normToolName === "write_file" || normToolName === "update_file") {
16605
16992
  const action = normToolName === "write_file" ? "Created" : "Edited";
16606
16993
  const path29 = parseArgs(toolCall.args).path || null;
@@ -16659,7 +17046,6 @@ ${ideErr} [/ERROR]`;
16659
17046
  "Panicking Softly",
16660
17047
  "Rethinking Career Choices",
16661
17048
  "Loading Cat Videos",
16662
- "Giving Up Entirely",
16663
17049
  // --- The New Chaos Pack ---
16664
17050
  "Summoning Braincell #2",
16665
17051
  "Pretending To Be Busy",
@@ -17333,8 +17719,10 @@ ${snippet2}`;
17333
17719
  }
17334
17720
  if (normToolName === "search_keyword") {
17335
17721
  const { keyword, path: path29 } = parseArgs(toolCall.args);
17722
+ const _isGlob = typeof result === "string" && result.startsWith("[GLOB]");
17723
+ if (_isGlob) result = result.slice(6).trimStart();
17336
17724
  const _isDir = typeof result === "string" && result.startsWith("[DIR]");
17337
- if (_isDir) result = result.slice(5);
17725
+ if (_isDir) result = result.slice(5).trimStart();
17338
17726
  let matchCount = 0;
17339
17727
  if (result) {
17340
17728
  const m = result.match(/Found (\d+) match/i);
@@ -17343,7 +17731,7 @@ ${snippet2}`;
17343
17731
  }
17344
17732
  }
17345
17733
  const _sp = path29 ? path29.replace(/[\/\\]+$/, "") : null;
17346
- const displayPath = _sp && _sp !== "." ? `"${_isDir ? `${_sp}/*` : _sp}"` : "./";
17734
+ const displayPath = _sp && _sp !== "." ? `"${_isGlob ? path29 : _isDir ? `${_sp}/*` : _sp}"` : "./";
17347
17735
  const postLabel = `${keyword ? "\u2714" : "\u2718"} Searched: "${keyword ? keyword : ""}" in ${displayPath.replaceAll("\\", "/")} \u2192 ${matchCount} Match${matchCount === 1 ? "" : "es"}`;
17348
17736
  let terminalWidth = 115;
17349
17737
  if (process.stdout.isTTY) {
@@ -17800,18 +18188,76 @@ Error Log can be found in ${path27.join(LOGS_DIR, "agent", "error.log")}`);
17800
18188
  runSubagent = async (task, settings, model = null, allowedTools = null, maxTurns = 50, logCallback = null) => {
17801
18189
  const savedSettings = await loadSettings();
17802
18190
  const mergedSettings = { ...savedSettings, ...settings };
17803
- const targetModel = model || settings?.modelName || settings?.activeModel || savedSettings.activeModel;
17804
- const SUBAGENT_TOOL_DEFINITIONS = {
17805
- "readfile": '- [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. View files',
17806
- "readfolder": '- [tool:functions.ReadFolder(path="...", recurse="integer 0-4 optional, default: 0")]. Detailed DIR stats including File Sizes',
17807
- "filemap": '- [tool:functions.FileMap(path="file")]. Shows file structure, functions, class, import/export, variables',
17808
- "patchfile": '- [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", replaceContent1="...", newContent1="...", ...MAX 15)]. Surgical patch. allowMultiple: Replace all matches. Multiple patches same file? Use replaceContent2/newContent2... Verify DIFFs',
17809
- "writefile": '- [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. VERIFY IMPORTS',
17810
- "searchkeyword": '- [tool:functions.SearchKeyword(keyword="...", path="optional, target directory/filename", subString="bool optional, default: false", regex="bool optional, default: auto")]. path limits scope to a file/dir. Find definitions/logic without full reads. Locate relevant code',
17811
- "websearch": '- [tool:functions.WebSearch(query="...", aiMode="bool optional, default: false", limit="integer 3-10, aiMode: exclude")]. Usage: unknown info/docs. aiMode: LLM search',
17812
- "webscrape": '- [tool:functions.WebScrape(url="...")]. Proactive use for specific webpage/docs/api',
17813
- "ask": `- [tool:functions.Ask(question="...", optionA="option::description", ...MAX 4)]. Ambiguity: MUST for path divergence, security risk. Ask, don't finish/guess. Suggest best options; no preferences. Keep options short`
18191
+ const envSubagentModel = process.env.SUBAGENT_MODEL ? process.env.SUBAGENT_MODEL.trim() : null;
18192
+ const envSubagentProviderRaw = process.env.SUBAGENT_PROVIDER ? process.env.SUBAGENT_PROVIDER.trim() : null;
18193
+ const subagentNow = /* @__PURE__ */ new Date();
18194
+ const time = `${subagentNow.getFullYear()}-${subagentNow.toLocaleString("en-US", { month: "short" }).toUpperCase()}-${String(subagentNow.getDate()).padStart(2, "0")}, ${subagentNow.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: true }).replace(":", "-")}`;
18195
+ const normalizeProvider = (pStr) => {
18196
+ if (!pStr) return null;
18197
+ const lower = pStr.toLowerCase();
18198
+ if (lower === "google") return "Google";
18199
+ if (lower === "deepseek") return "DeepSeek";
18200
+ if (lower === "openrouter") return "OpenRouter";
18201
+ if (lower === "nvidia") return "NVIDIA";
18202
+ if (lower === "mistral") return "Mistral";
18203
+ return null;
18204
+ };
18205
+ const envSubagentProvider = normalizeProvider(envSubagentProviderRaw);
18206
+ const configuredSubAgentModel = mergedSettings?.systemSettings?.CustomSubAgent ? mergedSettings?.systemSettings?.SubAgentModel : null;
18207
+ const configuredSubAgentProvider = mergedSettings?.systemSettings?.CustomSubAgent ? mergedSettings?.systemSettings?.SubAgentProvider : null;
18208
+ let subAgentCustomModel = null;
18209
+ if (configuredSubAgentModel === "ENV") {
18210
+ subAgentCustomModel = envSubagentModel;
18211
+ if (envSubagentModel && envSubagentProvider) {
18212
+ mergedSettings.aiProvider = envSubagentProvider;
18213
+ }
18214
+ } else if (configuredSubAgentModel && configuredSubAgentModel !== "Default") {
18215
+ subAgentCustomModel = configuredSubAgentModel;
18216
+ if (configuredSubAgentProvider) {
18217
+ mergedSettings.aiProvider = configuredSubAgentProvider;
18218
+ }
18219
+ } else if (envSubagentModel) {
18220
+ if (envSubagentProvider) {
18221
+ mergedSettings.aiProvider = envSubagentProvider;
18222
+ }
18223
+ }
18224
+ if (mergedSettings.aiProvider) {
18225
+ const providerApiKey = await getProviderAPIKey(mergedSettings.aiProvider);
18226
+ if (providerApiKey) {
18227
+ mergedSettings.apiKey = providerApiKey;
18228
+ }
18229
+ }
18230
+ const isSubagentCommandAllowed = (cmdString) => {
18231
+ if (!cmdString || typeof cmdString !== "string") return { allowed: true };
18232
+ const DANGEROUS_PATTERNS = [
18233
+ // Destructive file deletion / formatting
18234
+ /rm\s+-[rf]{1,2}\s+[\/*.]/i,
18235
+ /rmdir\s+\/[sq]/i,
18236
+ /del\s+\/[fsq]/i,
18237
+ /\bformat\b\s+[a-z]:/i,
18238
+ /mkfs/i,
18239
+ /dd\s+if=/i,
18240
+ // System shutdown / reboot / killall
18241
+ /\b(shutdown|reboot|poweroff|init\s+0|init\s+6)\b/i,
18242
+ // Low level disk / raw write / partition / chmod dangerous
18243
+ /chmod\s+(-R\s+)?777\s+[\/*.]/i,
18244
+ /chown\s+(-R\s+)?root/i,
18245
+ // Dangerous git force / reset operations on remote / system
18246
+ /git\s+push\s+.*--force/i,
18247
+ /git\s+clean\s+-fdx/i,
18248
+ // System-level privilege escalation
18249
+ /\bsudo\s+su\b/i,
18250
+ /\bsu\s+-\b/i
18251
+ ];
18252
+ for (const pattern of DANGEROUS_PATTERNS) {
18253
+ if (pattern.test(cmdString)) {
18254
+ return { allowed: false, reason: `Blocked potentially destructive or unsafe command pattern: "${pattern.source}"` };
18255
+ }
18256
+ }
18257
+ return { allowed: true };
17814
18258
  };
18259
+ const targetModel = model || subAgentCustomModel || settings?.modelName || settings?.activeModel || savedSettings.activeModel;
18260
+ const osDetected = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
17815
18261
  const providedToolsSection = `-- TOOL DEFINITIONS (path = relative to CWD, path separator: '/') --
17816
18262
  TO ACCESS TOOLS **STRICTLY USE THE EXACT FORMAT IN CHAT OUTPUT:** [tool:functions.ToolName(args)]
17817
18263
  **NO OTHER SYNTAX/MARKERS/BOUNDARY ALLOWED**
@@ -17819,15 +18265,26 @@ TO ACCESS TOOLS **STRICTLY USE THE EXACT FORMAT IN CHAT OUTPUT:** [tool:function
17819
18265
  TOOL POLICY:
17820
18266
  - MAX 3 TOOL CALLS PER TURN
17821
18267
  - Same file, many edits? Prefer multi search-replace in Patch \u2190 **HIGHLY RECOMMENDED**
17822
- - FileMap \u2192 ReadFile for efficient file understanding
17823
- - Need specific text ? SearchKeyword > Guessing/ReadFile
17824
- - Huge files ? SearchKeyword > FileMap/Full Read
17825
- - NO Shell Access
18268
+ - Need specific text OR huge file ? SearchKeyword > ReadFile
18269
+ - Tool denied? Use \`Ask\` immediately for user guidance \u2190 **MANDATORY**
18270
+ - Restricted Shell Access, NO DELETION
17826
18271
 
17827
- -- PROVIDED TOOLS --
17828
- ${Object.values(SUBAGENT_TOOL_DEFINITIONS).join("\n")}
18272
+ **PROVIDED TOOLS**
18273
+ -- Communication with USER --
18274
+ - [tool:functions.Ask(question="...", optionA="option::description", ...MAX 4)]. Ambiguity: MUST for path divergence, security risk. Ask, don't finish/guess. Suggest best options; no preferences. Keep options short
18275
+
18276
+ -- Web Tools --
18277
+ - [tool:functions.WebSearch(query="...", aiMode="bool optional, default: false", limit="integer 3-10, aiMode: exclude")]. Usage: unknown info/docs. aiMode: LLM search
18278
+ - [tool:functions.WebScrape(url="...")]. Proactive use for specific webpage/docs/api
18279
+
18280
+ -- Workspace Tools --
18281
+ - [tool:functions.SearchKeyword(keyword="...", path="optional, dir/file/glob/regex", fuzzy="bool optional, default: false", regex="bool optional, default: auto")]. path limits search scope. Find definitions/logic without full reads. Locate relevant code
18282
+ - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
18283
+ - [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. View files
18284
+ - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", replaceContent1="...", newContent1="...", ...MAX 15)]. Surgical patchs, TARGET SMALLEST LINES. allowMultiple: Replace all matches ONLY WHERE SURE. Use replaceContent2/newContent2... for multi blocks. Verify DIFFs
18285
+ - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. VERIFY IMPORTS
18286
+ - [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `WINDOWS POWERSHELL` : `WINDOWS CMD` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user
17829
18287
 
17830
- - VERIFY TOOL RESULT CONTENTS. Fix errors. No hallucinations
17831
18288
  - **Escape quotes: \\" for code strings**
17832
18289
  - **Literal escapes: Double-escape sequences (e.g., \\\\n)**
17833
18290
  - **File structure: Real newlines for code formatting**`.trim();
@@ -17838,12 +18295,12 @@ Your task is: "${task}"
17838
18295
  ${providedToolsSection.trimEnd()}
17839
18296
 
17840
18297
  -- THINKING GUIDANCE --
17841
- NO EXPLICIT THINKING REQUIRED. FOCUS ON COMPLETING THE TASK DIRECTLY
17842
-
18298
+ NO EXPLICIT THINKING REQUIRED. FOCUS ON TASK COMPLETION
17843
18299
  Keep main focus on tools and task, not chatting
17844
- Once you have fully completed the task, provide a detailed structured summary preferebly in Tables/Bullet Points with file modified info, if any task failed report back in detail, no hallucination
18300
+ On task completion, provide a detailed structured summary preferebly in Tables/Bullet Points with file modified info, if any task failed report back in detail, no hallucination
17845
18301
 
17846
18302
  CWD: ${process.cwd()}
18303
+ Current Time: ${time}
17847
18304
  === END SYSTEM PROMPT ===`;
17848
18305
  const subagentHistory = [
17849
18306
  { role: "user", text: `Complete this task: ${task}` }
@@ -17909,11 +18366,26 @@ ${cleanResponse}
17909
18366
  `;
17910
18367
  continue;
17911
18368
  }
18369
+ if (normalizedToolName === "exec_command" || normalizedToolName === "execcommand" || normalizedToolName === "run") {
18370
+ const cmdArg = parseArgs(toolCall.args).command || "";
18371
+ const cmdCheck = isSubagentCommandAllowed(cmdArg);
18372
+ if (!cmdCheck.allowed) {
18373
+ const blockMsg = `ERROR: [SECURITY RESTRICTION] Subagent execution blocked command: ${cmdCheck.reason}`;
18374
+ if (logCallback) logCallback(`[Blocked Command] ${cmdArg} - ${cmdCheck.reason}
18375
+ `);
18376
+ toolResultsStr += `${blockMsg}
18377
+
18378
+ `;
18379
+ continue;
18380
+ }
18381
+ }
17912
18382
  let label = "";
17913
18383
  if (normalizedToolName === "web_search" || normalizedToolName === "websearch") {
17914
- label = `\u2714 \x1B[95mSearched\x1B[0m`;
18384
+ const query = parseArgs(toolCall.args).query || "";
18385
+ label = `\u2714 \x1B[95mSearched\x1B[0m: ${query}`;
17915
18386
  } else if (normalizedToolName === "web_scrape" || normalizedToolName === "webscrape") {
17916
- label = `\u2714 \x1B[95mScraped\x1B[0m`;
18387
+ const url = parseArgs(toolCall.args).url || "";
18388
+ label = `\u2714 \x1B[95mScraped\x1B[0m: ${url}`;
17917
18389
  } else if (normalizedToolName === "search_keyword" || normalizedToolName === "searchkeyword") {
17918
18390
  const pArgs = parseArgs(toolCall.args);
17919
18391
  const keyword = pArgs.keyword || "";
@@ -17925,7 +18397,7 @@ ${cleanResponse}
17925
18397
  } else if (normalizedToolName === "list_files" || normalizedToolName === "read_folder" || normalizedToolName === "readfolder") {
17926
18398
  const path29 = parseArgs(toolCall.args).path || null;
17927
18399
  const recurse = parseArgs(toolCall.args).recurse || 0;
17928
- label = `${path29 ? "\u2714" : "\u2718"} \x1B[95mBrowsed\x1B[0m: ${path29 ? `${path29.replaceAll("\\", "/")}${recurse > 0 ? `${path29.endsWith("/") ? `*${recurse}` : `/*${recurse}`}` : `${path29.endsWith("/") ? "" : "/"}`}` : ""}`;
18400
+ label = `${path29 ? "\u2714" : "\u2718"} \x1B[95mBrowsed\x1B[0m: ${path29 ? `${path29.replaceAll("\\", "/")}${recurse > 0 ? `${path29.endsWith("/") ? `*${recurse}` : `/*${recurse}`}` : `${path29.endsWith("/") ? "" : "/"}`}` : ""}`;
17929
18401
  } else if (normalizedToolName === "write_file" || normalizedToolName === "writefile") {
17930
18402
  const path29 = parseArgs(toolCall.args).path || null;
17931
18403
  label = `${path29 ? "\u2714" : "\u2718"} \x1B[95mCreated\x1B[0m: ${path29 ? `${path29.replaceAll("\\", "/")}` : "No File Changes"}`;
@@ -17933,12 +18405,15 @@ ${cleanResponse}
17933
18405
  const path29 = parseArgs(toolCall.args).path || null;
17934
18406
  const content = parseArgs(toolCall.args).content || null;
17935
18407
  label = `${path29 ? "\u2714" : "\u2718"} \x1B[95mEdited\x1B[0m: ${path29 ? `${path29.replaceAll("\\", "/")}` : "No File Changes"}`;
18408
+ } else if (normalizedToolName === "exec_command" || normalizedToolName === "execcommand" || normalizedToolName === "run") {
18409
+ const command = parseArgs(toolCall.args).command || null;
18410
+ label = `${command ? "\u2714" : "\u2718"} \x1B[95mExecuted\x1B[0m: ${command ? command.slice(0, 100) + (command.length > 100 ? "..." : "") : "No Command"}`;
17936
18411
  } else if (normalizedToolName === "file_map" || normalizedToolName === "filemap") {
17937
18412
  const path29 = parseArgs(toolCall.args).path || "";
17938
18413
  label = `${path29 ? "\u2714" : "\u2718"} \x1B[95mIndexed\x1B[0m: ${path29 ? `${path29.replaceAll("\\", "/")}` : "File Not Found"}`;
17939
18414
  } else if (normalizedToolName === "await") {
17940
- const { time } = parseArgs(toolCall.args);
17941
- let sec = parseFloat(time) || 0;
18415
+ const { time: time2 } = parseArgs(toolCall.args);
18416
+ let sec = parseFloat(time2) || 0;
17942
18417
  if (sec < 10) sec = 10;
17943
18418
  if (sec > 180) sec = 180;
17944
18419
  const formatTime = (s) => {
@@ -19172,6 +19647,108 @@ function App({ args = [] }) {
19172
19647
  clearInterval(memInterval);
19173
19648
  };
19174
19649
  }, []);
19650
+ useEffect12(() => {
19651
+ const checkSubAgentModelOnStartup = async () => {
19652
+ try {
19653
+ const settings = await loadSettings();
19654
+ const sysSettings = settings?.systemSettings || {};
19655
+ const customSubAgent = sysSettings.CustomSubAgent;
19656
+ const configuredModel = sysSettings.SubAgentModel;
19657
+ if (!customSubAgent || !configuredModel || configuredModel === "Default") {
19658
+ return;
19659
+ }
19660
+ const envModel = process.env.SUBAGENT_MODEL ? process.env.SUBAGENT_MODEL.trim() : null;
19661
+ const envProviderRaw = process.env.SUBAGENT_PROVIDER ? process.env.SUBAGENT_PROVIDER.trim() : null;
19662
+ const ALL_PROVIDERS = ["Google", "DeepSeek", "OpenRouter", "NVIDIA", "Mistral"];
19663
+ const normalizeProvider = (pStr) => {
19664
+ if (!pStr) return null;
19665
+ const lower = pStr.toLowerCase();
19666
+ if (lower === "google") return "Google";
19667
+ if (lower === "deepseek") return "DeepSeek";
19668
+ if (lower === "openrouter") return "OpenRouter";
19669
+ if (lower === "nvidia") return "NVIDIA";
19670
+ if (lower === "mistral") return "Mistral";
19671
+ return null;
19672
+ };
19673
+ const envProvider = normalizeProvider(envProviderRaw);
19674
+ if (envModel && !envProvider) {
19675
+ const currentActiveProv = settings.aiProvider || aiProvider || "Google";
19676
+ setMessages((prev) => {
19677
+ setCompletedIndex(prev.length + 1);
19678
+ return [...prev, {
19679
+ id: "subagent-env-noprov-" + Date.now(),
19680
+ role: "system",
19681
+ text: `[SUBAGENT CONFIG] SUBAGENT_MODEL found in ENV but SUBAGENT_PROVIDER is missing/invalid. Active provider (${currentActiveProv}) will be used.`,
19682
+ isMeta: true
19683
+ }];
19684
+ });
19685
+ }
19686
+ if (configuredModel === "ENV") {
19687
+ if (!envModel) {
19688
+ setMessages((prev) => {
19689
+ setCompletedIndex(prev.length + 1);
19690
+ return [...prev, {
19691
+ id: "subagent-model-noenv-" + Date.now(),
19692
+ role: "system",
19693
+ text: "No SubAgent model is found in ENV, Using Deafult until changed",
19694
+ isMeta: true
19695
+ }];
19696
+ });
19697
+ }
19698
+ return;
19699
+ }
19700
+ const currentProvider = settings.aiProvider || aiProvider || "Google";
19701
+ const currentTier = settings.apiTier || apiTier || "Free";
19702
+ const quotasObj = settings.quotas || quotas || {};
19703
+ const availableModelNamesSet = /* @__PURE__ */ new Set();
19704
+ const currentModelsRaw = getModels(currentProvider, currentTier) || [];
19705
+ currentModelsRaw.forEach((m) => {
19706
+ const name = typeof m === "string" ? m : m.cmd || m.name || m.id || String(m);
19707
+ if (name) availableModelNamesSet.add(name);
19708
+ });
19709
+ for (const p of ALL_PROVIDERS) {
19710
+ try {
19711
+ const key = await getProviderAPIKey(p);
19712
+ if (key) {
19713
+ const tier = quotasObj?.providerTiers?.[p] || "Free";
19714
+ const pModels = getModels(p, tier) || [];
19715
+ pModels.forEach((m) => {
19716
+ const name = typeof m === "string" ? m : m.cmd || m.name || m.id || String(m);
19717
+ if (name) availableModelNamesSet.add(name);
19718
+ });
19719
+ }
19720
+ } catch (e) {
19721
+ }
19722
+ }
19723
+ const isModelAvailable = availableModelNamesSet.has(configuredModel);
19724
+ if (envModel) {
19725
+ if (envModel !== configuredModel) {
19726
+ setMessages((prev) => {
19727
+ setCompletedIndex(prev.length + 1);
19728
+ return [...prev, {
19729
+ id: "subagent-model-env-" + Date.now(),
19730
+ role: "system",
19731
+ text: "Current Seleted Sub-Agent model is not available in this provider. Using model from ENV unless changed.",
19732
+ isMeta: true
19733
+ }];
19734
+ });
19735
+ }
19736
+ } else if (!isModelAvailable) {
19737
+ setMessages((prev) => {
19738
+ setCompletedIndex(prev.length + 1);
19739
+ return [...prev, {
19740
+ id: "subagent-model-err-" + Date.now(),
19741
+ role: "system",
19742
+ text: "Current Seleted Sub-Agent model is not available in this provider. Using Default until changed",
19743
+ isMeta: true
19744
+ }];
19745
+ });
19746
+ }
19747
+ } catch (err) {
19748
+ }
19749
+ };
19750
+ checkSubAgentModelOnStartup();
19751
+ }, []);
19175
19752
  const parsedArgs = useMemo2(() => {
19176
19753
  const parsed = {};
19177
19754
  for (let i = 0; i < args.length; i++) {