fluxflow-cli 1.8.10 → 1.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/fluxflow.js +148 -14
  2. package/package.json +1 -1
package/dist/fluxflow.js CHANGED
@@ -2430,7 +2430,7 @@ var init_terminal = __esm({
2430
2430
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
2431
2431
  import path16 from "path";
2432
2432
  import fs16 from "fs";
2433
- var client, TERMINATION_SIGNAL, signalTermination, detectToolCalls, initAI, getAIStream;
2433
+ var client, TERMINATION_SIGNAL, signalTermination, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, getAIStream;
2434
2434
  var init_ai = __esm({
2435
2435
  "src/utils/ai.js"() {
2436
2436
  init_prompts();
@@ -2446,6 +2446,138 @@ var init_ai = __esm({
2446
2446
  signalTermination = () => {
2447
2447
  TERMINATION_SIGNAL = true;
2448
2448
  };
2449
+ getActiveToolContext = (text) => {
2450
+ const toolRegex = /(?:\[?\s*tool:functions\.)([a-z0-9_]+)\s*\(/gi;
2451
+ let match;
2452
+ while ((match = toolRegex.exec(text)) !== null) {
2453
+ const startIdx = match.index + match[0].length - 1;
2454
+ let balance = 0;
2455
+ let inString = null;
2456
+ let isEscaped = false;
2457
+ let closed = false;
2458
+ for (let i = startIdx; i < text.length; i++) {
2459
+ const char = text[i];
2460
+ if (!inString && (char === '"' || char === "'" || char === "`")) {
2461
+ inString = char;
2462
+ isEscaped = false;
2463
+ } else if (inString && char === inString && !isEscaped) {
2464
+ inString = null;
2465
+ }
2466
+ if (!inString) {
2467
+ if (char === "(") balance++;
2468
+ else if (char === ")") balance--;
2469
+ if (balance === 0) {
2470
+ closed = true;
2471
+ toolRegex.lastIndex = i + 1;
2472
+ break;
2473
+ }
2474
+ }
2475
+ if (char === "\\") isEscaped = !isEscaped;
2476
+ else isEscaped = false;
2477
+ }
2478
+ if (!closed) {
2479
+ return { inside: true, toolName: match[1], startIndex: match.index };
2480
+ }
2481
+ }
2482
+ return { inside: false };
2483
+ };
2484
+ getContextSafeText = (text, stripThoughts = true) => {
2485
+ const toolRegex = /(?:\[?\s*tool:functions\.)([a-z0-9_]+)\s*\(/gi;
2486
+ let result = "";
2487
+ let lastIdx = 0;
2488
+ let match;
2489
+ while ((match = toolRegex.exec(text)) !== null) {
2490
+ const before = text.substring(lastIdx, match.index);
2491
+ result += stripThoughts ? before.replace(/<think>[\s\S]*?(?:<\/think>|$)/gi, "") : before;
2492
+ const startIdx = match.index + match[0].length - 1;
2493
+ let balance = 0;
2494
+ let inString = null;
2495
+ let isEscaped = false;
2496
+ let endIdx = -1;
2497
+ for (let i = startIdx; i < text.length; i++) {
2498
+ const char = text[i];
2499
+ if (!inString && (char === '"' || char === "'" || char === "`")) {
2500
+ inString = char;
2501
+ isEscaped = false;
2502
+ } else if (inString && char === inString && !isEscaped) {
2503
+ inString = null;
2504
+ }
2505
+ if (!inString) {
2506
+ if (char === "(") balance++;
2507
+ else if (char === ")") balance--;
2508
+ if (balance === 0) {
2509
+ endIdx = i;
2510
+ break;
2511
+ }
2512
+ }
2513
+ if (char === "\\") isEscaped = !isEscaped;
2514
+ else isEscaped = false;
2515
+ }
2516
+ if (endIdx !== -1) {
2517
+ result += "tool:functions." + match[1] + "()";
2518
+ lastIdx = endIdx + 1;
2519
+ toolRegex.lastIndex = lastIdx;
2520
+ } else {
2521
+ result += "tool:functions." + match[1] + "(";
2522
+ lastIdx = text.length;
2523
+ break;
2524
+ }
2525
+ }
2526
+ if (lastIdx < text.length) {
2527
+ result += stripThoughts ? text.substring(lastIdx).replace(/<think>[\s\S]*?(?:<\/think>|$)/gi, "") : text.substring(lastIdx);
2528
+ }
2529
+ return result;
2530
+ };
2531
+ contextSafeReplace = (text, regex, replacement) => {
2532
+ const toolRegex = /(?:\[?\s*tool:functions\.)([a-z0-9_]+)\s*\(/gi;
2533
+ let result = "";
2534
+ let lastIdx = 0;
2535
+ let match;
2536
+ while ((match = toolRegex.exec(text)) !== null) {
2537
+ const before = text.substring(lastIdx, match.index);
2538
+ result += before.replace(regex, replacement);
2539
+ const startIdx = match.index + match[0].length - 1;
2540
+ let balance = 0;
2541
+ let inString = null;
2542
+ let isEscaped = false;
2543
+ let endIdx = -1;
2544
+ for (let i = startIdx; i < text.length; i++) {
2545
+ const char = text[i];
2546
+ if (!inString && (char === '"' || char === "'" || char === "`")) {
2547
+ inString = char;
2548
+ isEscaped = false;
2549
+ } else if (inString && char === inString && !isEscaped) {
2550
+ inString = null;
2551
+ }
2552
+ if (!inString) {
2553
+ if (char === "(") balance++;
2554
+ else if (char === ")") balance--;
2555
+ if (balance === 0) {
2556
+ endIdx = i;
2557
+ break;
2558
+ }
2559
+ }
2560
+ if (char === "\\") isEscaped = !isEscaped;
2561
+ else isEscaped = false;
2562
+ }
2563
+ if (endIdx !== -1) {
2564
+ result += text.substring(match.index, endIdx + 1);
2565
+ lastIdx = endIdx + 1;
2566
+ toolRegex.lastIndex = lastIdx;
2567
+ } else {
2568
+ result += text.substring(match.index);
2569
+ lastIdx = text.length;
2570
+ break;
2571
+ }
2572
+ }
2573
+ if (lastIdx < text.length) {
2574
+ result += text.substring(lastIdx).replace(regex, replacement);
2575
+ }
2576
+ return result;
2577
+ };
2578
+ getSanitizedText = (text) => {
2579
+ return getContextSafeText(text, true);
2580
+ };
2449
2581
  detectToolCalls = (text) => {
2450
2582
  const results = [];
2451
2583
  const toolRegex = /(?:\[?\s*tool:functions\.)([a-z0-9_]+)\s*\(/gi;
@@ -2661,17 +2793,18 @@ var init_ai = __esm({
2661
2793
  turnText += chunk.text;
2662
2794
  yield { type: "text", content: chunk.text };
2663
2795
  }
2664
- const actionableText = turnText.replace(/<think>[\s\S]*?(?:<\/think>|$)/gi, "");
2665
- if (actionableText.includes("tool:functions.")) {
2796
+ const signalSafeText2 = getSanitizedText(turnText);
2797
+ const toolContext = getActiveToolContext(turnText);
2798
+ if (toolContext.inside) {
2666
2799
  if (!lastToolEventTime) lastToolEventTime = Date.now();
2667
- const parts = actionableText.split("tool:functions.");
2668
- const potentialTool = parts[parts.length - 1].split("(")[0].trim();
2800
+ const potentialTool = toolContext.toolName;
2669
2801
  if (potentialTool && /^[a-z_]+$/.test(potentialTool) && potentialTool !== lastToolSniffed) {
2670
2802
  lastToolSniffed = potentialTool;
2671
2803
  yield { type: "status", content: `Working (${potentialTool})...` };
2672
2804
  }
2673
2805
  }
2674
- const thinkBlocks = turnText.match(/<think>([\s\S]*?)(?:<\/think>|$)/gi) || [];
2806
+ const contextSafeText = getContextSafeText(turnText, false);
2807
+ const thinkBlocks = contextSafeText.match(/<think>([\s\S]*?)(?:<\/think>|$)/gi) || [];
2675
2808
  const thinkContent = thinkBlocks.join("").trim();
2676
2809
  const sentences = thinkContent.split(/[.!?]\s+/);
2677
2810
  const uniqueSentences = new Set(sentences);
@@ -2686,7 +2819,8 @@ var init_ai = __esm({
2686
2819
  await new Promise((resolve) => setTimeout(resolve, 3e3));
2687
2820
  break;
2688
2821
  }
2689
- const allToolsFound = detectToolCalls(actionableText);
2822
+ const toolActionableText = turnText.replace(/<think>[\s\S]*?(?:<\/think>|$)/gi, "");
2823
+ const allToolsFound = detectToolCalls(toolActionableText);
2690
2824
  while (allToolsFound.length > toolCallPointer) {
2691
2825
  const toolCall = allToolsFound[toolCallPointer];
2692
2826
  yield { type: "status", content: `Working (${toolCall.toolName})...` };
@@ -2893,11 +3027,11 @@ ${boxBottom}
2893
3027
  if (thinkMatch) {
2894
3028
  textToProcess = turnText.replace(/<think>[\s\S]*?<\/think>/i, "");
2895
3029
  }
2896
- const finalActionableText = turnText.replace(/<think>[\s\S]*?(?:<\/think>|$)/gi, "");
2897
- const hasFinish = /\[\s*(turn\s*:)?\s*finish\s*\]/i.test(finalActionableText.toLowerCase());
3030
+ const signalSafeText = getSanitizedText(turnText);
3031
+ const hasFinish = /\[\s*(turn\s*:)?\s*finish\s*\]/i.test(signalSafeText.toLowerCase());
2898
3032
  const shouldContinue = toolCallPointer > 0;
2899
3033
  yield { type: "status", content: "Working..." };
2900
- const cleanedTurnText = turnText.replace(/\[\s*(turn\s*:)?\s*(continue|finish)\s*\]/gi, "").trim();
3034
+ const cleanedTurnText = contextSafeReplace(turnText, /\[\s*(turn\s*:)?\s*(continue|finish)\s*\]/gi, "").trim();
2901
3035
  let isActuallyFinished = hasFinish && !shouldContinue;
2902
3036
  if (isActuallyFinished) {
2903
3037
  yield { type: "status", content: "Finalizing..." };
@@ -4693,7 +4827,7 @@ Selection: ${val}`,
4693
4827
  }
4694
4828
  ));
4695
4829
  case "approval":
4696
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { color: "yellow", bold: true, underline: true }, "\u{1F6E1}\uFE0F SECURITY GATE: FILE WRITE PERMISSION"), /* @__PURE__ */ React10.createElement(Text10, { marginTop: 1 }, "The agent is attempting to modify: ", /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, parseArgs(pendingApproval?.args || "{}").path || "Unknown File")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1, borderStyle: "single", borderColor: "#333", paddingX: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, "--- PROPOSED CONTENT / DIFF ---"), (() => {
4830
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { color: "yellow", bold: true, underline: true }, "\u{1F510} SECURITY GATE: FILE WRITE PERMISSION"), /* @__PURE__ */ React10.createElement(Text10, { marginTop: 1 }, "The agent is attempting to modify: ", /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, parseArgs(pendingApproval?.args || "{}").path || "Unknown File")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1, borderStyle: "single", borderColor: "#333", paddingX: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, "--- PROPOSED CONTENT / DIFF ---"), (() => {
4697
4831
  const args = parseArgs(pendingApproval?.args || "{}");
4698
4832
  const oldVal = args.TargetContent || args.content_to_replace || null;
4699
4833
  const newVal = args.content || args.ReplacementContent || args.content_to_add || args.replacementContent || null;
@@ -4707,7 +4841,7 @@ Selection: ${val}`,
4707
4841
  title: "Action Required",
4708
4842
  items: [
4709
4843
  { label: "\u2705 Accept this time", value: "allow" },
4710
- { label: "\u{1F6E1}\uFE0F Accept for this session", value: "always" },
4844
+ { label: "\u{1F510} Accept for this session", value: "always" },
4711
4845
  { label: "\u274C Don't accept", value: "deny" }
4712
4846
  ],
4713
4847
  onSelect: (item) => {
@@ -4783,7 +4917,7 @@ Selection: ${val}`,
4783
4917
  }
4784
4918
  );
4785
4919
  case "terminalApproval":
4786
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { color: "red", bold: true, underline: true }, "\u{1F6E1}\uFE0F SECURITY GATE: TERMINAL COMMAND OVERSIGHT"), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, null, "Agent requested to run: ", /* @__PURE__ */ React10.createElement(Text10, { color: "yellow", bold: true }, parseArgs(pendingApproval?.args || "{}").command || "Unknown Command"))), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(
4920
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { color: "red", bold: true, underline: true }, "\u{1F510} SECURITY GATE: TERMINAL COMMAND OVERSIGHT"), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, null, "Agent requested to run: ", /* @__PURE__ */ React10.createElement(Text10, { color: "yellow", bold: true }, parseArgs(pendingApproval?.args || "{}").command || "Unknown Command"))), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(
4787
4921
  CommandMenu,
4788
4922
  {
4789
4923
  title: "Risk Assessment Required",
@@ -4921,7 +5055,7 @@ var init_app = __esm({
4921
5055
  init_text();
4922
5056
  SESSION_START_TIME = Date.now();
4923
5057
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
4924
- versionFluxflow = "1.8.10";
5058
+ versionFluxflow = "1.8.11";
4925
5059
  updatedOn = "2026-05-09";
4926
5060
  ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { color: "magenta", bold: true, underline: true }, "\u{1F7E3} STEERING HINT RESOLUTION"), /* @__PURE__ */ React10.createElement(Text10, { marginTop: 1 }, "The agent already finished the task before your hint was consumed."), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1, backgroundColor: "#222", paddingX: 1, width: "100%" }, /* @__PURE__ */ React10.createElement(Text10, { italic: true, color: "gray" }, '"', data, '"')), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, "How would you like to proceed?")), /* @__PURE__ */ React10.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(
4927
5061
  CommandMenu,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.8.10",
3
+ "version": "1.8.11",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",