fluxflow-cli 1.9.21 → 1.9.23
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/ARCHITECTURE.md +2 -2
- package/dist/fluxflow.js +228 -103
- package/package.json +1 -1
package/ARCHITECTURE.md
CHANGED
|
@@ -20,10 +20,10 @@ The execution flow of a single user prompt follows this loop:
|
|
|
20
20
|
3. **Detection & Tool Execution**: Once the stream completes for a given turn, the entire response is scanned for tool calls using a custom regex and bracket-balancing parser (looking for `tool:functions.tool_name(args...)`).
|
|
21
21
|
- If tools are found, the loop pauses.
|
|
22
22
|
- Each tool is dispatched to its respective handler in `src/tools/`.
|
|
23
|
-
- Tool outputs are collected and appended to the context as `[
|
|
23
|
+
- Tool outputs are collected and appended to the context as `[TOOL RESULT]: ...`.
|
|
24
24
|
4. **Security Governance**: During tool execution, the loop enforces security checks (e.g., blocking `exec_command` from accessing system root drives if "External Workspace Access" is off) and pauses for Human-in-the-Loop (HITL) approval if necessary.
|
|
25
25
|
5. **Turn Management & Continuation**: The model is instructed to append `[turn: finish]` if its goal is complete, or `[turn: continue]` if it expects tool results.
|
|
26
|
-
- If tools were called or `[turn: continue]` is present, the loop increments and re-prompts the model with the newly gathered `[
|
|
26
|
+
- If tools were called or `[turn: continue]` is present, the loop increments and re-prompts the model with the newly gathered `[TOOL RESULT]` data.
|
|
27
27
|
- If `[turn: finish]` is detected and no further tools were called, the main loop terminates, passing the final synthesized context to the background Janitor process.
|
|
28
28
|
6. **Loop Limits & Resilience**: To prevent infinite loops or excessive API usage, **Flux mode** is capped at 50 iterations per user prompt, while **Flow mode** is capped at 5.
|
|
29
29
|
- **Multi-Stage Failover**: The loop features a sophisticated 8-attempt retry engine with random backoff (800ms - 2s).
|
package/dist/fluxflow.js
CHANGED
|
@@ -154,7 +154,20 @@ var init_ChatLayout = __esm({
|
|
|
154
154
|
"web_scrape": "ReadSite",
|
|
155
155
|
"search_keyword": "FindFiles",
|
|
156
156
|
"write_pdf": "CreatePDF",
|
|
157
|
-
"write_docx": "CreateDocument"
|
|
157
|
+
"write_docx": "CreateDocument",
|
|
158
|
+
// PascalCase Support
|
|
159
|
+
"WriteFile": "WriteFile",
|
|
160
|
+
"PatchFile": "PatchFile",
|
|
161
|
+
"ReadFolder": "ReadFolder",
|
|
162
|
+
"ReadFile": "ReadFile",
|
|
163
|
+
"Run": "RunCommand",
|
|
164
|
+
"WebSearch": "WebSearch",
|
|
165
|
+
"WebScrape": "WebScrape",
|
|
166
|
+
"SearchKeyword": "SearchKeyword",
|
|
167
|
+
"WritePDF": "WritePDF",
|
|
168
|
+
"WriteDoc": "WriteDoc",
|
|
169
|
+
"Memory": "Memory",
|
|
170
|
+
"Chat": "Chat"
|
|
158
171
|
};
|
|
159
172
|
cleanSignals = (text) => {
|
|
160
173
|
if (!text) return text;
|
|
@@ -408,7 +421,7 @@ var init_ChatLayout = __esm({
|
|
|
408
421
|
});
|
|
409
422
|
MessageItem = React2.memo(({ msg, showFullThinking, columns = 80 }) => {
|
|
410
423
|
const isDiffResult = msg.role === "system" && (msg.text?.includes("[DIFF_START]") || msg.text?.includes("- Content Preview:"));
|
|
411
|
-
const isPatchError = msg.role === "system" && msg.text?.includes("[
|
|
424
|
+
const isPatchError = msg.role === "system" && msg.text?.includes("[TOOL RESULT]: ERROR:") && (msg.toolName === "update_file" || msg.text?.includes("Could not find exact match"));
|
|
412
425
|
const isTerminalRecord = msg.isTerminalRecord;
|
|
413
426
|
const isHomeWarning = msg.isHomeWarning;
|
|
414
427
|
if (isHomeWarning) {
|
|
@@ -426,7 +439,7 @@ var init_ChatLayout = __esm({
|
|
|
426
439
|
if (isPatchError) {
|
|
427
440
|
return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, paddingY: 0 }, /* @__PURE__ */ React2.createElement(Text2, { color: "red", bold: true, underline: true }, "\u274C PATCH FAILED"), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "Patch failed: ", /* @__PURE__ */ React2.createElement(Text2, { color: "white", bold: true }, "Model generated malformed edit.")))));
|
|
428
441
|
}
|
|
429
|
-
if (msg.role === "system" && msg.text?.includes("[
|
|
442
|
+
if (msg.role === "system" && msg.text?.includes("[TOOL RESULT]") && !isDiffResult && !isTerminalRecord && !isPatchError) return null;
|
|
430
443
|
if (msg.isAskRecord) {
|
|
431
444
|
const selectionMatch = msg.text.match(/Selection: (.*)/);
|
|
432
445
|
const selection = selectionMatch ? selectionMatch[1] : "No selection";
|
|
@@ -871,37 +884,37 @@ var init_main_tools = __esm({
|
|
|
871
884
|
TOOL_PROTOCOL = (mode) => `
|
|
872
885
|
-- TOOL DEFINITIONS --
|
|
873
886
|
Access to internal tools. To call a tool, MUST use the exact syntax on a new line:
|
|
874
|
-
[tool:functions.
|
|
887
|
+
[tool:functions.ToolName(args)]
|
|
875
888
|
|
|
876
889
|
- COMMUNICATION TOOLS -
|
|
877
|
-
1. Ask User: [tool:functions.
|
|
890
|
+
1. Ask User: [tool:functions.Ask(question="...", optionA="<option>::<description>", ...MAX 4)]. Ambiguity Resolution. Mandatory Triggers: Path Divergence, Security, Risk Mitigation. ask >> finish
|
|
878
891
|
Suggest best options; don't ask for preferences. System handles the rest
|
|
879
892
|
|
|
880
893
|
- WEB TOOLS -
|
|
881
|
-
1. Web Search: [tool:functions.
|
|
882
|
-
2. Web Scrape: [tool:functions.
|
|
894
|
+
1. Web Search: [tool:functions.WebSearch(query="...", limit=number)]. Find info (limit 3-10). Proactive use for unknown topics${mode === "Flux" ? " or docs" : ""}
|
|
895
|
+
2. Web Scrape: [tool:functions.WebScrape(url="...")]. Visit URL
|
|
883
896
|
|
|
884
897
|
${mode === "Flux" ? `- DEV TOOLS (path = relative to CWD) -
|
|
885
|
-
1.
|
|
886
|
-
2.
|
|
887
|
-
3.
|
|
888
|
-
4.
|
|
889
|
-
5.
|
|
890
|
-
6.
|
|
891
|
-
7.
|
|
892
|
-
8.
|
|
898
|
+
1. [tool:functions.ReadFile(path="...", start_line=N, end_line=N)]. Reads contents. Supports images/docs. User gives image/doc: VIEW FIRST
|
|
899
|
+
2. [tool:functions.ReadFolder(path="...")]. Detailed DIR stats
|
|
900
|
+
3. [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? -> update_file > write_file
|
|
901
|
+
4. [tool:functions.PatchFile(path="...", content_to_replace="old content", content_to_add="new content")]. Surgical patching. Unsure content_to_replace? -> view_file >> guessing.
|
|
902
|
+
5. [tool:functions.WritePDF(path="...", content="...", orientation="...")]. A4 PDF. Use CSS for layout (100vh/vw). Handle page breaks; no manual footers
|
|
903
|
+
6. [tool:functions.WriteDoc(path="...", content="...")]. A4 Word doc. Proper margins and page breaks
|
|
904
|
+
7. [tool:functions.Run(command="...")]. Runs a shell command. Destructive/Irreversible ops -> ask user
|
|
905
|
+
8. [tool:functions.SearchKeyword(keyword="...")]. Global search. Finds definitions/logic without reading every file
|
|
893
906
|
|
|
894
907
|
- VERIFY RESULT CONTENTS. Fix errors. No hallucinations
|
|
895
908
|
- File tools > code chat
|
|
896
909
|
|
|
897
|
-
- Escape quotes:
|
|
910
|
+
- Escape quotes: \\" for code strings
|
|
898
911
|
- Literal escapes: Double-escape sequences (e.g., \\\\n, \\\\t)
|
|
899
912
|
- File structure: Real newlines for code formatting`.trim() : `
|
|
900
|
-
- DEV TOOLS ARE NOT AVAILABLE IN FLOW MODE. If
|
|
913
|
+
- DEV TOOLS ARE NOT AVAILABLE IN FLOW MODE. If file access is needed, tell the user to switch to FLUX`.trim()}
|
|
901
914
|
|
|
902
|
-
- Results: Passed as [
|
|
915
|
+
- Results: Passed as [TOOL RESULT] SYSTEM
|
|
903
916
|
- Tool calls: End with [turn: continue]. Only use [turn: finish] after verifying goals
|
|
904
|
-
- Multi-call: Stack
|
|
917
|
+
- Multi-call: Stack upto 5`.trim();
|
|
905
918
|
}
|
|
906
919
|
});
|
|
907
920
|
|
|
@@ -910,20 +923,19 @@ var JANITOR_TOOLS_PROTOCOL;
|
|
|
910
923
|
var init_janitor_tools = __esm({
|
|
911
924
|
"src/data/janitor_tools.js"() {
|
|
912
925
|
JANITOR_TOOLS_PROTOCOL = (isMemoryEnabled = true, needTitle = true) => `
|
|
926
|
+
Your tool syntax is: '[tool:functions.ToolName(args...)]'
|
|
913
927
|
${needTitle ? `-- CHAT MANAGEMENT TOOLS --
|
|
914
|
-
1. YOU MUST UPDATE CHAT TITLE (URGENT PRIORITY):
|
|
915
|
-
[tool:functions.
|
|
916
|
-
-- MEMORY TOOLS (YOU SHOULD NOT OUTPUT ANYTHING OTHER THAN THESE SPECIFIC TOOLS) --
|
|
917
|
-
Your tool syntax is: '[tool:functions.function_name(args...)]'
|
|
918
|
-
You have access to the following memory functions to persist important information:
|
|
928
|
+
1. YOU MUST UPDATE CHAT TITLE (URGENT PRIORITY, MUST CALL THIS TOOL):
|
|
929
|
+
[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.`.trimEnd() : ""}
|
|
919
930
|
|
|
920
|
-
|
|
921
|
-
|
|
931
|
+
You have access to the following memory functions to persist important information:
|
|
932
|
+
1. Temporary context (URGENT PRIORITY, MUST CALL THIS TOOL):
|
|
933
|
+
[tool:functions.Memory(action='temp', content='<summary of the user prompt & model responses ONLY FROM LATEST PROMPT UNDER 40 WORDS>. [Talked on: <date> <hour>]')]
|
|
922
934
|
|
|
923
935
|
${isMemoryEnabled ? `2. User-specific long-term memory (USE BASED ON CONVERSATION CONTEXT):
|
|
924
|
-
- Add: [tool:functions.
|
|
925
|
-
- Delete: [tool:functions.
|
|
926
|
-
- Update: [tool:functions.
|
|
936
|
+
- Add: [tool:functions.Memory(action='user', method='add', content='<string to add>. [Saved on: <date ONLY>]')]
|
|
937
|
+
- Delete: [tool:functions.Memory(action='user', method='delete', content='exact memory id')]
|
|
938
|
+
- Update: [tool:functions.Memory(action='user', method='update', content-new='string to update', content-old='exact memory id')]
|
|
927
939
|
|
|
928
940
|
Usage Rules:
|
|
929
941
|
- Frequency for 'user' action: Only when explicit context from chat is found or explicitly requested by the user.
|
|
@@ -1014,49 +1026,51 @@ ${parts.join("\n\n")}
|
|
|
1014
1026
|
${foundFiles.map((f) => `- ${f.name}: ${f.desc}`).join("\n")}
|
|
1015
1027
|
Check these first; these files > training data for project consistency. Safety rules still apply` : "";
|
|
1016
1028
|
return `${nameStr}${nicknameStr}${userInstrStr}
|
|
1017
|
-
|
|
1029
|
+
[SYSTEM (OVERRIDES EVERYTHING)]
|
|
1018
1030
|
Identity: Flux Flow (by Kushal Roy Chowdhury). Sassy, Friendly CLI Agent. No flirting
|
|
1019
|
-
Mode: ${mode}
|
|
1020
|
-
|
|
1021
|
-
|
|
1031
|
+
Mode: ${mode}. ${mode === "Flux" ? "Goal-oriented" : "Conversational & UX-focused"}
|
|
1032
|
+
CWD: ${cwdStr}.${isSystemDir ? " [PROTECTED: ASK BEFORE MODIFYING]" : ""} OS: ${osDetected}${osDetected === "Windows" ? ". PS via CMD" : ""}
|
|
1033
|
+
High Priority: [SYSTEM], [STEERING HINT]
|
|
1022
1034
|
|
|
1023
|
-
-- THINKING
|
|
1035
|
+
-- THINKING RULES --
|
|
1024
1036
|
${thinkingConfig}
|
|
1025
1037
|
***THINKING POLICY***
|
|
1026
1038
|
- Always use <think> ... </think> before responding
|
|
1027
1039
|
- Never skip thinking, even for simple tasks, code, or greetings
|
|
1028
|
-
- Never jump to responses
|
|
1040
|
+
- Never jump to responses, regardless of task complexity
|
|
1029
1041
|
|
|
1030
1042
|
${TOOL_PROTOCOL(mode)}
|
|
1031
1043
|
${projectContextBlock}
|
|
1032
1044
|
|
|
1033
|
-
-- MEMORY
|
|
1045
|
+
-- MEMORY RULES --
|
|
1034
1046
|
- Memory: ${isMemoryEnabled ? "Use memories to subtly personalize" : "OFF (tell user to enable in /settings if needed)"}
|
|
1035
|
-
- Time: Logs are timestamped. RELATIVE TIME REFERENCE
|
|
1047
|
+
- Time: Logs are timestamped. RELATIVE TIME REFERENCE e.g. few mins ago <dd/mm/yyyy>
|
|
1036
1048
|
|
|
1037
|
-
-- SECURITY
|
|
1038
|
-
- EXTERNAL
|
|
1039
|
-
-
|
|
1040
|
-
-
|
|
1049
|
+
-- SECURITY RULES --
|
|
1050
|
+
- EXTERNAL ACCESS: ${systemSettings.allowExternalAccess ? "ENABLED" : "RESTRICTED CWD only"}
|
|
1051
|
+
- Sensitive files? Ask before Read
|
|
1052
|
+
- [SYSTEM] >>> [USER]
|
|
1041
1053
|
|
|
1042
1054
|
-- FORMATTING --
|
|
1043
1055
|
- Clean, concise responses
|
|
1044
1056
|
- Tables: GFM (Max 4 cols, short rows)
|
|
1045
|
-
- NO LaTeX. Code blocks for literature. Kaomojis
|
|
1057
|
+
- NO LaTeX. Code blocks for literature. Kaomojis
|
|
1046
1058
|
|
|
1047
|
-
-- RESPONSE
|
|
1059
|
+
-- RESPONSE RULES --
|
|
1048
1060
|
- End with [turn: continue] for more steps or [turn: finish] when done
|
|
1049
|
-
-
|
|
1061
|
+
- Stack tools if needed, but always end with [turn: continue] if called any tools
|
|
1050
1062
|
TO END THE LOOP, **MUST** WRITE [turn: finish] AT END OF RESPONSE
|
|
1051
|
-
|
|
1063
|
+
[/SYSTEM]`.trim();
|
|
1052
1064
|
};
|
|
1053
1065
|
getJanitorInstruction = (originalText, agentRaws, userMemories = "", isMemoryEnabled = true, needTitle = true) => {
|
|
1054
|
-
let agentRes = `${agentRaws.replace(
|
|
1066
|
+
let agentRes = `${agentRaws.replace(/\[tool:functions\..*?\]/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)}`;
|
|
1055
1067
|
if (agentRes.length > 3500) {
|
|
1056
1068
|
agentRes += "\n... (truncated) ...";
|
|
1057
1069
|
}
|
|
1058
|
-
let originalTextProcessed = originalText.replace(/\[Prompted on:.*?\]/g, "");
|
|
1059
|
-
|
|
1070
|
+
let originalTextProcessed = originalText.replace(/\[Prompted on:.*?\]/g, "").trim();
|
|
1071
|
+
agentRes = agentRes.replace(/\r?\n\r?\n/g, "\n").replace(/\n\n/g, "\n").replace(/\\n\\n/g, "").trim();
|
|
1072
|
+
return `[USER]: ${originalTextProcessed.substring(0, 600)}
|
|
1073
|
+
${originalTextProcessed.length > 600 ? "... (truncated) ...\n\n" : ""}
|
|
1060
1074
|
[AGENT (current turn)]: ${agentRes}
|
|
1061
1075
|
${userMemories ? `
|
|
1062
1076
|
|
|
@@ -1075,13 +1089,14 @@ YOU ARE A SILENT BACKGROUND SYSTEM PROCESS. YOU HAVE NO MOUTH. YOUR ONLY OUTPUT
|
|
|
1075
1089
|
5. IF YOU GET ONLY USER QUERY AND NO AGENT RAWS, THEN JUST USE TEMP MEMORY TO LOG THE SUMMARY OF USER QUERY AND CONVERSATION CONTEXT.
|
|
1076
1090
|
6. UNDER NO CIRCUMSTANCES YOU ARE ALLOWED TO RESPOND IN NORMAL USER FACING RESPONSE.
|
|
1077
1091
|
7. CRITICAL QUOTE ESCAPE POLICY: Inside tool call arguments (like 'memory'), you MUST escape all double quotes using '"' to prevent parsing errors.
|
|
1092
|
+
8. You MUST NOT WRITE ANYTHING OTHER THAN [tool:functions. ...].
|
|
1078
1093
|
|
|
1079
1094
|
YOUR JOB: Analyze the 'User prompt' and 'Agent Raws' to extract facts for long-term memory or handle system tasks.
|
|
1080
1095
|
${isMemoryEnabled ? `If user tell something that is important (like, hobbies, preferences, facts about user, hates, likes, etc) to know user better over time, use long term memory tools.` : ""}
|
|
1081
1096
|
|
|
1082
1097
|
${JANITOR_TOOLS_PROTOCOL(isMemoryEnabled, needTitle)}
|
|
1083
1098
|
|
|
1084
|
-
Current date and Time: ${(/* @__PURE__ */ new Date()).toLocaleString([], { year: "numeric", month: "numeric", day: "numeric", hour: "2-digit", hour12: true })}
|
|
1099
|
+
Current date and Time: ${(/* @__PURE__ */ new Date()).toLocaleString([], { year: "numeric", month: "numeric", day: "numeric", hour: "2-digit", hour12: true })}. <dd/mm/yyyy HH am/pm>.
|
|
1085
1100
|
=== END SYSTEM PROMPT ===`.trim();
|
|
1086
1101
|
};
|
|
1087
1102
|
}
|
|
@@ -2114,6 +2129,11 @@ var init_exec_command = __esm({
|
|
|
2114
2129
|
continue;
|
|
2115
2130
|
}
|
|
2116
2131
|
if (char === "\\") {
|
|
2132
|
+
if (command[i + 1] === " ") {
|
|
2133
|
+
current += " ";
|
|
2134
|
+
i++;
|
|
2135
|
+
continue;
|
|
2136
|
+
}
|
|
2117
2137
|
current += char;
|
|
2118
2138
|
isEscaped = true;
|
|
2119
2139
|
continue;
|
|
@@ -2127,6 +2147,18 @@ var init_exec_command = __esm({
|
|
|
2127
2147
|
if (char === '"' || char === "'") {
|
|
2128
2148
|
inQuote = char;
|
|
2129
2149
|
current += char;
|
|
2150
|
+
} else if (char === ";" && !current.includes("://")) {
|
|
2151
|
+
if (current.length > 0) {
|
|
2152
|
+
tokens.push(current);
|
|
2153
|
+
current = "";
|
|
2154
|
+
}
|
|
2155
|
+
tokens.push("&");
|
|
2156
|
+
} else if (char === "|" && !current.includes("://")) {
|
|
2157
|
+
if (current.length > 0) {
|
|
2158
|
+
tokens.push(current);
|
|
2159
|
+
current = "";
|
|
2160
|
+
}
|
|
2161
|
+
tokens.push("|");
|
|
2130
2162
|
} else if (/\s/.test(char)) {
|
|
2131
2163
|
if (current.length > 0) {
|
|
2132
2164
|
tokens.push(current);
|
|
@@ -2141,26 +2173,71 @@ var init_exec_command = __esm({
|
|
|
2141
2173
|
tokens.push(current);
|
|
2142
2174
|
}
|
|
2143
2175
|
const looksLikePath = (str) => {
|
|
2144
|
-
if (!str.includes("/")
|
|
2176
|
+
if (!str.includes("/")) return false;
|
|
2177
|
+
if (/^(https?|file|ftp):\/\//i.test(str)) return false;
|
|
2178
|
+
if (str.startsWith("/") && (str.match(/\//g) || []).length === 1) {
|
|
2145
2179
|
return false;
|
|
2146
2180
|
}
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
return false;
|
|
2151
|
-
}
|
|
2152
|
-
const hasDriveLetter = /^[a-zA-Z]:\//.test(str);
|
|
2153
|
-
const hasRelativeStart = /^\.?\.?\//.test(str);
|
|
2154
|
-
const hasMultipleSlashes = (str.match(/\//g) || []).length > 1;
|
|
2155
|
-
const hasExtension = /\.[a-zA-Z0-9_-]+$/.test(str);
|
|
2156
|
-
return hasDriveLetter || hasRelativeStart || hasMultipleSlashes || hasExtension;
|
|
2181
|
+
if (/\s\/|\/\s/.test(str)) return false;
|
|
2182
|
+
if (/[\(\)\{\}\;\<\>\=\'\"]/.test(str)) return false;
|
|
2183
|
+
return true;
|
|
2157
2184
|
};
|
|
2158
|
-
const
|
|
2159
|
-
|
|
2185
|
+
const translatedTokens = [];
|
|
2186
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
2187
|
+
const token = tokens[i];
|
|
2188
|
+
if (token === "|" && tokens[i + 1] === "tee") {
|
|
2189
|
+
if (tokens[i + 2] === "-a") {
|
|
2190
|
+
translatedTokens.push(">>");
|
|
2191
|
+
i += 2;
|
|
2192
|
+
} else {
|
|
2193
|
+
translatedTokens.push(">");
|
|
2194
|
+
i += 1;
|
|
2195
|
+
}
|
|
2196
|
+
continue;
|
|
2197
|
+
}
|
|
2198
|
+
if (token === "|" && tokens[i + 1] === "cat" && tokens[i + 2] === ">") {
|
|
2199
|
+
translatedTokens.push(">");
|
|
2200
|
+
i += 2;
|
|
2201
|
+
continue;
|
|
2202
|
+
}
|
|
2203
|
+
if (token === "|") {
|
|
2204
|
+
const nextToken = tokens[i + 1];
|
|
2205
|
+
if (nextToken) {
|
|
2206
|
+
const nextUnquoted = nextToken.replace(/^['"]|['"]$/g, "");
|
|
2207
|
+
const isWritableFile = /\.(txt|md|json|log|csv|html|css|py|js|xml|yaml|yml|pdf|docx|pptx|xlsx)$/i.test(nextUnquoted);
|
|
2208
|
+
if (looksLikePath(nextUnquoted) && isWritableFile) {
|
|
2209
|
+
translatedTokens.push(">");
|
|
2210
|
+
continue;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
translatedTokens.push(token);
|
|
2215
|
+
}
|
|
2216
|
+
let inEchoArguments = false;
|
|
2217
|
+
const processedTokens = translatedTokens.map((token) => {
|
|
2218
|
+
if (token === "echo") {
|
|
2219
|
+
inEchoArguments = true;
|
|
2220
|
+
return token;
|
|
2221
|
+
}
|
|
2222
|
+
const controlOperators = [">", ">>", "<", "&", "&&", "|", "||", ";"];
|
|
2223
|
+
if (controlOperators.includes(token)) {
|
|
2224
|
+
inEchoArguments = false;
|
|
2225
|
+
}
|
|
2226
|
+
const hasOuterQuotes = /^['"]|['"]$/.test(token);
|
|
2227
|
+
let processed = token;
|
|
2228
|
+
if (inEchoArguments && hasOuterQuotes) {
|
|
2229
|
+
processed = token.replace(/^['"]|['"]$/g, "");
|
|
2230
|
+
}
|
|
2231
|
+
const currentHasOuterQuotes = /^['"]|['"]$/.test(processed);
|
|
2232
|
+
const unquoted = processed.replace(/^['"]|['"]$/g, "");
|
|
2160
2233
|
if (looksLikePath(unquoted)) {
|
|
2161
|
-
|
|
2234
|
+
processed = processed.replace(/\//g, "\\");
|
|
2235
|
+
}
|
|
2236
|
+
const finalUnquoted = processed.replace(/^['"]|['"]$/g, "");
|
|
2237
|
+
if (finalUnquoted.includes(" ") && !currentHasOuterQuotes) {
|
|
2238
|
+
processed = `"${finalUnquoted}"`;
|
|
2162
2239
|
}
|
|
2163
|
-
return
|
|
2240
|
+
return processed;
|
|
2164
2241
|
});
|
|
2165
2242
|
return processedTokens.join(" ");
|
|
2166
2243
|
};
|
|
@@ -2208,18 +2285,18 @@ ${stderr}`);
|
|
|
2208
2285
|
if (code !== 0) result.push(`EXIT CODE: ${code}`);
|
|
2209
2286
|
const finalOutput = result.join("\n\n") || "Command executed with no output.";
|
|
2210
2287
|
if (code !== 0) {
|
|
2211
|
-
resolve(`ERROR: Command [${
|
|
2288
|
+
resolve(`ERROR: Command [${rawCommand}] failed with exit code [${code}].
|
|
2212
2289
|
|
|
2213
2290
|
${finalOutput}`);
|
|
2214
2291
|
} else {
|
|
2215
|
-
resolve(`SUCCESS: Command [${
|
|
2292
|
+
resolve(`SUCCESS: Command [${rawCommand}] completed.
|
|
2216
2293
|
|
|
2217
2294
|
${finalOutput}`);
|
|
2218
2295
|
}
|
|
2219
2296
|
});
|
|
2220
2297
|
child.on("error", (err) => {
|
|
2221
2298
|
activeChildProcess = null;
|
|
2222
|
-
resolve(`ERROR: Failed to start command [${
|
|
2299
|
+
resolve(`ERROR: Failed to start command [${rawCommand}]: ${err.message}`);
|
|
2223
2300
|
});
|
|
2224
2301
|
});
|
|
2225
2302
|
};
|
|
@@ -2568,7 +2645,21 @@ var init_tools = __esm({
|
|
|
2568
2645
|
write_pdf,
|
|
2569
2646
|
write_docx,
|
|
2570
2647
|
search_keyword,
|
|
2571
|
-
ask: ask_user
|
|
2648
|
+
ask: ask_user,
|
|
2649
|
+
// PascalCase Normalizations for Token Efficiency
|
|
2650
|
+
Ask: ask_user,
|
|
2651
|
+
WebSearch: web_search,
|
|
2652
|
+
WebScrape: web_scrape,
|
|
2653
|
+
ReadFile: view_file,
|
|
2654
|
+
ReadFolder: read_folder,
|
|
2655
|
+
WriteFile: write_file,
|
|
2656
|
+
PatchFile: update_file,
|
|
2657
|
+
WritePDF: write_pdf,
|
|
2658
|
+
WriteDoc: write_docx,
|
|
2659
|
+
Run: exec_command,
|
|
2660
|
+
SearchKeyword: search_keyword,
|
|
2661
|
+
Memory: memory,
|
|
2662
|
+
Chat: chat
|
|
2572
2663
|
};
|
|
2573
2664
|
dispatchTool = async (toolName, args, context = {}) => {
|
|
2574
2665
|
const tool = TOOL_MAP[toolName];
|
|
@@ -2633,7 +2724,7 @@ var init_ai = __esm({
|
|
|
2633
2724
|
const isMemoryEnabled = systemSettings?.memory !== false;
|
|
2634
2725
|
const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
|
|
2635
2726
|
const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
|
|
2636
|
-
const janitorContents = history.slice(-12).filter((msg) => msg.text && !msg.text.includes("[
|
|
2727
|
+
const janitorContents = history.slice(-12).filter((msg) => msg.text && !msg.text.includes("[TOOL RESULT]") && !msg.text.includes("OBSERVATION:")).map((msg) => ({
|
|
2637
2728
|
role: msg.role === "user" ? "user" : "model",
|
|
2638
2729
|
parts: [{ text: msg.text.replace(/<think>[\s\S]*?<\/think>/g, "").trim() }]
|
|
2639
2730
|
}));
|
|
@@ -3067,7 +3158,7 @@ DEBUG [${date}]: ${finalSynthesis}
|
|
|
3067
3158
|
[SYSTEM] Tool result received. Analyze output and proceed with your turn. **STRICTLY MAINTAIN THINKING PROTOCOL. NEVER START A RESPONSE WITHOUT THINKING**.`;
|
|
3068
3159
|
const lastUserMsg = contents[contents.length - 1];
|
|
3069
3160
|
let addedMarker = false;
|
|
3070
|
-
if (lastUserMsg && lastUserMsg.role === "user" && lastUserMsg.parts?.[0]?.text?.startsWith("[
|
|
3161
|
+
if (lastUserMsg && lastUserMsg.role === "user" && lastUserMsg.parts?.[0]?.text?.startsWith("[TOOL RESULT]")) {
|
|
3071
3162
|
lastUserMsg.parts[0].text += jitInstruction;
|
|
3072
3163
|
addedMarker = true;
|
|
3073
3164
|
}
|
|
@@ -3145,7 +3236,24 @@ DEBUG [${date}]: ${finalSynthesis}
|
|
|
3145
3236
|
const toolContext = getActiveToolContext(turnText);
|
|
3146
3237
|
if (toolContext.inside) {
|
|
3147
3238
|
if (!lastToolEventTime) lastToolEventTime = Date.now();
|
|
3148
|
-
const
|
|
3239
|
+
const rawToolName = toolContext.toolName;
|
|
3240
|
+
const NORMALIZE_MAP = {
|
|
3241
|
+
"Ask": "ask",
|
|
3242
|
+
"WebSearch": "web_search",
|
|
3243
|
+
"WebScrape": "web_scrape",
|
|
3244
|
+
"ReadFile": "view_file",
|
|
3245
|
+
"ReadFolder": "read_folder",
|
|
3246
|
+
"WriteFile": "write_file",
|
|
3247
|
+
"PatchFile": "update_file",
|
|
3248
|
+
"WritePDF": "write_pdf",
|
|
3249
|
+
"WriteDoc": "write_docx",
|
|
3250
|
+
"Run": "exec_command",
|
|
3251
|
+
"SearchKeyword": "search_keyword",
|
|
3252
|
+
"Memory": "memory",
|
|
3253
|
+
"Chat": "chat",
|
|
3254
|
+
"chat": "chat"
|
|
3255
|
+
};
|
|
3256
|
+
const potentialTool = NORMALIZE_MAP[rawToolName] || rawToolName;
|
|
3149
3257
|
const partialArgs = toolContext.args || "";
|
|
3150
3258
|
let detail = null;
|
|
3151
3259
|
if (["write_file", "update_file", "view_file", "read_folder", "write_pdf", "write_docx", "search_keyword"].includes(potentialTool)) {
|
|
@@ -3228,17 +3336,34 @@ DEBUG [${date}]: ${finalSynthesis}
|
|
|
3228
3336
|
const allToolsFound = detectToolCalls(toolActionableText);
|
|
3229
3337
|
while (allToolsFound.length > toolCallPointer) {
|
|
3230
3338
|
const toolCall = allToolsFound[toolCallPointer];
|
|
3231
|
-
const
|
|
3232
|
-
|
|
3339
|
+
const NORMALIZE_MAP = {
|
|
3340
|
+
"Ask": "ask",
|
|
3341
|
+
"WebSearch": "web_search",
|
|
3342
|
+
"WebScrape": "web_scrape",
|
|
3343
|
+
"ReadFile": "view_file",
|
|
3344
|
+
"ReadFolder": "read_folder",
|
|
3345
|
+
"WriteFile": "write_file",
|
|
3346
|
+
"PatchFile": "update_file",
|
|
3347
|
+
"WritePDF": "write_pdf",
|
|
3348
|
+
"WriteDoc": "write_docx",
|
|
3349
|
+
"Run": "exec_command",
|
|
3350
|
+
"SearchKeyword": "search_keyword",
|
|
3351
|
+
"Memory": "memory",
|
|
3352
|
+
"Chat": "chat",
|
|
3353
|
+
"chat": "chat"
|
|
3354
|
+
};
|
|
3355
|
+
const normToolName = NORMALIZE_MAP[toolCall.toolName] || toolCall.toolName;
|
|
3356
|
+
const displayLabel = TOOL_LABELS2[normToolName] || toolCall.toolName;
|
|
3357
|
+
const detail = getToolDetail(normToolName, toolCall.args);
|
|
3233
3358
|
yield { type: "status", content: `${displayLabel}${detail ? ` (${detail})` : ""}...` };
|
|
3234
3359
|
let label = "";
|
|
3235
|
-
if (
|
|
3360
|
+
if (normToolName === "web_search") {
|
|
3236
3361
|
const { query, limit = 10 } = parseArgs(toolCall.args);
|
|
3237
3362
|
label = `\u{1F50D} SEARCHED: "${query}" (${limit})`.toUpperCase();
|
|
3238
|
-
} else if (
|
|
3363
|
+
} else if (normToolName === "web_scrape") {
|
|
3239
3364
|
const url = parseArgs(toolCall.args).url || "...";
|
|
3240
3365
|
label = `\u{1F4D6} READ SITE: ${url}`.toUpperCase();
|
|
3241
|
-
} else if (
|
|
3366
|
+
} else if (normToolName === "view_file") {
|
|
3242
3367
|
const { path: targetPath2, StartLine, EndLine, start_line, end_line } = parseArgs(toolCall.args);
|
|
3243
3368
|
const rawStart = StartLine || start_line;
|
|
3244
3369
|
const rawEnd = EndLine || end_line;
|
|
@@ -3266,20 +3391,20 @@ DEBUG [${date}]: ${finalSynthesis}
|
|
|
3266
3391
|
} else {
|
|
3267
3392
|
label = `\u{1F4C4} ANALYZED FILE: ${targetPath2} | LINES: ${sLine}-${actualEndLine} OF ${totalLines}`.toUpperCase();
|
|
3268
3393
|
}
|
|
3269
|
-
} else if (
|
|
3270
|
-
const action =
|
|
3394
|
+
} else if (normToolName === "list_files" || normToolName === "read_folder") {
|
|
3395
|
+
const action = normToolName === "list_files" ? "LIST" : "ANALYSED";
|
|
3271
3396
|
label = `\u{1F4C2} ${action} FOLDER: ${parseArgs(toolCall.args).path || "."}`.toUpperCase();
|
|
3272
|
-
} else if (
|
|
3273
|
-
const action =
|
|
3397
|
+
} else if (normToolName === "write_file" || normToolName === "update_file") {
|
|
3398
|
+
const action = normToolName === "write_file" ? "WRITTEN" : "UPDATED FILE";
|
|
3274
3399
|
label = `\u{1F4BE} ${action}: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
3275
|
-
} else if (
|
|
3400
|
+
} else if (normToolName === "write_pdf") {
|
|
3276
3401
|
label = `\u{1F4D1} PDF CREATED: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
3277
|
-
} else if (
|
|
3402
|
+
} else if (normToolName === "write_docx") {
|
|
3278
3403
|
label = `\u{1F4DD} DOCX CREATED: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
3279
|
-
} else if (
|
|
3404
|
+
} else if (normToolName === "search_keyword") {
|
|
3280
3405
|
const { keyword } = parseArgs(toolCall.args);
|
|
3281
3406
|
label = `\u{1F50E} KEYWORD SEARCHED: "${keyword}"`.toUpperCase();
|
|
3282
|
-
} else if (
|
|
3407
|
+
} else if (normToolName === "exec_command" || normToolName === "ask") {
|
|
3283
3408
|
label = "";
|
|
3284
3409
|
} else {
|
|
3285
3410
|
label = `EXECUTED: ${toolCall.toolName}`.toUpperCase();
|
|
@@ -3293,7 +3418,7 @@ DEBUG [${date}]: ${finalSynthesis}
|
|
|
3293
3418
|
${boxMid}
|
|
3294
3419
|
${boxBottom}` };
|
|
3295
3420
|
}
|
|
3296
|
-
if (
|
|
3421
|
+
if (normToolName === "exec_command") {
|
|
3297
3422
|
const { command } = parseArgs(toolCall.args);
|
|
3298
3423
|
if (command && settings.systemSettings && settings.systemSettings.allowExternalAccess === false) {
|
|
3299
3424
|
const riskyPatterns = [/[a-zA-Z]:[\\\/]/i, /^\//, /\.\.[\\\/]/, /\/etc\//, /\/var\//, /\/root\//, /\/bin\//, /\/usr\//];
|
|
@@ -3307,8 +3432,8 @@ ${boxBottom}` };
|
|
|
3307
3432
|
});
|
|
3308
3433
|
if (isViolating) {
|
|
3309
3434
|
const denyMsg = `Access Denied. Terminal is prohibited from accessing system drives (C://) or external directories while "External Workspace Access" is disabled.`;
|
|
3310
|
-
toolResults.push({ role: "user", text: `[
|
|
3311
|
-
yield { type: "tool_result", content: `[
|
|
3435
|
+
toolResults.push({ role: "user", text: `[TOOL RESULT]: ERROR: ${denyMsg}` });
|
|
3436
|
+
yield { type: "tool_result", content: `[TOOL RESULT]: ERROR: ${denyMsg}` };
|
|
3312
3437
|
toolCallPointer++;
|
|
3313
3438
|
continue;
|
|
3314
3439
|
}
|
|
@@ -3324,25 +3449,25 @@ ${boxBottom}` };
|
|
|
3324
3449
|
const absoluteCwd = path15.resolve(process.cwd());
|
|
3325
3450
|
if (isExternalOff && !absoluteTarget.startsWith(absoluteCwd)) {
|
|
3326
3451
|
const denyMsg = `Access Denied. You are not allowed to access files outside the current workspace. To enable this, ask the user to turn on "External Workspace Access" in /settings.`;
|
|
3327
|
-
toolResults.push({ role: "user", text: `[
|
|
3452
|
+
toolResults.push({ role: "user", text: `[TOOL RESULT]: ERROR: ${denyMsg}
|
|
3328
3453
|
|
|
3329
3454
|
[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS HIGHEST PRIORITY. NEVER START A RESPONSE WITHOUT THINKING**.` });
|
|
3330
|
-
yield { type: "tool_result", content: `[
|
|
3455
|
+
yield { type: "tool_result", content: `[TOOL RESULT]: ERROR: ${denyMsg}` };
|
|
3331
3456
|
toolCallPointer++;
|
|
3332
3457
|
continue;
|
|
3333
3458
|
}
|
|
3334
3459
|
}
|
|
3335
3460
|
if (settings.onToolApproval) {
|
|
3336
|
-
let shouldPrompt =
|
|
3461
|
+
let shouldPrompt = normToolName === "write_file" || normToolName === "update_file" || normToolName === "exec_command";
|
|
3337
3462
|
if (shouldPrompt) {
|
|
3338
|
-
const approval = await settings.onToolApproval(
|
|
3463
|
+
const approval = await settings.onToolApproval(normToolName, toolCall.args);
|
|
3339
3464
|
if (approval === "deny") {
|
|
3340
|
-
if (
|
|
3341
|
-
const denyMsg = `Permission Denied: User rejected the ${
|
|
3342
|
-
toolResults.push({ role: "user", text: `[
|
|
3465
|
+
if (normToolName === "exec_command" && settings.onExecEnd) settings.onExecEnd();
|
|
3466
|
+
const denyMsg = `Permission Denied: User rejected the ${normToolName === "exec_command" ? "terminal execution" : "file edit"}.`;
|
|
3467
|
+
toolResults.push({ role: "user", text: `[TOOL RESULT]: DENIED: ${denyMsg}
|
|
3343
3468
|
|
|
3344
3469
|
[SYSTEM] **STRICTLY FOLLOW THINKING POLICY AS HIGHEST PRIORITY. NEVER START A RESPONSE WITHOUT THINKING**.` });
|
|
3345
|
-
yield { type: "tool_result", content: `[
|
|
3470
|
+
yield { type: "tool_result", content: `[TOOL RESULT]: DENIED: ${denyMsg}` };
|
|
3346
3471
|
await incrementUsage("toolDenied");
|
|
3347
3472
|
if (settings.onToolResult) settings.onToolResult("denied");
|
|
3348
3473
|
toolCallPointer++;
|
|
@@ -3352,7 +3477,7 @@ ${boxBottom}` };
|
|
|
3352
3477
|
}
|
|
3353
3478
|
const effectiveStart = lastToolEventTime || Date.now();
|
|
3354
3479
|
yield { type: "spinner", content: false };
|
|
3355
|
-
let result = await dispatchTool(
|
|
3480
|
+
let result = await dispatchTool(normToolName, toolCall.args, {
|
|
3356
3481
|
chatId,
|
|
3357
3482
|
history,
|
|
3358
3483
|
onChunk: (chunk2) => settings.onExecChunk ? settings.onExecChunk(chunk2) : null,
|
|
@@ -3367,7 +3492,7 @@ ${boxBottom}` };
|
|
|
3367
3492
|
binaryPart = result.binaryPart;
|
|
3368
3493
|
result = result.text;
|
|
3369
3494
|
}
|
|
3370
|
-
if (
|
|
3495
|
+
if (normToolName === "exec_command" && settings.onExecEnd) {
|
|
3371
3496
|
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
3372
3497
|
settings.onExecEnd();
|
|
3373
3498
|
}
|
|
@@ -3381,14 +3506,14 @@ ${boxBottom}` };
|
|
|
3381
3506
|
await incrementUsage("toolFailure");
|
|
3382
3507
|
if (settings.onToolResult) settings.onToolResult("failure");
|
|
3383
3508
|
}
|
|
3384
|
-
const aiContent = `[
|
|
3509
|
+
const aiContent = `[TOOL RESULT]: ${(result || "").toString().split(/\r?\n/).filter((line) => !line.includes("[UI_CONTEXT]")).join("\n")}`;
|
|
3385
3510
|
toolResults.push({ role: "user", text: aiContent, binaryPart });
|
|
3386
|
-
let uiContent = `[
|
|
3387
|
-
if (
|
|
3388
|
-
uiContent = `[
|
|
3511
|
+
let uiContent = `[TOOL RESULT]: ${result || ""}`;
|
|
3512
|
+
if (normToolName === "view_file" || normToolName === "web_scrape") {
|
|
3513
|
+
uiContent = `[TOOL RESULT]: ${label} (Context Locked for UI Clarity)`;
|
|
3389
3514
|
}
|
|
3390
|
-
yield { type: "tool_result", content: uiContent, aiContent, binaryPart, toolName:
|
|
3391
|
-
if (
|
|
3515
|
+
yield { type: "tool_result", content: uiContent, aiContent, binaryPart, toolName: normToolName };
|
|
3516
|
+
if (normToolName === "memory" && result.includes("SUCCESS")) yield { type: "memory_updated" };
|
|
3392
3517
|
toolCallPointer++;
|
|
3393
3518
|
}
|
|
3394
3519
|
}
|