fluxflow-cli 1.8.21 → 1.8.24

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 +168 -145
  2. package/package.json +1 -1
package/dist/fluxflow.js CHANGED
@@ -737,10 +737,10 @@ Use tools contextually when needed, don't flood with unnecessary tool calls.
737
737
  var JANITOR_TOOLS_PROTOCOL;
738
738
  var init_janitor_tools = __esm({
739
739
  "src/data/janitor_tools.js"() {
740
- JANITOR_TOOLS_PROTOCOL = (isMemoryEnabled = true, needTitle = false) => `
740
+ JANITOR_TOOLS_PROTOCOL = (isMemoryEnabled = true, needTitle = true) => `
741
741
  ${needTitle ? `-- START CHAT MANAGEMENT TOOLS --
742
742
  1. YOU MUST UPDATE CHAT TITLE (URGENT PRIORITY):
743
- tool:functions.chat(title='<short summary of conversation context in 3-5 words>')
743
+ tool:functions.chat(title='<short creative title of FULL conversation in 3-5 words>')
744
744
  -- END CHAT MANAGEMENT TOOLS --
745
745
 
746
746
  ` : ""}
@@ -748,7 +748,7 @@ ${needTitle ? `-- START CHAT MANAGEMENT TOOLS --
748
748
  Your tool syntax is: 'tool:functions.function_name(args...)'
749
749
  You have access to the following memory functions to persist important information:
750
750
 
751
- 1. Temporary context (MUST OUTPUT THIS TOOL CALL EVERY TIME):
751
+ 1. Temporary context (URGENT PRIORITY):
752
752
  tool:functions.memory(action='temp', content='<summary of the user prompt & model responses ONLY FROM LATEST PROMPT UNDER 40 WORDS>. [Talked on: <date> <hour>]')
753
753
 
754
754
  ${isMemoryEnabled ? `2. User-specific long-term memory (USE BASED ON CONVERSATION CONTEXT):
@@ -833,7 +833,7 @@ If you see a [STEERING HINT] from user, give that prompt priority for the task a
833
833
  -- START THINKING INSTRUCTIONS --
834
834
  ${thinkingConfig}
835
835
 
836
- BEFORE USING ANY TOOL THINKING IS **MANDATORY** WITH TOOL RULES. ALWAYS PRIORITIZE THINKING FIRST BEFORE RESPONDING. YOU ARE **FORBIDDEN** TO JUMP TO RESPONSES FIRST. THINKING IS REQUIRED EVEN WITH CONVERSATIONAL RESPONSES.
836
+ BEFORE USING ANY TOOL THINKING IS **MANDATORY** WITH TOOL RULES. ALWAYS PRIORITIZE THINKING FIRST BEFORE RESPONDING. YOU ARE **FORBIDDEN** TO JUMP TO RESPONSES FIRST. THINKING IS REQUIRED EVEN WITH SIMPLEST CONVERSATIONAL RESPONSES.
837
837
  -- END THINKING INSTRUCTIONS --
838
838
 
839
839
  ${TOOL_PROTOCOL(mode)}
@@ -891,7 +891,7 @@ When you 'finish' an agentic loop, you will lose your previous turn 'thinking' d
891
891
  Current date and Time is: ${dateTimeStr}
892
892
  --- END SYSTEM INSTRUCTION ---`.trim();
893
893
  };
894
- getJanitorInstruction = (originalText, agentRaws, userMemories = "", isMemoryEnabled = true, needTitle = false) => {
894
+ getJanitorInstruction = (originalText, agentRaws, userMemories = "", isMemoryEnabled = true, needTitle = true) => {
895
895
  let agentRes = `${agentRaws.replace(/tool:functions\..*\n/g, "").replace(/<think>.*<\/think>/g, "").replace(/\[Prompted on:.*?\]/g, "").replace(/\[turn: continue\]/g, "").replace(/\[turn: finish\]/g, "").replace(/\[TOOL_RESULTS\]/g, "").replace(/\[tool_results\]/g, "").substring(0, 3500)}`;
896
896
  if (agentRes.length > 3500) {
897
897
  agentRes += "\n... (truncated) ...";
@@ -2449,7 +2449,7 @@ var init_terminal = __esm({
2449
2449
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
2450
2450
  import path16 from "path";
2451
2451
  import fs16 from "fs";
2452
- var client, TERMINATION_SIGNAL, signalTermination, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, getAIStream;
2452
+ var client, TERMINATION_SIGNAL, signalTermination, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, getAIStream;
2453
2453
  var init_ai = __esm({
2454
2454
  "src/utils/ai.js"() {
2455
2455
  init_prompts();
@@ -2465,6 +2465,135 @@ var init_ai = __esm({
2465
2465
  signalTermination = () => {
2466
2466
  TERMINATION_SIGNAL = true;
2467
2467
  };
2468
+ runJanitorTask = async (settings, agentText, fullAgentTextRaw, history, callbacks = {}) => {
2469
+ const { onStatus, onMemoryUpdated, onBackgroundIncrement } = callbacks;
2470
+ const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats } = settings;
2471
+ const isMemoryEnabled = systemSettings?.memory !== false;
2472
+ const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
2473
+ const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
2474
+ const janitorContents = history.slice(-6).filter((msg) => msg.text && !msg.text.includes("[TOOL_RESULT]") && !msg.text.includes("OBSERVATION:")).map((msg) => ({
2475
+ role: msg.role === "user" ? "user" : "model",
2476
+ parts: [{ text: msg.text.replace(/<think>[\s\S]*?<\/think>/g, "").trim() }]
2477
+ }));
2478
+ const cleanedFullResponse = fullAgentTextRaw.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
2479
+ const janitorPrompt = getJanitorInstruction(
2480
+ agentText,
2481
+ cleanedFullResponse,
2482
+ janitorUserMemories,
2483
+ isMemoryEnabled,
2484
+ true
2485
+ );
2486
+ janitorContents.push({ role: "user", parts: [{ text: janitorPrompt }] });
2487
+ let finalSynthesis = "";
2488
+ let attempts = 0;
2489
+ const MAX_JANITOR_RETRIES = 2;
2490
+ while (attempts <= MAX_JANITOR_RETRIES) {
2491
+ try {
2492
+ if (!await checkQuota("background", settings)) {
2493
+ console.warn("Quota Exhausted for Background Model. Skipping refinement.");
2494
+ return;
2495
+ }
2496
+ let fullContent = "";
2497
+ let lastUsage = null;
2498
+ try {
2499
+ const timeoutPromise = new Promise(
2500
+ (_, reject) => setTimeout(() => reject(new Error("JANITOR_TIMEOUT")), attempts === 1 ? 25e3 : attempts === 2 ? 2e4 : 3e4)
2501
+ );
2502
+ const streamPromise = (async () => {
2503
+ const stream = await client.models.generateContentStream({
2504
+ model: janitorModel || "gemma-4-26b-a4b-it",
2505
+ contents: janitorContents,
2506
+ config: {
2507
+ maxOutputTokens: 384,
2508
+ temperature: 0.69,
2509
+ safetySettings: [
2510
+ { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
2511
+ { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
2512
+ { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
2513
+ { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
2514
+ ],
2515
+ thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.MINIMAL }
2516
+ }
2517
+ });
2518
+ await incrementUsage("background");
2519
+ const iterator2 = stream[Symbol.asyncIterator]();
2520
+ const firstResult2 = await iterator2.next();
2521
+ return { iterator: iterator2, firstResult: firstResult2 };
2522
+ })();
2523
+ const { iterator, firstResult } = await Promise.race([streamPromise, timeoutPromise]);
2524
+ let { value: firstChunk, done: firstDone } = firstResult;
2525
+ if (!firstDone && firstChunk) {
2526
+ const parts = firstChunk.candidates?.[0]?.content?.parts;
2527
+ const chunkText = parts?.[1]?.text || parts?.[0]?.text || (typeof firstChunk.text === "function" ? firstChunk.text() : "");
2528
+ if (chunkText) {
2529
+ fullContent += chunkText;
2530
+ }
2531
+ lastUsage = firstChunk.usageMetadata;
2532
+ for await (const chunk of { [Symbol.asyncIterator]: () => iterator }) {
2533
+ const p = chunk.candidates?.[0]?.content?.parts;
2534
+ const t = p?.[1]?.text || p?.[0]?.text || (typeof chunk.text === "function" ? chunk.text() : "");
2535
+ if (t) fullContent += t;
2536
+ lastUsage = chunk.usageMetadata;
2537
+ }
2538
+ }
2539
+ } catch (e) {
2540
+ if (e.message === "JANITOR_TIMEOUT") {
2541
+ console.error("Janitor API Timeout: No tokens received within 30s.");
2542
+ }
2543
+ throw e;
2544
+ }
2545
+ if (fullContent) {
2546
+ finalSynthesis = fullContent;
2547
+ if (lastUsage) await addToUsage("tokens", lastUsage.totalTokenCount || 0);
2548
+ const date = (/* @__PURE__ */ new Date()).toLocaleString();
2549
+ const janitorLogDir = path16.join(LOGS_DIR, "janitor");
2550
+ if (!fs16.existsSync(janitorLogDir)) fs16.mkdirSync(janitorLogDir, { recursive: true });
2551
+ fs16.appendFileSync(path16.join(janitorLogDir, "debug.log"), `
2552
+
2553
+ ---------------------------------------------------
2554
+
2555
+
2556
+ DEBUG [${date}]: ${finalSynthesis}
2557
+
2558
+ `);
2559
+ } else {
2560
+ throw new Error("No synthesis generated by Janitor.");
2561
+ }
2562
+ if (onBackgroundIncrement) onBackgroundIncrement();
2563
+ const janitorToolCalls = detectToolCalls(finalSynthesis);
2564
+ for (const janitorToolCall of janitorToolCalls) {
2565
+ const toolContext = { chatId, sessionId: chatId, history };
2566
+ const result = await dispatchTool(janitorToolCall.toolName, janitorToolCall.args, toolContext);
2567
+ const date = (/* @__PURE__ */ new Date()).toLocaleString();
2568
+ const janitorLogDir = path16.join(LOGS_DIR, "janitor");
2569
+ fs16.appendFileSync(path16.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${janitorToolCall.toolName}]: ${result}
2570
+ `);
2571
+ if (janitorToolCall.toolName === "memory" && !janitorToolCall.args.includes("action='temp'")) {
2572
+ if (onMemoryUpdated) onMemoryUpdated();
2573
+ }
2574
+ }
2575
+ break;
2576
+ } catch (janitorErr) {
2577
+ attempts++;
2578
+ const date = (/* @__PURE__ */ new Date()).toLocaleString();
2579
+ const janitorErrDir = path16.join(LOGS_DIR, "janitor");
2580
+ if (!fs16.existsSync(janitorErrDir)) fs16.mkdirSync(janitorErrDir, { recursive: true });
2581
+ fs16.appendFileSync(path16.join(janitorErrDir, "error.log"), `ERROR [Attempt ${attempts}/${MAX_JANITOR_RETRIES + 1}] [${date}]: ${String(janitorErr)}
2582
+
2583
+ `);
2584
+ if (attempts > MAX_JANITOR_RETRIES) break;
2585
+ const backoff = Math.min(1e3 * Math.pow(2, attempts - 1), 5e3);
2586
+ await new Promise((resolve) => setTimeout(resolve, backoff));
2587
+ }
2588
+ }
2589
+ if (attempts) {
2590
+ const janitorErrDir = path16.join(LOGS_DIR, "janitor");
2591
+ fs16.appendFileSync(path16.join(janitorErrDir, "error.log"), `-----------------------------------------------------------------------------
2592
+
2593
+
2594
+ `);
2595
+ }
2596
+ };
2468
2597
  getActiveToolContext = (text) => {
2469
2598
  const toolRegex = /(?:\[?\s*tool:functions\.)([a-z0-9_]+)\s*\(/gi;
2470
2599
  let match;
@@ -2736,10 +2865,10 @@ var init_ai = __esm({
2736
2865
  throw new Error("Error: Daily Quota Exausted for Agent");
2737
2866
  }
2738
2867
  let targetModel = modelName;
2739
- if (retryCount === 6) {
2868
+ if (retryCount === 7) {
2740
2869
  targetModel = "gemini-3-flash-preview";
2741
2870
  yield { type: "model_update", content: "Trying with fallback model" };
2742
- } else if (retryCount >= 7) {
2871
+ } else if (retryCount >= 8) {
2743
2872
  targetModel = "gemini-3.1-flash-lite-preview";
2744
2873
  yield { type: "model_update", content: "Trying with fallback model lite" };
2745
2874
  } else if (retryCount > 0) {
@@ -3069,7 +3198,7 @@ ${boxBottom}
3069
3198
  if (turnText.trim().length > 0) {
3070
3199
  if (inStreamRetryCount <= MAX_RETRIES) {
3071
3200
  inStreamRetryCount++;
3072
- const waitTime = Math.min(1e3 * Math.pow(2, inStreamRetryCount - 1), 12e3);
3201
+ const waitTime = Math.min(1e3 * Math.pow(2, inStreamRetryCount - 1), 16e3);
3073
3202
  modifiedHistory.push({ role: "agent", text: turnText });
3074
3203
  if (toolResults.length > 0) {
3075
3204
  toolResults.forEach((tr) => modifiedHistory.push(tr));
@@ -3078,20 +3207,20 @@ ${boxBottom}
3078
3207
  accumulatedContext += turnText;
3079
3208
  yield { type: "status", content: `Error Occured. Recovering Stream (${inStreamRetryCount}/${MAX_RETRIES}) [${(waitTime / 1e3).toFixed(0)}s]...` };
3080
3209
  await new Promise((resolve) => setTimeout(resolve, waitTime));
3081
- } else {
3082
3210
  yield { type: "status", content: `Error Occured. Recovering Stream...` };
3211
+ } else {
3083
3212
  throw new Error(`Stream collapsed too many times. (Failed to resolve ${MAX_RETRIES} times)
3084
3213
  Error Log can be found in ${path16.join(LOGS_DIR, "agent", "error.log")}`);
3085
3214
  }
3086
3215
  } else {
3087
3216
  if (retryCount <= MAX_RETRIES) {
3088
3217
  retryCount++;
3089
- const waitTime = Math.min(1e3 * Math.pow(2, retryCount - 1), 12e3);
3218
+ const waitTime = Math.min(1e3 * Math.pow(2, retryCount - 1), 16e3);
3090
3219
  isInitialAttempt = true;
3091
3220
  yield { type: "status", content: `Retrying Connection (${retryCount}/${MAX_RETRIES}) [${(waitTime / 1e3).toFixed(0)}s]...` };
3092
3221
  await new Promise((resolve) => setTimeout(resolve, waitTime));
3093
- } else {
3094
3222
  yield { type: "status", content: `Retrying Connection...` };
3223
+ } else {
3095
3224
  throw new Error(`Model cannot be reached. (Failed ${MAX_RETRIES} times)
3096
3225
  Error Log can be found in ${path16.join(LOGS_DIR, "agent", "error.log")}`);
3097
3226
  }
@@ -3115,128 +3244,17 @@ Error Log can be found in ${path16.join(LOGS_DIR, "agent", "error.log")}`);
3115
3244
  const cleanedTurnText = contextSafeReplace(turnText, /\[\s*(turn\s*:)?\s*(continue|finish)\s*\]/gi, "").trim();
3116
3245
  let isActuallyFinished = hasFinish && !shouldContinue;
3117
3246
  if (isActuallyFinished) {
3118
- yield { type: "status", content: "Finalizing..." };
3119
- const janitorContents = history.slice(-3).filter((msg) => msg.text && !msg.text.includes("[TOOL_RESULT]") && !msg.text.includes("OBSERVATION:")).map((msg) => ({
3120
- role: msg.role === "user" ? "user" : "model",
3121
- parts: [{ text: msg.text.replace(/<think>[\s\S]*?<\/think>/g, "").trim() }]
3122
- }));
3123
3247
  const fullAgentTextRaw = fullAgentResponseChunks.join("\n");
3124
3248
  const cleanedFullResponse = fullAgentTextRaw.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
3125
- const janitorPrompt = getJanitorInstruction(
3126
- agentText,
3127
- cleanedFullResponse,
3128
- janitorUserMemories,
3129
- isMemoryEnabled,
3130
- true
3131
- );
3132
- janitorContents.push({ role: "user", parts: [{ text: janitorPrompt }] });
3133
- let finalSynthesis = "";
3134
- try {
3135
- if (!await checkQuota("background", settings)) {
3136
- console.warn("Quota Exhausted for Background Model. Skipping refinement.");
3137
- throw new Error("QUOTA_BLOCKED");
3138
- }
3139
- yield { type: "spinner", content: false };
3140
- let fullContent = "";
3141
- let lastUsage2 = null;
3142
- try {
3143
- const timeoutPromise = new Promise(
3144
- (_, reject) => setTimeout(() => reject(new Error("JANITOR_TIMEOUT")), 2e4)
3145
- );
3146
- const streamPromise = (async () => {
3147
- const stream2 = await client.models.generateContentStream({
3148
- model: janitorModel || "gemma-4-26b-a4b-it",
3149
- contents: janitorContents,
3150
- config: {
3151
- maxOutputTokens: 384,
3152
- temperature: 0.69,
3153
- safetySettings: [
3154
- { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
3155
- { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
3156
- { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
3157
- { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
3158
- ],
3159
- thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.MINIMAL }
3160
- }
3161
- });
3162
- await incrementUsage("background");
3163
- const iterator2 = stream2[Symbol.asyncIterator]();
3164
- const firstResult2 = await iterator2.next();
3165
- return { iterator: iterator2, firstResult: firstResult2 };
3166
- })();
3167
- const { iterator, firstResult } = await Promise.race([streamPromise, timeoutPromise]);
3168
- let { value: firstChunk, done: firstDone } = firstResult;
3169
- if (!firstDone && firstChunk) {
3170
- const parts = firstChunk.candidates?.[0]?.content?.parts;
3171
- const chunkText = parts?.[1]?.text || parts?.[0]?.text || (typeof firstChunk.text === "function" ? firstChunk.text() : "");
3172
- if (chunkText) {
3173
- fullContent += chunkText;
3174
- yield { type: "status", content: "Finishing..." };
3175
- }
3176
- lastUsage2 = firstChunk.usageMetadata;
3177
- for await (const chunk of { [Symbol.asyncIterator]: () => iterator }) {
3178
- const p = chunk.candidates?.[0]?.content?.parts;
3179
- const t = p?.[1]?.text || p?.[0]?.text || (typeof chunk.text === "function" ? chunk.text() : "");
3180
- if (t) {
3181
- fullContent += t;
3182
- }
3183
- lastUsage2 = chunk.usageMetadata;
3184
- }
3185
- }
3186
- } catch (e) {
3187
- if (e.message === "JANITOR_TIMEOUT") {
3188
- throw new Error("Janitor API Timeout: No tokens received within 20s.");
3189
- }
3190
- throw e;
3191
- }
3192
- yield { type: "spinner", content: true };
3193
- if (fullContent) {
3194
- finalSynthesis = fullContent;
3195
- if (lastUsage2) {
3196
- await addToUsage("tokens", lastUsage2.totalTokenCount || 0);
3197
- }
3198
- const date = (/* @__PURE__ */ new Date()).toLocaleString();
3199
- const janitorLogDir = path16.join(LOGS_DIR, "janitor");
3200
- if (!fs16.existsSync(janitorLogDir)) {
3201
- fs16.mkdirSync(janitorLogDir, { recursive: true });
3202
- }
3203
- fs16.appendFileSync(path16.join(janitorLogDir, "debug.log"), `
3204
-
3205
- ---------------------------------------------------
3206
-
3207
-
3208
- DEBUG [${date}]: ${finalSynthesis}
3209
-
3210
- `);
3211
- } else {
3212
- throw new Error("No synthesis generated by Janitor.");
3213
- }
3214
- yield { type: "background_increment" };
3215
- const janitorToolCalls = detectToolCalls(finalSynthesis);
3216
- for (const janitorToolCall of janitorToolCalls) {
3217
- const toolContext = { chatId, sessionId: chatId, history };
3218
- const result = await dispatchTool(janitorToolCall.toolName, janitorToolCall.args, toolContext);
3219
- const date = (/* @__PURE__ */ new Date()).toLocaleString();
3220
- const janitorLogDir = path16.join(LOGS_DIR, "janitor");
3221
- fs16.appendFileSync(path16.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${janitorToolCall.toolName}]: ${result}
3222
- `);
3223
- if (janitorToolCall.toolName === "memory" && !janitorToolCall.args.includes("action='temp'")) {
3224
- yield { type: "memory_updated" };
3225
- }
3249
+ yield {
3250
+ type: "interactive_turn_finished",
3251
+ data: {
3252
+ agentText,
3253
+ fullAgentTextRaw,
3254
+ history: [...modifiedHistory],
3255
+ needTitle
3226
3256
  }
3227
- } catch (janitorErr) {
3228
- const date = (/* @__PURE__ */ new Date()).toLocaleString();
3229
- const janitorErrDir = path16.join(LOGS_DIR, "janitor");
3230
- if (!fs16.existsSync(janitorErrDir)) {
3231
- fs16.mkdirSync(janitorErrDir, { recursive: true });
3232
- }
3233
- fs16.appendFileSync(path16.join(janitorErrDir, "error.log"), `ERROR [${date}]: ${String(janitorErr)}
3234
-
3235
- ----------------------------------------------------------------------
3236
-
3237
-
3238
- `);
3239
- }
3257
+ };
3240
3258
  const timestamp = `Responded on ${(/* @__PURE__ */ new Date()).toLocaleString()}`;
3241
3259
  const finalWithTime = `${cleanedFullResponse}
3242
3260
 
@@ -4377,7 +4395,7 @@ Selection: ${val}`,
4377
4395
  let toolCallEncounteredInTurn = false;
4378
4396
  let toolCallBalance = 0;
4379
4397
  let inToolCallString = null;
4380
- const signalRegex = /\[?_DISABLED_SIGNAL_REGEX_\]?/gi;
4398
+ const signalRegex = /\[?\s*turn\s*:\s*.*?\s*\]?/gi;
4381
4399
  for await (const packet of stream) {
4382
4400
  if (packet.type === "status") {
4383
4401
  setStatusText(packet.content);
@@ -4401,14 +4419,23 @@ Selection: ${val}`,
4401
4419
  thinkConsumedInTurn = false;
4402
4420
  continue;
4403
4421
  }
4404
- if (packet.type === "memory_updated") {
4405
- setMessages((prev) => {
4406
- const newMsgs = [...prev];
4407
- if (newMsgs.length > 0) {
4408
- newMsgs[newMsgs.length - 1].memoryUpdated = true;
4422
+ if (packet.type === "interactive_turn_finished") {
4423
+ setIsProcessing(false);
4424
+ runJanitorTask(
4425
+ { profile: profileData, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats },
4426
+ packet.data.agentText,
4427
+ packet.data.fullAgentTextRaw,
4428
+ packet.data.history,
4429
+ packet.data.needTitle,
4430
+ {
4431
+ onMemoryUpdated: () => setMessages((prev) => {
4432
+ const newMsgs = [...prev];
4433
+ if (newMsgs.length > 0) newMsgs[newMsgs.length - 1].memoryUpdated = true;
4434
+ return newMsgs;
4435
+ }),
4436
+ onBackgroundIncrement: () => setSessionBackgroundCalls((prev) => prev + 1)
4409
4437
  }
4410
- return newMsgs;
4411
- });
4438
+ );
4412
4439
  continue;
4413
4440
  }
4414
4441
  if (packet.type === "visual_feedback") {
@@ -4434,10 +4461,6 @@ Selection: ${val}`,
4434
4461
  setSessionAgentCalls((prev) => prev + 1);
4435
4462
  continue;
4436
4463
  }
4437
- if (packet.type === "background_increment") {
4438
- setSessionBackgroundCalls((prev) => prev + 1);
4439
- continue;
4440
- }
4441
4464
  if (packet.type === "tool_time") {
4442
4465
  setSessionToolTime((prev) => prev + packet.content);
4443
4466
  continue;
@@ -5209,8 +5232,8 @@ var init_app = __esm({
5209
5232
  init_text();
5210
5233
  SESSION_START_TIME = Date.now();
5211
5234
  CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
5212
- versionFluxflow = "1.8.21";
5213
- updatedOn = "2026-05-10";
5235
+ versionFluxflow = "1.8.24";
5236
+ updatedOn = "2026-05-11";
5214
5237
  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(
5215
5238
  CommandMenu,
5216
5239
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "1.8.21",
3
+ "version": "1.8.24",
4
4
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
5
5
  "keywords": [
6
6
  "ai",