fluxflow-cli 3.16.6 → 3.17.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, 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();
@@ -3345,12 +3345,20 @@ var init_text = __esm({
3345
3345
  };
3346
3346
  REGEX_INITIAL_THINK = /<\/think>(\r?\n){2}/gi;
3347
3347
  REGEX_INITIAL_TOOL = /(\r?\n){2}(?=\[?(?:tool:functions|tool\.functions|agent:generalist|agent\.generalist|\s*turn\s*:))/gi;
3348
+ isInsideBacktick = (str, idx) => {
3349
+ let inCode = false;
3350
+ for (let i = 0; i < idx; i++) {
3351
+ if (str[i] === "`") inCode = !inCode;
3352
+ }
3353
+ return inCode;
3354
+ };
3348
3355
  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
3356
  REGEX_ARROWS_ALL = /(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)|(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)|(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)|(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)|(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi;
3350
3357
  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;
3358
+ bypassBacktick = false;
3351
3359
  cleanSignals = (text) => {
3352
3360
  if (!text) return text;
3353
- let result = text.replace(REGEX_INITIAL_THINK, "</think>").replace(REGEX_INITIAL_TOOL, "");
3361
+ let result = text.replace(REGEX_INITIAL_THINK, "</think>").replace(REGEX_INITIAL_TOOL, (match, _nl, offset, str) => !bypassBacktick && isInsideBacktick(str, offset) ? match : "");
3354
3362
  const trigger = "tool:functions.";
3355
3363
  const subagentTrigger = "agent:generalist.";
3356
3364
  if (result.toLowerCase().includes(trigger) || result.toLowerCase().includes(subagentTrigger)) {
@@ -3365,6 +3373,35 @@ var init_text = __esm({
3365
3373
  triggerIdxToUse = subagentIdx;
3366
3374
  }
3367
3375
  if (triggerIdxToUse === -1) break;
3376
+ if (!bypassBacktick && isInsideBacktick(result, triggerIdxToUse)) {
3377
+ const searchFrom = triggerIdxToUse + currentTrigger.length;
3378
+ const nextTool = lowerResult.indexOf(trigger, searchFrom);
3379
+ const nextAgent = lowerResult.indexOf(subagentTrigger, searchFrom);
3380
+ if (nextTool === -1 && nextAgent === -1) break;
3381
+ let safeIdx = -1;
3382
+ let searchPos = 0;
3383
+ while (true) {
3384
+ const tIdx = lowerResult.indexOf(trigger, searchPos);
3385
+ const aIdx = lowerResult.indexOf(subagentTrigger, searchPos);
3386
+ let candidate = -1;
3387
+ let candidateTrigger = trigger;
3388
+ if (tIdx === -1 && aIdx === -1) break;
3389
+ if (tIdx === -1 || aIdx !== -1 && aIdx < tIdx) {
3390
+ candidate = aIdx;
3391
+ candidateTrigger = subagentTrigger;
3392
+ } else {
3393
+ candidate = tIdx;
3394
+ }
3395
+ if (!isInsideBacktick(result, candidate)) {
3396
+ safeIdx = candidate;
3397
+ currentTrigger = candidateTrigger;
3398
+ break;
3399
+ }
3400
+ searchPos = candidate + candidateTrigger.length;
3401
+ }
3402
+ if (safeIdx === -1) break;
3403
+ triggerIdxToUse = safeIdx;
3404
+ }
3368
3405
  let startIdx = triggerIdxToUse;
3369
3406
  let hasOuterBracket = false;
3370
3407
  let k = triggerIdxToUse - 1;
@@ -6808,8 +6845,8 @@ var init_main_tools = __esm({
6808
6845
  }
6809
6846
  return `
6810
6847
  -- TOOL DEFINITIONS --
6811
- Tool calls: ONLY use [tool:functions.ToolName(args)]
6812
- **NO OTHER SYNTAX/MARKERS/BOUNDARY ALLOWED**
6848
+ Tool calls: ONLY use [tool:functions.ToolName(arg1="value1")]
6849
+ **NO OTHER SYNTAX/MARKERS/WRAPPER/BOUNDARY ALLOWED**
6813
6850
 
6814
6851
  **TOOL CALLS POLICY:**
6815
6852
  - MAX 4 TOOL CALLS/TURN${mode === "Flux" ? " (Todo: 4+, Run: max 1 or 2 consecutive)" : ""}
@@ -6830,25 +6867,26 @@ ${mode === "Flux" ? `- Escape quotes: \\" for code strings
6830
6867
 
6831
6868
  ${mode === "Flux" ? `- WORKSPACE TOOLS (path = relative; FIRST ARGUMENT, path separator: '/') -
6832
6869
  - [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
6870
+ - [tool:functions.ReadFolder(path="...", recurse="integer 1-3 optional, default: 1")]. DIR Contents + File Size. Minimize recursion
6834
6871
  - [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
6835
6872
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile
6836
6873
  - [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
6874
  - [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' : ""}
6875
+ - [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
6876
  ${_cachedAdvanceRollback ? `
6840
- - EMERGENCY SAFETY TOOLS -
6877
+ - EMERGENCY TOOLS -
6841
6878
  Info: \`initial\` = current task prompt. Revert \`id\` = turn before disaster (eg. disaster: \`turn_3\` \u2192 revert: \`turn_2\`). Reason explicitly
6842
6879
  - [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
6880
  ` : ""}${enableSubAgents ? `
6844
6881
  - SUB AGENT TOOLS -
6845
6882
  **PROACTIVE sub-agent use HIGHLY RECOMMENDED. Prefer for any task with even slight benefit, no user nudge needed**
6846
6883
  Invocations:
6847
- \u2022 Invoke (async/background, \u22647 parallel). Parallelize long tasks. NEVER repeat while active, meantime, do your OWN work
6884
+ \u2022 Invoke (async/background, \u22647 parallel). Parallelize long tasks. May take time
6848
6885
  \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: '/') -
6886
+ - [tool:functions.InvokeSync/Invoke(title="...", task="...")]. Task must be detailed: exact file paths, imports/exports, dependencies & folder structure
6887
+ - [tool:functions.Await(id="...", timeout="integer seconds, default: 120")]. Event-driven wait
6888
+ - [tool:functions.GetProgress(id="...")]. Poll \`getProgress\` sparingly; NO initial poll. Work or await. Never end while subagent runs
6889
+ - [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
6890
  - [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
6891
  - [tool:functions.WriteDoc(path="...", content="...")]. A4 Word document, NO WATERMARKS, stable margins & headers/footers
6854
6892
  - WORKSPACE & SUB AGENT TOOLS ARE NOT AVAILABLE IN FLOW`.trim()}`.trim();
@@ -8565,7 +8603,7 @@ Check these first; These Files > Training Data. Safety rules apply
8565
8603
  Identity: Flux Flow. Sassy, CLI Agent
8566
8604
  ${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
8605
 
8568
- - RESOLVE FILES AND PATHS FROM THE PROVIDED DIRECTORY STRUCTURE
8606
+ - USE DIRECTORY STRUCTURE FOR FILE AVAILABILITY AND PATH RESOLUTION
8569
8607
  - USE RELATIVE TIME REFERENCE eg. few mins ago
8570
8608
 
8571
8609
  -- THINKING GUIDANCE --
@@ -8585,7 +8623,7 @@ ${projectContextBlock}${isMemoryEnabled ? `
8585
8623
  -- CHAT FORMATTING --
8586
8624
  - GFM Markdown ONLY
8587
8625
  - Same Language as User Query
8588
- - Before tool calls, briefly state intent. After, emit no chat
8626
+ - After tool calls emit no chat in this turn
8589
8627
  - On completion: summarize changes (why) + edited files${mode === "Flux" ? "" : "\n- Use Kaomojis HEAVILY"}
8590
8628
  === END SYSTEM PROMPT ===
8591
8629
 
@@ -12493,10 +12531,33 @@ var init_invokeSync = __esm({
12493
12531
  });
12494
12532
 
12495
12533
  // src/utils/subagent_state.js
12496
- var subagentProgress;
12534
+ var subagent_state_exports = {};
12535
+ __export(subagent_state_exports, {
12536
+ addPendingNudge: () => addPendingNudge,
12537
+ clearPendingNudges: () => clearPendingNudges,
12538
+ consumePendingNudges: () => consumePendingNudges,
12539
+ pendingSubagentNudges: () => pendingSubagentNudges,
12540
+ subagentProgress: () => subagentProgress
12541
+ });
12542
+ var subagentProgress, pendingSubagentNudges, addPendingNudge, consumePendingNudges, clearPendingNudges;
12497
12543
  var init_subagent_state = __esm({
12498
12544
  "src/utils/subagent_state.js"() {
12499
12545
  subagentProgress = [];
12546
+ pendingSubagentNudges = [];
12547
+ addPendingNudge = (msg) => {
12548
+ if (msg) {
12549
+ pendingSubagentNudges.push(msg);
12550
+ }
12551
+ };
12552
+ consumePendingNudges = () => {
12553
+ if (pendingSubagentNudges.length === 0) return [];
12554
+ const nudges = [...pendingSubagentNudges];
12555
+ pendingSubagentNudges = [];
12556
+ return nudges;
12557
+ };
12558
+ clearPendingNudges = () => {
12559
+ pendingSubagentNudges = [];
12560
+ };
12500
12561
  }
12501
12562
  });
12502
12563
 
@@ -12528,13 +12589,24 @@ var init_invoke = __esm({
12528
12589
  }
12529
12590
  }
12530
12591
  const taskId = `subagent-${Date.now()}-${Math.floor(Math.random() * 1e3)}`;
12592
+ let _resolveCompletion = null;
12593
+ let _rejectCompletion = null;
12594
+ const completionPromise = new Promise((res, rej) => {
12595
+ _resolveCompletion = res;
12596
+ _rejectCompletion = rej;
12597
+ });
12531
12598
  const taskEntry = {
12532
12599
  id: taskId,
12533
12600
  title: title || task.substring(0, 30),
12534
12601
  task,
12535
12602
  status: "running",
12603
+ startedAt: Date.now(),
12536
12604
  lastChunkTime: Date.now(),
12537
12605
  wps: 0,
12606
+ questions: [],
12607
+ completionPromise,
12608
+ _resolveCompletion,
12609
+ _rejectCompletion,
12538
12610
  progress: []
12539
12611
  // Array of arrays containing logs for each turn
12540
12612
  };
@@ -12547,6 +12619,32 @@ var init_invoke = __esm({
12547
12619
  const subagentContext = {
12548
12620
  ...context,
12549
12621
  taskId,
12622
+ onAskMain: async (questionText, optionsObj) => {
12623
+ const questionId = `q-${Date.now()}-${Math.floor(Math.random() * 1e3)}`;
12624
+ let questionResolver = null;
12625
+ const qPromise = new Promise((resolve) => {
12626
+ questionResolver = resolve;
12627
+ });
12628
+ const qEntry = {
12629
+ id: questionId,
12630
+ question: questionText,
12631
+ options: optionsObj,
12632
+ answered: false,
12633
+ answer: null,
12634
+ askedAt: Date.now(),
12635
+ _resolve: questionResolver
12636
+ };
12637
+ taskEntry.questions.push(qEntry);
12638
+ taskEntry.status = "waiting";
12639
+ if (context.onSubagentUpdate) {
12640
+ context.onSubagentUpdate();
12641
+ }
12642
+ addPendingNudge(`[SYSTEM] Background subagent "${taskEntry.title}" is WAITING FOR YOUR INPUT: "${questionText}"
12643
+ Respond using tool: [tool:functions.Answer(id="${taskId}", answer="...")]
12644
+ [/SYSTEM]`);
12645
+ const answer = await qPromise;
12646
+ return answer;
12647
+ },
12550
12648
  onVisualFeedback: (feedbackLabel) => {
12551
12649
  taskEntry.lastChunkTime = Date.now();
12552
12650
  const clean = feedbackLabel.replace(/\x1b\[[0-9;]*m/g, "");
@@ -12608,16 +12706,22 @@ var init_invoke = __esm({
12608
12706
  if (context.onSubagentUpdate) {
12609
12707
  context.onSubagentUpdate();
12610
12708
  }
12611
- }).then((finalAnswer) => {
12612
- if (taskEntry.status === "cancelled") return;
12613
- currentTurnLogs.push(`[SUBAGENT SUCCESS] Final Answer:
12614
- ${finalAnswer}`);
12615
- taskEntry.progress.push([...currentTurnLogs]);
12709
+ }, true).then((finalAnswer) => {
12710
+ if (taskEntry.status === "cancelled") {
12711
+ if (taskEntry._resolveCompletion) taskEntry._resolveCompletion(finalAnswer);
12712
+ return;
12713
+ }
12714
+ if (currentTurnLogs.length > 0) {
12715
+ taskEntry.progress.push([...currentTurnLogs]);
12716
+ currentTurnLogs = [];
12717
+ }
12616
12718
  taskEntry.status = "completed";
12617
12719
  taskEntry.finalAnswer = finalAnswer;
12618
12720
  if (context.onSubagentUpdate) {
12619
12721
  context.onSubagentUpdate();
12620
12722
  }
12723
+ addPendingNudge(`[SYSTEM] Background subagent "${taskEntry.title}" (id: ${taskId}) has FINISHED. Call GetProgress(id="${taskId}") to see the final result. [/SYSTEM]`);
12724
+ if (taskEntry._resolveCompletion) taskEntry._resolveCompletion(finalAnswer);
12621
12725
  }).catch(async (err) => {
12622
12726
  const { isTerminationSignaled: isTerminationSignaled2 } = await init_ai().then(() => ai_exports);
12623
12727
  const isCancelled = err.message === "Subagent task was cancelled." || taskEntry.status === "cancelled" || isTerminationSignaled2();
@@ -12628,6 +12732,7 @@ ${finalAnswer}`);
12628
12732
  if (context.onSubagentUpdate) {
12629
12733
  context.onSubagentUpdate();
12630
12734
  }
12735
+ if (taskEntry._resolveCompletion) taskEntry._resolveCompletion(null);
12631
12736
  return;
12632
12737
  }
12633
12738
  currentTurnLogs.push(`[SUBAGENT FAILURE] Error: ${err.message}`);
@@ -12637,6 +12742,8 @@ ${finalAnswer}`);
12637
12742
  if (context.onSubagentUpdate) {
12638
12743
  context.onSubagentUpdate();
12639
12744
  }
12745
+ addPendingNudge(`[SYSTEM] Background subagent "${taskEntry.title}" (id: ${taskId}) FAILED with error: ${err.message}. [/SYSTEM]`);
12746
+ if (taskEntry._rejectCompletion) taskEntry._rejectCompletion(err);
12640
12747
  });
12641
12748
  return `SUCCESS: Background subagent started. Task ID: ${taskId}`;
12642
12749
  };
@@ -12664,14 +12771,47 @@ var init_getProgress = __esm({
12664
12771
  output += `Title: ${task.title}
12665
12772
  `;
12666
12773
  output += `Task: ${task.task}
12774
+ `;
12775
+ if (task.startedAt) {
12776
+ const elapsedSec = Math.floor((Date.now() - task.startedAt) / 1e3);
12777
+ output += `Elapsed Time: ${elapsedSec}s
12778
+ `;
12779
+ }
12780
+ output += `Turns Completed: ${task.progress.length}
12781
+ `;
12782
+ if (task.status === "running" || task.status === "waiting") {
12783
+ if (task.currentTool) output += `Current Tool: ${task.currentTool}
12784
+ `;
12785
+ if (task.wps > 0) output += `WPS: ${task.wps}
12786
+ `;
12787
+ }
12788
+ if (task.questions && task.questions.length > 0) {
12789
+ const pending = task.questions.filter((q) => !q.answered);
12790
+ if (pending.length > 0) {
12791
+ output += `
12792
+ **PENDING QUESTION**
12793
+ `;
12794
+ pending.forEach((q) => {
12795
+ output += `"${q.question}"
12796
+ `;
12797
+ if (q.options && Object.keys(q.options).length > 0) {
12798
+ output += `Options: ${JSON.stringify(q.options)}
12799
+ `;
12800
+ }
12801
+ });
12802
+ output += `Respond using tool: [tool:functions.Answer(id="${task.id}", answer="...")]
12667
12803
 
12668
12804
  `;
12669
- output += `Progress Log:
12805
+ }
12806
+ }
12807
+ output += `
12808
+ Progress Log:
12670
12809
  `;
12671
12810
  task.progress.forEach((turnLogs, index) => {
12672
12811
  output += `--- Turn ${index + 1} ---
12673
12812
  `;
12674
- const processedLogs = turnLogs.map((log) => {
12813
+ const filteredLogs = turnLogs.filter((log) => !log.startsWith("[SUBAGENT SUCCESS]"));
12814
+ const processedLogs = filteredLogs.map((log) => {
12675
12815
  if (log.startsWith("[Subagent Response]")) {
12676
12816
  const header = "[Subagent Response]";
12677
12817
  const body = log.substring(header.length);
@@ -12746,7 +12886,7 @@ ${task.finalAnswer}
12746
12886
  output += `Failure Error: ${task.error}
12747
12887
  `;
12748
12888
  }
12749
- const sanitized = output.trim().replace(/\[TOOL RESULT\]/gi, "TOOL RESULT:");
12889
+ const sanitized = output.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim().replace(/\[TOOL RESULT\]/gi, "TOOL RESULT:");
12750
12890
  return sanitized;
12751
12891
  };
12752
12892
  }
@@ -12784,37 +12924,9 @@ var init_cancel = __esm({
12784
12924
  });
12785
12925
 
12786
12926
  // src/tools/await.js
12787
- var awaitTool;
12788
12927
  var init_await = __esm({
12789
12928
  "src/tools/await.js"() {
12790
12929
  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
12930
  }
12819
12931
  });
12820
12932
 
@@ -13169,6 +13281,120 @@ Tools Used: ${toolsStr}
13169
13281
  }
13170
13282
  });
13171
13283
 
13284
+ // src/tools/awaitSubagent.js
13285
+ var awaitSubagent;
13286
+ var init_awaitSubagent = __esm({
13287
+ "src/tools/awaitSubagent.js"() {
13288
+ init_subagent_state();
13289
+ init_arg_parser();
13290
+ awaitSubagent = async (args, context = {}) => {
13291
+ const parsed = parseArgs(args);
13292
+ const id = parsed.id;
13293
+ let timeoutSec = parseInt(parsed.timeout || parsed.time || "120", 10);
13294
+ if (isNaN(timeoutSec) || timeoutSec <= 0) timeoutSec = 120;
13295
+ if (timeoutSec > 300) timeoutSec = 300;
13296
+ if (!id) {
13297
+ if (parsed.time) {
13298
+ await new Promise((resolve) => setTimeout(resolve, timeoutSec * 1e3));
13299
+ return `SUCCESS: Waited for ${timeoutSec}s.`;
13300
+ }
13301
+ return 'ERROR: Missing "id" argument for Await.';
13302
+ }
13303
+ const task = subagentProgress.find((t) => t.id === id);
13304
+ if (!task) {
13305
+ return `ERROR: Subagent task with ID [${id}] not found.`;
13306
+ }
13307
+ if (task.status === "completed") {
13308
+ return `SUCCESS: Subagent task [${id}] completed.
13309
+ Final Answer:
13310
+ ${task.finalAnswer || "(No output)"}`;
13311
+ }
13312
+ if (task.status === "failed") {
13313
+ return `ERROR: Subagent task [${id}] failed.
13314
+ Error: ${task.error || "Unknown error"}`;
13315
+ }
13316
+ if (task.status === "cancelled") {
13317
+ return `INFO: Subagent task [${id}] was cancelled.`;
13318
+ }
13319
+ let timeoutId;
13320
+ const timeoutPromise = new Promise((resolve) => {
13321
+ timeoutId = setTimeout(() => {
13322
+ resolve({ type: "timeout" });
13323
+ }, timeoutSec * 1e3);
13324
+ });
13325
+ try {
13326
+ const result = await Promise.race([
13327
+ task.completionPromise.then(() => ({ type: "completion" })),
13328
+ timeoutPromise
13329
+ ]);
13330
+ clearTimeout(timeoutId);
13331
+ if (result.type === "timeout") {
13332
+ return `TIMEOUT: Subagent task [${id}] is still running (status: ${task.status.toUpperCase()}) after ${timeoutSec}s. You can continue other work or call Await again.`;
13333
+ }
13334
+ if (task.status === "completed") {
13335
+ return `SUCCESS: Subagent task [${id}] completed.
13336
+ Final Answer:
13337
+ ${task.finalAnswer || "(No output)"}`;
13338
+ } else if (task.status === "failed") {
13339
+ return `ERROR: Subagent task [${id}] failed.
13340
+ Error: ${task.error || "Unknown error"}`;
13341
+ } else if (task.status === "cancelled") {
13342
+ return `INFO: Subagent task [${id}] was cancelled.`;
13343
+ } else {
13344
+ return `INFO: Subagent task [${id}] status changed to ${task.status.toUpperCase()}.`;
13345
+ }
13346
+ } catch (err) {
13347
+ clearTimeout(timeoutId);
13348
+ return `ERROR: Exception while awaiting subagent [${id}]: ${err.message}`;
13349
+ }
13350
+ };
13351
+ }
13352
+ });
13353
+
13354
+ // src/tools/answerSubagent.js
13355
+ var answerSubagent;
13356
+ var init_answerSubagent = __esm({
13357
+ "src/tools/answerSubagent.js"() {
13358
+ init_subagent_state();
13359
+ init_arg_parser();
13360
+ answerSubagent = async (args, context = {}) => {
13361
+ const parsed = parseArgs(args);
13362
+ const id = parsed.id;
13363
+ const answer = parsed.answer || parsed.response;
13364
+ if (!id) {
13365
+ return 'ERROR: Missing "id" argument for Answer.';
13366
+ }
13367
+ if (!answer) {
13368
+ return 'ERROR: Missing "answer" argument for Answer.';
13369
+ }
13370
+ const task = subagentProgress.find((t) => t.id === id);
13371
+ if (!task) {
13372
+ return `ERROR: Subagent task with ID [${id}] not found.`;
13373
+ }
13374
+ if (!task.questions || task.questions.length === 0) {
13375
+ return `INFO: Subagent task [${id}] has no pending questions.`;
13376
+ }
13377
+ const pending = task.questions.filter((q) => !q.answered);
13378
+ if (pending.length === 0) {
13379
+ return `INFO: Subagent task [${id}] has no unanswered questions.`;
13380
+ }
13381
+ pending.forEach((q) => {
13382
+ q.answered = true;
13383
+ q.answer = answer;
13384
+ q.answeredAt = Date.now();
13385
+ if (q._resolve) {
13386
+ q._resolve(answer);
13387
+ }
13388
+ });
13389
+ task.status = "running";
13390
+ if (context.onSubagentUpdate) {
13391
+ context.onSubagentUpdate();
13392
+ }
13393
+ return `SUCCESS: Answer provided to subagent task [${id}]. Subagent execution resumed.`;
13394
+ };
13395
+ }
13396
+ });
13397
+
13172
13398
  // src/utils/tools.js
13173
13399
  var TOOL_MAP, dispatchTool;
13174
13400
  var init_tools = __esm({
@@ -13197,6 +13423,8 @@ var init_tools = __esm({
13197
13423
  init_cancel();
13198
13424
  init_await();
13199
13425
  init_emergency_rollback();
13426
+ init_awaitSubagent();
13427
+ init_answerSubagent();
13200
13428
  TOOL_MAP = {
13201
13429
  web_search,
13202
13430
  web_scrape,
@@ -13219,8 +13447,12 @@ var init_tools = __esm({
13219
13447
  invoke,
13220
13448
  getProgress,
13221
13449
  cancel,
13450
+ awaitSubagent,
13451
+ answerSubagent,
13222
13452
  invoke_sync: invokeSync,
13223
13453
  get_progress: getProgress,
13454
+ await_subagent: awaitSubagent,
13455
+ answer_subagent: answerSubagent,
13224
13456
  ask: ask_user,
13225
13457
  // PascalCase Normalizations for Token Efficiency
13226
13458
  Ask: ask_user,
@@ -13245,14 +13477,12 @@ var init_tools = __esm({
13245
13477
  addMemoryScore: addMemScore,
13246
13478
  AddMemoryScore: addMemScore,
13247
13479
  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,
13480
+ answer: answerSubagent,
13481
+ Answer: answerSubagent,
13482
+ AnswerSubagent: answerSubagent,
13483
+ await: awaitSubagent,
13484
+ Await: awaitSubagent,
13485
+ AwaitSubagent: awaitSubagent,
13256
13486
  EmergencyRollback: emergency_rollback,
13257
13487
  emergency_rollback
13258
13488
  };
@@ -13521,6 +13751,7 @@ __export(ai_exports, {
13521
13751
  deleteChatSummary: () => deleteChatSummary,
13522
13752
  getAIStream: () => getAIStream,
13523
13753
  getCleanGroupedLength: () => getCleanGroupedLength,
13754
+ getGoogleClient: () => getGoogleClient,
13524
13755
  initAI: () => initAI,
13525
13756
  isModelMultimodal: () => isModelMultimodal,
13526
13757
  isTerminationSignaled: () => isTerminationSignaled,
@@ -13532,7 +13763,7 @@ import dotenv from "dotenv";
13532
13763
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
13533
13764
  import path26, { normalize } from "path";
13534
13765
  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;
13766
+ 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
13767
  var init_ai = __esm({
13537
13768
  async "src/utils/ai.js"() {
13538
13769
  await init_prompts();
@@ -13563,15 +13794,27 @@ var init_ai = __esm({
13563
13794
  RE_STUTTER_WORD_BOUNDARY = /^[^\w]+|[^\w]+$/g;
13564
13795
  RE_STUTTER_NON_ALNUM = /[^a-z0-9]/gi;
13565
13796
  RE_TOOL_CALL_FUNC = /\[\s*tool:functions\.([a-z0-9_]+)\s*\(/gi;
13797
+ RE_TOOL_CALL_ANY = /\[\s*(?:tool:functions\.|agent:generalist\.)([a-z0-9_]+)\s*\(/gi;
13566
13798
  RE_TOOL_PARTIAL_ARGS_FALLBACK = /(?:path|targetFile|TargetFile|directory|keyword|id|taskId|title|task)\s*=\s*\\?["']?([^\\"' \),]+)/;
13567
13799
  RE_STRIP_QUOTES = /["']/g;
13568
13800
  RE_BACKSLASH_SLASH = /\\/g;
13801
+ RE_STRIP_THINK_CLOSED = /(?:<(think|thought)>|\[(think|thought)\])[\s\S]*?(?:<\/(think|thought)>|\[\/(think|thought)\])/gi;
13802
+ RE_STRIP_THINK_OPEN = /(?:<(think|thought)>|\[(think|thought)\])[\s\S]*$/gi;
13803
+ RE_STRIP_THINK_SIMPLE = /(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\]|$)/gi;
13804
+ RE_STRIP_THINK_FULL = /(?:<(think|thought|thoughts)>|\[(think|thought|thoughts)\])[\s\S]*?(?:<\/(think|thought|thoughts)>|\[\/(think|thought|thoughts)\]|$)/gi;
13805
+ RE_BACKTICK_SPAN = /`[^`]*`/g;
13806
+ RE_BACKTICK_OPEN = /`[^`]*$/;
13807
+ 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;
13808
+ RE_KIMI_JSON_PAIR = /"([^"]+)"\s*:\s*(?:"([^"]*)"|(\d+)|true|false|null)/g;
13809
+ RE_KIMI_SECTION_BEGIN = /<\|\s*tool_calls_section_begin\s*\|>/gi;
13810
+ RE_KIMI_SECTION_END = /<\|\s*tool_calls_section_end\s*\|>/gi;
13811
+ bypassBacktick2 = false;
13569
13812
  client = null;
13570
13813
  globalSettings = {};
13571
13814
  systemInstructionCache = { key: null, value: null };
13572
13815
  colorMainWords = (label) => {
13573
13816
  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) => {
13817
+ 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
13818
  return `${ansiBefore || ""}${icon}${ansiAfter || ""} \x1B[95m${word}\x1B[0m`;
13576
13819
  });
13577
13820
  };
@@ -14496,12 +14739,14 @@ var init_ai = __esm({
14496
14739
  "generate_image": "Generating",
14497
14740
  "todo": "Planning",
14498
14741
  "Todo": "Planning",
14499
- "invoke_sync": "Generalist",
14500
- "invoke": "Generalist",
14742
+ "invoke_sync": "Sub-Agent Working",
14743
+ "invoke": "Starting Agent",
14501
14744
  "get_progress": "Checking Progress",
14502
14745
  "cancel": "Cancelling",
14503
14746
  "await": "Waiting",
14504
- "EmergencyRollback": "Don't Panic. Lookin' into it"
14747
+ "EmergencyRollback": "Don't Panic. Lookin' into it",
14748
+ "answer": "Answering Sub-Agent",
14749
+ "Answer": "Answering Sub-Agent"
14505
14750
  };
14506
14751
  getToolDetail = (toolName, argsStr) => {
14507
14752
  try {
@@ -14519,6 +14764,12 @@ var init_ai = __esm({
14519
14764
  return null;
14520
14765
  }
14521
14766
  };
14767
+ getGoogleClient = (apiKey) => {
14768
+ if (apiKey) {
14769
+ return new GoogleGenAI({ apiKey });
14770
+ }
14771
+ return client;
14772
+ };
14522
14773
  runJanitorTask = async (settings, agentText, fullAgentTextRaw, history, callbacks = {}) => {
14523
14774
  const USER_CONTEXT_LENGTH = 4 * (1024 * 2);
14524
14775
  const AGENT_CONTEXT_LENGTH = 4 * (1024 * 8);
@@ -14657,7 +14908,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14657
14908
  const firstResult2 = await iterator2.next();
14658
14909
  return { iterator: iterator2, firstResult: firstResult2 };
14659
14910
  } else {
14660
- const stream = await client.models.generateContentStream({
14911
+ const googleClient = getGoogleClient(apiKey);
14912
+ const stream = await googleClient.models.generateContentStream({
14661
14913
  model: janitorModel || (attempts === MAX_JANITOR_RETRIES ? getFallbackValue("janitor_default") : getFallbackValue("gemma_janitor_fallback_google")),
14662
14914
  contents: janitorContents,
14663
14915
  config: {
@@ -14812,16 +15064,17 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14812
15064
  }
14813
15065
  };
14814
15066
  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, "");
15067
+ const cleanText = text.replace(RE_STRIP_THINK_CLOSED, "").replace(RE_STRIP_THINK_OPEN, "");
15068
+ const scanText = bypassBacktick2 ? cleanText : cleanText.replace(RE_BACKTICK_SPAN, (m) => " ".repeat(m.length)).replace(RE_BACKTICK_OPEN, (m) => " ".repeat(m.length));
14816
15069
  RE_TOOL_CALL_FUNC.lastIndex = 0;
14817
15070
  let match;
14818
- while ((match = RE_TOOL_CALL_FUNC.exec(cleanText)) !== null) {
15071
+ while ((match = RE_TOOL_CALL_FUNC.exec(scanText)) !== null) {
14819
15072
  const startIdx = match.index + match[0].length - 1;
14820
15073
  let balance = 0;
14821
15074
  let inString = null;
14822
15075
  let isEscaped = false;
14823
15076
  let closed = false;
14824
- for (let i = startIdx; i < cleanText.length; i++) {
15077
+ for (let i = startIdx; i < scanText.length; i++) {
14825
15078
  const char = cleanText[i];
14826
15079
  if (!inString && (char === '"' || char === "'" || char === "`")) {
14827
15080
  inString = char;
@@ -14834,8 +15087,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14834
15087
  else if (char === ")") balance--;
14835
15088
  if (balance === 0) {
14836
15089
  let j = i + 1;
14837
- while (j < cleanText.length && /\s/.test(cleanText[j])) j++;
14838
- if (j < cleanText.length && cleanText[j] === "]") {
15090
+ while (j < scanText.length && /\s/.test(scanText[j])) j++;
15091
+ if (j < scanText.length && scanText[j] === "]") {
14839
15092
  closed = true;
14840
15093
  RE_TOOL_CALL_FUNC.lastIndex = j + 1;
14841
15094
  break;
@@ -14852,13 +15105,14 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14852
15105
  return { inside: false };
14853
15106
  };
14854
15107
  getContextSafeText = (text, stripThoughts = true) => {
14855
- const toolRegex = /\[\s*tool:functions\.([a-z0-9_]+)\s*\(/gi;
15108
+ const toolRegex = RE_TOOL_CALL_FUNC;
15109
+ toolRegex.lastIndex = 0;
14856
15110
  let result = "";
14857
15111
  let lastIdx = 0;
14858
15112
  let match;
14859
15113
  while ((match = toolRegex.exec(text)) !== null) {
14860
15114
  const before = text.substring(lastIdx, match.index);
14861
- result += stripThoughts ? before.replace(/(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\]|$)/gi, "") : before;
15115
+ result += stripThoughts ? before.replace(RE_STRIP_THINK_SIMPLE, "") : before;
14862
15116
  const startIdx = match.index + match[0].length - 1;
14863
15117
  let balance = 0;
14864
15118
  let inString = null;
@@ -14904,12 +15158,13 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14904
15158
  }
14905
15159
  }
14906
15160
  if (lastIdx < text.length) {
14907
- result += stripThoughts ? text.substring(lastIdx).replace(/(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\]|$)/gi, "") : text.substring(lastIdx);
15161
+ result += stripThoughts ? text.substring(lastIdx).replace(RE_STRIP_THINK_SIMPLE, "") : text.substring(lastIdx);
14908
15162
  }
14909
15163
  return result;
14910
15164
  };
14911
15165
  contextSafeReplace = (text, regex, replacement) => {
14912
- const toolRegex = /\[\s*tool:functions\.([a-z0-9_]+)\s*\(/gi;
15166
+ const toolRegex = RE_TOOL_CALL_FUNC;
15167
+ toolRegex.lastIndex = 0;
14913
15168
  let result = "";
14914
15169
  let lastIdx = 0;
14915
15170
  let match;
@@ -14992,8 +15247,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
14992
15247
  const toPascalCase = (str) => {
14993
15248
  return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
14994
15249
  };
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) => {
15250
+ RE_KIMI_TOOL_CALL.lastIndex = 0;
15251
+ let result = text.replace(RE_KIMI_TOOL_CALL, (match, toolName, argsJsonStr) => {
14997
15252
  let parsedArgs = "";
14998
15253
  try {
14999
15254
  const argsObj = JSON.parse(argsJsonStr.trim());
@@ -15006,7 +15261,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15006
15261
  }
15007
15262
  } catch (e) {
15008
15263
  const pairs = [];
15009
- const pairRegex = /"([^"]+)"\s*:\s*(?:"([^"]*)"|(\d+)|true|false|null)/g;
15264
+ const pairRegex = RE_KIMI_JSON_PAIR;
15265
+ pairRegex.lastIndex = 0;
15010
15266
  let pMatch;
15011
15267
  while ((pMatch = pairRegex.exec(argsJsonStr)) !== null) {
15012
15268
  const key = pMatch[1];
@@ -15023,8 +15279,10 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15023
15279
  const normToolName = PASCAL_MAP[cleanKey] || toPascalCase(toolName);
15024
15280
  return `[tool:functions.${normToolName}(${parsedArgs})]`;
15025
15281
  });
15026
- result = result.replace(/<\|\s*tool_calls_section_begin\s*\|>/gi, "");
15027
- result = result.replace(/<\|\s*tool_calls_section_end\s*\|>/gi, "");
15282
+ RE_KIMI_SECTION_BEGIN.lastIndex = 0;
15283
+ RE_KIMI_SECTION_END.lastIndex = 0;
15284
+ result = result.replace(RE_KIMI_SECTION_BEGIN, "");
15285
+ result = result.replace(RE_KIMI_SECTION_END, "");
15028
15286
  return result;
15029
15287
  };
15030
15288
  REGEX_PLACEHOLDER_ARG = /(?:path|query|url|keyword|command|method|title|task|id)\s*=\s*['"`]?\s*\.\.\.\s*['"`]?/i;
@@ -15037,18 +15295,21 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15037
15295
  detectToolCalls = (text) => {
15038
15296
  if (!text) return [];
15039
15297
  const translatedText = translateKimiToolCalls(text);
15040
- const cleanText = translatedText.replace(/(?:<(think|thought|thoughts)>|\[(think|thought|thoughts)\])[\s\S]*?(?:<\/(think|thought|thoughts)>|\[\/(think|thought|thoughts)\]|$)/gi, "");
15298
+ RE_STRIP_THINK_FULL.lastIndex = 0;
15299
+ const cleanText = translatedText.replace(RE_STRIP_THINK_FULL, "");
15041
15300
  const results = [];
15042
- const toolRegex = /\[\s*(?:tool:functions\.|agent:generalist\.)([a-z0-9_]+)\s*\(/gi;
15301
+ const scanText = bypassBacktick2 ? cleanText : cleanText.replace(RE_BACKTICK_SPAN, (m) => " ".repeat(m.length)).replace(RE_BACKTICK_OPEN, (m) => " ".repeat(m.length));
15302
+ const toolRegex = RE_TOOL_CALL_ANY;
15303
+ toolRegex.lastIndex = 0;
15043
15304
  let match;
15044
- while ((match = toolRegex.exec(cleanText)) !== null) {
15305
+ while ((match = toolRegex.exec(scanText)) !== null) {
15045
15306
  const toolName = match[1];
15046
15307
  const startIdx = match.index + match[0].length - 1;
15047
15308
  let balance = 0;
15048
15309
  let inString = null;
15049
15310
  let endIdx = -1;
15050
15311
  let closingParenIdx = -1;
15051
- for (let i = startIdx; i < cleanText.length; i++) {
15312
+ for (let i = startIdx; i < scanText.length; i++) {
15052
15313
  const char = cleanText[i];
15053
15314
  if (inString) {
15054
15315
  if (char === inString) {
@@ -15070,8 +15331,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15070
15331
  if (balance === 0) {
15071
15332
  closingParenIdx = i;
15072
15333
  let j = i + 1;
15073
- while (j < cleanText.length && /\s/.test(cleanText[j])) j++;
15074
- if (j < cleanText.length && cleanText[j] === "]") {
15334
+ while (j < scanText.length && /\s/.test(scanText[j])) j++;
15335
+ if (j < scanText.length && scanText[j] === "]") {
15075
15336
  endIdx = j;
15076
15337
  break;
15077
15338
  }
@@ -15126,7 +15387,6 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15126
15387
  }
15127
15388
  };
15128
15389
  }
15129
- return client;
15130
15390
  };
15131
15391
  generateSimpleContent = async (settings, model, contents, systemInstruction, thinkingLevel = "Fast", temperature = 0.75, usageKey = "agent") => {
15132
15392
  return withRetry(async () => {
@@ -15153,7 +15413,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
15153
15413
  } else if (aiProvider === "NVIDIA") {
15154
15414
  stream = getNVIDIAStream(apiKey, model, normalizedContents, systemInstruction, thinkingLevel, mode, isModelMultimodal(model), signal, temperature);
15155
15415
  } else {
15156
- const genStream = await client.models.generateContentStream({
15416
+ const googleClient = getGoogleClient(apiKey);
15417
+ const genStream = await googleClient.models.generateContentStream({
15157
15418
  model,
15158
15419
  contents: normalizedContents,
15159
15420
  config: {
@@ -16058,7 +16319,7 @@ OS: ${osDetected}${systemSettings?.dynamicDirAwareness ? dirStructure : ""}${cwd
16058
16319
  WARNING: CWD Changed from previous: "${lastCwd}" to current: "${process.cwd()}", write change in chat to avoid future path mismatches
16059
16320
  ` : ""}${memoryPrompt}${ideBlock}
16060
16321
  [/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]
16322
+ ${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
16323
  ${taggedContextStr}[USER PROMPT]
16063
16324
  ${cleanPromptForModel.trim()}
16064
16325
  [/USER PROMPT]`.trim();
@@ -16093,10 +16354,31 @@ ${cleanPromptForModel.trim()}
16093
16354
  yield { type: "status", content: "Working" };
16094
16355
  }
16095
16356
  if (TERMINATION_SIGNAL) {
16357
+ try {
16358
+ const { clearPendingNudges: clearPendingNudges2 } = await Promise.resolve().then(() => (init_subagent_state(), subagent_state_exports));
16359
+ clearPendingNudges2();
16360
+ } catch (e) {
16361
+ }
16096
16362
  yield { type: "status", content: "Request Cancelled" };
16097
16363
  yield { type: "text", content: "\n\n\x1B[33m\u24D8 Request Cancelled\x1B[0m" };
16098
16364
  break;
16099
16365
  }
16366
+ try {
16367
+ const { consumePendingNudges: consumePendingNudges2 } = await Promise.resolve().then(() => (init_subagent_state(), subagent_state_exports));
16368
+ const pendingNudges = consumePendingNudges2();
16369
+ if (pendingNudges && pendingNudges.length > 0) {
16370
+ const combinedNudge = pendingNudges.join("\n\n");
16371
+ if (modifiedHistory.length > 0 && modifiedHistory[modifiedHistory.length - 1].role === "user") {
16372
+ modifiedHistory[modifiedHistory.length - 1].text += `
16373
+
16374
+ ${combinedNudge}`;
16375
+ } else {
16376
+ modifiedHistory.push({ role: "user", text: combinedNudge });
16377
+ }
16378
+ yield { type: "status", content: "Subagent Update" };
16379
+ }
16380
+ } catch (e) {
16381
+ }
16100
16382
  if (steeringCallback) {
16101
16383
  const hint = await steeringCallback();
16102
16384
  if (hint) {
@@ -16358,7 +16640,8 @@ ${ideErr} [/ERROR]`;
16358
16640
  );
16359
16641
  stream = wrapNvidiaStreamWithQueueDepth(rawStream, targetModel);
16360
16642
  } else {
16361
- const apiCallPromise = client.models.generateContentStream({
16643
+ const googleClient = getGoogleClient(settings?.apiKey);
16644
+ const apiCallPromise = googleClient.models.generateContentStream({
16362
16645
  model: targetModel || "gemini-3-flash-preview",
16363
16646
  contents: activeContents,
16364
16647
  config: {
@@ -16699,19 +16982,19 @@ ${ideErr} [/ERROR]`;
16699
16982
  "getProgress": "get_progress",
16700
16983
  "GetProgress": "get_progress",
16701
16984
  "Cancel": "cancel",
16702
- "await": "await",
16703
- "Await": "await"
16985
+ "Await": "await",
16986
+ "Answer": "answer"
16704
16987
  };
16705
16988
  const potentialTool = NORMALIZE_MAP[toolContext.toolName] || toolContext.toolName;
16706
16989
  const partialArgs = toolContext.args || "";
16707
16990
  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)) {
16991
+ 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
16992
  const pArgs = parseArgs(partialArgs);
16710
16993
  const filePath = pArgs.path || pArgs.targetFile || pArgs.TargetFile || pArgs.directory;
16711
16994
  const keyword = pArgs.keyword;
16712
16995
  const title = pArgs.title || pArgs.task;
16713
16996
  const id = pArgs.id || pArgs.taskId;
16714
- const timeVal = pArgs.time;
16997
+ const timeVal = pArgs.timeout || pArgs.time;
16715
16998
  if (keyword !== void 0 && keyword !== null) {
16716
16999
  detail = String(keyword).replace(RE_STRIP_QUOTES, "");
16717
17000
  } else if (filePath) {
@@ -16778,17 +17061,19 @@ ${ideErr} [/ERROR]`;
16778
17061
  "Ask": "User Input Required",
16779
17062
  "Memory": "Updating Memory",
16780
17063
  "GenerateImage": "Generating",
16781
- "InvokeSync": "Generalist Working",
16782
- "invoke_sync": "Generalist Working",
16783
- "Invoke": "Generalist Working",
16784
- "invoke": "Generalist Working",
17064
+ "InvokeSync": "Sub-Agent Working",
17065
+ "invoke_sync": "Sub-Agent Working",
17066
+ "Invoke": "Working",
17067
+ "invoke": "Working",
16785
17068
  "GetProgress": "Checking Progress",
16786
17069
  "get_progress": "Checking Progress",
16787
17070
  "Cancel": "Stopping Generalist",
16788
17071
  "cancel": "Stopping Generalist",
16789
17072
  "Await": "Waiting",
16790
17073
  "await": "Waiting",
16791
- "EmergencyRollback": "Rolling the Ball"
17074
+ "EmergencyRollback": "Rolling the Ball",
17075
+ "Answer": "Answering Sub-Agent",
17076
+ "answer": "Answering Sub-Agent"
16792
17077
  };
16793
17078
  const toolTitle = TOOL_TITLES[potentialTool] || "Working";
16794
17079
  process.stdout.write(`\x1B]0;${toolTitle}...\x07`);
@@ -16920,14 +17205,20 @@ ${ideErr} [/ERROR]`;
16920
17205
  "generate_image": "generate_image",
16921
17206
  "todo": "todo",
16922
17207
  "Todo": "todo",
16923
- "invoke": "invoke",
17208
+ "Invoke": "invoke",
16924
17209
  "InvokeSync": "invoke_sync",
16925
17210
  "getProgress": "get_progress",
16926
17211
  "GetProgress": "get_progress",
17212
+ "Await": "await",
17213
+ "await": "await",
17214
+ "AwaitSubagent": "await",
17215
+ "awaitSubagent": "await",
17216
+ "Answer": "answer",
17217
+ "answer": "answer",
17218
+ "AnswerSubagent": "answer",
17219
+ "answerSubagent": "answer",
16927
17220
  "Cancel": "cancel",
16928
17221
  "cancel": "cancel",
16929
- "await": "await",
16930
- "Await": "await",
16931
17222
  "EmergencyRollback": "EmergencyRollback"
16932
17223
  };
16933
17224
  const normToolName = NORMALIZE_MAP[toolCall.toolName] || toolCall.toolName;
@@ -17013,10 +17304,9 @@ ${ideErr} [/ERROR]`;
17013
17304
  const { method } = parseArgs(toolCall.args);
17014
17305
  label = method === "forceRevert" ? "" : "\u2714 Rollback Point Checked";
17015
17306
  } 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;
17307
+ const { time, timeout } = parseArgs(toolCall.args);
17308
+ let sec = parseFloat(timeout || time) || 0;
17309
+ if (!sec) sec = 120;
17020
17310
  const formatTime = (s) => {
17021
17311
  if (s >= 60) {
17022
17312
  const m = Math.floor(s / 60);
@@ -17055,6 +17345,8 @@ ${ideErr} [/ERROR]`;
17055
17345
  ];
17056
17346
  let randomVibe = existentialVibes[Math.floor(Math.random() * existentialVibes.length)];
17057
17347
  label = `\u2714 ${randomVibe} \u2192 ${formatTime(sec)}`;
17348
+ } else if (normToolName === "Answer" || normToolName === "answer") {
17349
+ label = "\u2714 Resolved Sub-Agent Query";
17058
17350
  } else if (normToolName === "exec_command" || normToolName === "ask") {
17059
17351
  label = "";
17060
17352
  } else {
@@ -17983,7 +18275,7 @@ ${snippet2}`;
17983
18275
  const waitTime = Math.min(1e3 * Math.pow(2, inStreamRetryCount - 1), 24e3);
17984
18276
  if (turnText.trim().length > 0) {
17985
18277
  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]";
18278
+ 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
18279
  if (toolResults.length > 0) {
17988
18280
  toolResults.forEach((tr, idx) => {
17989
18281
  if (idx === toolResults.length - 1) {
@@ -18176,7 +18468,7 @@ Error Log can be found in ${path26.join(LOGS_DIR, "agent", "error.log")}`);
18176
18468
  }
18177
18469
  yield { type: "status", content: null };
18178
18470
  };
18179
- runSubagent = async (task, settings, model = null, allowedTools = null, maxTurns = 50, logCallback = null) => {
18471
+ runSubagent = async (task, settings, model = null, allowedTools = null, maxTurns = 50, logCallback = null, isAsync = false) => {
18180
18472
  const savedSettings = await loadSettings();
18181
18473
  const mergedSettings = { ...savedSettings, ...settings };
18182
18474
  const envSubagentModel = process.env.SUBAGENT_MODEL ? process.env.SUBAGENT_MODEL.trim() : null;
@@ -18250,8 +18542,8 @@ Error Log can be found in ${path26.join(LOGS_DIR, "agent", "error.log")}`);
18250
18542
  const targetModel = model || subAgentCustomModel || settings?.modelName || settings?.activeModel || savedSettings.activeModel;
18251
18543
  const osDetected = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
18252
18544
  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**
18545
+ TO ACCESS TOOLS **STRICTLY USE THE EXACT FORMAT IN CHAT OUTPUT:** [tool:functions.ToolName(arg1="value1")]
18546
+ **NO OTHER SYNTAX/MARKERS/WRAPPER/BOUNDARY ALLOWED**
18255
18547
 
18256
18548
  TOOL POLICY:
18257
18549
  - Escape quotes: \\" for code strings
@@ -18262,10 +18554,12 @@ TOOL POLICY:
18262
18554
  - Need text or huge files? SearchKeyword > Full Read
18263
18555
  - Update Todos from realtime progress each turn
18264
18556
  - Restricted Shell Access, No Deletion
18557
+ - ONLY valid tools and syntax defined below are allowed
18265
18558
 
18266
18559
  **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
18560
+ -- Communication Tools --
18561
+ - [tool:functions.Ask(question="...", optionA="option::description", ...MAX 4)]. Communicate with USER. Ambiguity: MUST for path divergence, security risk. Ask, don't finish/guess. Suggest best options; no preferences. Keep options short
18562
+ ${isAsync ? `- [tool:functions.AskMain(question="...", optionA="option::description", ...MAX 4)]. Communicate with PARENT/MAIN AGENT. When clarification/decision is needed for a task` : ""}
18269
18563
 
18270
18564
  -- Web Tools --
18271
18565
  - [tool:functions.WebSearch(query="...", aiMode="bool optional, default: false", limit="integer 3-10, aiMode: exclude")]. Usage: unknown info/docs. aiMode: LLM search
@@ -18278,7 +18572,7 @@ TOOL POLICY:
18278
18572
  - [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
18279
18573
  - [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. VERIFY IMPORTS
18280
18574
  - [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 ===
18575
+ const systemInstructionSubAgent = `=== START SYSTEM PROMPT ===
18282
18576
  You are a subagent helping the main FluxFlow CLI agent
18283
18577
  Your task is: "${task}"
18284
18578
 
@@ -18318,7 +18612,7 @@ Current Time: ${time}
18318
18612
  parts: [{ text: m.text }]
18319
18613
  }));
18320
18614
  if (logCallback) logCallback(`[Subagent Turn ${turn + 1}] Invoking model ${targetModel}...`);
18321
- const response = await generateSimpleContent(mergedSettings, targetModel, contents, systemInstruction, "Fast");
18615
+ const response = await generateSimpleContent(mergedSettings, targetModel, contents, systemInstructionSubAgent, "Fast");
18322
18616
  const responseText = response.text || "";
18323
18617
  const cleanResponse = responseText.replace(/(?:<think>|\[think\])[\s\S]*?(?:<\/think>|\[\/think\])/gi, "").trim();
18324
18618
  finalAnswer = cleanResponse;
@@ -18330,6 +18624,8 @@ ${cleanResponse}
18330
18624
  if (toolCalls.length === 0) {
18331
18625
  break;
18332
18626
  }
18627
+ const askMainCalls = toolCalls.filter((tc) => tc.toolName.toLowerCase() === "askmain" || tc.toolName.toLowerCase() === "ask_main");
18628
+ let processedAskMainInTurn = false;
18333
18629
  let toolResultsStr = "";
18334
18630
  for (const toolCall of toolCalls) {
18335
18631
  if (TERMINATION_SIGNAL) {
@@ -18346,6 +18642,39 @@ ${cleanResponse}
18346
18642
  }
18347
18643
  }
18348
18644
  const normalizedToolName = toolCall.toolName.toLowerCase();
18645
+ if (normalizedToolName === "askmain" || normalizedToolName === "ask_main") {
18646
+ if (processedAskMainInTurn) continue;
18647
+ processedAskMainInTurn = true;
18648
+ let questionText = "";
18649
+ let optionsObj = {};
18650
+ if (askMainCalls.length === 1) {
18651
+ const pArgs = parseArgs(askMainCalls[0].args);
18652
+ questionText = pArgs.question || askMainCalls[0].args;
18653
+ optionsObj = pArgs;
18654
+ } else {
18655
+ questionText = askMainCalls.map((tc, idx) => {
18656
+ const pArgs = parseArgs(tc.args);
18657
+ return `Q${idx + 1}: ${pArgs.question || tc.args}`;
18658
+ }).join("\n");
18659
+ optionsObj = {};
18660
+ }
18661
+ if (settings.onAskMain) {
18662
+ if (logCallback) logCallback(`[Executing Tool] AskMain("${questionText}")...`);
18663
+ const answer = await settings.onAskMain(questionText, optionsObj);
18664
+ if (logCallback) logCallback(`[Tool Result]
18665
+ Answer from Main Agent: ${answer}
18666
+ `);
18667
+ toolResultsStr += `[TOOL RESULT for AskMain]: Answer from Main Agent: ${answer}
18668
+
18669
+ `;
18670
+ await incrementUsage("toolSuccess");
18671
+ } else {
18672
+ toolResultsStr += `[TOOL RESULT for AskMain]: ERROR: Main agent communication channel not available.
18673
+
18674
+ `;
18675
+ }
18676
+ continue;
18677
+ }
18349
18678
  const allowed = allowedTools ? allowedTools.some((t) => t.toLowerCase() === normalizedToolName) : true;
18350
18679
  if (!allowed) {
18351
18680
  const errorMsg = `ERROR: Tool [${toolCall.toolName}] is not in the allowed tools list for this subagent.`;
@@ -22601,10 +22930,27 @@ Selection: ${val}`,
22601
22930
  commitActiveStreamingMessage();
22602
22931
  inThinkMode = true;
22603
22932
  thinkConsumedInTurn = true;
22604
- let thinkStartText = afterText.replace(/<(think|thought)>/gi, "");
22605
22933
  currentThinkId = "think-" + Date.now();
22606
22934
  activeStreamingMsgRef.current = { id: currentThinkId, role: "think", text: "", isStreaming: true, startTime: Date.now() };
22607
- appendStreamText(thinkStartText);
22935
+ if (afterText.match(/<\/(think|thought)>/i)) {
22936
+ const parts = afterText.split(/<\/(think|thought)>/i);
22937
+ const rawThinkContent = parts[0] || "";
22938
+ const thinkContent = rawThinkContent.replace(/^<(think|thought)>/i, "");
22939
+ const agentContent = parts.slice(2).join("").replace(/<\/?(think|thought)>/gi, "");
22940
+ activeStreamingMsgRef.current.text = flattenString(thinkContent);
22941
+ const startTime = activeStreamingMsgRef.current.startTime || Date.now();
22942
+ activeStreamingMsgRef.current.duration = Date.now() - startTime;
22943
+ commitActiveStreamingMessage();
22944
+ inThinkMode = false;
22945
+ currentAgentId = "agent-" + Date.now();
22946
+ activeStreamingMsgRef.current = { id: currentAgentId, role: "agent", text: "", isStreaming: true };
22947
+ if (agentContent) {
22948
+ appendStreamText(agentContent);
22949
+ }
22950
+ } else {
22951
+ let thinkStartText = afterText.replace(/^<(think|thought)>/gi, "");
22952
+ appendStreamText(thinkStartText);
22953
+ }
22608
22954
  continue;
22609
22955
  }
22610
22956
  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.17.0",
4
+ "date": "2026-08-03",
5
5
  "description": "A High-Fidelity Agentic CLI with Sub-Agents for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",