fluxflow-cli 1.12.8 → 1.13.1
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 +289 -38
- package/package.json +2 -2
package/dist/fluxflow.js
CHANGED
|
@@ -821,6 +821,7 @@ __export(paths_exports, {
|
|
|
821
821
|
MEMORIES_FILE: () => MEMORIES_FILE,
|
|
822
822
|
SECRET_DIR: () => SECRET_DIR,
|
|
823
823
|
SETTINGS_FILE: () => SETTINGS_FILE,
|
|
824
|
+
TEMP_MEM_CHAT_FILE: () => TEMP_MEM_CHAT_FILE,
|
|
824
825
|
TEMP_MEM_FILE: () => TEMP_MEM_FILE,
|
|
825
826
|
USAGE_FILE: () => USAGE_FILE
|
|
826
827
|
});
|
|
@@ -828,7 +829,7 @@ import os2 from "os";
|
|
|
828
829
|
import path2 from "path";
|
|
829
830
|
import fs2 from "fs";
|
|
830
831
|
import crypto2 from "crypto";
|
|
831
|
-
var FLUXFLOW_DIR, SETTINGS_FILE, externalDir, DATA_DIR, LOGS_DIR, SECRET_DIR, HISTORY_FILE, USAGE_FILE, MEMORIES_FILE, TEMP_MEM_FILE;
|
|
832
|
+
var FLUXFLOW_DIR, SETTINGS_FILE, externalDir, DATA_DIR, LOGS_DIR, SECRET_DIR, HISTORY_FILE, USAGE_FILE, MEMORIES_FILE, TEMP_MEM_FILE, TEMP_MEM_CHAT_FILE;
|
|
832
833
|
var init_paths = __esm({
|
|
833
834
|
"src/utils/paths.js"() {
|
|
834
835
|
FLUXFLOW_DIR = path2.join(os2.homedir(), ".fluxflow");
|
|
@@ -868,6 +869,7 @@ var init_paths = __esm({
|
|
|
868
869
|
USAGE_FILE = path2.join(FLUXFLOW_DIR, "usage.json");
|
|
869
870
|
MEMORIES_FILE = path2.join(SECRET_DIR, "memories.json");
|
|
870
871
|
TEMP_MEM_FILE = path2.join(SECRET_DIR, "memory-temp.json");
|
|
872
|
+
TEMP_MEM_CHAT_FILE = path2.join(SECRET_DIR, "temp-memory-chat.json");
|
|
871
873
|
}
|
|
872
874
|
});
|
|
873
875
|
|
|
@@ -991,13 +993,17 @@ Your tool syntax is: '[tool:functions.ToolName(args...)]'
|
|
|
991
993
|
|
|
992
994
|
-- CHAT MANAGEMENT TOOLS (MUST CALL THESE 2 TOOLS ALWAYS) --
|
|
993
995
|
[tool:functions.Chat(title="<short creative title of FULL conversation in 3-5 words>")]. Consider full chat context to generate title NOT just latest message.
|
|
994
|
-
[tool:functions.Memory(action="temp", content="<summary of the user prompt & model responses ONLY FROM LATEST PROMPT UNDER 40 WORDS>. [Talked on: <date> <hour>]")]
|
|
996
|
+
[tool:functions.Memory(action="temp", content="<summary of the user prompt & model responses ONLY FROM LATEST PROMPT UNDER 40 WORDS>. [Talked on: <date> <hour>]")]. Time format: YYYY-MM-DD HH am/pm
|
|
995
997
|
|
|
996
998
|
${isMemoryEnabled ? `-- User-specific long-term/permanent memory (USE BASED ON CONVERSATION CONTEXT, DO NOT RE-SAVE MEMORY WHICH IS ALREADY SAVED) --
|
|
997
|
-
- Add: [tool:functions.Memory(action="user", method="add", content="<string to add>. [Saved on: <date ONLY>]")]
|
|
999
|
+
- Add: [tool:functions.Memory(action="user", method="add", content="<string to add>. [Saved on: <date ONLY>]", score=2)] (Set score=2 ONLY if the user explicitly asked to "remember" or "save" this information, else omit this parameter entirely to default to 0.5)
|
|
998
1000
|
- Delete: [tool:functions.Memory(action="user", method="delete", id="<memory id>")]
|
|
999
1001
|
- Update: [tool:functions.Memory(action="user", method="update", content-new="string to update", id="<memory id>")]
|
|
1000
1002
|
|
|
1003
|
+
-- Memory Relevance Decay Tool --
|
|
1004
|
+
- Score Adjustment: [tool:functions.addMemScore(id="<memory id>")]
|
|
1005
|
+
You MUST call this tool when a specific saved memory in the '-- CURRENT SAVED USER MEMORIES --' list was highly relevant, referenced, or helpful in the agent's response or user prompt. You can stack multiple calls.
|
|
1006
|
+
|
|
1001
1007
|
Explicit Triggers for permanent memory:
|
|
1002
1008
|
- User explicitly asks to 'remember' something.
|
|
1003
1009
|
- User mentions something important that should be remembered.
|
|
@@ -1228,6 +1234,11 @@ var init_history = __esm({
|
|
|
1228
1234
|
delete temp[id];
|
|
1229
1235
|
writeEncryptedJson(TEMP_MEM_FILE, temp);
|
|
1230
1236
|
}
|
|
1237
|
+
const cache = readEncryptedJson(TEMP_MEM_CHAT_FILE, {});
|
|
1238
|
+
if (cache[id]) {
|
|
1239
|
+
delete cache[id];
|
|
1240
|
+
writeEncryptedJson(TEMP_MEM_CHAT_FILE, cache);
|
|
1241
|
+
}
|
|
1231
1242
|
return history;
|
|
1232
1243
|
});
|
|
1233
1244
|
};
|
|
@@ -2100,16 +2111,21 @@ ${cleanedHtml}${htmlContent.length > 3e4 ? "\n\n[TRUNCATED AT 30K CHARS]" : ""}`
|
|
|
2100
2111
|
});
|
|
2101
2112
|
|
|
2102
2113
|
// src/tools/memory.js
|
|
2103
|
-
var memory;
|
|
2114
|
+
var USER_MEMORY_SIZE, memory;
|
|
2104
2115
|
var init_memory = __esm({
|
|
2105
2116
|
"src/tools/memory.js"() {
|
|
2106
2117
|
init_crypto();
|
|
2107
2118
|
init_paths();
|
|
2119
|
+
USER_MEMORY_SIZE = 4 * (1024 * 2);
|
|
2108
2120
|
memory = async (rawArgs, context = {}) => {
|
|
2109
2121
|
const parseArg = (key) => {
|
|
2110
|
-
const
|
|
2111
|
-
const
|
|
2112
|
-
|
|
2122
|
+
const quotedRegex = new RegExp(`${key}\\s*[:=]\\s*(["'])(.*?)\\1(?=\\s*[,)]|\\s+\\w+\\s*[:=]|$)`, "s");
|
|
2123
|
+
const quotedMatch = rawArgs.match(quotedRegex);
|
|
2124
|
+
if (quotedMatch) return quotedMatch[2].trim();
|
|
2125
|
+
const unquotedRegex = new RegExp(`${key}\\s*[:=]\\s*([^,\\s)]+)`, "s");
|
|
2126
|
+
const unquotedMatch = rawArgs.match(unquotedRegex);
|
|
2127
|
+
if (unquotedMatch) return unquotedMatch[1].trim();
|
|
2128
|
+
return null;
|
|
2113
2129
|
};
|
|
2114
2130
|
const action = parseArg("action");
|
|
2115
2131
|
const method = parseArg("method");
|
|
@@ -2122,33 +2138,37 @@ var init_memory = __esm({
|
|
|
2122
2138
|
if (!content) return "ERROR: Missing 'content' for temp memory.";
|
|
2123
2139
|
const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
2124
2140
|
if (!tempStorage[chatId]) tempStorage[chatId] = [];
|
|
2125
|
-
const MAX_CHARS = 1024 * 4;
|
|
2126
|
-
let currentTotalLength = tempStorage[chatId].reduce((acc, m) => acc + m.length, 0);
|
|
2127
|
-
while (tempStorage[chatId].length > 0 && currentTotalLength + content.length > MAX_CHARS) {
|
|
2128
|
-
const removed = tempStorage[chatId].shift();
|
|
2129
|
-
currentTotalLength -= removed.length;
|
|
2130
|
-
}
|
|
2131
2141
|
tempStorage[chatId].push(content);
|
|
2132
2142
|
writeEncryptedJson(TEMP_MEM_FILE, tempStorage);
|
|
2133
|
-
|
|
2143
|
+
const currentTotalLength = tempStorage[chatId].reduce((acc, m) => acc + m.length, 0);
|
|
2144
|
+
return `SUCCESS: Temporary context saved for session [${chatId}]. (Size: ${currentTotalLength} chars)`;
|
|
2134
2145
|
}
|
|
2135
2146
|
if (action === "user") {
|
|
2136
|
-
const memories = readEncryptedJson(MEMORIES_FILE, [])
|
|
2147
|
+
const memories = readEncryptedJson(MEMORIES_FILE, []).map((m) => {
|
|
2148
|
+
if (m.score === void 0) m.score = 0.5;
|
|
2149
|
+
return m;
|
|
2150
|
+
});
|
|
2137
2151
|
if (method === "add") {
|
|
2138
2152
|
if (!content) return "ERROR: Missing 'content' for memory addition.";
|
|
2139
2153
|
const now = /* @__PURE__ */ new Date();
|
|
2140
2154
|
const dateStr = `${now.getDate()}/${now.getMonth() + 1}/${now.getFullYear()}`;
|
|
2141
2155
|
const formattedContent = content.includes("[Saved on:") ? content : `${content.trim()} [Saved on: ${dateStr}]`;
|
|
2142
|
-
const MAX_CHARS =
|
|
2156
|
+
const MAX_CHARS = USER_MEMORY_SIZE;
|
|
2143
2157
|
let currentTotalLength = memories.reduce((acc, m) => acc + (m.memory?.length || 0), 0);
|
|
2144
2158
|
while (memories.length > 0 && currentTotalLength + formattedContent.length > MAX_CHARS) {
|
|
2145
2159
|
const removed = memories.shift();
|
|
2146
2160
|
currentTotalLength -= removed.memory?.length || 0;
|
|
2147
2161
|
}
|
|
2148
|
-
const
|
|
2162
|
+
const scoreArg = parseArg("score");
|
|
2163
|
+
const initialScore = scoreArg ? parseFloat(scoreArg) : 0.5;
|
|
2164
|
+
const newMemory = {
|
|
2165
|
+
id: `mem-${Date.now().toString(36)}`,
|
|
2166
|
+
memory: formattedContent,
|
|
2167
|
+
score: Math.min(2, isNaN(initialScore) ? 0.5 : initialScore)
|
|
2168
|
+
};
|
|
2149
2169
|
memories.push(newMemory);
|
|
2150
2170
|
writeEncryptedJson(MEMORIES_FILE, memories);
|
|
2151
|
-
return `SUCCESS: Memory added with ID [${newMemory.id}]. (Vault Size: ${currentTotalLength + formattedContent.length} chars)`;
|
|
2171
|
+
return `SUCCESS: Memory added with ID [${newMemory.id}] and score [${newMemory.score}]. (Vault Size: ${currentTotalLength + formattedContent.length} chars)`;
|
|
2152
2172
|
}
|
|
2153
2173
|
if (method === "update") {
|
|
2154
2174
|
const memId = id || contentOld;
|
|
@@ -3037,13 +3057,13 @@ var init_search_keyword = __esm({
|
|
|
3037
3057
|
let command = "";
|
|
3038
3058
|
if (isWindows) {
|
|
3039
3059
|
const excludePattern = excludes.join("|").replace(/\./g, "\\.");
|
|
3040
|
-
command = `powershell -Command "Get-ChildItem -Path . -Recurse -File | Where-Object { $_.FullName -notmatch '${excludePattern}' } | Select-String -Pattern '${keyword}' | Select-Object -First
|
|
3060
|
+
command = `powershell -Command "Get-ChildItem -Path . -Recurse -File | Where-Object { $_.FullName -notmatch '${excludePattern}' } | Select-String -Pattern '${keyword}' | Select-Object -First 150 | ForEach-Object { $rel = Resolve-Path $_.Path -Relative; '{0}:{1}:' -f $rel, $_.LineNumber }"`;
|
|
3041
3061
|
} else {
|
|
3042
3062
|
const excludeDirArgs = excludes.map((d) => `--exclude-dir="${d}"`).join(" ");
|
|
3043
|
-
command = `grep -rnI ${excludeDirArgs} "${keyword}" . | head -n
|
|
3063
|
+
command = `grep -rnI ${excludeDirArgs} "${keyword}" . | head -n 150`;
|
|
3044
3064
|
}
|
|
3045
3065
|
return new Promise((resolve) => {
|
|
3046
|
-
exec(command, { cwd: process.cwd(), maxBuffer:
|
|
3066
|
+
exec(command, { cwd: process.cwd(), maxBuffer: 15 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
3047
3067
|
if (error && error.code === 1 && !stdout) {
|
|
3048
3068
|
return resolve(`Found 0 matches for keyword: "${keyword}"`);
|
|
3049
3069
|
}
|
|
@@ -3056,8 +3076,8 @@ var init_search_keyword = __esm({
|
|
|
3056
3076
|
const lower = line.toLowerCase();
|
|
3057
3077
|
return !lower.includes("node_modules") && !lower.includes(".git") && !lower.includes("dist") && !lower.includes(".next") && !lower.includes(".gemini");
|
|
3058
3078
|
});
|
|
3059
|
-
if (filteredLines.length === 0) return resolve(`Found 0 matches for keyword: "${keyword}"
|
|
3060
|
-
const matches = filteredLines.slice(0,
|
|
3079
|
+
if (filteredLines.length === 0) return resolve(`Found 0 matches for keyword: "${keyword}"`);
|
|
3080
|
+
const matches = filteredLines.slice(0, 150).map((line) => {
|
|
3061
3081
|
const firstColon = line.indexOf(":");
|
|
3062
3082
|
const secondColon = line.indexOf(":", firstColon + 1);
|
|
3063
3083
|
if (firstColon === -1 || secondColon === -1) return null;
|
|
@@ -3069,8 +3089,8 @@ var init_search_keyword = __esm({
|
|
|
3069
3089
|
|
|
3070
3090
|
`;
|
|
3071
3091
|
output += matches.join("\n");
|
|
3072
|
-
if (filteredLines.length >
|
|
3073
|
-
output += "\n\n... (Truncated to first
|
|
3092
|
+
if (filteredLines.length > 150) {
|
|
3093
|
+
output += "\n\n... (Truncated to first 150 matches)";
|
|
3074
3094
|
}
|
|
3075
3095
|
resolve(output);
|
|
3076
3096
|
});
|
|
@@ -3411,6 +3431,89 @@ var init_generate_image = __esm({
|
|
|
3411
3431
|
}
|
|
3412
3432
|
});
|
|
3413
3433
|
|
|
3434
|
+
// src/tools/saveSummary.js
|
|
3435
|
+
var saveSummary;
|
|
3436
|
+
var init_saveSummary = __esm({
|
|
3437
|
+
"src/tools/saveSummary.js"() {
|
|
3438
|
+
init_crypto();
|
|
3439
|
+
init_paths();
|
|
3440
|
+
saveSummary = async (rawArgs, context = {}) => {
|
|
3441
|
+
const parseArg = (key) => {
|
|
3442
|
+
const regex = new RegExp(`${key}\\s*[:=]\\s*(["'])(.*?)\\1(?=\\s*[,)]|\\s+\\w+\\s*[:=]|$)`, "s");
|
|
3443
|
+
const match = rawArgs.match(regex);
|
|
3444
|
+
return match ? match[2].trim() : null;
|
|
3445
|
+
};
|
|
3446
|
+
const id = parseArg("id");
|
|
3447
|
+
const summary = parseArg("summary");
|
|
3448
|
+
if (!id || !summary) {
|
|
3449
|
+
return "ERROR: Missing 'id' or 'summary' for saveSummary tool.";
|
|
3450
|
+
}
|
|
3451
|
+
try {
|
|
3452
|
+
const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
3453
|
+
const cacheStorage = readEncryptedJson(TEMP_MEM_CHAT_FILE, {});
|
|
3454
|
+
cacheStorage[id] = summary;
|
|
3455
|
+
delete tempStorage[id];
|
|
3456
|
+
writeEncryptedJson(TEMP_MEM_CHAT_FILE, cacheStorage);
|
|
3457
|
+
writeEncryptedJson(TEMP_MEM_FILE, tempStorage);
|
|
3458
|
+
return `SUCCESS: Saved summary and purged raw memories for chat [${id}].`;
|
|
3459
|
+
} catch (err) {
|
|
3460
|
+
return `ERROR: Failed to save summary for chat [${id}]: ${err.message}`;
|
|
3461
|
+
}
|
|
3462
|
+
};
|
|
3463
|
+
}
|
|
3464
|
+
});
|
|
3465
|
+
|
|
3466
|
+
// src/tools/addMemScore.js
|
|
3467
|
+
var addMemScore;
|
|
3468
|
+
var init_addMemScore = __esm({
|
|
3469
|
+
"src/tools/addMemScore.js"() {
|
|
3470
|
+
init_crypto();
|
|
3471
|
+
init_paths();
|
|
3472
|
+
addMemScore = async (rawArgs, context = {}) => {
|
|
3473
|
+
const parseArg = (key) => {
|
|
3474
|
+
const regex = new RegExp(`${key}\\s*[:=]\\s*(["'])(.*?)\\1(?=\\s*[,)]|\\s+\\w+\\s*[:=]|$)`, "s");
|
|
3475
|
+
const match = rawArgs.match(regex);
|
|
3476
|
+
return match ? match[2].trim() : null;
|
|
3477
|
+
};
|
|
3478
|
+
const id = parseArg("id");
|
|
3479
|
+
if (!id) {
|
|
3480
|
+
return "ERROR: Missing 'id' parameter for addMemScore tool.";
|
|
3481
|
+
}
|
|
3482
|
+
try {
|
|
3483
|
+
const memories = readEncryptedJson(MEMORIES_FILE, []);
|
|
3484
|
+
let found = false;
|
|
3485
|
+
const updatedMemories = [];
|
|
3486
|
+
for (const mem of memories) {
|
|
3487
|
+
if (mem.score === void 0) {
|
|
3488
|
+
mem.score = 0.5;
|
|
3489
|
+
}
|
|
3490
|
+
if (mem.id === id) {
|
|
3491
|
+
mem.score = Math.min(2, mem.score + 0.2);
|
|
3492
|
+
found = true;
|
|
3493
|
+
} else {
|
|
3494
|
+
mem.score *= 0.98;
|
|
3495
|
+
if (mem.score < 0.05) mem.score = 0;
|
|
3496
|
+
}
|
|
3497
|
+
mem.score = Math.round(mem.score * 1e5) / 1e5;
|
|
3498
|
+
if (mem.score > 0) {
|
|
3499
|
+
updatedMemories.push(mem);
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
writeEncryptedJson(MEMORIES_FILE, updatedMemories);
|
|
3503
|
+
if (!found) {
|
|
3504
|
+
return `WARNING: Memory ID [${id}] not found. Other memories decayed by -0.01.`;
|
|
3505
|
+
}
|
|
3506
|
+
const activeTarget = updatedMemories.find((m) => m.id === id);
|
|
3507
|
+
const finalScoreStr = activeTarget ? activeTarget.score.toFixed(2) : "deleted (score <= 0)";
|
|
3508
|
+
const deletedCount = memories.length - updatedMemories.length;
|
|
3509
|
+
return `SUCCESS: Adjusted memory scores. Target [${id}] is now ${finalScoreStr}.${deletedCount > 0 ? ` Purged ${deletedCount} decayed memories.` : ""}`;
|
|
3510
|
+
} catch (err) {
|
|
3511
|
+
return `ERROR: Failed to adjust memory score for [${id}]: ${err.message}`;
|
|
3512
|
+
}
|
|
3513
|
+
};
|
|
3514
|
+
}
|
|
3515
|
+
});
|
|
3516
|
+
|
|
3414
3517
|
// src/utils/tools.js
|
|
3415
3518
|
var TOOL_MAP, dispatchTool;
|
|
3416
3519
|
var init_tools = __esm({
|
|
@@ -3429,6 +3532,8 @@ var init_tools = __esm({
|
|
|
3429
3532
|
init_write_docx();
|
|
3430
3533
|
init_search_keyword();
|
|
3431
3534
|
init_generate_image();
|
|
3535
|
+
init_saveSummary();
|
|
3536
|
+
init_addMemScore();
|
|
3432
3537
|
TOOL_MAP = {
|
|
3433
3538
|
web_search,
|
|
3434
3539
|
web_scrape,
|
|
@@ -3443,6 +3548,8 @@ var init_tools = __esm({
|
|
|
3443
3548
|
write_docx,
|
|
3444
3549
|
search_keyword,
|
|
3445
3550
|
generate_image,
|
|
3551
|
+
saveSummary,
|
|
3552
|
+
addMemScore,
|
|
3446
3553
|
ask: ask_user,
|
|
3447
3554
|
// PascalCase Normalizations for Token Efficiency
|
|
3448
3555
|
Ask: ask_user,
|
|
@@ -3458,7 +3565,14 @@ var init_tools = __esm({
|
|
|
3458
3565
|
SearchKeyword: search_keyword,
|
|
3459
3566
|
Memory: memory,
|
|
3460
3567
|
Chat: chat,
|
|
3461
|
-
GenerateImage: generate_image
|
|
3568
|
+
GenerateImage: generate_image,
|
|
3569
|
+
saveSumary: saveSummary,
|
|
3570
|
+
SaveSummary: saveSummary,
|
|
3571
|
+
SaveSumary: saveSummary,
|
|
3572
|
+
add_mem_score: addMemScore,
|
|
3573
|
+
AddMemScore: addMemScore,
|
|
3574
|
+
addMemoryScore: addMemScore,
|
|
3575
|
+
AddMemoryScore: addMemScore
|
|
3462
3576
|
};
|
|
3463
3577
|
dispatchTool = async (toolName, args, context = {}) => {
|
|
3464
3578
|
const tool = TOOL_MAP[toolName];
|
|
@@ -3478,7 +3592,7 @@ var init_tools = __esm({
|
|
|
3478
3592
|
import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
|
|
3479
3593
|
import path14 from "path";
|
|
3480
3594
|
import fs15 from "fs";
|
|
3481
|
-
var client, TERMINATION_SIGNAL, signalTermination, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, getAIStream;
|
|
3595
|
+
var client, TERMINATION_SIGNAL, signalTermination, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, consolidatePastMemories, getAIStream;
|
|
3482
3596
|
var init_ai = __esm({
|
|
3483
3597
|
"src/utils/ai.js"() {
|
|
3484
3598
|
init_prompts();
|
|
@@ -3587,8 +3701,8 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
|
|
|
3587
3701
|
contents: janitorContents,
|
|
3588
3702
|
config: {
|
|
3589
3703
|
systemInstruction: janitorPrompt,
|
|
3590
|
-
maxOutputTokens:
|
|
3591
|
-
temperature: 0.
|
|
3704
|
+
maxOutputTokens: 512,
|
|
3705
|
+
temperature: 0.3,
|
|
3592
3706
|
safetySettings: [
|
|
3593
3707
|
{ category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
|
|
3594
3708
|
{ category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
|
|
@@ -3632,14 +3746,19 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
|
|
|
3632
3746
|
await incrementUsage("background");
|
|
3633
3747
|
}
|
|
3634
3748
|
const janitorToolCalls = detectToolCalls(finalSynthesis);
|
|
3749
|
+
let scoreToolCalled = false;
|
|
3635
3750
|
for (const janitorToolCall of janitorToolCalls) {
|
|
3751
|
+
const toolName = janitorToolCall.toolName;
|
|
3752
|
+
if (["addMemScore", "add_mem_score", "AddMemScore", "addMemoryScore", "AddMemoryScore"].includes(toolName)) {
|
|
3753
|
+
scoreToolCalled = true;
|
|
3754
|
+
}
|
|
3636
3755
|
const toolContext = { chatId, sessionId: chatId, history };
|
|
3637
|
-
const result = await dispatchTool(
|
|
3756
|
+
const result = await dispatchTool(toolName, janitorToolCall.args, toolContext);
|
|
3638
3757
|
const date = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
3639
3758
|
const janitorLogDir = path14.join(LOGS_DIR, "janitor");
|
|
3640
|
-
fs15.appendFileSync(path14.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${
|
|
3759
|
+
fs15.appendFileSync(path14.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${toolName}]: ${result}
|
|
3641
3760
|
`);
|
|
3642
|
-
if (
|
|
3761
|
+
if (toolName.toLowerCase() === "memory" && !janitorToolCall.args.includes("action='temp'")) {
|
|
3643
3762
|
if (onMemoryUpdated) onMemoryUpdated();
|
|
3644
3763
|
if (process.stdout.isTTY) {
|
|
3645
3764
|
process.stdout.write(`\x1B]0;Memory Updated\x07`);
|
|
@@ -3647,6 +3766,27 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
|
|
|
3647
3766
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
3648
3767
|
}
|
|
3649
3768
|
}
|
|
3769
|
+
if (!scoreToolCalled) {
|
|
3770
|
+
try {
|
|
3771
|
+
const memories = readEncryptedJson(MEMORIES_FILE, []);
|
|
3772
|
+
if (memories.length > 0) {
|
|
3773
|
+
const updatedMemories = [];
|
|
3774
|
+
for (const mem of memories) {
|
|
3775
|
+
if (mem.score === void 0) {
|
|
3776
|
+
mem.score = 0.5;
|
|
3777
|
+
}
|
|
3778
|
+
mem.score *= 0.9995;
|
|
3779
|
+
if (mem.score < 0.05) mem.score = 0;
|
|
3780
|
+
mem.score = Math.round(mem.score * 1e5) / 1e5;
|
|
3781
|
+
if (mem.score > 0) {
|
|
3782
|
+
updatedMemories.push(mem);
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
writeEncryptedJson(MEMORIES_FILE, updatedMemories);
|
|
3786
|
+
}
|
|
3787
|
+
} catch (decayErr) {
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3650
3790
|
break;
|
|
3651
3791
|
} catch (janitorErr) {
|
|
3652
3792
|
attempts++;
|
|
@@ -3883,6 +4023,100 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
|
|
|
3883
4023
|
client = new GoogleGenAI({ apiKey });
|
|
3884
4024
|
return client;
|
|
3885
4025
|
};
|
|
4026
|
+
consolidatePastMemories = async (currentChatId, settings) => {
|
|
4027
|
+
try {
|
|
4028
|
+
const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
4029
|
+
const totalMemoriesCount = Object.values(tempStorage).flat().length;
|
|
4030
|
+
if (totalMemoriesCount <= 15) return;
|
|
4031
|
+
const chatsToSummarize = Object.keys(tempStorage).filter((id) => {
|
|
4032
|
+
return id !== currentChatId && Array.isArray(tempStorage[id]) && tempStorage[id].length > 3;
|
|
4033
|
+
});
|
|
4034
|
+
if (chatsToSummarize.length === 0) return;
|
|
4035
|
+
let prompt = `You are a silent background process for the FluxFlow CLI Agent.
|
|
4036
|
+
Your task is to summarize or merge temporary context memories from one or more past conversation sessions.
|
|
4037
|
+
For each Chat ID provided, you must output a tool call to save the consolidated summary.
|
|
4038
|
+
|
|
4039
|
+
The tool call format MUST be:
|
|
4040
|
+
[tool:functions.saveSummary(id="<chat-id>", summary="<updated summary string, max 400 words>")]
|
|
4041
|
+
|
|
4042
|
+
Guidelines:
|
|
4043
|
+
- Create a single, updated, highly cohesive, and concise summary statement (max 400 words) for each Chat ID. It should contain WHAT user talked about, WHAT were the tasks, Temporal info, HOW/WHAT the model responded. DON'T REMOVE ANY KEY AND TURN BY TURN INFO DENSITY.
|
|
4044
|
+
- Focus on key goals, preferences, modified files, and technical decisions.
|
|
4045
|
+
- Under no circumstances write normal conversational text. Output ONLY the tool calls.
|
|
4046
|
+
- You can stack multiple tool calls for multiple chats.
|
|
4047
|
+
|
|
4048
|
+
Chats to process:
|
|
4049
|
+
|
|
4050
|
+
`;
|
|
4051
|
+
const cacheStorage = readEncryptedJson(TEMP_MEM_CHAT_FILE, {});
|
|
4052
|
+
for (const id of chatsToSummarize) {
|
|
4053
|
+
const rawMemories = tempStorage[id];
|
|
4054
|
+
const newMemoryListStr = rawMemories.map((m) => `- ${m}`).join("\n");
|
|
4055
|
+
const oldSummary = cacheStorage[id];
|
|
4056
|
+
prompt += `[Chat ID: ${id}]
|
|
4057
|
+
`;
|
|
4058
|
+
if (oldSummary) {
|
|
4059
|
+
prompt += `- Existing Summary: "${oldSummary}"
|
|
4060
|
+
`;
|
|
4061
|
+
prompt += `-- New Memories to integrate:
|
|
4062
|
+
${newMemoryListStr}
|
|
4063
|
+
|
|
4064
|
+
`;
|
|
4065
|
+
} else {
|
|
4066
|
+
prompt += `-- Individual Memories:
|
|
4067
|
+
${newMemoryListStr}
|
|
4068
|
+
|
|
4069
|
+
`;
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
let attempts = 0;
|
|
4073
|
+
const maxAttempts = 3;
|
|
4074
|
+
let success = false;
|
|
4075
|
+
while (attempts < maxAttempts && !success) {
|
|
4076
|
+
attempts++;
|
|
4077
|
+
try {
|
|
4078
|
+
const response = await client.models.generateContent({
|
|
4079
|
+
model: "gemini-3.1-flash-lite",
|
|
4080
|
+
contents: prompt,
|
|
4081
|
+
config: {
|
|
4082
|
+
temperature: 0.3,
|
|
4083
|
+
safetySettings: [
|
|
4084
|
+
{ category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
|
|
4085
|
+
{ category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
|
|
4086
|
+
{ category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
|
|
4087
|
+
{ category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
|
|
4088
|
+
],
|
|
4089
|
+
thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.LOW }
|
|
4090
|
+
}
|
|
4091
|
+
});
|
|
4092
|
+
const responseText = response.text || "";
|
|
4093
|
+
const janitorToolCalls = detectToolCalls(responseText);
|
|
4094
|
+
if (janitorToolCalls.length === 0) {
|
|
4095
|
+
throw new Error("No tool calls detected in synthesis response");
|
|
4096
|
+
}
|
|
4097
|
+
for (const janitorToolCall of janitorToolCalls) {
|
|
4098
|
+
const toolName = janitorToolCall.toolName;
|
|
4099
|
+
if (["saveSummary", "saveSumary", "SaveSummary", "SaveSumary"].includes(toolName)) {
|
|
4100
|
+
await dispatchTool(toolName, janitorToolCall.args, { chatId: currentChatId });
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
success = true;
|
|
4104
|
+
} catch (err) {
|
|
4105
|
+
if (attempts >= maxAttempts) {
|
|
4106
|
+
throw new Error(`Failed after ${maxAttempts} attempts. Last error: ${err.message}`);
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
} catch (err) {
|
|
4111
|
+
const janitorLogDir = path14.join(LOGS_DIR, "janitor");
|
|
4112
|
+
if (!fs15.existsSync(janitorLogDir)) fs15.mkdirSync(janitorLogDir, { recursive: true });
|
|
4113
|
+
fs15.appendFileSync(
|
|
4114
|
+
path14.join(janitorLogDir, "error.log"),
|
|
4115
|
+
`[${(/* @__PURE__ */ new Date()).toLocaleString()}] Past memory batch consolidation error: ${err.message}
|
|
4116
|
+
`
|
|
4117
|
+
);
|
|
4118
|
+
}
|
|
4119
|
+
};
|
|
3886
4120
|
getAIStream = async function* (modelName, history, settings, steeringCallback, versionFluxflow2) {
|
|
3887
4121
|
if (!client) throw new Error("AI not initialized");
|
|
3888
4122
|
const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats } = settings;
|
|
@@ -3896,8 +4130,15 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
|
|
|
3896
4130
|
if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > 254e3) {
|
|
3897
4131
|
modifiedHistory = getTruncatedHistory(modifiedHistory, 6);
|
|
3898
4132
|
}
|
|
4133
|
+
if (isFirstPrompt && isMemoryEnabled) {
|
|
4134
|
+
yield { type: "status", content: "Condensing past chat memories..." };
|
|
4135
|
+
await consolidatePastMemories(chatId, settings);
|
|
4136
|
+
}
|
|
3899
4137
|
const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
3900
|
-
const
|
|
4138
|
+
const cacheStorage = readEncryptedJson(TEMP_MEM_CHAT_FILE, {});
|
|
4139
|
+
const otherRawMemories = Object.entries(tempStorage).filter(([id]) => id !== chatId).flatMap(([_, mems]) => mems);
|
|
4140
|
+
const cachedSummaries = Object.entries(cacheStorage).filter(([id]) => id !== chatId).slice(-20).map(([id, summary]) => `[Chat Summary]: ${summary}`);
|
|
4141
|
+
const otherMemories = [...cachedSummaries, ...otherRawMemories].map((mem) => `- ${mem}`).join("\n");
|
|
3901
4142
|
const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
|
|
3902
4143
|
const mainUserMemories = persistentStorage.map((m) => `- ${m.memory}`).join("\n");
|
|
3903
4144
|
const isContext32k = (sessionStats?.tokens || 0) >= 32e3;
|
|
@@ -3992,8 +4233,8 @@ ${thinkingLevel != "Fast" ? "[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS STRIC
|
|
|
3992
4233
|
targetModel = "gemini-3-flash-preview";
|
|
3993
4234
|
yield { type: "model_update", content: "Trying with fallback model" };
|
|
3994
4235
|
} else if (retryCount === MAX_RETRIES) {
|
|
3995
|
-
targetModel = "gemini-3.
|
|
3996
|
-
yield { type: "model_update", content: "Trying with fallback model
|
|
4236
|
+
targetModel = "gemini-3.5-flash";
|
|
4237
|
+
yield { type: "model_update", content: "Trying with fallback model" };
|
|
3997
4238
|
} else if (retryCount > 12 && retryCount < MAX_RETRIES - 2 && settings.apiKey !== "custom") {
|
|
3998
4239
|
targetModel = "gemma-4-31b-it";
|
|
3999
4240
|
yield { type: "model_update", content: "Trying with fallback Gemma Model" };
|
|
@@ -4759,8 +5000,19 @@ function MemoryModal({ onClose }) {
|
|
|
4759
5000
|
if (!text) return "";
|
|
4760
5001
|
return text.replace(/\[Saved on: .*?\]/g, "").replace(/\\+'/g, "'").trim();
|
|
4761
5002
|
};
|
|
5003
|
+
const totalCapacity = 4 * 1024 * 2;
|
|
5004
|
+
const currentLength = memories.reduce((acc, m) => acc + (m.memory?.length || 0), 0);
|
|
5005
|
+
const usagePercent = Math.min(100, Math.round(currentLength / totalCapacity * 100));
|
|
5006
|
+
const barWidth = 12;
|
|
5007
|
+
const filledCount = Math.round(usagePercent / 100 * barWidth);
|
|
5008
|
+
const barStr = "\u2588".repeat(filledCount) + "\u2591".repeat(Math.max(0, barWidth - filledCount));
|
|
5009
|
+
const getBarColor = () => {
|
|
5010
|
+
if (usagePercent < 50) return "green";
|
|
5011
|
+
if (usagePercent < 90) return "yellow";
|
|
5012
|
+
return "red";
|
|
5013
|
+
};
|
|
4762
5014
|
const s = emojiSpace(2);
|
|
4763
|
-
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: 80 }, /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan", bold: true }, "\u{1F9E0} AGENT MEMORY: LONG-TERM KNOWLEDGE")), !isMemoryOn && memories.length > 0 ? /* @__PURE__ */ React8.createElement(Box8, { paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React8.createElement(Text8, { italic: true, color: "gray" }, "Memory is currently Off...")) : memories.length === 0 ? /* @__PURE__ */ React8.createElement(Box8, { paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React8.createElement(Text8, { italic: true, color: "gray" }, isMemoryOn ? "Learning..." : "Memory not available...")) : /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, memories.map((mem, idx) => {
|
|
5015
|
+
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: 80 }, /* @__PURE__ */ React8.createElement(Box8, { paddingX: 1, marginBottom: 1, justifyContent: "space-between" }, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan", bold: true }, "\u{1F9E0} AGENT MEMORY: LONG-TERM KNOWLEDGE"), /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "Vault: "), /* @__PURE__ */ React8.createElement(Text8, { color: getBarColor() }, barStr), /* @__PURE__ */ React8.createElement(Text8, { color: "white", bold: true }, " ", usagePercent, "%"))), !isMemoryOn && memories.length > 0 ? /* @__PURE__ */ React8.createElement(Box8, { paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React8.createElement(Text8, { italic: true, color: "gray" }, "Memory is currently Off...")) : memories.length === 0 ? /* @__PURE__ */ React8.createElement(Box8, { paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React8.createElement(Text8, { italic: true, color: "gray" }, isMemoryOn ? "Learning..." : "Memory not available...")) : /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, memories.map((mem, idx) => {
|
|
4764
5016
|
const isSelected = idx === selectedIndex;
|
|
4765
5017
|
return /* @__PURE__ */ React8.createElement(
|
|
4766
5018
|
Box8,
|
|
@@ -5518,8 +5770,7 @@ function App({ args = [] }) {
|
|
|
5518
5770
|
{ cmd: "gemma-4-31b-it", desc: apiTier === "Free" ? "Standard Default (Free, Recommended)" : "Standard Default (Free, Recommended) - Use Free API Key to use this model " },
|
|
5519
5771
|
{ cmd: "gemini-3.1-pro-preview", desc: "Most Capable (Paid)" },
|
|
5520
5772
|
{ cmd: "gemini-3-flash-preview", desc: "Fast & Lightweight (Paid, Limited Free quota)" },
|
|
5521
|
-
{ cmd: "gemini-3.5-flash", desc: "New (Paid, Limited Free quota)" }
|
|
5522
|
-
{ cmd: "gemini-3.1-flash-lite", desc: "Ultra Fast (Paid, Decent Free quota)" }
|
|
5773
|
+
{ cmd: "gemini-3.5-flash", desc: "New (Paid, Limited Free quota)" }
|
|
5523
5774
|
]
|
|
5524
5775
|
},
|
|
5525
5776
|
{ cmd: "/settings", desc: "Configure system prefs" },
|