fluxflow-cli 3.16.6 → 3.18.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
@@ -2570,7 +2570,7 @@ var init_build = __esm({
2570
2570
 
2571
2571
  // src/utils/text.js
2572
2572
  import os2 from "os";
2573
- var flattenString, wrapText, formatTokens, truncatePath, parsePatchPairs, applyPatches, generateHighFidelityDiff, parseLineInfo, getSimilarity, alignChangeGroup, blocksCache, streamingBlocksCache, MAX_CACHE_SIZE, CHUNK_SIZE, indexBlockIntoMap, parseMessageToBlocks, TOOL_LABELS, REGEX_INITIAL_THINK, REGEX_INITIAL_TOOL, REGEX_CLEAN_SIGNALS, REGEX_ARROWS_ALL, REGEX_TOOLS, cleanSignals, clearBlocksCache;
2573
+ var flattenString, wrapText, formatTokens, truncatePath, parsePatchPairs, buildEscapeFuzzyRegex, applyPatches, generateHighFidelityDiff, parseLineInfo, getSimilarity, alignChangeGroup, blocksCache, streamingBlocksCache, MAX_CACHE_SIZE, CHUNK_SIZE, indexBlockIntoMap, parseMessageToBlocks, TOOL_LABELS, REGEX_INITIAL_THINK, REGEX_INITIAL_TOOL, isInsideBacktick, REGEX_CLEAN_SIGNALS, REGEX_ARROWS_ALL, REGEX_TOOLS, bypassBacktick, cleanSignals, clearBlocksCache;
2574
2574
  var init_text = __esm({
2575
2575
  "src/utils/text.js"() {
2576
2576
  init_paths();
@@ -2677,6 +2677,53 @@ var init_text = __esm({
2677
2677
  }
2678
2678
  return { patchPairs, allowMultiple };
2679
2679
  };
2680
+ buildEscapeFuzzyRegex = (content_to_replace) => {
2681
+ if (!content_to_replace) return null;
2682
+ let pattern = "";
2683
+ let i = 0;
2684
+ const len = content_to_replace.length;
2685
+ while (i < len) {
2686
+ const char = content_to_replace[i];
2687
+ if (char === "\\") {
2688
+ while (i < len && content_to_replace[i] === "\\") {
2689
+ i++;
2690
+ }
2691
+ pattern += "\\\\*";
2692
+ } else if (char === "\n") {
2693
+ pattern += "(?:\\r?\\n|\\\\*n|\\\\*r|\\s*)";
2694
+ i++;
2695
+ } else if (char === "\r") {
2696
+ pattern += "(?:\\r|\\\\*r)?";
2697
+ i++;
2698
+ } else if (char === " ") {
2699
+ pattern += "(?:\\t|\\\\*t|\\s*)";
2700
+ i++;
2701
+ } else if (char === '"' || char === "'" || char === "`") {
2702
+ if (!pattern.endsWith("\\\\*")) {
2703
+ pattern += "\\\\*";
2704
+ }
2705
+ pattern += char;
2706
+ i++;
2707
+ } else if (/[.*+?^${}()|[\]]/.test(char)) {
2708
+ pattern += "\\" + char;
2709
+ i++;
2710
+ } else if (/\s/.test(char)) {
2711
+ while (i < len && /\s/.test(content_to_replace[i]) && content_to_replace[i] !== "\n" && content_to_replace[i] !== "\r" && content_to_replace[i] !== " ") {
2712
+ i++;
2713
+ }
2714
+ pattern += "\\s*";
2715
+ } else {
2716
+ pattern += char;
2717
+ i++;
2718
+ }
2719
+ }
2720
+ if (!pattern) return null;
2721
+ try {
2722
+ return new RegExp(pattern, "g");
2723
+ } catch (e) {
2724
+ return null;
2725
+ }
2726
+ };
2680
2727
  applyPatches = (content, patches, options = {}) => {
2681
2728
  const allowMultiple = typeof options === "boolean" ? options : !!(options && options.allowMultiple);
2682
2729
  let currentFileContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
@@ -2713,8 +2760,40 @@ var init_text = __esm({
2713
2760
  const patchMatches = [];
2714
2761
  for (let i = 0; i < patches.length; i++) {
2715
2762
  const pair = patches[i];
2716
- const content_to_replace = strip(pair.replace || "");
2763
+ const rawReplace = (pair.replace || "").trim();
2717
2764
  const content_to_add = strip(pair.new || "");
2765
+ if (rawReplace.startsWith("^LINE:") && rawReplace.endsWith("$")) {
2766
+ const body = rawReplace.slice(6, -1).trim();
2767
+ const parts = body.split("..");
2768
+ const startLine = parseInt(parts[0]);
2769
+ const endLine = parts[1] !== void 0 ? parseInt(parts[1]) : startLine;
2770
+ if (!isNaN(startLine) && !isNaN(endLine)) {
2771
+ const fileLines = currentFileContent.split("\n");
2772
+ if (startLine < 1 || startLine > fileLines.length || endLine < startLine || endLine > fileLines.length) {
2773
+ patchMatches.push({
2774
+ index: i,
2775
+ success: false,
2776
+ error: `Block ${i + 1}: Line range ^LINE:${startLine}..${endLine}$ out of bounds (file has ${fileLines.length} lines).`
2777
+ });
2778
+ continue;
2779
+ }
2780
+ const slicedLines = fileLines.slice(startLine - 1, endLine);
2781
+ const firstMatchContent = slicedLines.join("\n");
2782
+ let startPos = 0;
2783
+ for (let k = 0; k < startLine - 1; k++) {
2784
+ startPos += fileLines[k].length + 1;
2785
+ }
2786
+ patchMatches.push({
2787
+ index: i,
2788
+ success: true,
2789
+ startPos,
2790
+ firstMatchContent,
2791
+ content_to_add
2792
+ });
2793
+ continue;
2794
+ }
2795
+ }
2796
+ const content_to_replace = strip(pair.replace || "");
2718
2797
  if (content_to_replace === "" && content_to_add === "") {
2719
2798
  patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Empty replace and add content.` });
2720
2799
  continue;
@@ -2736,7 +2815,13 @@ var init_text = __esm({
2736
2815
  matchRegex = new RegExp(exactPattern, "g");
2737
2816
  }
2738
2817
  }
2739
- const matches = [...currentFileContent.matchAll(matchRegex)];
2818
+ let matches = [...currentFileContent.matchAll(matchRegex)];
2819
+ if (matches.length === 0 && content_to_replace !== "") {
2820
+ const escapeFuzzyRegex = buildEscapeFuzzyRegex(content_to_replace);
2821
+ if (escapeFuzzyRegex) {
2822
+ matches = [...currentFileContent.matchAll(escapeFuzzyRegex)];
2823
+ }
2824
+ }
2740
2825
  if (matches.length === 0) {
2741
2826
  patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Could not find match.` });
2742
2827
  continue;
@@ -3345,12 +3430,20 @@ var init_text = __esm({
3345
3430
  };
3346
3431
  REGEX_INITIAL_THINK = /<\/think>(\r?\n){2}/gi;
3347
3432
  REGEX_INITIAL_TOOL = /(\r?\n){2}(?=\[?(?:tool:functions|tool\.functions|agent:generalist|agent\.generalist|\s*turn\s*:))/gi;
3433
+ isInsideBacktick = (str, idx) => {
3434
+ let inCode = false;
3435
+ for (let i = 0; i < idx; i++) {
3436
+ if (str[i] === "`") inCode = !inCode;
3437
+ }
3438
+ return inCode;
3439
+ };
3348
3440
  REGEX_CLEAN_SIGNALS = /\[SYSTEM\][\s\S]*?\[\/SYSTEM\]|<(think|thought)>[\s\S]*?(?:<\/(think|thought)>|$)|\[ANSWER\][\s\S]*?(?:\[\/ANSWER\]|$)|\[TOOL RESULT\]:?\s*|^\s*(SUCCESS|ERROR):.*(\r?\n)?|\[\s*turn\s*:\s*(continue|finish)\s*\]|\[\[END\]\]|\[\s*turn\s*:?.*?$|\n\s*turn\s*:?.*?$|\[\s*$|\n\nResponded on .*|\n\n\[Prompted on: .*\]|@\[TerminalName:.*?, ProcessId:.*?\]/gmi;
3349
3441
  REGEX_ARROWS_ALL = /(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)|(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)|(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)|(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)|(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi;
3350
3442
  REGEX_TOOLS = /\b(write_file|update_file|read_folder|view_file|exec_command|web_search|web_scrape|search_keyword|write_pdf|write_docx|generate_image)\b/gi;
3443
+ bypassBacktick = false;
3351
3444
  cleanSignals = (text) => {
3352
3445
  if (!text) return text;
3353
- let result = text.replace(REGEX_INITIAL_THINK, "</think>").replace(REGEX_INITIAL_TOOL, "");
3446
+ let result = text.replace(REGEX_INITIAL_THINK, "</think>").replace(REGEX_INITIAL_TOOL, (match, _nl, offset, str) => !bypassBacktick && isInsideBacktick(str, offset) ? match : "");
3354
3447
  const trigger = "tool:functions.";
3355
3448
  const subagentTrigger = "agent:generalist.";
3356
3449
  if (result.toLowerCase().includes(trigger) || result.toLowerCase().includes(subagentTrigger)) {
@@ -3365,6 +3458,35 @@ var init_text = __esm({
3365
3458
  triggerIdxToUse = subagentIdx;
3366
3459
  }
3367
3460
  if (triggerIdxToUse === -1) break;
3461
+ if (!bypassBacktick && isInsideBacktick(result, triggerIdxToUse)) {
3462
+ const searchFrom = triggerIdxToUse + currentTrigger.length;
3463
+ const nextTool = lowerResult.indexOf(trigger, searchFrom);
3464
+ const nextAgent = lowerResult.indexOf(subagentTrigger, searchFrom);
3465
+ if (nextTool === -1 && nextAgent === -1) break;
3466
+ let safeIdx = -1;
3467
+ let searchPos = 0;
3468
+ while (true) {
3469
+ const tIdx = lowerResult.indexOf(trigger, searchPos);
3470
+ const aIdx = lowerResult.indexOf(subagentTrigger, searchPos);
3471
+ let candidate = -1;
3472
+ let candidateTrigger = trigger;
3473
+ if (tIdx === -1 && aIdx === -1) break;
3474
+ if (tIdx === -1 || aIdx !== -1 && aIdx < tIdx) {
3475
+ candidate = aIdx;
3476
+ candidateTrigger = subagentTrigger;
3477
+ } else {
3478
+ candidate = tIdx;
3479
+ }
3480
+ if (!isInsideBacktick(result, candidate)) {
3481
+ safeIdx = candidate;
3482
+ currentTrigger = candidateTrigger;
3483
+ break;
3484
+ }
3485
+ searchPos = candidate + candidateTrigger.length;
3486
+ }
3487
+ if (safeIdx === -1) break;
3488
+ triggerIdxToUse = safeIdx;
3489
+ }
3368
3490
  let startIdx = triggerIdxToUse;
3369
3491
  let hasOuterBracket = false;
3370
3492
  let k = triggerIdxToUse - 1;
@@ -6808,21 +6930,18 @@ var init_main_tools = __esm({
6808
6930
  }
6809
6931
  return `
6810
6932
  -- TOOL DEFINITIONS --
6811
- Tool calls: ONLY use [tool:functions.ToolName(args)]
6812
- **NO OTHER SYNTAX/MARKERS/BOUNDARY ALLOWED**
6933
+ Tool calls: ONLY use [tool:functions.ToolName(arg1="value1")] IN NEW LINE
6934
+ **NO OTHER SYNTAX/MARKERS/WRAPPER/BOUNDARY ALLOWED**
6813
6935
 
6814
6936
  **TOOL CALLS POLICY:**
6815
6937
  - MAX 4 TOOL CALLS/TURN${mode === "Flux" ? " (Todo: 4+, Run: max 1 or 2 consecutive)" : ""}
6816
- ${mode === "Flux" ? `- Escape quotes: \\" for code strings
6817
- - Double-escape literal sequences (eg. \\\\n)
6818
- - Use real newlines for code formatting
6938
+ ${mode === "Flux" ? `- JSON ESCAPE ALL LITERAL ESCAPE SEQUENCES IN TOOL ARGUMENTS
6819
6939
  - SAME file, MULTIPLE edits? ONE PatchFile (\u226415 blocks) \u2190 PRIORITY
6820
6940
  - Tool denied? Ask for guidance \u2190 MANDATORY
6821
6941
  - Need text or huge files? SearchKeyword > Full Read
6822
- - Update Todos from realtime progress each turn
6823
6942
  ` : ""}
6824
6943
  - COMMUNICATION WITH USER -
6825
- - [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
6944
+ - [tool:functions.Ask(question="...", optionA="title::description", ...MAX4)]. Ambiguity: MUST for path divergence, security risk. Ask, don't finish/guess. Suggest best options; no preferences. Keep titles short
6826
6945
 
6827
6946
  - WEB TOOLS -
6828
6947
  - [tool:functions.WebSearch(query="...", aiMode="bool optional, default: false", limit="integer 3-10, aiMode: exclude")]. Usage: unknown info/docs. aiMode: LLM search
@@ -6830,25 +6949,26 @@ ${mode === "Flux" ? `- Escape quotes: \\" for code strings
6830
6949
 
6831
6950
  ${mode === "Flux" ? `- WORKSPACE TOOLS (path = relative; FIRST ARGUMENT, path separator: '/') -
6832
6951
  - [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. ${aiProvider !== "Google" ? `${isMultiModal ? `Supports images/docs` : ""}` : `Supports images/docs`}
6833
- - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Last resort. Minimize recursion
6834
- - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", replaceContent1="...", newContent1="...", ...MAX15)]. TARGET MINIMAL DIFF. allowMultiple: Replace all matches ONLY WHEN SURE. Multi-blocks: replaceContent2/newContent2... Verify diffs
6952
+ - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
6953
+ - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", replaceContent1="string OR ^LINE:start..end$", newContent1="...", ...MAX15)]. TARGET MINIMAL DIFF. replaceContent accepts exact string OR "^LINE:start..end$" to target line ranges. Multi-blocks supported. Verify diffs
6835
6954
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile
6836
6955
  - [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
6837
6956
  - [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `POWERSHELL` : `WINDOWS CMD` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user
6838
- - [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' : ""}
6957
+ - [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**
6839
6958
  ${_cachedAdvanceRollback ? `
6840
- - EMERGENCY SAFETY TOOLS -
6959
+ - EMERGENCY TOOLS -
6841
6960
  Info: \`initial\` = current task prompt. Revert \`id\` = turn before disaster (eg. disaster: \`turn_3\` \u2192 revert: \`turn_2\`). Reason explicitly
6842
6961
  - [tool:functions.EmergencyRollback(method="getCheckpoint/forceRevert", id="...")]. Rollback workspace in THIS agent loop. ONLY for catastrophic corruption. Before ending, verify no catastrophe. \`id\` omitted for \`getCheckpoint\`
6843
6962
  ` : ""}${enableSubAgents ? `
6844
6963
  - SUB AGENT TOOLS -
6845
6964
  **PROACTIVE sub-agent use HIGHLY RECOMMENDED. Prefer for any task with even slight benefit, no user nudge needed**
6846
6965
  Invocations:
6847
- \u2022 Invoke (async/background, \u22647 parallel). Parallelize long tasks. NEVER repeat while active, meantime, do your OWN work
6966
+ \u2022 Invoke (async/background, \u22647 parallel). Parallelize long tasks. May take time
6848
6967
  \u2022 InvokeSync (sync/blocking). Sequential, repetitive or delegated tasks. Saves tokens/cost
6849
- - [agent:generalist.InvokeSync/Invoke(title="...", task="...")]. Task must be detailed: exact file paths, imports/exports, dependencies & folder structure
6850
- - [agent:generalist.GetProgress(id="...")]. Poll \`getProgress\` sparingly (exp backoff Await); **NO IMMEDIATE FIRST POLL**
6851
- - [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: '/') -
6968
+ - [tool:functions.InvokeSync/Invoke(title="...", task="...")]. Task must be detailed: exact file paths, imports/exports, dependencies & folder structure
6969
+ - [tool:functions.Await(id="...", timeout="integer seconds, default: 120")]. Event-driven wait
6970
+ - [tool:functions.GetProgress(id="...")]. Poll \`getProgress\` sparingly; NO initial poll. Work or await. Never end while subagent runs
6971
+ - [tool:functions.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: '/') -
6852
6972
  - [tool:functions.WritePDF(path="...", content="...", orientation="...")]. PROACTIVE A4 PAGE BREAKS MUST IN CSS. HTML/CSS for PREMIUM layout, stable margins & headers/footers, NO WATERMARKS
6853
6973
  - [tool:functions.WriteDoc(path="...", content="...")]. A4 Word document, NO WATERMARKS, stable margins & headers/footers
6854
6974
  - WORKSPACE & SUB AGENT TOOLS ARE NOT AVAILABLE IN FLOW`.trim()}`.trim();
@@ -8565,7 +8685,7 @@ Check these first; These Files > Training Data. Safety rules apply
8565
8685
  Identity: Flux Flow. Sassy, CLI Agent
8566
8686
  ${mode === "Flux" ? "Logical, task-driven. Prioritize scalable, modular architecture, clean abstractions, stepwise execution. Use latest practices/libraries, verify imports, run automated tests" : `Mode: ${mode}. Concise, Conversational, Sassy, Friendly, Humorous, Sarcastic`}
8567
8687
 
8568
- - RESOLVE FILES AND PATHS FROM THE PROVIDED DIRECTORY STRUCTURE
8688
+ - USE DIRECTORY STRUCTURE FOR FILE AVAILABILITY AND PATH RESOLUTION
8569
8689
  - USE RELATIVE TIME REFERENCE eg. few mins ago
8570
8690
 
8571
8691
  -- THINKING GUIDANCE --
@@ -8585,8 +8705,7 @@ ${projectContextBlock}${isMemoryEnabled ? `
8585
8705
  -- CHAT FORMATTING --
8586
8706
  - GFM Markdown ONLY
8587
8707
  - Same Language as User Query
8588
- - Before tool calls, briefly state intent. After, emit no chat
8589
- - On completion: summarize changes (why) + edited files${mode === "Flux" ? "" : "\n- Use Kaomojis HEAVILY"}
8708
+ - Finish all chatting before tool calls${mode === "Flux" ? "" : "\n- Use Kaomojis HEAVILY"}
8590
8709
  === END SYSTEM PROMPT ===
8591
8710
 
8592
8711
  ${nameStr}${nicknameStr}${userInstrStr}${userMemoriesStr}`.trim();
@@ -9047,8 +9166,14 @@ var init_history = __esm({
9047
9166
  const datePart = parts[0];
9048
9167
  const timePart = parts[1] || "";
9049
9168
  const ampm = parts[2] || "";
9050
- const dateNums = datePart.split(/[-/.]/).map(Number);
9169
+ const dateNums = datePart.split(/[-\/.]/).map(Number);
9051
9170
  if (dateNums.length !== 3) return null;
9171
+ let locale = "en";
9172
+ try {
9173
+ locale = Intl.DateTimeFormat().resolvedOptions().locale || "en";
9174
+ } catch {
9175
+ }
9176
+ const isDayFirst = !/^en[-_]?us$|^en$/.test(locale?.toLowerCase());
9052
9177
  let year, month, day;
9053
9178
  if (dateNums[0] > 1e3) {
9054
9179
  year = dateNums[0];
@@ -9063,12 +9188,18 @@ var init_history = __esm({
9063
9188
  day = dateNums[1];
9064
9189
  month = dateNums[0];
9065
9190
  } else {
9066
- month = dateNums[0];
9067
- day = dateNums[1];
9191
+ if (isDayFirst) {
9192
+ day = dateNums[0];
9193
+ month = dateNums[1];
9194
+ } else {
9195
+ month = dateNums[0];
9196
+ day = dateNums[1];
9197
+ }
9068
9198
  }
9069
9199
  } else {
9070
9200
  return null;
9071
9201
  }
9202
+ if (month < 1 || month > 12 || day < 1 || day > 31) return null;
9072
9203
  let hours = 0, minutes = 0, seconds = 0;
9073
9204
  if (timePart) {
9074
9205
  const timeNums = timePart.split(":").map(Number);
@@ -9113,16 +9244,19 @@ var init_history = __esm({
9113
9244
  const threshold = 7 * 24 * 60 * 60 * 1e3;
9114
9245
  const now = Date.now();
9115
9246
  const keptEntries = [];
9116
- const timestampRegex = /(\d{1,4}[-/.]\d{1,4}[-/.]\d{1,4}(?:,\s*|\s+)?(?:\d{1,2}:\d{2}:\d{2}(?:\s*[aApP][mM])?)?)/;
9247
+ const timestampRegex = /(\d{1,4}[-\/.]\d{1,4}[-\/.]\d{1,4}(?:,\s*|\s+)?(?:\d{1,2}:\d{2}:\d{2}(?:\s*[aApP][mM])?)?)/;
9117
9248
  for (const entry of entries) {
9118
- const entryText = entry.header + (entry.body.length > 0 ? "\n" + entry.body.join("\n") : "");
9119
- const match = entryText.match(timestampRegex);
9120
- if (match) {
9121
- const timeMs = parseCustomDate(match[1]);
9122
- if (timeMs && now - timeMs > threshold) {
9123
- continue;
9249
+ const isLogEntry = entryStartRegex.test(entry.header);
9250
+ if (isLogEntry) {
9251
+ const headerMatch = entry.header.match(timestampRegex);
9252
+ if (headerMatch) {
9253
+ const timeMs = parseCustomDate(headerMatch[1]);
9254
+ if (timeMs && now - timeMs > threshold) {
9255
+ continue;
9256
+ }
9124
9257
  }
9125
9258
  }
9259
+ const entryText = entry.header + (entry.body.length > 0 ? "\n" + entry.body.join("\n") : "");
9126
9260
  keptEntries.push(entryText);
9127
9261
  }
9128
9262
  const finalContent = keptEntries.join("\n").trim();
@@ -12493,10 +12627,33 @@ var init_invokeSync = __esm({
12493
12627
  });
12494
12628
 
12495
12629
  // src/utils/subagent_state.js
12496
- var subagentProgress;
12630
+ var subagent_state_exports = {};
12631
+ __export(subagent_state_exports, {
12632
+ addPendingNudge: () => addPendingNudge,
12633
+ clearPendingNudges: () => clearPendingNudges,
12634
+ consumePendingNudges: () => consumePendingNudges,
12635
+ pendingSubagentNudges: () => pendingSubagentNudges,
12636
+ subagentProgress: () => subagentProgress
12637
+ });
12638
+ var subagentProgress, pendingSubagentNudges, addPendingNudge, consumePendingNudges, clearPendingNudges;
12497
12639
  var init_subagent_state = __esm({
12498
12640
  "src/utils/subagent_state.js"() {
12499
12641
  subagentProgress = [];
12642
+ pendingSubagentNudges = [];
12643
+ addPendingNudge = (msg) => {
12644
+ if (msg) {
12645
+ pendingSubagentNudges.push(msg);
12646
+ }
12647
+ };
12648
+ consumePendingNudges = () => {
12649
+ if (pendingSubagentNudges.length === 0) return [];
12650
+ const nudges = [...pendingSubagentNudges];
12651
+ pendingSubagentNudges = [];
12652
+ return nudges;
12653
+ };
12654
+ clearPendingNudges = () => {
12655
+ pendingSubagentNudges = [];
12656
+ };
12500
12657
  }
12501
12658
  });
12502
12659
 
@@ -12528,13 +12685,24 @@ var init_invoke = __esm({
12528
12685
  }
12529
12686
  }
12530
12687
  const taskId = `subagent-${Date.now()}-${Math.floor(Math.random() * 1e3)}`;
12688
+ let _resolveCompletion = null;
12689
+ let _rejectCompletion = null;
12690
+ const completionPromise = new Promise((res, rej) => {
12691
+ _resolveCompletion = res;
12692
+ _rejectCompletion = rej;
12693
+ });
12531
12694
  const taskEntry = {
12532
12695
  id: taskId,
12533
12696
  title: title || task.substring(0, 30),
12534
12697
  task,
12535
12698
  status: "running",
12699
+ startedAt: Date.now(),
12536
12700
  lastChunkTime: Date.now(),
12537
12701
  wps: 0,
12702
+ questions: [],
12703
+ completionPromise,
12704
+ _resolveCompletion,
12705
+ _rejectCompletion,
12538
12706
  progress: []
12539
12707
  // Array of arrays containing logs for each turn
12540
12708
  };
@@ -12547,6 +12715,31 @@ var init_invoke = __esm({
12547
12715
  const subagentContext = {
12548
12716
  ...context,
12549
12717
  taskId,
12718
+ onAskMain: async (questionText) => {
12719
+ const questionId = `q-${Date.now()}-${Math.floor(Math.random() * 1e3)}`;
12720
+ let questionResolver = null;
12721
+ const qPromise = new Promise((resolve) => {
12722
+ questionResolver = resolve;
12723
+ });
12724
+ const qEntry = {
12725
+ id: questionId,
12726
+ question: questionText,
12727
+ answered: false,
12728
+ answer: null,
12729
+ askedAt: Date.now(),
12730
+ _resolve: questionResolver
12731
+ };
12732
+ taskEntry.questions.push(qEntry);
12733
+ taskEntry.status = "waiting";
12734
+ if (context.onSubagentUpdate) {
12735
+ context.onSubagentUpdate();
12736
+ }
12737
+ addPendingNudge(`[SYSTEM] Background subagent "${taskEntry.title}" is WAITING FOR YOUR INPUT: "${questionText}"
12738
+ Respond using tool: [tool:functions.Answer(id="${taskId}", answer="...")]
12739
+ [/SYSTEM]`);
12740
+ const answer = await qPromise;
12741
+ return answer;
12742
+ },
12550
12743
  onVisualFeedback: (feedbackLabel) => {
12551
12744
  taskEntry.lastChunkTime = Date.now();
12552
12745
  const clean = feedbackLabel.replace(/\x1b\[[0-9;]*m/g, "");
@@ -12608,16 +12801,22 @@ var init_invoke = __esm({
12608
12801
  if (context.onSubagentUpdate) {
12609
12802
  context.onSubagentUpdate();
12610
12803
  }
12611
- }).then((finalAnswer) => {
12612
- if (taskEntry.status === "cancelled") return;
12613
- currentTurnLogs.push(`[SUBAGENT SUCCESS] Final Answer:
12614
- ${finalAnswer}`);
12615
- taskEntry.progress.push([...currentTurnLogs]);
12804
+ }, true).then((finalAnswer) => {
12805
+ if (taskEntry.status === "cancelled") {
12806
+ if (taskEntry._resolveCompletion) taskEntry._resolveCompletion(finalAnswer);
12807
+ return;
12808
+ }
12809
+ if (currentTurnLogs.length > 0) {
12810
+ taskEntry.progress.push([...currentTurnLogs]);
12811
+ currentTurnLogs = [];
12812
+ }
12616
12813
  taskEntry.status = "completed";
12617
12814
  taskEntry.finalAnswer = finalAnswer;
12618
12815
  if (context.onSubagentUpdate) {
12619
12816
  context.onSubagentUpdate();
12620
12817
  }
12818
+ addPendingNudge(`[SYSTEM] Background subagent "${taskEntry.title}" (id: ${taskId}) has FINISHED. Call GetProgress(id="${taskId}") to see the final result. [/SYSTEM]`);
12819
+ if (taskEntry._resolveCompletion) taskEntry._resolveCompletion(finalAnswer);
12621
12820
  }).catch(async (err) => {
12622
12821
  const { isTerminationSignaled: isTerminationSignaled2 } = await init_ai().then(() => ai_exports);
12623
12822
  const isCancelled = err.message === "Subagent task was cancelled." || taskEntry.status === "cancelled" || isTerminationSignaled2();
@@ -12628,6 +12827,7 @@ ${finalAnswer}`);
12628
12827
  if (context.onSubagentUpdate) {
12629
12828
  context.onSubagentUpdate();
12630
12829
  }
12830
+ if (taskEntry._resolveCompletion) taskEntry._resolveCompletion(null);
12631
12831
  return;
12632
12832
  }
12633
12833
  currentTurnLogs.push(`[SUBAGENT FAILURE] Error: ${err.message}`);
@@ -12637,6 +12837,8 @@ ${finalAnswer}`);
12637
12837
  if (context.onSubagentUpdate) {
12638
12838
  context.onSubagentUpdate();
12639
12839
  }
12840
+ addPendingNudge(`[SYSTEM] Background subagent "${taskEntry.title}" (id: ${taskId}) FAILED with error: ${err.message}. [/SYSTEM]`);
12841
+ if (taskEntry._rejectCompletion) taskEntry._rejectCompletion(err);
12640
12842
  });
12641
12843
  return `SUCCESS: Background subagent started. Task ID: ${taskId}`;
12642
12844
  };
@@ -12664,14 +12866,43 @@ var init_getProgress = __esm({
12664
12866
  output += `Title: ${task.title}
12665
12867
  `;
12666
12868
  output += `Task: ${task.task}
12869
+ `;
12870
+ if (task.startedAt) {
12871
+ const elapsedSec = Math.floor((Date.now() - task.startedAt) / 1e3);
12872
+ output += `Elapsed Time: ${elapsedSec}s
12873
+ `;
12874
+ }
12875
+ output += `Turns Completed: ${task.progress.length}
12876
+ `;
12877
+ if (task.status === "running" || task.status === "waiting") {
12878
+ if (task.currentTool) output += `Current Tool: ${task.currentTool}
12879
+ `;
12880
+ if (task.wps > 0) output += `TPS: ${task.wps}
12881
+ `;
12882
+ }
12883
+ if (task.questions && task.questions.length > 0) {
12884
+ const pending = task.questions.filter((q) => !q.answered);
12885
+ if (pending.length > 0) {
12886
+ output += `
12887
+ **PENDING QUESTION**
12888
+ `;
12889
+ pending.forEach((q) => {
12890
+ output += `"${q.question}"
12891
+ `;
12892
+ });
12893
+ output += `Respond using tool: [tool:functions.Answer(id="${task.id}", answer="...")]
12667
12894
 
12668
12895
  `;
12669
- output += `Progress Log:
12896
+ }
12897
+ }
12898
+ output += `
12899
+ Progress Log:
12670
12900
  `;
12671
12901
  task.progress.forEach((turnLogs, index) => {
12672
12902
  output += `--- Turn ${index + 1} ---
12673
12903
  `;
12674
- const processedLogs = turnLogs.map((log) => {
12904
+ const filteredLogs = turnLogs.filter((log) => !log.startsWith("[SUBAGENT SUCCESS]"));
12905
+ const processedLogs = filteredLogs.map((log) => {
12675
12906
  if (log.startsWith("[Subagent Response]")) {
12676
12907
  const header = "[Subagent Response]";
12677
12908
  const body = log.substring(header.length);
@@ -12746,7 +12977,7 @@ ${task.finalAnswer}
12746
12977
  output += `Failure Error: ${task.error}
12747
12978
  `;
12748
12979
  }
12749
- const sanitized = output.trim().replace(/\[TOOL RESULT\]/gi, "TOOL RESULT:");
12980
+ const sanitized = output.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim().replace(/\[TOOL RESULT\]/gi, "TOOL RESULT:");
12750
12981
  return sanitized;
12751
12982
  };
12752
12983
  }
@@ -12784,37 +13015,9 @@ var init_cancel = __esm({
12784
13015
  });
12785
13016
 
12786
13017
  // src/tools/await.js
12787
- var awaitTool;
12788
13018
  var init_await = __esm({
12789
13019
  "src/tools/await.js"() {
12790
13020
  init_arg_parser();
12791
- awaitTool = async (args, context = {}) => {
12792
- const parsed = parseArgs(args);
12793
- const timeStr = parsed.time;
12794
- if (!timeStr) {
12795
- return 'ERROR: Missing "time" argument for await.';
12796
- }
12797
- let seconds = parseFloat(timeStr);
12798
- if (isNaN(seconds)) {
12799
- return `ERROR: Invalid time value "${timeStr}". Must be a number.`;
12800
- }
12801
- if (seconds < 10) {
12802
- seconds = 10;
12803
- } else if (seconds > 180) {
12804
- seconds = 180;
12805
- }
12806
- const formatTime = (s) => {
12807
- if (s >= 60) {
12808
- const m = Math.floor(s / 60);
12809
- const rem = s % 60;
12810
- return `${m}m${rem > 0 ? ` ${rem}s` : ""}`;
12811
- }
12812
- return `${s}s`;
12813
- };
12814
- const formatted = formatTime(seconds);
12815
- await new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
12816
- return `SUCCESS: Waited for ${formatted}${seconds > 180 ? " (Max: 180s)" : ""}${seconds < 10 ? " (Min: 10s)" : ""}.`;
12817
- };
12818
13021
  }
12819
13022
  });
12820
13023
 
@@ -13169,6 +13372,120 @@ Tools Used: ${toolsStr}
13169
13372
  }
13170
13373
  });
13171
13374
 
13375
+ // src/tools/awaitSubagent.js
13376
+ var awaitSubagent;
13377
+ var init_awaitSubagent = __esm({
13378
+ "src/tools/awaitSubagent.js"() {
13379
+ init_subagent_state();
13380
+ init_arg_parser();
13381
+ awaitSubagent = async (args, context = {}) => {
13382
+ const parsed = parseArgs(args);
13383
+ const id = parsed.id;
13384
+ let timeoutSec = parseInt(parsed.timeout || parsed.time || "120", 10);
13385
+ if (isNaN(timeoutSec) || timeoutSec <= 0) timeoutSec = 120;
13386
+ if (timeoutSec > 300) timeoutSec = 300;
13387
+ if (!id) {
13388
+ if (parsed.time) {
13389
+ await new Promise((resolve) => setTimeout(resolve, timeoutSec * 1e3));
13390
+ return `SUCCESS: Waited for ${timeoutSec}s.`;
13391
+ }
13392
+ return 'ERROR: Missing "id" argument for Await.';
13393
+ }
13394
+ const task = subagentProgress.find((t) => t.id === id);
13395
+ if (!task) {
13396
+ return `ERROR: Subagent task with ID [${id}] not found.`;
13397
+ }
13398
+ if (task.status === "completed") {
13399
+ return `SUCCESS: Subagent task [${id}] completed.
13400
+ Final Answer:
13401
+ ${task.finalAnswer || "(No output)"}`;
13402
+ }
13403
+ if (task.status === "failed") {
13404
+ return `ERROR: Subagent task [${id}] failed.
13405
+ Error: ${task.error || "Unknown error"}`;
13406
+ }
13407
+ if (task.status === "cancelled") {
13408
+ return `INFO: Subagent task [${id}] was cancelled.`;
13409
+ }
13410
+ let timeoutId;
13411
+ const timeoutPromise = new Promise((resolve) => {
13412
+ timeoutId = setTimeout(() => {
13413
+ resolve({ type: "timeout" });
13414
+ }, timeoutSec * 1e3);
13415
+ });
13416
+ try {
13417
+ const result = await Promise.race([
13418
+ task.completionPromise.then(() => ({ type: "completion" })),
13419
+ timeoutPromise
13420
+ ]);
13421
+ clearTimeout(timeoutId);
13422
+ if (result.type === "timeout") {
13423
+ return `TIMEOUT: Subagent task [${id}] is still running (status: ${task.status.toUpperCase()}) after ${timeoutSec}s. You can continue other work or call Await again.`;
13424
+ }
13425
+ if (task.status === "completed") {
13426
+ return `SUCCESS: Subagent task [${id}] completed.
13427
+ Final Answer:
13428
+ ${task.finalAnswer || "(No output)"}`;
13429
+ } else if (task.status === "failed") {
13430
+ return `ERROR: Subagent task [${id}] failed.
13431
+ Error: ${task.error || "Unknown error"}`;
13432
+ } else if (task.status === "cancelled") {
13433
+ return `INFO: Subagent task [${id}] was cancelled.`;
13434
+ } else {
13435
+ return `INFO: Subagent task [${id}] status changed to ${task.status.toUpperCase()}.`;
13436
+ }
13437
+ } catch (err) {
13438
+ clearTimeout(timeoutId);
13439
+ return `ERROR: Exception while awaiting subagent [${id}]: ${err.message}`;
13440
+ }
13441
+ };
13442
+ }
13443
+ });
13444
+
13445
+ // src/tools/answerSubagent.js
13446
+ var answerSubagent;
13447
+ var init_answerSubagent = __esm({
13448
+ "src/tools/answerSubagent.js"() {
13449
+ init_subagent_state();
13450
+ init_arg_parser();
13451
+ answerSubagent = async (args, context = {}) => {
13452
+ const parsed = parseArgs(args);
13453
+ const id = parsed.id;
13454
+ const answer = parsed.answer || parsed.response;
13455
+ if (!id) {
13456
+ return 'ERROR: Missing "id" argument for Answer.';
13457
+ }
13458
+ if (!answer) {
13459
+ return 'ERROR: Missing "answer" argument for Answer.';
13460
+ }
13461
+ const task = subagentProgress.find((t) => t.id === id);
13462
+ if (!task) {
13463
+ return `ERROR: Subagent task with ID [${id}] not found.`;
13464
+ }
13465
+ if (!task.questions || task.questions.length === 0) {
13466
+ return `INFO: Subagent task [${id}] has no pending questions.`;
13467
+ }
13468
+ const pending = task.questions.filter((q) => !q.answered);
13469
+ if (pending.length === 0) {
13470
+ return `INFO: Subagent task [${id}] has no unanswered questions.`;
13471
+ }
13472
+ pending.forEach((q) => {
13473
+ q.answered = true;
13474
+ q.answer = answer;
13475
+ q.answeredAt = Date.now();
13476
+ if (q._resolve) {
13477
+ q._resolve(answer);
13478
+ }
13479
+ });
13480
+ task.status = "running";
13481
+ if (context.onSubagentUpdate) {
13482
+ context.onSubagentUpdate();
13483
+ }
13484
+ return `SUCCESS: Answer provided to subagent task [${id}]. Subagent execution resumed.`;
13485
+ };
13486
+ }
13487
+ });
13488
+
13172
13489
  // src/utils/tools.js
13173
13490
  var TOOL_MAP, dispatchTool;
13174
13491
  var init_tools = __esm({
@@ -13197,6 +13514,8 @@ var init_tools = __esm({
13197
13514
  init_cancel();
13198
13515
  init_await();
13199
13516
  init_emergency_rollback();
13517
+ init_awaitSubagent();
13518
+ init_answerSubagent();
13200
13519
  TOOL_MAP = {
13201
13520
  web_search,
13202
13521
  web_scrape,
@@ -13219,8 +13538,12 @@ var init_tools = __esm({
13219
13538
  invoke,
13220
13539
  getProgress,
13221
13540
  cancel,
13541
+ awaitSubagent,
13542
+ answerSubagent,
13222
13543
  invoke_sync: invokeSync,
13223
13544
  get_progress: getProgress,
13545
+ await_subagent: awaitSubagent,
13546
+ answer_subagent: answerSubagent,
13224
13547
  ask: ask_user,
13225
13548
  // PascalCase Normalizations for Token Efficiency
13226
13549
  Ask: ask_user,
@@ -13245,14 +13568,12 @@ var init_tools = __esm({
13245
13568
  addMemoryScore: addMemScore,
13246
13569
  AddMemoryScore: addMemScore,
13247
13570
  FileMap: file_map,
13248
- Todo: todo,
13249
- TODO: todo,
13250
- InvokeSync: invokeSync,
13251
- Invoke: invoke,
13252
- GetProgress: getProgress,
13253
- Cancel: cancel,
13254
- await: awaitTool,
13255
- Await: awaitTool,
13571
+ answer: answerSubagent,
13572
+ Answer: answerSubagent,
13573
+ AnswerSubagent: answerSubagent,
13574
+ await: awaitSubagent,
13575
+ Await: awaitSubagent,
13576
+ AwaitSubagent: awaitSubagent,
13256
13577
  EmergencyRollback: emergency_rollback,
13257
13578
  emergency_rollback
13258
13579
  };
@@ -13521,6 +13842,7 @@ __export(ai_exports, {
13521
13842
  deleteChatSummary: () => deleteChatSummary,
13522
13843
  getAIStream: () => getAIStream,
13523
13844
  getCleanGroupedLength: () => getCleanGroupedLength,
13845
+ getGoogleClient: () => getGoogleClient,
13524
13846
  initAI: () => initAI,
13525
13847
  isModelMultimodal: () => isModelMultimodal,
13526
13848
  isTerminationSignaled: () => isTerminationSignaled,
@@ -13532,7 +13854,7 @@ import dotenv from "dotenv";
13532
13854
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
13533
13855
  import path26, { normalize } from "path";
13534
13856
  import fs27 from "fs";
13535
- 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;
13857
+ 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_CALL_ANY, RE_TOOL_PARTIAL_ARGS_FALLBACK, RE_STRIP_QUOTES, RE_BACKSLASH_SLASH, RE_STRIP_THINK_CLOSED, RE_STRIP_THINK_OPEN, RE_STRIP_THINK_SIMPLE, RE_STRIP_THINK_FULL, RE_BACKTICK_SPAN, RE_BACKTICK_OPEN, RE_KIMI_TOOL_CALL, RE_KIMI_JSON_PAIR, RE_KIMI_SECTION_BEGIN, RE_KIMI_SECTION_END, bypassBacktick2, client, globalSettings, systemInstructionCache, colorMainWords, withRetry, TERMINATION_SIGNAL, getCleanGroupedLength, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getMistralStream, getNVIDIAStream, wrapNvidiaStreamWithQueueDepth, getOpenRouterStream, signalTermination, isTerminationSignaled, TOOL_LABELS2, getToolDetail, getGoogleClient, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, translateKimiToolCalls, REGEX_PLACEHOLDER_ARG, REGEX_PLACEHOLDER_VAL, isPlaceholderVal, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, compressHistory, deleteChatSummary, getAIStream, runSubagent;
13536
13858
  var init_ai = __esm({
13537
13859
  async "src/utils/ai.js"() {
13538
13860
  await init_prompts();
@@ -13563,15 +13885,27 @@ var init_ai = __esm({
13563
13885
  RE_STUTTER_WORD_BOUNDARY = /^[^\w]+|[^\w]+$/g;
13564
13886
  RE_STUTTER_NON_ALNUM = /[^a-z0-9]/gi;
13565
13887
  RE_TOOL_CALL_FUNC = /\[\s*tool:functions\.([a-z0-9_]+)\s*\(/gi;
13888
+ RE_TOOL_CALL_ANY = /\[\s*(?:tool:functions\.|agent:generalist\.)([a-z0-9_]+)\s*\(/gi;
13566
13889
  RE_TOOL_PARTIAL_ARGS_FALLBACK = /(?:path|targetFile|TargetFile|directory|keyword|id|taskId|title|task)\s*=\s*\\?["']?([^\\"' \),]+)/;
13567
13890
  RE_STRIP_QUOTES = /["']/g;
13568
13891
  RE_BACKSLASH_SLASH = /\\/g;
13892
+ RE_STRIP_THINK_CLOSED = /(?:<(think|thought)>|\[(think|thought)\])[\s\S]*?(?:<\/(think|thought)>|\[\/(think|thought)\])/gi;
13893
+ RE_STRIP_THINK_OPEN = /(?:<(think|thought)>|\[(think|thought)\])[\s\S]*$/gi;
13894
+ RE_STRIP_THINK_SIMPLE = /(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\]|$)/gi;
13895
+ RE_STRIP_THINK_FULL = /(?:<(think|thought|thoughts)>|\[(think|thought|thoughts)\])[\s\S]*?(?:<\/(think|thought|thoughts)>|\[\/(think|thought|thoughts)\]|$)/gi;
13896
+ RE_BACKTICK_SPAN = /`[^`]*`/g;
13897
+ RE_BACKTICK_OPEN = /`[^`]*$/;
13898
+ RE_KIMI_TOOL_CALL = /<\|\s*tool_call_begin\s*\|>\s*(?:(?:tool|functions)\b[\s._]*)*([a-zA-Z0-9_]+)(?::\d+)?\s*<\|\s*tool_call_argument_begin\s*\|>([\s\S]*?)<\|\s*tool_call_end\s*\|>/gi;
13899
+ RE_KIMI_JSON_PAIR = /"([^"]+)"\s*:\s*(?:"([^"]*)"|(\d+)|true|false|null)/g;
13900
+ RE_KIMI_SECTION_BEGIN = /<\|\s*tool_calls_section_begin\s*\|>/gi;
13901
+ RE_KIMI_SECTION_END = /<\|\s*tool_calls_section_end\s*\|>/gi;
13902
+ bypassBacktick2 = false;
13569
13903
  client = null;
13570
13904
  globalSettings = {};
13571
13905
  systemInstructionCache = { key: null, value: null };
13572
13906
  colorMainWords = (label) => {
13573
13907
  if (!label) return label;
13574
- 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) => {
13908
+ 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|Resolved Sub-Agent Query|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) => {
13575
13909
  return `${ansiBefore || ""}${icon}${ansiAfter || ""} \x1B[95m${word}\x1B[0m`;
13576
13910
  });
13577
13911
  };
@@ -14496,12 +14830,14 @@ var init_ai = __esm({
14496
14830
  "generate_image": "Generating",
14497
14831
  "todo": "Planning",
14498
14832
  "Todo": "Planning",
14499
- "invoke_sync": "Generalist",
14500
- "invoke": "Generalist",
14833
+ "invoke_sync": "Sub-Agent Working",
14834
+ "invoke": "Starting Agent",
14501
14835
  "get_progress": "Checking Progress",
14502
14836
  "cancel": "Cancelling",
14503
14837
  "await": "Waiting",
14504
- "EmergencyRollback": "Don't Panic. Lookin' into it"
14838
+ "EmergencyRollback": "Don't Panic. Lookin' into it",
14839
+ "answer": "Answering Sub-Agent",
14840
+ "Answer": "Answering Sub-Agent"
14505
14841
  };
14506
14842
  getToolDetail = (toolName, argsStr) => {
14507
14843
  try {
@@ -14519,6 +14855,12 @@ var init_ai = __esm({
14519
14855
  return null;
14520
14856
  }
14521
14857
  };
14858
+ getGoogleClient = (apiKey) => {
14859
+ if (apiKey) {
14860
+ return new GoogleGenAI({ apiKey });
14861
+ }
14862
+ return client;
14863
+ };
14522
14864
  runJanitorTask = async (settings, agentText, fullAgentTextRaw, history, callbacks = {}) => {
14523
14865
  const USER_CONTEXT_LENGTH = 4 * (1024 * 2);
14524
14866
  const AGENT_CONTEXT_LENGTH = 4 * (1024 * 8);
@@ -14657,7 +14999,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14657
14999
  const firstResult2 = await iterator2.next();
14658
15000
  return { iterator: iterator2, firstResult: firstResult2 };
14659
15001
  } else {
14660
- const stream = await client.models.generateContentStream({
15002
+ const googleClient = getGoogleClient(apiKey);
15003
+ const stream = await googleClient.models.generateContentStream({
14661
15004
  model: janitorModel || (attempts === MAX_JANITOR_RETRIES ? getFallbackValue("janitor_default") : getFallbackValue("gemma_janitor_fallback_google")),
14662
15005
  contents: janitorContents,
14663
15006
  config: {
@@ -14812,16 +15155,17 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14812
15155
  }
14813
15156
  };
14814
15157
  getActiveToolContext = (text) => {
14815
- const cleanText = text.replace(/(?:<(think|thought)>|\[(think|thought)\])[\s\S]*?(?:<\/(think|thought)>|\[\/(think|thought)\])/gi, "").replace(/(?:<(think|thought)>|\[(think|thought)\])[\s\S]*$/gi, "");
15158
+ const cleanText = text.replace(RE_STRIP_THINK_CLOSED, "").replace(RE_STRIP_THINK_OPEN, "");
15159
+ const scanText = bypassBacktick2 ? cleanText : cleanText.replace(RE_BACKTICK_SPAN, (m) => " ".repeat(m.length)).replace(RE_BACKTICK_OPEN, (m) => " ".repeat(m.length));
14816
15160
  RE_TOOL_CALL_FUNC.lastIndex = 0;
14817
15161
  let match;
14818
- while ((match = RE_TOOL_CALL_FUNC.exec(cleanText)) !== null) {
15162
+ while ((match = RE_TOOL_CALL_FUNC.exec(scanText)) !== null) {
14819
15163
  const startIdx = match.index + match[0].length - 1;
14820
15164
  let balance = 0;
14821
15165
  let inString = null;
14822
15166
  let isEscaped = false;
14823
15167
  let closed = false;
14824
- for (let i = startIdx; i < cleanText.length; i++) {
15168
+ for (let i = startIdx; i < scanText.length; i++) {
14825
15169
  const char = cleanText[i];
14826
15170
  if (!inString && (char === '"' || char === "'" || char === "`")) {
14827
15171
  inString = char;
@@ -14834,8 +15178,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14834
15178
  else if (char === ")") balance--;
14835
15179
  if (balance === 0) {
14836
15180
  let j = i + 1;
14837
- while (j < cleanText.length && /\s/.test(cleanText[j])) j++;
14838
- if (j < cleanText.length && cleanText[j] === "]") {
15181
+ while (j < scanText.length && /\s/.test(scanText[j])) j++;
15182
+ if (j < scanText.length && scanText[j] === "]") {
14839
15183
  closed = true;
14840
15184
  RE_TOOL_CALL_FUNC.lastIndex = j + 1;
14841
15185
  break;
@@ -14852,13 +15196,14 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14852
15196
  return { inside: false };
14853
15197
  };
14854
15198
  getContextSafeText = (text, stripThoughts = true) => {
14855
- const toolRegex = /\[\s*tool:functions\.([a-z0-9_]+)\s*\(/gi;
15199
+ const toolRegex = RE_TOOL_CALL_FUNC;
15200
+ toolRegex.lastIndex = 0;
14856
15201
  let result = "";
14857
15202
  let lastIdx = 0;
14858
15203
  let match;
14859
15204
  while ((match = toolRegex.exec(text)) !== null) {
14860
15205
  const before = text.substring(lastIdx, match.index);
14861
- result += stripThoughts ? before.replace(/(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\]|$)/gi, "") : before;
15206
+ result += stripThoughts ? before.replace(RE_STRIP_THINK_SIMPLE, "") : before;
14862
15207
  const startIdx = match.index + match[0].length - 1;
14863
15208
  let balance = 0;
14864
15209
  let inString = null;
@@ -14904,12 +15249,13 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14904
15249
  }
14905
15250
  }
14906
15251
  if (lastIdx < text.length) {
14907
- result += stripThoughts ? text.substring(lastIdx).replace(/(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\]|$)/gi, "") : text.substring(lastIdx);
15252
+ result += stripThoughts ? text.substring(lastIdx).replace(RE_STRIP_THINK_SIMPLE, "") : text.substring(lastIdx);
14908
15253
  }
14909
15254
  return result;
14910
15255
  };
14911
15256
  contextSafeReplace = (text, regex, replacement) => {
14912
- const toolRegex = /\[\s*tool:functions\.([a-z0-9_]+)\s*\(/gi;
15257
+ const toolRegex = RE_TOOL_CALL_FUNC;
15258
+ toolRegex.lastIndex = 0;
14913
15259
  let result = "";
14914
15260
  let lastIdx = 0;
14915
15261
  let match;
@@ -14992,8 +15338,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14992
15338
  const toPascalCase = (str) => {
14993
15339
  return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
14994
15340
  };
14995
- const kimiRegex = /<\|\s*tool_call_begin\s*\|>\s*(?:(?:tool|functions)\b[\s._]*)*([a-zA-Z0-9_]+)(?::\d+)?\s*<\|\s*tool_call_argument_begin\s*\|>([\s\S]*?)<\|\s*tool_call_end\s*\|>/gi;
14996
- let result = text.replace(kimiRegex, (match, toolName, argsJsonStr) => {
15341
+ RE_KIMI_TOOL_CALL.lastIndex = 0;
15342
+ let result = text.replace(RE_KIMI_TOOL_CALL, (match, toolName, argsJsonStr) => {
14997
15343
  let parsedArgs = "";
14998
15344
  try {
14999
15345
  const argsObj = JSON.parse(argsJsonStr.trim());
@@ -15006,7 +15352,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15006
15352
  }
15007
15353
  } catch (e) {
15008
15354
  const pairs = [];
15009
- const pairRegex = /"([^"]+)"\s*:\s*(?:"([^"]*)"|(\d+)|true|false|null)/g;
15355
+ const pairRegex = RE_KIMI_JSON_PAIR;
15356
+ pairRegex.lastIndex = 0;
15010
15357
  let pMatch;
15011
15358
  while ((pMatch = pairRegex.exec(argsJsonStr)) !== null) {
15012
15359
  const key = pMatch[1];
@@ -15023,8 +15370,10 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15023
15370
  const normToolName = PASCAL_MAP[cleanKey] || toPascalCase(toolName);
15024
15371
  return `[tool:functions.${normToolName}(${parsedArgs})]`;
15025
15372
  });
15026
- result = result.replace(/<\|\s*tool_calls_section_begin\s*\|>/gi, "");
15027
- result = result.replace(/<\|\s*tool_calls_section_end\s*\|>/gi, "");
15373
+ RE_KIMI_SECTION_BEGIN.lastIndex = 0;
15374
+ RE_KIMI_SECTION_END.lastIndex = 0;
15375
+ result = result.replace(RE_KIMI_SECTION_BEGIN, "");
15376
+ result = result.replace(RE_KIMI_SECTION_END, "");
15028
15377
  return result;
15029
15378
  };
15030
15379
  REGEX_PLACEHOLDER_ARG = /(?:path|query|url|keyword|command|method|title|task|id)\s*=\s*['"`]?\s*\.\.\.\s*['"`]?/i;
@@ -15037,18 +15386,21 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15037
15386
  detectToolCalls = (text) => {
15038
15387
  if (!text) return [];
15039
15388
  const translatedText = translateKimiToolCalls(text);
15040
- const cleanText = translatedText.replace(/(?:<(think|thought|thoughts)>|\[(think|thought|thoughts)\])[\s\S]*?(?:<\/(think|thought|thoughts)>|\[\/(think|thought|thoughts)\]|$)/gi, "");
15389
+ RE_STRIP_THINK_FULL.lastIndex = 0;
15390
+ const cleanText = translatedText.replace(RE_STRIP_THINK_FULL, "");
15041
15391
  const results = [];
15042
- const toolRegex = /\[\s*(?:tool:functions\.|agent:generalist\.)([a-z0-9_]+)\s*\(/gi;
15392
+ const scanText = bypassBacktick2 ? cleanText : cleanText.replace(RE_BACKTICK_SPAN, (m) => " ".repeat(m.length)).replace(RE_BACKTICK_OPEN, (m) => " ".repeat(m.length));
15393
+ const toolRegex = RE_TOOL_CALL_ANY;
15394
+ toolRegex.lastIndex = 0;
15043
15395
  let match;
15044
- while ((match = toolRegex.exec(cleanText)) !== null) {
15396
+ while ((match = toolRegex.exec(scanText)) !== null) {
15045
15397
  const toolName = match[1];
15046
15398
  const startIdx = match.index + match[0].length - 1;
15047
15399
  let balance = 0;
15048
15400
  let inString = null;
15049
15401
  let endIdx = -1;
15050
15402
  let closingParenIdx = -1;
15051
- for (let i = startIdx; i < cleanText.length; i++) {
15403
+ for (let i = startIdx; i < scanText.length; i++) {
15052
15404
  const char = cleanText[i];
15053
15405
  if (inString) {
15054
15406
  if (char === inString) {
@@ -15070,8 +15422,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15070
15422
  if (balance === 0) {
15071
15423
  closingParenIdx = i;
15072
15424
  let j = i + 1;
15073
- while (j < cleanText.length && /\s/.test(cleanText[j])) j++;
15074
- if (j < cleanText.length && cleanText[j] === "]") {
15425
+ while (j < scanText.length && /\s/.test(scanText[j])) j++;
15426
+ if (j < scanText.length && scanText[j] === "]") {
15075
15427
  endIdx = j;
15076
15428
  break;
15077
15429
  }
@@ -15126,7 +15478,6 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15126
15478
  }
15127
15479
  };
15128
15480
  }
15129
- return client;
15130
15481
  };
15131
15482
  generateSimpleContent = async (settings, model, contents, systemInstruction, thinkingLevel = "Fast", temperature = 0.75, usageKey = "agent") => {
15132
15483
  return withRetry(async () => {
@@ -15153,7 +15504,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15153
15504
  } else if (aiProvider === "NVIDIA") {
15154
15505
  stream = getNVIDIAStream(apiKey, model, normalizedContents, systemInstruction, thinkingLevel, mode, isModelMultimodal(model), signal, temperature);
15155
15506
  } else {
15156
- const genStream = await client.models.generateContentStream({
15507
+ const googleClient = getGoogleClient(apiKey);
15508
+ const genStream = await googleClient.models.generateContentStream({
15157
15509
  model,
15158
15510
  contents: normalizedContents,
15159
15511
  config: {
@@ -16058,7 +16410,7 @@ OS: ${osDetected}${systemSettings?.dynamicDirAwareness ? dirStructure : ""}${cwd
16058
16410
  WARNING: CWD Changed from previous: "${lastCwd}" to current: "${process.cwd()}", write change in chat to avoid future path mismatches
16059
16411
  ` : ""}${memoryPrompt}${ideBlock}
16060
16412
  [/METADATA]
16061
- ${activeSummaryBlock}${thinkingLevel !== "Fast" && (aiProvider === "Mistral" || thinkingLevel !== "xHigh" && aiProvider === "Google") ? `${aiProvider === "Mistral" || modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS HIGH PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[SYSTEM Priority: HIGH] ONLY use the system prompt tool schema [tool:functions.ToolName(...)] [/SYSTEM]
16413
+ ${activeSummaryBlock}${thinkingLevel !== "Fast" && (aiProvider === "Mistral" || thinkingLevel !== "xHigh" && aiProvider === "Google") ? `${aiProvider === "Mistral" || modelName.toLowerCase().startsWith("gemma") ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS HIGH PRIORITY. DO NOT START A RESPONSE WITHOUT <think> ... </think>** [/SYSTEM]\n" : ""}` : ""}[SYSTEM Priority: HIGH] ONLY use the system prompt tool schema [tool:functions.ToolName(arg1="value1")] [/SYSTEM]
16062
16414
  ${taggedContextStr}[USER PROMPT]
16063
16415
  ${cleanPromptForModel.trim()}
16064
16416
  [/USER PROMPT]`.trim();
@@ -16093,10 +16445,31 @@ ${cleanPromptForModel.trim()}
16093
16445
  yield { type: "status", content: "Working" };
16094
16446
  }
16095
16447
  if (TERMINATION_SIGNAL) {
16448
+ try {
16449
+ const { clearPendingNudges: clearPendingNudges2 } = await Promise.resolve().then(() => (init_subagent_state(), subagent_state_exports));
16450
+ clearPendingNudges2();
16451
+ } catch (e) {
16452
+ }
16096
16453
  yield { type: "status", content: "Request Cancelled" };
16097
16454
  yield { type: "text", content: "\n\n\x1B[33m\u24D8 Request Cancelled\x1B[0m" };
16098
16455
  break;
16099
16456
  }
16457
+ try {
16458
+ const { consumePendingNudges: consumePendingNudges2 } = await Promise.resolve().then(() => (init_subagent_state(), subagent_state_exports));
16459
+ const pendingNudges = consumePendingNudges2();
16460
+ if (pendingNudges && pendingNudges.length > 0) {
16461
+ const combinedNudge = pendingNudges.join("\n\n");
16462
+ if (modifiedHistory.length > 0 && modifiedHistory[modifiedHistory.length - 1].role === "user") {
16463
+ modifiedHistory[modifiedHistory.length - 1].text += `
16464
+
16465
+ ${combinedNudge}`;
16466
+ } else {
16467
+ modifiedHistory.push({ role: "user", text: combinedNudge });
16468
+ }
16469
+ yield { type: "status", content: "Subagent Update" };
16470
+ }
16471
+ } catch (e) {
16472
+ }
16100
16473
  if (steeringCallback) {
16101
16474
  const hint = await steeringCallback();
16102
16475
  if (hint) {
@@ -16358,7 +16731,8 @@ ${ideErr} [/ERROR]`;
16358
16731
  );
16359
16732
  stream = wrapNvidiaStreamWithQueueDepth(rawStream, targetModel);
16360
16733
  } else {
16361
- const apiCallPromise = client.models.generateContentStream({
16734
+ const googleClient = getGoogleClient(settings?.apiKey);
16735
+ const apiCallPromise = googleClient.models.generateContentStream({
16362
16736
  model: targetModel || "gemini-3-flash-preview",
16363
16737
  contents: activeContents,
16364
16738
  config: {
@@ -16699,19 +17073,19 @@ ${ideErr} [/ERROR]`;
16699
17073
  "getProgress": "get_progress",
16700
17074
  "GetProgress": "get_progress",
16701
17075
  "Cancel": "cancel",
16702
- "await": "await",
16703
- "Await": "await"
17076
+ "Await": "await",
17077
+ "Answer": "answer"
16704
17078
  };
16705
17079
  const potentialTool = NORMALIZE_MAP[toolContext.toolName] || toolContext.toolName;
16706
17080
  const partialArgs = toolContext.args || "";
16707
17081
  let detail = null;
16708
- if (["write_file", "update_file", "view_file", "read_folder", "write_pdf", "write_docx", "search_keyword", "generate_image", "file_map", "invoke", "invoke_sync", "get_progress", "await"].includes(potentialTool)) {
17082
+ if (["write_file", "update_file", "view_file", "read_folder", "write_pdf", "write_docx", "search_keyword", "generate_image", "file_map", "invoke", "invoke_sync", "get_progress", "await", "answer"].includes(potentialTool)) {
16709
17083
  const pArgs = parseArgs(partialArgs);
16710
17084
  const filePath = pArgs.path || pArgs.targetFile || pArgs.TargetFile || pArgs.directory;
16711
17085
  const keyword = pArgs.keyword;
16712
17086
  const title = pArgs.title || pArgs.task;
16713
17087
  const id = pArgs.id || pArgs.taskId;
16714
- const timeVal = pArgs.time;
17088
+ const timeVal = pArgs.timeout || pArgs.time;
16715
17089
  if (keyword !== void 0 && keyword !== null) {
16716
17090
  detail = String(keyword).replace(RE_STRIP_QUOTES, "");
16717
17091
  } else if (filePath) {
@@ -16778,17 +17152,19 @@ ${ideErr} [/ERROR]`;
16778
17152
  "Ask": "User Input Required",
16779
17153
  "Memory": "Updating Memory",
16780
17154
  "GenerateImage": "Generating",
16781
- "InvokeSync": "Generalist Working",
16782
- "invoke_sync": "Generalist Working",
16783
- "Invoke": "Generalist Working",
16784
- "invoke": "Generalist Working",
17155
+ "InvokeSync": "Sub-Agent Working",
17156
+ "invoke_sync": "Sub-Agent Working",
17157
+ "Invoke": "Working",
17158
+ "invoke": "Working",
16785
17159
  "GetProgress": "Checking Progress",
16786
17160
  "get_progress": "Checking Progress",
16787
17161
  "Cancel": "Stopping Generalist",
16788
17162
  "cancel": "Stopping Generalist",
16789
17163
  "Await": "Waiting",
16790
17164
  "await": "Waiting",
16791
- "EmergencyRollback": "Rolling the Ball"
17165
+ "EmergencyRollback": "Rolling the Ball",
17166
+ "Answer": "Answering Sub-Agent",
17167
+ "answer": "Answering Sub-Agent"
16792
17168
  };
16793
17169
  const toolTitle = TOOL_TITLES[potentialTool] || "Working";
16794
17170
  process.stdout.write(`\x1B]0;${toolTitle}...\x07`);
@@ -16920,14 +17296,20 @@ ${ideErr} [/ERROR]`;
16920
17296
  "generate_image": "generate_image",
16921
17297
  "todo": "todo",
16922
17298
  "Todo": "todo",
16923
- "invoke": "invoke",
17299
+ "Invoke": "invoke",
16924
17300
  "InvokeSync": "invoke_sync",
16925
17301
  "getProgress": "get_progress",
16926
17302
  "GetProgress": "get_progress",
17303
+ "Await": "await",
17304
+ "await": "await",
17305
+ "AwaitSubagent": "await",
17306
+ "awaitSubagent": "await",
17307
+ "Answer": "answer",
17308
+ "answer": "answer",
17309
+ "AnswerSubagent": "answer",
17310
+ "answerSubagent": "answer",
16927
17311
  "Cancel": "cancel",
16928
17312
  "cancel": "cancel",
16929
- "await": "await",
16930
- "Await": "await",
16931
17313
  "EmergencyRollback": "EmergencyRollback"
16932
17314
  };
16933
17315
  const normToolName = NORMALIZE_MAP[toolCall.toolName] || toolCall.toolName;
@@ -17013,10 +17395,9 @@ ${ideErr} [/ERROR]`;
17013
17395
  const { method } = parseArgs(toolCall.args);
17014
17396
  label = method === "forceRevert" ? "" : "\u2714 Rollback Point Checked";
17015
17397
  } else if (normToolName === "await" || normToolName === "Await") {
17016
- const { time } = parseArgs(toolCall.args);
17017
- let sec = parseFloat(time) || 0;
17018
- if (sec < 10) sec = 10;
17019
- if (sec > 180) sec = 180;
17398
+ const { time, timeout } = parseArgs(toolCall.args);
17399
+ let sec = parseFloat(timeout || time) || 0;
17400
+ if (!sec) sec = 120;
17020
17401
  const formatTime = (s) => {
17021
17402
  if (s >= 60) {
17022
17403
  const m = Math.floor(s / 60);
@@ -17055,6 +17436,8 @@ ${ideErr} [/ERROR]`;
17055
17436
  ];
17056
17437
  let randomVibe = existentialVibes[Math.floor(Math.random() * existentialVibes.length)];
17057
17438
  label = `\u2714 ${randomVibe} \u2192 ${formatTime(sec)}`;
17439
+ } else if (normToolName === "Answer" || normToolName === "answer") {
17440
+ label = "\u2714 Resolved Sub-Agent Query";
17058
17441
  } else if (normToolName === "exec_command" || normToolName === "ask") {
17059
17442
  label = "";
17060
17443
  } else {
@@ -17983,7 +18366,7 @@ ${snippet2}`;
17983
18366
  const waitTime = Math.min(1e3 * Math.pow(2, inStreamRetryCount - 1), 24e3);
17984
18367
  if (turnText.trim().length > 0) {
17985
18368
  modifiedHistory.push({ role: "agent", text: turnText });
17986
- const recoveryText = "[SYSTEM]\n- SEAMLESS CONTINUATION: Resume immediately. Pick up from last words with zero gap/disruption\n- NO REPETITION: Do not repeat any text already written\n- NO RE-THINK: Do not restart or open <think> if reasoning already started. Continue the thinking and close thinking block </think> if opened before outputting user response\n- MID-TOOL SAFETY: If cutoff was mid-tool call, restart that tool call from start\n- STEALTH: Do not mention/apologize for cutoff [/SYSTEM]";
18369
+ const recoveryText = "[SYSTEM]\n- SEAMLESS CONTINUATION: Resume immediately. Pick up from last words with zero gap/disruption\n- NO REPETITION: Do not repeat any text already written\n- NO RE-THINK: Do not restart or open <think> if reasoning already started. Continue the thinking and close thinking block </think> BEFORE CHAT OUTPUT\n- MID-TOOL SAFETY: If cutoff was mid-tool call, restart that tool call from start\n- STEALTH: Do not mention/apologize for cutoff [/SYSTEM]";
17987
18370
  if (toolResults.length > 0) {
17988
18371
  toolResults.forEach((tr, idx) => {
17989
18372
  if (idx === toolResults.length - 1) {
@@ -18176,7 +18559,7 @@ Error Log can be found in ${path26.join(LOGS_DIR, "agent", "error.log")}`);
18176
18559
  }
18177
18560
  yield { type: "status", content: null };
18178
18561
  };
18179
- runSubagent = async (task, settings, model = null, allowedTools = null, maxTurns = 50, logCallback = null) => {
18562
+ runSubagent = async (task, settings, model = null, allowedTools = null, maxTurns = 50, logCallback = null, isAsync = false) => {
18180
18563
  const savedSettings = await loadSettings();
18181
18564
  const mergedSettings = { ...savedSettings, ...settings };
18182
18565
  const envSubagentModel = process.env.SUBAGENT_MODEL ? process.env.SUBAGENT_MODEL.trim() : null;
@@ -18250,22 +18633,20 @@ Error Log can be found in ${path26.join(LOGS_DIR, "agent", "error.log")}`);
18250
18633
  const targetModel = model || subAgentCustomModel || settings?.modelName || settings?.activeModel || savedSettings.activeModel;
18251
18634
  const osDetected = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
18252
18635
  const providedToolsSection = `-- TOOL DEFINITIONS (path = relative to CWD, path separator: '/') --
18253
- TO ACCESS TOOLS **STRICTLY USE THE EXACT FORMAT IN CHAT OUTPUT:** [tool:functions.ToolName(args)]
18254
- **NO OTHER SYNTAX/MARKERS/BOUNDARY ALLOWED**
18636
+ TO ACCESS TOOLS **STRICTLY USE THE EXACT FORMAT IN CHAT OUTPUT:** [tool:functions.ToolName(arg1="value1")]
18637
+ **NO OTHER SYNTAX/MARKERS/WRAPPER/BOUNDARY ALLOWED**
18255
18638
 
18256
18639
  TOOL POLICY:
18257
- - Escape quotes: \\" for code strings
18258
- - Double-escape literal sequences (eg. \\\\n)
18259
- - Use real newlines for code formatting
18640
+ - JSON ESCAPE ALL LITERAL ESCAPE SEQUENCES IN TOOL ARGUMENTS
18260
18641
  - SAME file, MULTIPLE edits? ONE PatchFile (\u226415 blocks) \u2190 PRIORITY
18261
- - Tool denied? Ask for guidance \u2190 MANDATORY
18262
18642
  - Need text or huge files? SearchKeyword > Full Read
18263
- - Update Todos from realtime progress each turn
18264
18643
  - Restricted Shell Access, No Deletion
18644
+ - ONLY valid tools and syntax defined below are allowed
18265
18645
 
18266
18646
  **PROVIDED TOOLS**
18267
- -- Communication with USER --
18268
- - [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
18647
+ -- Communication Tools --
18648
+ - [tool:functions.Ask(question="...", optionA="title::description", ...MAX4)]. Communicate with USER. Ambiguity: MUST for path divergence, security risk. Ask, don't finish/guess. Suggest best options; no preferences. Keep titles short
18649
+ ${isAsync ? `- [tool:functions.AskMain(question="...")]. Communicate with PARENT/MAIN AGENT. When clarification/decision is needed for a task` : ""}
18269
18650
 
18270
18651
  -- Web Tools --
18271
18652
  - [tool:functions.WebSearch(query="...", aiMode="bool optional, default: false", limit="integer 3-10, aiMode: exclude")]. Usage: unknown info/docs. aiMode: LLM search
@@ -18275,10 +18656,10 @@ TOOL POLICY:
18275
18656
  - [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
18276
18657
  - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
18277
18658
  - [tool:functions.ReadFile(path="...", startLine="integer", endLine="integer")]. View files
18278
- - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", replaceContent1="...", newContent1="...", ...MAX15)]. TARGET MINIMAL DIFF. allowMultiple: Replace all matches ONLY WHEN SURE. Multi-blocks: replaceContent2/newContent2... Verify diffs
18659
+ - [tool:functions.PatchFile(path="...", allowMultiple="bool optional, default: false", replaceContent1="string OR ^LINE:start..end$", newContent1="...", ...MAX15)]. TARGET MINIMAL DIFF. replaceContent accepts exact string OR "^LINE:start..end$" to target line ranges. Multi-blocks supported. Verify diffs
18279
18660
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. VERIFY IMPORTS
18280
18661
  - [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `WINDOWS POWERSHELL` : `WINDOWS CMD` : `BASH`} command. Destructive/Irreversible ops \u2192 Ask user`.trim();
18281
- const systemInstruction = `=== START SYSTEM PROMPT ===
18662
+ const systemInstructionSubAgent = `=== START SYSTEM PROMPT ===
18282
18663
  You are a subagent helping the main FluxFlow CLI agent
18283
18664
  Your task is: "${task}"
18284
18665
 
@@ -18317,8 +18698,8 @@ Current Time: ${time}
18317
18698
  role: m.role === "user" ? "user" : "model",
18318
18699
  parts: [{ text: m.text }]
18319
18700
  }));
18320
- if (logCallback) logCallback(`[Subagent Turn ${turn + 1}] Invoking model ${targetModel}...`);
18321
- const response = await generateSimpleContent(mergedSettings, targetModel, contents, systemInstruction, "Fast");
18701
+ if (logCallback) logCallback(`[Subagent Turn ${turn + 1}]...`);
18702
+ const response = await generateSimpleContent(mergedSettings, targetModel, contents, systemInstructionSubAgent, "Fast");
18322
18703
  const responseText = response.text || "";
18323
18704
  const cleanResponse = responseText.replace(/(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\])/gi, "").trim();
18324
18705
  finalAnswer = cleanResponse;
@@ -18330,6 +18711,8 @@ ${cleanResponse}
18330
18711
  if (toolCalls.length === 0) {
18331
18712
  break;
18332
18713
  }
18714
+ const askMainCalls = toolCalls.filter((tc) => tc.toolName.toLowerCase() === "askmain" || tc.toolName.toLowerCase() === "ask_main");
18715
+ let processedAskMainInTurn = false;
18333
18716
  let toolResultsStr = "";
18334
18717
  for (const toolCall of toolCalls) {
18335
18718
  if (TERMINATION_SIGNAL) {
@@ -18346,6 +18729,36 @@ ${cleanResponse}
18346
18729
  }
18347
18730
  }
18348
18731
  const normalizedToolName = toolCall.toolName.toLowerCase();
18732
+ if (normalizedToolName === "askmain" || normalizedToolName === "ask_main") {
18733
+ if (processedAskMainInTurn) continue;
18734
+ processedAskMainInTurn = true;
18735
+ let questionText = "";
18736
+ if (askMainCalls.length === 1) {
18737
+ const pArgs = parseArgs(askMainCalls[0].args);
18738
+ questionText = pArgs.question || askMainCalls[0].args;
18739
+ } else {
18740
+ questionText = askMainCalls.map((tc, idx) => {
18741
+ const pArgs = parseArgs(tc.args);
18742
+ return `Q${idx + 1}: ${pArgs.question || tc.args}`;
18743
+ }).join("\n");
18744
+ }
18745
+ if (settings.onAskMain) {
18746
+ if (logCallback) logCallback(`[Executing Tool] AskMain("${questionText}")...`);
18747
+ const answer = await settings.onAskMain(questionText);
18748
+ if (logCallback) logCallback(`[Tool Result]
18749
+ Answer from Main Agent: ${answer}
18750
+ `);
18751
+ toolResultsStr += `[TOOL RESULT for AskMain]: Answer from Main Agent: ${answer}
18752
+
18753
+ `;
18754
+ await incrementUsage("toolSuccess");
18755
+ } else {
18756
+ toolResultsStr += `[TOOL RESULT for AskMain]: ERROR: Main agent communication channel not available.
18757
+
18758
+ `;
18759
+ }
18760
+ continue;
18761
+ }
18349
18762
  const allowed = allowedTools ? allowedTools.some((t) => t.toLowerCase() === normalizedToolName) : true;
18350
18763
  if (!allowed) {
18351
18764
  const errorMsg = `ERROR: Tool [${toolCall.toolName}] is not in the allowed tools list for this subagent.`;
@@ -22601,10 +23014,27 @@ Selection: ${val}`,
22601
23014
  commitActiveStreamingMessage();
22602
23015
  inThinkMode = true;
22603
23016
  thinkConsumedInTurn = true;
22604
- let thinkStartText = afterText.replace(/<(think|thought)>/gi, "");
22605
23017
  currentThinkId = "think-" + Date.now();
22606
23018
  activeStreamingMsgRef.current = { id: currentThinkId, role: "think", text: "", isStreaming: true, startTime: Date.now() };
22607
- appendStreamText(thinkStartText);
23019
+ if (afterText.match(/<\/(think|thought)>/i)) {
23020
+ const parts = afterText.split(/<\/(think|thought)>/i);
23021
+ const rawThinkContent = parts[0] || "";
23022
+ const thinkContent = rawThinkContent.replace(/^<(think|thought)>/i, "");
23023
+ const agentContent = parts.slice(2).join("").replace(/<\/?(think|thought)>/gi, "");
23024
+ activeStreamingMsgRef.current.text = flattenString(thinkContent);
23025
+ const startTime = activeStreamingMsgRef.current.startTime || Date.now();
23026
+ activeStreamingMsgRef.current.duration = Date.now() - startTime;
23027
+ commitActiveStreamingMessage();
23028
+ inThinkMode = false;
23029
+ currentAgentId = "agent-" + Date.now();
23030
+ activeStreamingMsgRef.current = { id: currentAgentId, role: "agent", text: "", isStreaming: true };
23031
+ if (agentContent) {
23032
+ appendStreamText(agentContent);
23033
+ }
23034
+ } else {
23035
+ let thinkStartText = afterText.replace(/^<(think|thought)>/gi, "");
23036
+ appendStreamText(thinkStartText);
23037
+ }
22608
23038
  continue;
22609
23039
  }
22610
23040
  if ((chunkLower.includes("</think>") || chunkLower.includes("</thought>")) && activeStreamingMsgRef.current?.role === "think") {
package/model_config.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 0,
3
- "release": 20260801,
3
+ "release": 20260803,
4
4
  "fallbacks": {
5
5
  "janitor_default": "gemini-3.1-flash-lite",
6
6
  "janitor_attempts_fallback": "gemma-4-26b-a4b-it",
@@ -363,7 +363,7 @@
363
363
  {
364
364
  "cmd": "google/diffusiongemma-26b-a4b-it",
365
365
  "multimodal": false,
366
- "desc": "Mega Fast [EXPERIMENTAL]"
366
+ "desc": "Mega Fast"
367
367
  },
368
368
  {
369
369
  "cmd": "\n--- Mistral Models ---",
@@ -495,7 +495,7 @@
495
495
  {
496
496
  "cmd": "google/diffusiongemma-26b-a4b-it",
497
497
  "multimodal": false,
498
- "desc": "Mega Fast [EXPERIMENTAL]"
498
+ "desc": "Mega Fast"
499
499
  },
500
500
  {
501
501
  "cmd": "\n--- Mistral Models ---",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "3.16.6",
4
- "date": "2026-08-02",
3
+ "version": "3.18.0",
4
+ "date": "2026-08-04",
5
5
  "description": "A High-Fidelity Agentic CLI with Sub-Agents for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",