fluxflow-cli 1.1.7 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fluxflow.js +1736 -1482
- package/package.json +1 -1
package/dist/fluxflow.js
CHANGED
|
@@ -1,233 +1,240 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { exec } from "child_process";
|
|
12
|
-
import { MultilineInput } from "ink-multiline-input";
|
|
13
|
-
import TextInput3 from "ink-text-input";
|
|
14
|
-
|
|
15
|
-
// src/components/ChatLayout.jsx
|
|
16
|
-
import React2 from "react";
|
|
17
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
18
11
|
|
|
19
12
|
// src/components/TerminalBox.jsx
|
|
20
13
|
import React from "react";
|
|
21
14
|
import { Box, Text } from "ink";
|
|
22
|
-
var TerminalBox
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
var TerminalBox;
|
|
16
|
+
var init_TerminalBox = __esm({
|
|
17
|
+
"src/components/TerminalBox.jsx"() {
|
|
18
|
+
TerminalBox = React.memo(({ command, output, completed = false }) => {
|
|
19
|
+
const cleanOutput = (output || "").replace(/\r/g, "").trim();
|
|
20
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: completed ? "#334155" : "cyan", paddingX: 2, paddingY: completed ? 0 : 1, width: "100%" }, /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "cyan", bold: true }, completed ? "\u{1F3C1} FINISHED:" : "\u26A1 EXECUTING:", " "), /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "white" }, command)), /* @__PURE__ */ React.createElement(Text, { color: completed ? "#475569" : "yellow", bold: true }, completed ? "\u25CF ARCHIVED" : "\u25CF LIVE")), cleanOutput ? /* @__PURE__ */ React.createElement(Box, { marginTop: completed ? 0 : 1, backgroundColor: "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: completed ? "gray" : "green", wrap: "anywhere" }, cleanOutput)) : !completed && /* @__PURE__ */ React.createElement(Box, { marginTop: 1, backgroundColor: "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", italic: true }, "Waiting for output...")), !completed && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true, italic: true }, "Double-press ESC to terminate if hanging.")));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
25
23
|
});
|
|
26
24
|
|
|
27
25
|
// src/components/ChatLayout.jsx
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let
|
|
38
|
-
while (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
26
|
+
import React2 from "react";
|
|
27
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
28
|
+
var cleanSignals, formatThinkText, MarkdownText, DiffLine, DiffBlock, CodeRenderer, MessageItem, ChatLayout, ChatLayout_default;
|
|
29
|
+
var init_ChatLayout = __esm({
|
|
30
|
+
"src/components/ChatLayout.jsx"() {
|
|
31
|
+
init_TerminalBox();
|
|
32
|
+
cleanSignals = (text) => {
|
|
33
|
+
if (!text) return text;
|
|
34
|
+
const parts = [];
|
|
35
|
+
let i = 0;
|
|
36
|
+
while (i < text.length) {
|
|
37
|
+
const trigger = "tool:functions.";
|
|
38
|
+
if (text.substring(i, i + trigger.length).toLowerCase() === trigger) {
|
|
39
|
+
let balance = 0;
|
|
40
|
+
let foundStart = false;
|
|
41
|
+
let j = i;
|
|
42
|
+
while (j < text.length) {
|
|
43
|
+
if (text[j] === "(") {
|
|
44
|
+
balance++;
|
|
45
|
+
foundStart = true;
|
|
46
|
+
} else if (text[j] === ")") {
|
|
47
|
+
balance--;
|
|
48
|
+
}
|
|
49
|
+
j++;
|
|
50
|
+
if (foundStart && balance === 0) break;
|
|
51
|
+
}
|
|
52
|
+
i = j;
|
|
53
|
+
} else {
|
|
54
|
+
parts.push(text[i]);
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
47
57
|
}
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
58
|
+
return parts.join("").replace(/^\[TOOL_RESULT\]:\s*/gi, "").split("\n").filter((line) => !line.trim().startsWith("SUCCESS:") && !line.trim().startsWith("ERROR:")).join("\n").replace(/\[\s*(turn\s*:)?\s*(continue|finish)\s*\]/gi, "").replace(/\n\s*turn\s*:\s*(continue|finish)\s*$/gi, "").replace(/\n\nResponded on .*/g, "").replace(/\n\n\[Prompted on: .*\]/g, "").replace(/(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)/gi, "\u2192").trim();
|
|
59
|
+
};
|
|
60
|
+
formatThinkText = (cleaned) => {
|
|
61
|
+
if (!cleaned) return null;
|
|
62
|
+
const lines = cleaned.split("\n").filter((l) => l.trim() !== "");
|
|
63
|
+
return lines.map((line, i) => {
|
|
64
|
+
const trimmed = line.trim();
|
|
65
|
+
if (trimmed.startsWith("**") && trimmed.endsWith("**") || trimmed.startsWith("#")) {
|
|
66
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginTop: i === 0 ? 0 : 1, marginBottom: 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, trimmed.replace(/\*|#/g, "").trim()));
|
|
67
|
+
}
|
|
68
|
+
const isBullet = trimmed.startsWith("*");
|
|
69
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginLeft: isBullet ? 2 : 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { italic: true, color: "gray", wrap: "anywhere" }, isBullet ? "\u2022 " : "", trimmed.replace(/^\*|\s\*/g, "").trim()));
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
MarkdownText = React2.memo(({ text, color = "white" }) => {
|
|
73
|
+
if (!text) return null;
|
|
74
|
+
const lines = text.split("\n");
|
|
75
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, lines.map((line, i) => {
|
|
76
|
+
const trimmed = line.trim();
|
|
77
|
+
if (trimmed === "---" || trimmed === "***" || trimmed === "___") {
|
|
78
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginY: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, width: "100%", borderColor: "#333" });
|
|
79
|
+
}
|
|
80
|
+
const headingMatch = trimmed.match(/^(#{1,3})\s+(.*)/);
|
|
81
|
+
if (headingMatch) {
|
|
82
|
+
const level = headingMatch[1].length;
|
|
83
|
+
const hText = headingMatch[2];
|
|
84
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: i, marginTop: 1, marginBottom: 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: level === 1 ? "cyan" : level === 2 ? "magenta" : "yellow", underline: true }, hText.toUpperCase()));
|
|
85
|
+
}
|
|
86
|
+
const isUnordered = trimmed.startsWith("* ") || trimmed.startsWith("- ");
|
|
87
|
+
const isOrdered = /^\d+\.\s/.test(trimmed);
|
|
88
|
+
let content = trimmed;
|
|
89
|
+
if (isUnordered || isOrdered) {
|
|
90
|
+
content = (isUnordered ? " \u2022 " : "") + trimmed.replace(/^[\*\-\d+\.]+\s/, "");
|
|
91
|
+
}
|
|
92
|
+
const parts = content.split(/(\*\*.*?\*\*|\*.*?\*|`.*?`)/g);
|
|
93
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: i, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color, wrap: "anywhere" }, parts.map((part, j) => {
|
|
94
|
+
if (part.startsWith("**") && part.endsWith("**")) {
|
|
95
|
+
return /* @__PURE__ */ React2.createElement(Text2, { key: j, bold: true, color: "white" }, part.slice(2, -2));
|
|
96
|
+
}
|
|
97
|
+
if (part.startsWith("*") && part.endsWith("*")) {
|
|
98
|
+
return /* @__PURE__ */ React2.createElement(Text2, { key: j, italic: true, color: "gray" }, part.slice(1, -1));
|
|
99
|
+
}
|
|
100
|
+
if (part.startsWith("`") && part.endsWith("`")) {
|
|
101
|
+
return /* @__PURE__ */ React2.createElement(Text2, { key: j, color: "cyan", backgroundColor: "#003333" }, " ", part.slice(1, -1), " ");
|
|
102
|
+
}
|
|
103
|
+
return part;
|
|
104
|
+
})));
|
|
105
|
+
}));
|
|
106
|
+
});
|
|
107
|
+
DiffLine = React2.memo(({ line }) => {
|
|
108
|
+
const isContext = line.includes("[UI_CONTEXT]");
|
|
109
|
+
const cleanLine = line.replace("[UI_CONTEXT]", "");
|
|
110
|
+
const isRemoval = cleanLine.startsWith("-");
|
|
111
|
+
const isAddition = cleanLine.startsWith("+");
|
|
112
|
+
const parts = cleanLine.substring(1).split("|");
|
|
113
|
+
const lineNum = parts[0]?.trim() || "";
|
|
114
|
+
const content = parts.slice(1).join("|");
|
|
115
|
+
const bgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : "#1a1a1a";
|
|
116
|
+
const textColor = isRemoval ? "#ff4d4d" : isAddition ? "#4dff88" : "white";
|
|
117
|
+
return /* @__PURE__ */ React2.createElement(Box2, { backgroundColor: bgColor, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { width: 5, flexShrink: 0 }, /* @__PURE__ */ React2.createElement(Text2, { color: isRemoval ? "#cf3a3a" : isAddition ? "#3acf65" : "gray", dimColor: true }, lineNum)), /* @__PURE__ */ React2.createElement(Box2, { width: 2, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: textColor, bold: true }, isRemoval ? "-" : isAddition ? "+" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: textColor, wrap: "anywhere" }, content)));
|
|
118
|
+
});
|
|
119
|
+
DiffBlock = React2.memo(({ text }) => {
|
|
120
|
+
const beforeDiff = text.substring(0, text.indexOf("[DIFF_START]")).trim();
|
|
121
|
+
const afterDiff = text.substring(text.indexOf("[DIFF_END]") + 10).trim();
|
|
122
|
+
const match = text.match(/\[DIFF_START\]([\s\S]*?)\[DIFF_END\]/);
|
|
123
|
+
const diffBody = match ? match[1].trim() : "";
|
|
124
|
+
const diffLines = diffBody.split("\n");
|
|
125
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, beforeDiff && /* @__PURE__ */ React2.createElement(MarkdownText, { text: beforeDiff }), /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, backgroundColor: "#1a1a1a", paddingY: 0, width: "100%" }, diffLines.map((line, i) => /* @__PURE__ */ React2.createElement(DiffLine, { key: i, line }))), afterDiff && /* @__PURE__ */ React2.createElement(MarkdownText, { text: afterDiff }));
|
|
126
|
+
});
|
|
127
|
+
CodeRenderer = React2.memo(({ text }) => {
|
|
128
|
+
if (!text) return null;
|
|
129
|
+
if (text.includes("[DIFF_START]")) {
|
|
130
|
+
return /* @__PURE__ */ React2.createElement(DiffBlock, { text });
|
|
92
131
|
}
|
|
93
|
-
if (
|
|
94
|
-
|
|
132
|
+
if (text.includes("```")) {
|
|
133
|
+
const parts = text.split(/(```[\s\S]*?```)/g);
|
|
134
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, parts.map((part, i) => {
|
|
135
|
+
if (part.startsWith("```") && part.endsWith("```")) {
|
|
136
|
+
const match = part.match(/```(\w*)\n([\s\S]*?)```/);
|
|
137
|
+
const lang = match ? match[1] : "code";
|
|
138
|
+
const code = match ? match[2] : part.slice(3, -3);
|
|
139
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: i, flexDirection: "column", marginY: 1, backgroundColor: "#111", borderStyle: "round", borderColor: "#333", paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { alignSelf: "flex-end", marginTop: -1, marginRight: 1 }, /* @__PURE__ */ React2.createElement(Text2, { backgroundColor: "#333", color: "white" }, " ", lang.toUpperCase(), " ")), /* @__PURE__ */ React2.createElement(Box2, { paddingY: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "cyan", wrap: "anywhere" }, code.trim())));
|
|
140
|
+
}
|
|
141
|
+
return /* @__PURE__ */ React2.createElement(MarkdownText, { key: i, text: part });
|
|
142
|
+
}));
|
|
95
143
|
}
|
|
96
|
-
|
|
97
|
-
|
|
144
|
+
return /* @__PURE__ */ React2.createElement(MarkdownText, { text });
|
|
145
|
+
});
|
|
146
|
+
MessageItem = React2.memo(({ msg, showFullThinking }) => {
|
|
147
|
+
const isDiffResult = msg.role === "system" && msg.text.includes("[DIFF_START]");
|
|
148
|
+
const isTerminalRecord = msg.isTerminalRecord;
|
|
149
|
+
if (msg.role === "system" && msg.text.includes("[TOOL_RESULT]") && !isDiffResult && !isTerminalRecord) return null;
|
|
150
|
+
if (msg.isAskRecord) {
|
|
151
|
+
const selectionMatch = msg.text.match(/Selection: (.*)/);
|
|
152
|
+
const selection = selectionMatch ? selectionMatch[1] : "No selection";
|
|
153
|
+
return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 0, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "cyan", bold: true, underline: true }, "\u{1F4AC} ASK USER"), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 0 }, /* @__PURE__ */ React2.createElement(Text2, { color: "white" }, "Selection: ", /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", bold: true }, selection)))));
|
|
98
154
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}));
|
|
102
|
-
});
|
|
103
|
-
var DiffLine = React2.memo(({ line }) => {
|
|
104
|
-
const isContext = line.includes("[UI_CONTEXT]");
|
|
105
|
-
const cleanLine = line.replace("[UI_CONTEXT]", "");
|
|
106
|
-
const isRemoval = cleanLine.startsWith("-");
|
|
107
|
-
const isAddition = cleanLine.startsWith("+");
|
|
108
|
-
const parts = cleanLine.substring(1).split("|");
|
|
109
|
-
const lineNum = parts[0]?.trim() || "";
|
|
110
|
-
const content = parts.slice(1).join("|");
|
|
111
|
-
const bgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : "#1a1a1a";
|
|
112
|
-
const textColor = isRemoval ? "#ff4d4d" : isAddition ? "#4dff88" : "white";
|
|
113
|
-
return /* @__PURE__ */ React2.createElement(Box2, { backgroundColor: bgColor, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { width: 5, flexShrink: 0 }, /* @__PURE__ */ React2.createElement(Text2, { color: isRemoval ? "#cf3a3a" : isAddition ? "#3acf65" : "gray", dimColor: true }, lineNum)), /* @__PURE__ */ React2.createElement(Box2, { width: 2, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: textColor, bold: true }, isRemoval ? "-" : isAddition ? "+" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: textColor, wrap: "anywhere" }, content)));
|
|
114
|
-
});
|
|
115
|
-
var DiffBlock = React2.memo(({ text }) => {
|
|
116
|
-
const beforeDiff = text.substring(0, text.indexOf("[DIFF_START]")).trim();
|
|
117
|
-
const afterDiff = text.substring(text.indexOf("[DIFF_END]") + 10).trim();
|
|
118
|
-
const match = text.match(/\[DIFF_START\]([\s\S]*?)\[DIFF_END\]/);
|
|
119
|
-
const diffBody = match ? match[1].trim() : "";
|
|
120
|
-
const diffLines = diffBody.split("\n");
|
|
121
|
-
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, beforeDiff && /* @__PURE__ */ React2.createElement(MarkdownText, { text: beforeDiff }), /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, backgroundColor: "#1a1a1a", paddingY: 0, width: "100%" }, diffLines.map((line, i) => /* @__PURE__ */ React2.createElement(DiffLine, { key: i, line }))), afterDiff && /* @__PURE__ */ React2.createElement(MarkdownText, { text: afterDiff }));
|
|
122
|
-
});
|
|
123
|
-
var CodeRenderer = React2.memo(({ text }) => {
|
|
124
|
-
if (!text) return null;
|
|
125
|
-
if (text.includes("[DIFF_START]")) {
|
|
126
|
-
return /* @__PURE__ */ React2.createElement(DiffBlock, { text });
|
|
127
|
-
}
|
|
128
|
-
if (text.includes("```")) {
|
|
129
|
-
const parts = text.split(/(```[\s\S]*?```)/g);
|
|
130
|
-
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, parts.map((part, i) => {
|
|
131
|
-
if (part.startsWith("```") && part.endsWith("```")) {
|
|
132
|
-
const match = part.match(/```(\w*)\n([\s\S]*?)```/);
|
|
133
|
-
const lang = match ? match[1] : "code";
|
|
134
|
-
const code = match ? match[2] : part.slice(3, -3);
|
|
135
|
-
return /* @__PURE__ */ React2.createElement(Box2, { key: i, flexDirection: "column", marginY: 1, backgroundColor: "#111", borderStyle: "round", borderColor: "#333", paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { alignSelf: "flex-end", marginTop: -1, marginRight: 1 }, /* @__PURE__ */ React2.createElement(Text2, { backgroundColor: "#333", color: "white" }, " ", lang.toUpperCase(), " ")), /* @__PURE__ */ React2.createElement(Box2, { paddingY: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "cyan", wrap: "anywhere" }, code.trim())));
|
|
155
|
+
if (msg.isUpdateNotification) {
|
|
156
|
+
return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", bold: true, underline: true }, "\u{1F680} UPDATE AVAILABLE"), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: msg.text }))));
|
|
136
157
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
if (isTerminalRecord) {
|
|
159
|
+
const cmdMatch = msg.text.match(/COMMAND: (.*)\n/);
|
|
160
|
+
const outputMatch = msg.text.match(/OUTPUT: ([\s\S]*)$/);
|
|
161
|
+
const cmd = cmdMatch ? cmdMatch[1] : "Unknown";
|
|
162
|
+
const outputList = outputMatch ? outputMatch[1] : "";
|
|
163
|
+
return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(TerminalBox, { command: cmd, output: outputList, completed: true }));
|
|
164
|
+
}
|
|
165
|
+
const content = React2.useMemo(() => cleanSignals(msg.text), [msg.text]);
|
|
166
|
+
const finalContent = React2.useMemo(() => {
|
|
167
|
+
if (msg.role === "think" && !showFullThinking) {
|
|
168
|
+
const lines = content.split("\n").filter((line) => {
|
|
169
|
+
const trimmed = line.trim();
|
|
170
|
+
const isHeading = trimmed.startsWith("# ");
|
|
171
|
+
const isActionStep = trimmed.startsWith("**") && trimmed.endsWith("**");
|
|
172
|
+
return isHeading || isActionStep;
|
|
173
|
+
});
|
|
174
|
+
if (lines.length === 0) return "*Reasoning...*";
|
|
175
|
+
return lines.join("\n");
|
|
176
|
+
}
|
|
177
|
+
return content;
|
|
178
|
+
}, [content, msg.role, showFullThinking]);
|
|
179
|
+
return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, flexDirection: "column", flexShrink: 0, width: "100%" }, msg.role === "user" ? /* @__PURE__ */ React2.createElement(
|
|
180
|
+
Box2,
|
|
181
|
+
{
|
|
182
|
+
backgroundColor: "#262626",
|
|
183
|
+
paddingX: 1,
|
|
184
|
+
paddingY: 1,
|
|
185
|
+
width: "100%",
|
|
186
|
+
flexDirection: "column"
|
|
187
|
+
},
|
|
188
|
+
finalContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\n/g, "\n").replace(/\\$/, "").split("\n").map((line, lineIdx) => /* @__PURE__ */ React2.createElement(Box2, { key: lineIdx, flexDirection: "row", width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexShrink: 0, width: 2 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, lineIdx === 0 ? "\u276F" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: msg.color || "white", wrap: "anywhere" }, line))))
|
|
189
|
+
) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Thinking..."), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, flexDirection: "column", width: "100%" }, formatThinkText(finalContent))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: finalContent }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]"))));
|
|
190
|
+
});
|
|
191
|
+
ChatLayout = React2.memo(({ messages, showFullThinking }) => {
|
|
192
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, messages.map((msg, idx) => /* @__PURE__ */ React2.createElement(
|
|
193
|
+
MessageItem,
|
|
194
|
+
{
|
|
195
|
+
key: msg.id || idx,
|
|
196
|
+
msg,
|
|
197
|
+
showFullThinking
|
|
198
|
+
}
|
|
199
|
+
)));
|
|
200
|
+
});
|
|
201
|
+
ChatLayout_default = ChatLayout;
|
|
160
202
|
}
|
|
161
|
-
const content = React2.useMemo(() => cleanSignals(msg.text), [msg.text]);
|
|
162
|
-
const finalContent = React2.useMemo(() => {
|
|
163
|
-
if (msg.role === "think" && !showFullThinking) {
|
|
164
|
-
const lines = content.split("\n").filter((line) => {
|
|
165
|
-
const trimmed = line.trim();
|
|
166
|
-
const isHeading = trimmed.startsWith("# ");
|
|
167
|
-
const isActionStep = trimmed.startsWith("**") && trimmed.endsWith("**");
|
|
168
|
-
return isHeading || isActionStep;
|
|
169
|
-
});
|
|
170
|
-
if (lines.length === 0) return "*Reasoning...*";
|
|
171
|
-
return lines.join("\n");
|
|
172
|
-
}
|
|
173
|
-
return content;
|
|
174
|
-
}, [content, msg.role, showFullThinking]);
|
|
175
|
-
return /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1, flexDirection: "column", flexShrink: 0, width: "100%" }, msg.role === "user" ? /* @__PURE__ */ React2.createElement(
|
|
176
|
-
Box2,
|
|
177
|
-
{
|
|
178
|
-
backgroundColor: "#262626",
|
|
179
|
-
paddingX: 1,
|
|
180
|
-
paddingY: 1,
|
|
181
|
-
width: "100%",
|
|
182
|
-
flexDirection: "column"
|
|
183
|
-
},
|
|
184
|
-
finalContent.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\\\n/g, "\n").replace(/\\$/, "").split("\n").map((line, lineIdx) => /* @__PURE__ */ React2.createElement(Box2, { key: lineIdx, flexDirection: "row", width: "100%" }, /* @__PURE__ */ React2.createElement(Box2, { flexShrink: 0, width: 2 }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, lineIdx === 0 ? "\u276F" : " ")), /* @__PURE__ */ React2.createElement(Box2, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: msg.color || "white", wrap: "anywhere" }, line))))
|
|
185
|
-
) : msg.role === "think" ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "white" }, "Thinking..."), /* @__PURE__ */ React2.createElement(Box2, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 2, flexDirection: "column", width: "100%" }, formatThinkText(finalContent))) : /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(CodeRenderer, { text: finalContent }), msg.memoryUpdated && /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React2.createElement(Text2, { color: "yellow", italic: true }, "\u2728 [Memory Updated]"))));
|
|
186
203
|
});
|
|
187
|
-
var ChatLayout = React2.memo(({ messages, showFullThinking }) => {
|
|
188
|
-
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", width: "100%" }, messages.map((msg, idx) => /* @__PURE__ */ React2.createElement(
|
|
189
|
-
MessageItem,
|
|
190
|
-
{
|
|
191
|
-
key: msg.id || idx,
|
|
192
|
-
msg,
|
|
193
|
-
showFullThinking
|
|
194
|
-
}
|
|
195
|
-
)));
|
|
196
|
-
});
|
|
197
|
-
var ChatLayout_default = ChatLayout;
|
|
198
204
|
|
|
199
205
|
// src/components/StatusBar.jsx
|
|
200
206
|
import React3 from "react";
|
|
201
207
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
202
|
-
var StatusBar
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
208
|
+
var StatusBar, StatusBar_default;
|
|
209
|
+
var init_StatusBar = __esm({
|
|
210
|
+
"src/components/StatusBar.jsx"() {
|
|
211
|
+
StatusBar = React3.memo(({ mode, thinkingLevel, tokens = "0.0k", tokensTotal = "0.0k", chatId = "NEW-SESSION", isMemoryEnabled = true }) => {
|
|
212
|
+
const modeColor = mode === "Flux" ? "yellow" : "cyan";
|
|
213
|
+
const modeIcon = mode === "Flux" ? "\u26A1" : "\u{1F30A}";
|
|
214
|
+
const memStatus = isMemoryEnabled ? "ON" : "OFF";
|
|
215
|
+
return /* @__PURE__ */ React3.createElement(
|
|
216
|
+
Box3,
|
|
217
|
+
{
|
|
218
|
+
borderStyle: "single",
|
|
219
|
+
borderColor: "gray",
|
|
220
|
+
flexDirection: "row",
|
|
221
|
+
justifyContent: "space-between",
|
|
222
|
+
paddingX: 1,
|
|
223
|
+
width: "100%"
|
|
224
|
+
},
|
|
225
|
+
/* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: modeColor, bold: true }, modeIcon, " ", mode.toUpperCase()), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "magenta" }, "\u{1F9E0} ", thinkingLevel)),
|
|
226
|
+
/* @__PURE__ */ React3.createElement(Box3, { flexGrow: 1, justifyContent: "center", paddingX: 2 }, /* @__PURE__ */ React3.createElement(Text3, { color: "gray", dimColor: true }, "\u{1F4C1} "), /* @__PURE__ */ React3.createElement(Text3, { color: "blue", dimColor: true, italic: true }, process.cwd())),
|
|
227
|
+
/* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, "MEM: "), /* @__PURE__ */ React3.createElement(Text3, { color: memStatus === "ON" ? "green" : "red" }, memStatus), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, tokensTotal > 1e3 ? `${(tokensTotal / 1e3).toFixed(1)}k` : tokensTotal, " (", Math.round(tokens / 254e3 * 100), "%)"), /* @__PURE__ */ React3.createElement(Text3, { color: "gray" }, " \u2502 "), /* @__PURE__ */ React3.createElement(Text3, { color: "dim" }, chatId, " "))
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
StatusBar_default = StatusBar;
|
|
231
|
+
}
|
|
220
232
|
});
|
|
221
|
-
var StatusBar_default = StatusBar;
|
|
222
233
|
|
|
223
234
|
// src/components/CommandMenu.jsx
|
|
224
235
|
import React4 from "react";
|
|
225
236
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
226
237
|
import SelectInput from "ink-select-input";
|
|
227
|
-
var CustomItem = ({ label, isSelected }) => {
|
|
228
|
-
const isCancel = label === "Cancel" || label.toLowerCase().includes("exit");
|
|
229
|
-
return /* @__PURE__ */ React4.createElement(Box4, { marginTop: isCancel ? 1 : 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : "white" }, isSelected ? "\u276F " : " ", label));
|
|
230
|
-
};
|
|
231
238
|
function CommandMenu({ title, items, onSelect }) {
|
|
232
239
|
return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", padding: 1, marginTop: 1, flexShrink: 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: "magenta", bold: true }, title), /* @__PURE__ */ React4.createElement(Box4, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React4.createElement(
|
|
233
240
|
SelectInput,
|
|
@@ -239,6 +246,15 @@ function CommandMenu({ title, items, onSelect }) {
|
|
|
239
246
|
}
|
|
240
247
|
)), /* @__PURE__ */ React4.createElement(Text4, { color: "gray", dimColor: true, marginTop: 1 }, "(Use Up/Down arrows to select, Enter to confirm)"));
|
|
241
248
|
}
|
|
249
|
+
var CustomItem;
|
|
250
|
+
var init_CommandMenu = __esm({
|
|
251
|
+
"src/components/CommandMenu.jsx"() {
|
|
252
|
+
CustomItem = ({ label, isSelected }) => {
|
|
253
|
+
const isCancel = label === "Cancel" || label.toLowerCase().includes("exit");
|
|
254
|
+
return /* @__PURE__ */ React4.createElement(Box4, { marginTop: isCancel ? 1 : 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: isSelected ? "cyan" : "white" }, isSelected ? "\u276F " : " ", label));
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
});
|
|
242
258
|
|
|
243
259
|
// src/components/ProfileForm.jsx
|
|
244
260
|
import React5, { useState } from "react";
|
|
@@ -273,136 +289,157 @@ function ProfileForm({ onSave, onCancel }) {
|
|
|
273
289
|
}
|
|
274
290
|
)), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true, marginTop: 1 }, "(Press Enter to submit, type /cancel to abort)"));
|
|
275
291
|
}
|
|
292
|
+
var init_ProfileForm = __esm({
|
|
293
|
+
"src/components/ProfileForm.jsx"() {
|
|
294
|
+
}
|
|
295
|
+
});
|
|
276
296
|
|
|
277
297
|
// src/components/AskUserModal.jsx
|
|
278
298
|
import React6, { useState as useState2 } from "react";
|
|
279
299
|
import { Box as Box6, Text as Text6, useInput } from "ink";
|
|
280
300
|
import TextInput2 from "ink-text-input";
|
|
281
|
-
var AskUserModal
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
{
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
301
|
+
var AskUserModal, AskUserModal_default;
|
|
302
|
+
var init_AskUserModal = __esm({
|
|
303
|
+
"src/components/AskUserModal.jsx"() {
|
|
304
|
+
AskUserModal = ({ question, options, onResolve }) => {
|
|
305
|
+
const [isSuggestingElse, setIsSuggestingElse] = useState2(false);
|
|
306
|
+
const [customInput, setCustomInput] = useState2("");
|
|
307
|
+
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
308
|
+
const allOptions = [...options, { id: "CUSTOM", label: "Suggest something else...", description: "Provide a custom response" }];
|
|
309
|
+
useInput((input, key) => {
|
|
310
|
+
if (isSuggestingElse) return;
|
|
311
|
+
if (key.leftArrow || key.upArrow) {
|
|
312
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
313
|
+
}
|
|
314
|
+
if (key.rightArrow || key.downArrow) {
|
|
315
|
+
setSelectedIndex((prev) => Math.min(allOptions.length - 1, prev + 1));
|
|
316
|
+
}
|
|
317
|
+
if (key.return) {
|
|
318
|
+
const selected = allOptions[selectedIndex];
|
|
319
|
+
if (selected.id === "CUSTOM") {
|
|
320
|
+
setIsSuggestingElse(true);
|
|
321
|
+
} else {
|
|
322
|
+
onResolve(selected.label);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
if (isSuggestingElse) {
|
|
327
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan", bold: true, underline: true }, "\u{1F4AC} SUGGEST SOMETHING ELSE"), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { italic: true, color: "gray" }, "Replying to: ", question)), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, flexDirection: "row" }, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "\u276F "), /* @__PURE__ */ React6.createElement(
|
|
328
|
+
TextInput2,
|
|
329
|
+
{
|
|
330
|
+
value: customInput,
|
|
331
|
+
onChange: setCustomInput,
|
|
332
|
+
onSubmit: () => onResolve(customInput)
|
|
333
|
+
}
|
|
334
|
+
)), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", dimColor: true, italic: true }, "(Press Enter to send)")));
|
|
310
335
|
}
|
|
311
|
-
|
|
336
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan", bold: true, underline: true }, "\u{1F4AC} ASK USER"), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, question)), /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "row", flexWrap: "wrap", width: "100%" }, options.map((opt, idx) => {
|
|
337
|
+
const isSelected = idx === selectedIndex;
|
|
338
|
+
return /* @__PURE__ */ React6.createElement(Box6, { key: opt.id, flexDirection: "column", marginRight: 4, marginBottom: 1, width: 30 }, /* @__PURE__ */ React6.createElement(Text6, { color: isSelected ? "cyan" : "white", bold: isSelected }, isSelected ? "\u276F " : " ", opt.label), opt.description && /* @__PURE__ */ React6.createElement(Box6, { marginLeft: 4 }, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", italic: true, dimColor: true }, opt.description)));
|
|
339
|
+
})), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: selectedIndex === options.length ? "cyan" : "white", bold: selectedIndex === options.length }, selectedIndex === options.length ? "\u276F " : " ", "Suggest something else...")), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", dimColor: true, italic: true }, "(Use Arrows to navigate, Enter to confirm)")));
|
|
340
|
+
};
|
|
341
|
+
AskUserModal_default = AskUserModal;
|
|
312
342
|
}
|
|
313
|
-
|
|
314
|
-
const isSelected = idx === selectedIndex;
|
|
315
|
-
return /* @__PURE__ */ React6.createElement(Box6, { key: opt.id, flexDirection: "column", marginRight: 4, marginBottom: 1, width: 30 }, /* @__PURE__ */ React6.createElement(Text6, { color: isSelected ? "cyan" : "white", bold: isSelected }, isSelected ? "\u276F " : " ", opt.label), opt.description && /* @__PURE__ */ React6.createElement(Box6, { marginLeft: 4 }, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", italic: true, dimColor: true }, opt.description)));
|
|
316
|
-
})), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: selectedIndex === options.length ? "cyan" : "white", bold: selectedIndex === options.length }, selectedIndex === options.length ? "\u276F " : " ", "Suggest something else...")), /* @__PURE__ */ React6.createElement(Box6, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "gray", dimColor: true, italic: true }, "(Use Arrows to navigate, Enter to confirm)")));
|
|
317
|
-
};
|
|
318
|
-
var AskUserModal_default = AskUserModal;
|
|
319
|
-
|
|
320
|
-
// src/app.jsx
|
|
321
|
-
import gradient from "gradient-string";
|
|
322
|
-
|
|
323
|
-
// src/utils/secrets.js
|
|
324
|
-
import fs2 from "fs-extra";
|
|
343
|
+
});
|
|
325
344
|
|
|
326
345
|
// src/utils/crypto.js
|
|
327
346
|
import fs from "fs";
|
|
328
347
|
import path from "path";
|
|
329
|
-
var XOR_KEY
|
|
330
|
-
var
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
348
|
+
var XOR_KEY, xorTransform, readEncryptedJson, writeEncryptedJson;
|
|
349
|
+
var init_crypto = __esm({
|
|
350
|
+
"src/utils/crypto.js"() {
|
|
351
|
+
XOR_KEY = 66;
|
|
352
|
+
xorTransform = (data) => {
|
|
353
|
+
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
354
|
+
const result = Buffer.alloc(buffer.length);
|
|
355
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
356
|
+
result[i] = buffer[i] ^ XOR_KEY;
|
|
357
|
+
}
|
|
358
|
+
return result;
|
|
359
|
+
};
|
|
360
|
+
readEncryptedJson = (filePath, defaultValue = {}) => {
|
|
361
|
+
try {
|
|
362
|
+
if (!fs.existsSync(filePath)) return defaultValue;
|
|
363
|
+
const encryptedData = fs.readFileSync(filePath);
|
|
364
|
+
const decryptedData = xorTransform(encryptedData).toString();
|
|
365
|
+
return JSON.parse(decryptedData);
|
|
366
|
+
} catch (err) {
|
|
367
|
+
console.error(`Vault Read Error [${path.basename(filePath)}]:`, err.message);
|
|
368
|
+
return defaultValue;
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
writeEncryptedJson = (filePath, data) => {
|
|
372
|
+
try {
|
|
373
|
+
const dir = path.dirname(filePath);
|
|
374
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
375
|
+
const jsonData = JSON.stringify(data, null, 2);
|
|
376
|
+
const encryptedData = xorTransform(jsonData);
|
|
377
|
+
fs.writeFileSync(filePath, encryptedData);
|
|
378
|
+
} catch (err) {
|
|
379
|
+
console.error(`Vault Write Error [${path.basename(filePath)}]:`, err.message);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
358
382
|
}
|
|
359
|
-
};
|
|
383
|
+
});
|
|
360
384
|
|
|
361
385
|
// src/utils/paths.js
|
|
362
386
|
import os from "os";
|
|
363
387
|
import path2 from "path";
|
|
364
|
-
var FLUXFLOW_DIR
|
|
365
|
-
var
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
388
|
+
var FLUXFLOW_DIR, LOGS_DIR, SECRET_DIR, SETTINGS_FILE, HISTORY_FILE, USAGE_FILE, MEMORIES_FILE, TEMP_MEM_FILE;
|
|
389
|
+
var init_paths = __esm({
|
|
390
|
+
"src/utils/paths.js"() {
|
|
391
|
+
FLUXFLOW_DIR = path2.join(os.homedir(), ".fluxflow");
|
|
392
|
+
LOGS_DIR = path2.join(FLUXFLOW_DIR, "logs");
|
|
393
|
+
SECRET_DIR = path2.join(FLUXFLOW_DIR, "secret");
|
|
394
|
+
SETTINGS_FILE = path2.join(FLUXFLOW_DIR, "settings.json");
|
|
395
|
+
HISTORY_FILE = path2.join(SECRET_DIR, "history.json");
|
|
396
|
+
USAGE_FILE = path2.join(SECRET_DIR, "usage.json");
|
|
397
|
+
MEMORIES_FILE = path2.join(SECRET_DIR, "memories.json");
|
|
398
|
+
TEMP_MEM_FILE = path2.join(SECRET_DIR, "memory-temp.json");
|
|
399
|
+
}
|
|
400
|
+
});
|
|
372
401
|
|
|
373
402
|
// src/utils/secrets.js
|
|
403
|
+
import fs2 from "fs-extra";
|
|
374
404
|
import path3 from "path";
|
|
375
|
-
var SECRET_FILE
|
|
376
|
-
var
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
405
|
+
var SECRET_FILE, getAPIKey, saveSecret, saveAPIKey, removeSecret, removeAPIKey;
|
|
406
|
+
var init_secrets = __esm({
|
|
407
|
+
"src/utils/secrets.js"() {
|
|
408
|
+
init_crypto();
|
|
409
|
+
init_paths();
|
|
410
|
+
SECRET_FILE = path3.join(SECRET_DIR, "secrets.json");
|
|
411
|
+
getAPIKey = async () => {
|
|
412
|
+
try {
|
|
413
|
+
const secrets = readEncryptedJson(SECRET_FILE, {});
|
|
414
|
+
if (secrets.API_KEY) return secrets.API_KEY;
|
|
415
|
+
} catch (e) {
|
|
416
|
+
}
|
|
417
|
+
return null;
|
|
418
|
+
};
|
|
419
|
+
saveSecret = async (key, value) => {
|
|
420
|
+
await fs2.ensureDir(SECRET_DIR);
|
|
421
|
+
let current = readEncryptedJson(SECRET_FILE, {});
|
|
422
|
+
current[key] = value;
|
|
423
|
+
writeEncryptedJson(SECRET_FILE, current);
|
|
424
|
+
};
|
|
425
|
+
saveAPIKey = async (apiKey) => saveSecret("API_KEY", apiKey);
|
|
426
|
+
removeSecret = async (key) => {
|
|
427
|
+
try {
|
|
428
|
+
const secrets = readEncryptedJson(SECRET_FILE, {});
|
|
429
|
+
delete secrets[key];
|
|
430
|
+
writeEncryptedJson(SECRET_FILE, secrets);
|
|
431
|
+
} catch (e) {
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
removeAPIKey = async () => removeSecret("API_KEY");
|
|
397
435
|
}
|
|
398
|
-
};
|
|
399
|
-
var removeAPIKey = async () => removeSecret("API_KEY");
|
|
400
|
-
|
|
401
|
-
// src/utils/ai.js
|
|
402
|
-
import { GoogleGenAI, ThinkingLevel } from "@google/genai";
|
|
436
|
+
});
|
|
403
437
|
|
|
404
438
|
// src/data/main_tools.js
|
|
405
|
-
var TOOL_PROTOCOL
|
|
439
|
+
var TOOL_PROTOCOL;
|
|
440
|
+
var init_main_tools = __esm({
|
|
441
|
+
"src/data/main_tools.js"() {
|
|
442
|
+
TOOL_PROTOCOL = (mode) => `
|
|
406
443
|
-- START FUNCTION CALLING PROTOCOL --
|
|
407
444
|
You have access to internal tools. To call a tool, you MUST use the following exact syntax on a new line:
|
|
408
445
|
tool:functions.tool_name(arguments)
|
|
@@ -426,9 +463,14 @@ WHEN CALLING TOOLS, YOU **MUST** end your response with '[turn: continue]'. NEVE
|
|
|
426
463
|
Do NOT over-use tools. Use them only when strictly necessary for the user's objective. You can stack multiple tool calls 1-by-1.
|
|
427
464
|
Distinguish clearly between tool discussion and execution. Use the 'tool:' prefix ONLY when calling a function. When discussing tools with the user, refer to them by name as nouns (e.g., 'write_file', 'list_files') to avoid accidental triggers and context bloat. Even in your <think> ... </think> tags, do not use the 'tool:' prefix when planning to select a tool.
|
|
428
465
|
-- END FUNCTION CALLING PROTOCOL --`.trim();
|
|
466
|
+
}
|
|
467
|
+
});
|
|
429
468
|
|
|
430
469
|
// src/data/janitor_tools.js
|
|
431
|
-
var JANITOR_TOOLS_PROTOCOL
|
|
470
|
+
var JANITOR_TOOLS_PROTOCOL;
|
|
471
|
+
var init_janitor_tools = __esm({
|
|
472
|
+
"src/data/janitor_tools.js"() {
|
|
473
|
+
JANITOR_TOOLS_PROTOCOL = (isMemoryEnabled = true, needTitle = false) => `
|
|
432
474
|
${needTitle ? `-- START CHAT MANAGEMENT TOOLS --
|
|
433
475
|
1. YOU MUST UPDATE CHAT TITLE (URGENT PRIORITY):
|
|
434
476
|
tool:functions.chat(title='<short summary of conversation in 3-5 words>')
|
|
@@ -450,38 +492,51 @@ ${isMemoryEnabled ? `2. User-specific long-term memory (USE BASED ON CONVERSATIO
|
|
|
450
492
|
Usage Rules:
|
|
451
493
|
- Frequency for 'user' action: Only when explicit context from chat is found or explicitly requested by the user.` : ""}
|
|
452
494
|
-- END MEMORY TOOLS --`.trim();
|
|
495
|
+
}
|
|
496
|
+
});
|
|
453
497
|
|
|
454
498
|
// src/data/thinking_prompts.json
|
|
455
|
-
var thinking_prompts_default
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
499
|
+
var thinking_prompts_default;
|
|
500
|
+
var init_thinking_prompts = __esm({
|
|
501
|
+
"src/data/thinking_prompts.json"() {
|
|
502
|
+
thinking_prompts_default = {
|
|
503
|
+
Max: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MAX\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 10**, **MAX 30**. EACH HEADING SHOULD HAVE MIN 6, MAX 10 STEPS OF INTERNAL MONOLOGUE. EXPLORE EDGE CASES & NUANCES.\n\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\n-- END THINKING INSTRUCTIONS --",
|
|
504
|
+
High: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: HIGH\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 8**, **MAX 25**. EACH HEADING SHOULD HAVE MIN 4, MAX 8 STEPS OF INTERNAL MONOLOGUE. EXPLORE EDGE CASES & THINK LONGER before responding.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\n-- END THINKING INSTRUCTIONS --",
|
|
505
|
+
Medium: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: MEDIUM\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 5**, **MAX 9**. EACH HEADING SHOULD HAVE MIN 4, MAX 6 STEPS OF INTERNAL MONOLOGUE. THINK LONGER FOR COMPLEX QUERIES.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\n-- END THINKING INSTRUCTIONS --",
|
|
506
|
+
Minimal: "-- START THINKING INSTRUCTIONS --\nEFFORT_LEVEL: LOW\nThink Step by Step in Chain-of-Thought. Provide the thinking in <think>...</think> block, length given. Thinking should be structured in this format:\n\n<think>\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\n\\n\\n\n**Heading-ing**\n\\n\\n\n* steps...\ncontinue.\n\\n\\n\n</think>\\n\\n\n\n(MUST START THE RESPONSE IN NEW LINE AFTER ENDING THINKING BLOCK)\n\n**CRITICAL**: Heading blocks: **MIN 0**, **MAX 3**. EACH HEADING SHOULD HAVE MIN 1, MAX 2 STEPS OF INTERNAL MONOLOGUE. No Thinking is preferred if query is simple.\nNEVER WRITE FUNCTION CALLS IN THINKING BLOCK WITH 'tool:' PREFIX AND NEVER WRITE '[turn: ...]' INSIDE THINKING BLOCK.\n-- END THINKING INSTRUCTIONS --"
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
});
|
|
461
510
|
|
|
462
511
|
// src/utils/prompts.js
|
|
463
|
-
var getSystemInstruction
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
512
|
+
var getSystemInstruction, getJanitorInstruction;
|
|
513
|
+
var init_prompts = __esm({
|
|
514
|
+
"src/utils/prompts.js"() {
|
|
515
|
+
init_main_tools();
|
|
516
|
+
init_janitor_tools();
|
|
517
|
+
init_thinking_prompts();
|
|
518
|
+
getSystemInstruction = (profile, thinkingLevel, mode, systemSettings, tempMemories = "", userMemories = "", isMemoryEnabled = true, isContext50 = false) => {
|
|
519
|
+
let levelKey = thinkingLevel;
|
|
520
|
+
if (thinkingLevel === "Low") levelKey = "Minimal";
|
|
521
|
+
if (thinkingLevel === "xHigh" || thinkingLevel === "Max") levelKey = "Max";
|
|
522
|
+
const thinkingConfig = thinking_prompts_default[levelKey] || thinking_prompts_default["Medium"];
|
|
523
|
+
const osDetected = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
|
|
524
|
+
const nameStr = profile.name && profile.name?.length > 0 ? `User Name: ${profile.name}` : "";
|
|
525
|
+
const nicknameStr = profile.nickname && profile.nickname?.length > 0 ? `. User Nickname: ${profile.nickname}.` : "";
|
|
526
|
+
const userInstrStr = profile.instructions && profile.instructions?.length > 0 ? `. User Instructions: ${profile.instructions}.` : "";
|
|
527
|
+
const dateTimeStr = (/* @__PURE__ */ new Date()).toLocaleString();
|
|
528
|
+
const cwdStr = process.cwd();
|
|
529
|
+
const tempMemoriesStr = tempMemories?.length > 0 && !isContext50 ? `
|
|
475
530
|
-- RECENT CONTEXT FROM OTHER CHAT THREADS --
|
|
476
531
|
${tempMemories}
|
|
477
532
|
------------------------------------------
|
|
478
533
|
` : "";
|
|
479
|
-
|
|
534
|
+
const userMemoriesStr = userMemories?.length > 0 ? `
|
|
480
535
|
--- PERSISTENT USER MEMORIES ---
|
|
481
536
|
${userMemories}
|
|
482
537
|
--------------------------------
|
|
483
538
|
` : "";
|
|
484
|
-
|
|
539
|
+
return `${isMemoryEnabled ? `${userMemoriesStr}
|
|
485
540
|
|
|
486
541
|
` : ""}${isMemoryEnabled ? `${tempMemoriesStr}
|
|
487
542
|
|
|
@@ -490,6 +545,7 @@ You are Flux Flow (made by Kushal Roy Chowdhury/Flux Flow Team). A CLI Agent. Yo
|
|
|
490
545
|
CURRENT_WORKING_DIRECTORY: ${cwdStr}.
|
|
491
546
|
OS: ${osDetected}. ${osDetected && mode == "Flux" ? "Your terminal commands will run on CMD. Prefer PS scripts via CMD instead of raw CMD commands." : ""}
|
|
492
547
|
${nameStr}${nicknameStr}${userInstrStr}
|
|
548
|
+
If you see a [STEERING HINT] from user, give that prompt priority, user can use it to help you guide if you go wrong way.
|
|
493
549
|
|
|
494
550
|
${thinkingConfig}
|
|
495
551
|
|
|
@@ -530,9 +586,9 @@ When you 'finish' an agentic loop, you will lose your previous turn 'thinking' d
|
|
|
530
586
|
-- END REPONSE FINISH PROTOCOL --
|
|
531
587
|
Current date and Time is: ${dateTimeStr}
|
|
532
588
|
--- END SYSTEM INSTRUCTION ---`.trim();
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
|
|
589
|
+
};
|
|
590
|
+
getJanitorInstruction = (originalText, agentRaws, userMemories = "", isMemoryEnabled = true, needTitle = false) => {
|
|
591
|
+
return `ORIGINAL USER PROMPT: ${originalText.substring(0, 500)}
|
|
536
592
|
AGENT RAWS (responses from this turn):
|
|
537
593
|
${agentRaws.substring(0, 2e3).replace(/tool:functions\..*\n/g, "").replace(/<think>.*<\/think>/g, "").replace(/\[Prompted on:.*?\]/g, "").replace(/\[turn: continue\]/g, "").replace(/\[turn: finish\]/g, "")}${agentRaws.length > 1500 ? "\n... (truncated) ..." : ""}
|
|
538
594
|
|
|
@@ -557,1218 +613,1338 @@ ${userMemories}
|
|
|
557
613
|
Current date and Time: ${(/* @__PURE__ */ new Date()).toLocaleString()}
|
|
558
614
|
|
|
559
615
|
--- END SYSTEM INSTRUCTION ---`.trim();
|
|
560
|
-
};
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
});
|
|
561
619
|
|
|
562
620
|
// src/utils/history.js
|
|
563
621
|
import fs3 from "fs-extra";
|
|
564
622
|
import path4 from "path";
|
|
565
623
|
import { nanoid } from "nanoid";
|
|
566
|
-
var WRITE_LOCK
|
|
567
|
-
var
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
return
|
|
584
|
-
}
|
|
624
|
+
var WRITE_LOCK, withLock, loadHistory, saveChat, saveChatTitle, deleteChat, generateChatId, cleanupOldHistory, getTruncatedHistory;
|
|
625
|
+
var init_history = __esm({
|
|
626
|
+
"src/utils/history.js"() {
|
|
627
|
+
init_crypto();
|
|
628
|
+
init_paths();
|
|
629
|
+
WRITE_LOCK = Promise.resolve();
|
|
630
|
+
withLock = (op) => {
|
|
631
|
+
const nextLock = WRITE_LOCK.then(async () => {
|
|
632
|
+
try {
|
|
633
|
+
return await op();
|
|
634
|
+
} catch (e) {
|
|
635
|
+
console.error("Lock Operation Failed:", e);
|
|
636
|
+
throw e;
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
WRITE_LOCK = nextLock.catch(() => {
|
|
640
|
+
});
|
|
641
|
+
return nextLock;
|
|
642
|
+
};
|
|
643
|
+
loadHistory = async () => {
|
|
644
|
+
if (await fs3.pathExists(HISTORY_FILE)) {
|
|
645
|
+
try {
|
|
646
|
+
return await fs3.readJson(HISTORY_FILE);
|
|
647
|
+
} catch (e) {
|
|
648
|
+
return {};
|
|
649
|
+
}
|
|
650
|
+
}
|
|
585
651
|
return {};
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return {};
|
|
589
|
-
};
|
|
590
|
-
var saveChat = async (id, name, messages) => {
|
|
591
|
-
return withLock(async () => {
|
|
592
|
-
const history = await loadHistory();
|
|
593
|
-
const existingChat = history[id];
|
|
594
|
-
const finalName = name || (existingChat ? existingChat.name : `Session ${id.slice(-6)}`);
|
|
595
|
-
history[id] = {
|
|
596
|
-
name: finalName,
|
|
597
|
-
messages,
|
|
598
|
-
updatedAt: Date.now()
|
|
599
652
|
};
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
653
|
+
saveChat = async (id, name, messages) => {
|
|
654
|
+
return withLock(async () => {
|
|
655
|
+
const history = await loadHistory();
|
|
656
|
+
const existingChat = history[id];
|
|
657
|
+
const finalName = name || (existingChat ? existingChat.name : `Session ${id.slice(-6)}`);
|
|
658
|
+
history[id] = {
|
|
659
|
+
name: finalName,
|
|
660
|
+
messages,
|
|
661
|
+
updatedAt: Date.now()
|
|
662
|
+
};
|
|
663
|
+
await fs3.ensureDir(path4.dirname(HISTORY_FILE));
|
|
664
|
+
await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
|
|
665
|
+
});
|
|
666
|
+
};
|
|
667
|
+
saveChatTitle = async (id, title) => {
|
|
668
|
+
return withLock(async () => {
|
|
669
|
+
const history = await loadHistory();
|
|
670
|
+
if (history[id]) {
|
|
671
|
+
history[id].name = title;
|
|
672
|
+
history[id].updatedAt = Date.now();
|
|
673
|
+
} else {
|
|
674
|
+
history[id] = { name: title, messages: [], updatedAt: Date.now() };
|
|
675
|
+
}
|
|
676
|
+
await fs3.ensureDir(path4.dirname(HISTORY_FILE));
|
|
677
|
+
await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
|
|
678
|
+
});
|
|
679
|
+
};
|
|
680
|
+
deleteChat = async (id) => {
|
|
681
|
+
return withLock(async () => {
|
|
682
|
+
const history = await loadHistory();
|
|
683
|
+
delete history[id];
|
|
684
|
+
await fs3.writeJson(HISTORY_FILE, history, { spaces: 2 });
|
|
685
|
+
const temp = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
686
|
+
if (temp[id]) {
|
|
687
|
+
delete temp[id];
|
|
688
|
+
writeEncryptedJson(TEMP_MEM_FILE, temp);
|
|
689
|
+
}
|
|
690
|
+
return history;
|
|
691
|
+
});
|
|
692
|
+
};
|
|
693
|
+
generateChatId = () => `flow-${nanoid(6)}`;
|
|
694
|
+
cleanupOldHistory = async (retentionSetting) => {
|
|
695
|
+
if (!retentionSetting || retentionSetting === "Never") return;
|
|
696
|
+
const days = parseInt(retentionSetting);
|
|
697
|
+
if (isNaN(days)) return;
|
|
698
|
+
const history = await loadHistory();
|
|
699
|
+
const now = Date.now();
|
|
700
|
+
const threshold = days * 24 * 60 * 60 * 1e3;
|
|
701
|
+
let deletedCount = 0;
|
|
702
|
+
for (const id in history) {
|
|
703
|
+
const chat2 = history[id];
|
|
704
|
+
if (chat2.updatedAt && now - chat2.updatedAt > threshold) {
|
|
705
|
+
await deleteChat(id);
|
|
706
|
+
deletedCount++;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return deletedCount;
|
|
710
|
+
};
|
|
711
|
+
getTruncatedHistory = (history, exchangesToRemove = 4) => {
|
|
712
|
+
if (history.length <= 1) return history;
|
|
713
|
+
const welcome = history[0];
|
|
714
|
+
const rest = history.slice(1);
|
|
715
|
+
const sliceIndex = exchangesToRemove * 2;
|
|
716
|
+
const truncated = rest.slice(sliceIndex);
|
|
717
|
+
return [welcome, ...truncated];
|
|
718
|
+
};
|
|
645
719
|
}
|
|
646
|
-
|
|
647
|
-
};
|
|
648
|
-
var getTruncatedHistory = (history, exchangesToRemove = 4) => {
|
|
649
|
-
if (history.length <= 1) return history;
|
|
650
|
-
const welcome = history[0];
|
|
651
|
-
const rest = history.slice(1);
|
|
652
|
-
const sliceIndex = exchangesToRemove * 2;
|
|
653
|
-
const truncated = rest.slice(sliceIndex);
|
|
654
|
-
return [welcome, ...truncated];
|
|
655
|
-
};
|
|
720
|
+
});
|
|
656
721
|
|
|
657
722
|
// src/utils/usage.js
|
|
658
723
|
import fs4 from "fs-extra";
|
|
659
724
|
import path5 from "path";
|
|
660
|
-
var getDailyUsage
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
725
|
+
var getDailyUsage, incrementUsage, checkQuota;
|
|
726
|
+
var init_usage = __esm({
|
|
727
|
+
"src/utils/usage.js"() {
|
|
728
|
+
init_paths();
|
|
729
|
+
getDailyUsage = async () => {
|
|
730
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
731
|
+
try {
|
|
732
|
+
if (await fs4.exists(USAGE_FILE)) {
|
|
733
|
+
const data = await fs4.readJson(USAGE_FILE);
|
|
734
|
+
if (data.date === today) {
|
|
735
|
+
return data.stats;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
} catch (err) {
|
|
739
|
+
console.error("Failed to read usage:", err);
|
|
667
740
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
};
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
return true;
|
|
741
|
+
const defaultStats = { agent: 0, background: 0, search: 0 };
|
|
742
|
+
await fs4.ensureDir(path5.dirname(USAGE_FILE));
|
|
743
|
+
await fs4.writeJson(USAGE_FILE, { date: today, stats: defaultStats }, { spaces: 2 });
|
|
744
|
+
return defaultStats;
|
|
745
|
+
};
|
|
746
|
+
incrementUsage = async (key) => {
|
|
747
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
748
|
+
const data = await fs4.readJson(USAGE_FILE).catch(() => ({ date: today, stats: { agent: 0, background: 0, search: 0 } }));
|
|
749
|
+
if (data.date !== today) {
|
|
750
|
+
data.date = today;
|
|
751
|
+
data.stats = { agent: 0, background: 0, search: 0 };
|
|
752
|
+
}
|
|
753
|
+
if (data.stats[key] !== void 0) {
|
|
754
|
+
data.stats[key]++;
|
|
755
|
+
await fs4.ensureDir(path5.dirname(USAGE_FILE));
|
|
756
|
+
await fs4.writeJson(USAGE_FILE, data, { spaces: 2 });
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
checkQuota = async (key, settings) => {
|
|
760
|
+
const usage = await getDailyUsage();
|
|
761
|
+
const tier = settings.apiTier || "Free";
|
|
762
|
+
const quotas = settings.quotas || {};
|
|
763
|
+
if (tier === "Free") {
|
|
764
|
+
if (key === "agent" || key === "background") {
|
|
765
|
+
return usage.agent + usage.background < 1500;
|
|
766
|
+
}
|
|
767
|
+
if (key === "search") return true;
|
|
768
|
+
}
|
|
769
|
+
if (tier === "Paid" || tier === "Custom") {
|
|
770
|
+
if (key === "agent") return usage.agent < (quotas.agentLimit || 1500);
|
|
771
|
+
if (key === "background") return usage.background < (quotas.backgroundLimit || 1500);
|
|
772
|
+
if (key === "search") return true;
|
|
773
|
+
return true;
|
|
774
|
+
}
|
|
775
|
+
return true;
|
|
776
|
+
};
|
|
705
777
|
}
|
|
706
|
-
|
|
707
|
-
};
|
|
708
|
-
|
|
709
|
-
// src/tools/web_search.js
|
|
710
|
-
import * as cuimp from "cuimp";
|
|
778
|
+
});
|
|
711
779
|
|
|
712
780
|
// src/utils/arg_parser.js
|
|
713
|
-
var parseArgs
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
value =
|
|
723
|
-
|
|
724
|
-
|
|
781
|
+
var parseArgs;
|
|
782
|
+
var init_arg_parser = __esm({
|
|
783
|
+
"src/utils/arg_parser.js"() {
|
|
784
|
+
parseArgs = (argsString) => {
|
|
785
|
+
const args = {};
|
|
786
|
+
const regex = /(\w+)\s*=\s*(?:(["'])((?:\\.|(?!\2)[\s\S])*)\2|([^,\s\)]+))/g;
|
|
787
|
+
let match;
|
|
788
|
+
while ((match = regex.exec(argsString)) !== null) {
|
|
789
|
+
const key = match[1];
|
|
790
|
+
let value = match[3] !== void 0 ? match[3] : match[4];
|
|
791
|
+
if (match[3] !== void 0) {
|
|
792
|
+
try {
|
|
793
|
+
value = JSON.parse(`"${value.replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`);
|
|
794
|
+
} catch (e) {
|
|
795
|
+
value = value.replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\\\\/g, "\\");
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (value === "true") value = true;
|
|
799
|
+
else if (value === "false") value = false;
|
|
800
|
+
else if (typeof value === "string" && !isNaN(value) && value.trim() !== "") value = Number(value);
|
|
801
|
+
args[key] = value;
|
|
725
802
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
else if (value === "false") value = false;
|
|
729
|
-
else if (typeof value === "string" && !isNaN(value) && value.trim() !== "") value = Number(value);
|
|
730
|
-
args[key] = value;
|
|
803
|
+
return args;
|
|
804
|
+
};
|
|
731
805
|
}
|
|
732
|
-
|
|
733
|
-
};
|
|
806
|
+
});
|
|
734
807
|
|
|
735
808
|
// src/tools/web_search.js
|
|
809
|
+
import * as cuimp from "cuimp";
|
|
736
810
|
import fs5 from "fs";
|
|
737
811
|
import path6 from "path";
|
|
738
|
-
var web_search
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
812
|
+
var web_search;
|
|
813
|
+
var init_web_search = __esm({
|
|
814
|
+
"src/tools/web_search.js"() {
|
|
815
|
+
init_arg_parser();
|
|
816
|
+
init_paths();
|
|
817
|
+
web_search = async (argsString) => {
|
|
818
|
+
const { query, limit = 10 } = parseArgs(argsString);
|
|
819
|
+
if (!query) return 'ERROR: Missing "query" argument for web_search.';
|
|
820
|
+
try {
|
|
821
|
+
await new Promise((r) => setTimeout(r, Math.random() * 1e3 + 500));
|
|
822
|
+
const response = await cuimp.get(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`, {
|
|
823
|
+
headers: {
|
|
824
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
const html = response.data;
|
|
828
|
+
const results = [];
|
|
829
|
+
const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g;
|
|
830
|
+
let match;
|
|
831
|
+
let count = 0;
|
|
832
|
+
while ((match = resultRegex.exec(html)) !== null && count < limit) {
|
|
833
|
+
let url = match[1];
|
|
834
|
+
if (url.includes("uddg=")) {
|
|
835
|
+
url = decodeURIComponent(url.split("uddg=")[1].split("&")[0]);
|
|
836
|
+
}
|
|
837
|
+
const title = match[2].replace(/<[^>]*>/g, "").trim();
|
|
838
|
+
const snippet = match[3].replace(/<[^>]*>/g, "").trim();
|
|
839
|
+
results.push(`${count + 1}. ${title}
|
|
761
840
|
Source: ${url}
|
|
762
841
|
Snippet: ${snippet}`);
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
842
|
+
count++;
|
|
843
|
+
}
|
|
844
|
+
const toolLogDir = path6.join(LOGS_DIR, "tools");
|
|
845
|
+
if (!fs5.existsSync(toolLogDir)) {
|
|
846
|
+
fs5.mkdirSync(toolLogDir, { recursive: true });
|
|
847
|
+
}
|
|
848
|
+
fs5.appendFileSync(path6.join(toolLogDir, "search-results.log"), `RESULTS ${(/* @__PURE__ */ new Date()).toISOString()} -
|
|
770
849
|
Query: [${query}]. Results Count: ${results.length}.
|
|
771
850
|
Results: ${results}
|
|
772
851
|
|
|
773
852
|
|
|
774
853
|
`);
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
854
|
+
if (results.length === 0) {
|
|
855
|
+
if (html.includes("anomaly")) {
|
|
856
|
+
const toolErrDir = path6.join(LOGS_DIR, "tools");
|
|
857
|
+
if (!fs5.existsSync(toolErrDir)) {
|
|
858
|
+
fs5.mkdirSync(toolErrDir, { recursive: true });
|
|
859
|
+
}
|
|
860
|
+
fs5.appendFileSync(path6.join(toolErrDir, "error.log"), `ERROR ${(/* @__PURE__ */ new Date()).toISOString()} - DDG detected unusual activity. Cuimp impersonation might need adjustment.
|
|
782
861
|
`);
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
862
|
+
throw new Error("DDG detected unusual activity. Cuimp impersonation might need adjustment.");
|
|
863
|
+
}
|
|
864
|
+
return `No results found for query: [${query}].`;
|
|
865
|
+
}
|
|
866
|
+
const finalResults = results.join("\n\n");
|
|
867
|
+
return `Search results for [${query}]:
|
|
789
868
|
|
|
790
869
|
${finalResults}`;
|
|
791
|
-
|
|
792
|
-
|
|
870
|
+
} catch (err) {
|
|
871
|
+
return `ERROR: Stealth Search failed. ${err.message}`;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
793
874
|
}
|
|
794
|
-
};
|
|
875
|
+
});
|
|
795
876
|
|
|
796
877
|
// src/tools/web_scrape.js
|
|
797
878
|
import fs6 from "fs";
|
|
798
879
|
import path7 from "path";
|
|
799
880
|
import * as cuimp2 from "cuimp";
|
|
800
|
-
var web_scrape
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
881
|
+
var web_scrape;
|
|
882
|
+
var init_web_scrape = __esm({
|
|
883
|
+
"src/tools/web_scrape.js"() {
|
|
884
|
+
init_paths();
|
|
885
|
+
web_scrape = async (args) => {
|
|
886
|
+
const urlMatch = args.match(/url\s*=\s*["'](.*)["']/);
|
|
887
|
+
const url = urlMatch ? urlMatch[1] : args;
|
|
888
|
+
try {
|
|
889
|
+
const response = await cuimp2.get(url, {
|
|
890
|
+
headers: {
|
|
891
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
|
|
892
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
893
|
+
"Accept-Language": "en-US,en;q=0.5"
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
let html = response.data;
|
|
897
|
+
if (!html) throw new Error("No content received from URL.");
|
|
898
|
+
html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
|
|
899
|
+
html = html.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "");
|
|
900
|
+
html = html.replace(/<nav\b[^<]*(?:(?!<\/nav>)<[^<]*)*<\/nav>/gi, "");
|
|
901
|
+
html = html.replace(/<footer\b[^<]*(?:(?!<\/footer>)<[^<]*)*<\/footer>/gi, "");
|
|
902
|
+
html = html.replace(/<header\b[^<]*(?:(?!<\/header>)<[^<]*)*<\/header>/gi, "");
|
|
903
|
+
let text = html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
904
|
+
const finalContent = text.substring(0, 2e4);
|
|
905
|
+
const toolLogDir = path7.join(LOGS_DIR, "tools");
|
|
906
|
+
if (!fs6.existsSync(toolLogDir)) {
|
|
907
|
+
fs6.mkdirSync(toolLogDir, { recursive: true });
|
|
908
|
+
}
|
|
909
|
+
fs6.appendFileSync(path7.join(toolLogDir, "search-scraped.log"), `RESULTS ${(/* @__PURE__ */ new Date()).toISOString()} -
|
|
825
910
|
Query: [${url}].
|
|
826
911
|
Results: ${finalContent}
|
|
827
912
|
|
|
828
913
|
|
|
829
914
|
`);
|
|
830
|
-
|
|
915
|
+
return `CONTENT FROM [${url}]:
|
|
831
916
|
|
|
832
917
|
${finalContent}${text.length > 2e4 ? "\n\n[TRUNCATED AT 20K CHARS]" : ""}`;
|
|
833
|
-
|
|
834
|
-
|
|
918
|
+
} catch (err) {
|
|
919
|
+
return `ERROR: Failed to read page at ${url}. ${err.message}`;
|
|
920
|
+
}
|
|
921
|
+
};
|
|
835
922
|
}
|
|
836
|
-
};
|
|
923
|
+
});
|
|
837
924
|
|
|
838
925
|
// src/tools/memory.js
|
|
839
|
-
var memory
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
const MAX_CHARS = 2e3 * 4;
|
|
870
|
-
let currentTotalLength = memories.reduce((acc, m) => acc + (m.memory?.length || 0), 0);
|
|
871
|
-
while (memories.length > 0 && currentTotalLength + content.length > MAX_CHARS) {
|
|
872
|
-
const removed = memories.shift();
|
|
873
|
-
currentTotalLength -= removed.memory?.length || 0;
|
|
926
|
+
var memory;
|
|
927
|
+
var init_memory = __esm({
|
|
928
|
+
"src/tools/memory.js"() {
|
|
929
|
+
init_crypto();
|
|
930
|
+
init_paths();
|
|
931
|
+
memory = async (rawArgs, context = {}) => {
|
|
932
|
+
const parseArg = (key) => {
|
|
933
|
+
const regex = new RegExp(`${key}\\s*=\\s*(["'])(.*?)\\1(?=\\s*[,)]|\\s+\\w+\\s*=|$)`, "s");
|
|
934
|
+
const match = rawArgs.match(regex);
|
|
935
|
+
return match ? match[2].trim() : null;
|
|
936
|
+
};
|
|
937
|
+
const action = parseArg("action");
|
|
938
|
+
const method = parseArg("method");
|
|
939
|
+
const content = parseArg("content");
|
|
940
|
+
const contentNew = parseArg("content-new");
|
|
941
|
+
const contentOld = parseArg("content-old");
|
|
942
|
+
const chatId = parseArg("chat-id") || context.chatId || context.sessionId || "default-session";
|
|
943
|
+
if (action === "temp") {
|
|
944
|
+
if (!content) return "ERROR: Missing 'content' for temp memory.";
|
|
945
|
+
const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
946
|
+
if (!tempStorage[chatId]) tempStorage[chatId] = [];
|
|
947
|
+
const MAX_CHARS = 3e3 * 4;
|
|
948
|
+
let currentTotalLength = tempStorage[chatId].reduce((acc, m) => acc + m.length, 0);
|
|
949
|
+
while (tempStorage[chatId].length > 0 && currentTotalLength + content.length > MAX_CHARS) {
|
|
950
|
+
const removed = tempStorage[chatId].shift();
|
|
951
|
+
currentTotalLength -= removed.length;
|
|
952
|
+
}
|
|
953
|
+
tempStorage[chatId].push(content);
|
|
954
|
+
writeEncryptedJson(TEMP_MEM_FILE, tempStorage);
|
|
955
|
+
return `SUCCESS: Temporary context saved for session [${chatId}]. (Size: ${currentTotalLength + content.length} chars)`;
|
|
874
956
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
957
|
+
if (action === "user") {
|
|
958
|
+
const memories = readEncryptedJson(MEMORIES_FILE, []);
|
|
959
|
+
if (method === "add") {
|
|
960
|
+
if (!content) return "ERROR: Missing 'content' for memory addition.";
|
|
961
|
+
const MAX_CHARS = 2e3 * 4;
|
|
962
|
+
let currentTotalLength = memories.reduce((acc, m) => acc + (m.memory?.length || 0), 0);
|
|
963
|
+
while (memories.length > 0 && currentTotalLength + content.length > MAX_CHARS) {
|
|
964
|
+
const removed = memories.shift();
|
|
965
|
+
currentTotalLength -= removed.memory?.length || 0;
|
|
966
|
+
}
|
|
967
|
+
const newMemory = { id: `mem-${Date.now().toString(36)}`, memory: content };
|
|
968
|
+
memories.push(newMemory);
|
|
969
|
+
writeEncryptedJson(MEMORIES_FILE, memories);
|
|
970
|
+
return `SUCCESS: Memory added with ID [${newMemory.id}]. (Vault Size: ${currentTotalLength + content.length} chars)`;
|
|
971
|
+
}
|
|
972
|
+
if (method === "update") {
|
|
973
|
+
const memId = contentOld;
|
|
974
|
+
const newText = contentNew;
|
|
975
|
+
if (!memId || !newText) return "ERROR: Missing 'content-old' (id) or 'content-new' for update.";
|
|
976
|
+
const index = memories.findIndex((m) => m.id === memId);
|
|
977
|
+
if (index === -1) return `ERROR: Memory ID [${memId}] not found.`;
|
|
978
|
+
memories[index].memory = newText;
|
|
979
|
+
writeEncryptedJson(MEMORIES_FILE, memories);
|
|
980
|
+
return `SUCCESS: Memory [${memId}] updated.`;
|
|
981
|
+
}
|
|
982
|
+
if (method === "delete") {
|
|
983
|
+
const memId = content;
|
|
984
|
+
if (!memId) return "ERROR: Missing 'content' (id) for deletion.";
|
|
985
|
+
const initialLen = memories.length;
|
|
986
|
+
const updatedMemories = memories.filter((m) => m.id !== memId);
|
|
987
|
+
if (updatedMemories.length === initialLen) return `ERROR: Memory ID [${memId}] not found.`;
|
|
988
|
+
writeEncryptedJson(MEMORIES_FILE, updatedMemories);
|
|
989
|
+
return `SUCCESS: Memory [${memId}] deleted.`;
|
|
990
|
+
}
|
|
991
|
+
return `ERROR: Invalid method [${method}] for user memory. Use 'add', 'update', or 'delete'.`;
|
|
992
|
+
}
|
|
993
|
+
return `ERROR: Unknown action [${action}] for memory tool.`;
|
|
994
|
+
};
|
|
900
995
|
}
|
|
901
|
-
|
|
902
|
-
};
|
|
996
|
+
});
|
|
903
997
|
|
|
904
998
|
// src/tools/chat.js
|
|
905
|
-
var chat
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
999
|
+
var chat;
|
|
1000
|
+
var init_chat = __esm({
|
|
1001
|
+
"src/tools/chat.js"() {
|
|
1002
|
+
init_history();
|
|
1003
|
+
init_arg_parser();
|
|
1004
|
+
chat = async (rawArgs, context = {}) => {
|
|
1005
|
+
const title = parseArgs(rawArgs).title;
|
|
1006
|
+
const chatId = context.chatId || context.sessionId;
|
|
1007
|
+
if (!chatId) return "ERROR: No active chatId found in tool context.";
|
|
1008
|
+
if (!title) return "ERROR: Missing 'title' argument.";
|
|
1009
|
+
try {
|
|
1010
|
+
await saveChatTitle(chatId, title);
|
|
1011
|
+
return `SUCCESS: Chat title updated to [${title}] for session [${chatId}].`;
|
|
1012
|
+
} catch (err) {
|
|
1013
|
+
return `ERROR: Failed to update chat title: ${err.message}`;
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
915
1016
|
}
|
|
916
|
-
};
|
|
1017
|
+
});
|
|
917
1018
|
|
|
918
1019
|
// src/tools/list_files.js
|
|
919
1020
|
import fs7 from "fs";
|
|
920
1021
|
import path8 from "path";
|
|
921
|
-
var list_files
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
const stats = fs7.statSync(absolutePath);
|
|
929
|
-
if (!stats.isDirectory()) {
|
|
930
|
-
return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
|
|
931
|
-
}
|
|
932
|
-
const files2 = fs7.readdirSync(absolutePath);
|
|
933
|
-
if (files2.length === 0) {
|
|
934
|
-
return `Directory [${targetPath}] is empty.`;
|
|
935
|
-
}
|
|
936
|
-
const totalFiles = files2.length;
|
|
937
|
-
const maxDisplay = 100;
|
|
938
|
-
const displayFiles = files2.slice(0, maxDisplay);
|
|
939
|
-
const list = displayFiles.map((file) => {
|
|
940
|
-
const fPath = path8.join(absolutePath, file);
|
|
941
|
-
let indicator = "\u{1F4C4}";
|
|
942
|
-
let metaPart = "";
|
|
1022
|
+
var list_files;
|
|
1023
|
+
var init_list_files = __esm({
|
|
1024
|
+
"src/tools/list_files.js"() {
|
|
1025
|
+
init_arg_parser();
|
|
1026
|
+
list_files = async (args) => {
|
|
1027
|
+
const { path: targetPath = "." } = parseArgs(args);
|
|
1028
|
+
const absolutePath = path8.resolve(process.cwd(), targetPath);
|
|
943
1029
|
try {
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1030
|
+
if (!fs7.existsSync(absolutePath)) {
|
|
1031
|
+
return `ERROR: Path [${targetPath}] does not exist.`;
|
|
1032
|
+
}
|
|
1033
|
+
const stats = fs7.statSync(absolutePath);
|
|
1034
|
+
if (!stats.isDirectory()) {
|
|
1035
|
+
return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
|
|
1036
|
+
}
|
|
1037
|
+
const files2 = fs7.readdirSync(absolutePath);
|
|
1038
|
+
if (files2.length === 0) {
|
|
1039
|
+
return `Directory [${targetPath}] is empty.`;
|
|
1040
|
+
}
|
|
1041
|
+
const totalFiles = files2.length;
|
|
1042
|
+
const maxDisplay = 100;
|
|
1043
|
+
const displayFiles = files2.slice(0, maxDisplay);
|
|
1044
|
+
const list = displayFiles.map((file) => {
|
|
1045
|
+
const fPath = path8.join(absolutePath, file);
|
|
1046
|
+
let indicator = "\u{1F4C4}";
|
|
1047
|
+
let metaPart = "";
|
|
1048
|
+
try {
|
|
1049
|
+
const fStats = fs7.statSync(fPath);
|
|
1050
|
+
indicator = fStats.isDirectory() ? "\u{1F4C1}" : "\u{1F4C4}";
|
|
1051
|
+
const sizeKB = (fStats.size / 1024).toFixed(1);
|
|
1052
|
+
metaPart = fStats.isFile() ? ` - [${sizeKB} KB]` : "";
|
|
1053
|
+
} catch (e) {
|
|
1054
|
+
indicator = "\u2753";
|
|
1055
|
+
metaPart = " - [Access Denied]";
|
|
1056
|
+
}
|
|
1057
|
+
return `${indicator} ${file}${metaPart}`;
|
|
1058
|
+
}).join("\n");
|
|
1059
|
+
let footer = `
|
|
955
1060
|
|
|
956
1061
|
(Total items: ${totalFiles})`;
|
|
957
|
-
|
|
958
|
-
|
|
1062
|
+
if (totalFiles > maxDisplay) {
|
|
1063
|
+
footer = `
|
|
959
1064
|
|
|
960
1065
|
\u26A0\uFE0F TRUNCATED: Showing first ${maxDisplay} of ${totalFiles} items. Use more specific paths to see others.`;
|
|
961
|
-
|
|
962
|
-
|
|
1066
|
+
}
|
|
1067
|
+
const result = `Contents of [${targetPath}]:
|
|
963
1068
|
|
|
964
1069
|
${list}${footer}`;
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1070
|
+
files2.length = 0;
|
|
1071
|
+
displayFiles.length = 0;
|
|
1072
|
+
return result;
|
|
1073
|
+
} catch (err) {
|
|
1074
|
+
return `ERROR: Failed to list files in [${targetPath}]: ${err.message}`;
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
970
1077
|
}
|
|
971
|
-
};
|
|
1078
|
+
});
|
|
972
1079
|
|
|
973
1080
|
// src/tools/view_file.js
|
|
974
1081
|
import fs8 from "fs";
|
|
975
1082
|
import path9 from "path";
|
|
976
|
-
var view_file
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
return
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1083
|
+
var view_file;
|
|
1084
|
+
var init_view_file = __esm({
|
|
1085
|
+
"src/tools/view_file.js"() {
|
|
1086
|
+
init_arg_parser();
|
|
1087
|
+
view_file = async (args) => {
|
|
1088
|
+
const { path: targetPath, start_line = 1, end_line = 500 } = parseArgs(args);
|
|
1089
|
+
if (!targetPath) return 'ERROR: Missing "path" argument for view_file.';
|
|
1090
|
+
const absolutePath = path9.resolve(process.cwd(), targetPath);
|
|
1091
|
+
try {
|
|
1092
|
+
if (!fs8.existsSync(absolutePath)) {
|
|
1093
|
+
return `ERROR: File [${targetPath}] does not exist.`;
|
|
1094
|
+
}
|
|
1095
|
+
const stats = fs8.statSync(absolutePath);
|
|
1096
|
+
if (stats.isDirectory()) {
|
|
1097
|
+
return `ERROR: Path [${targetPath}] is a directory. Use list_files instead.`;
|
|
1098
|
+
}
|
|
1099
|
+
const content = fs8.readFileSync(absolutePath, "utf8");
|
|
1100
|
+
const lines = content.split("\n");
|
|
1101
|
+
const totalLines = lines.length;
|
|
1102
|
+
const start = Math.max(0, start_line - 1);
|
|
1103
|
+
const end = Math.min(totalLines, end_line);
|
|
1104
|
+
const resultLines = lines.slice(start, end);
|
|
1105
|
+
const header = `File: [${targetPath}] (Showing lines ${start + 1}-${end} of ${totalLines})`;
|
|
1106
|
+
const code = resultLines.map((line, i) => `${String(start + i + 1).padStart(4)}: ${line}`).join("\n");
|
|
1107
|
+
return `${header}
|
|
997
1108
|
|
|
998
1109
|
${code}`;
|
|
999
|
-
|
|
1000
|
-
|
|
1110
|
+
} catch (err) {
|
|
1111
|
+
return `ERROR: Failed to read file [${targetPath}]: ${err.message}`;
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1001
1114
|
}
|
|
1002
|
-
};
|
|
1115
|
+
});
|
|
1003
1116
|
|
|
1004
1117
|
// src/tools/write_file.js
|
|
1005
1118
|
import fs9 from "fs";
|
|
1006
1119
|
import path10 from "path";
|
|
1007
|
-
var write_file
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1120
|
+
var write_file;
|
|
1121
|
+
var init_write_file = __esm({
|
|
1122
|
+
"src/tools/write_file.js"() {
|
|
1123
|
+
init_arg_parser();
|
|
1124
|
+
write_file = async (args) => {
|
|
1125
|
+
let { path: targetPath, content } = parseArgs(args);
|
|
1126
|
+
if (!targetPath) return 'ERROR: Missing "path" argument for write_file.';
|
|
1127
|
+
if (content === void 0) return 'ERROR: Missing "content" argument for write_file.';
|
|
1128
|
+
content = content.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").trim();
|
|
1129
|
+
const absolutePath = path10.resolve(process.cwd(), targetPath);
|
|
1130
|
+
const parentDir = path10.dirname(absolutePath);
|
|
1017
1131
|
try {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1132
|
+
let ancestry = "";
|
|
1133
|
+
if (fs9.existsSync(absolutePath)) {
|
|
1134
|
+
try {
|
|
1135
|
+
const oldData = fs9.readFileSync(absolutePath, "utf8");
|
|
1136
|
+
const lines = oldData.split(/\r?\n/);
|
|
1137
|
+
ancestry = `Old File contents:
|
|
1021
1138
|
${lines.map((l, i) => `${i + 1} | ${l}`).join("\n")}
|
|
1022
1139
|
|
|
1023
1140
|
`;
|
|
1024
|
-
|
|
1025
|
-
|
|
1141
|
+
} catch (e) {
|
|
1142
|
+
ancestry = `[Note: Could not read existing file for reversal reference]
|
|
1026
1143
|
|
|
1027
1144
|
`;
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
if (!fs9.existsSync(parentDir)) {
|
|
1148
|
+
fs9.mkdirSync(parentDir, { recursive: true });
|
|
1149
|
+
}
|
|
1150
|
+
const lineCount = content.split(/\r?\n/).length;
|
|
1151
|
+
const originalSize = Buffer.byteLength(content, "utf8");
|
|
1152
|
+
fs9.writeFileSync(absolutePath, content, "utf8");
|
|
1153
|
+
let verifiedContent = fs9.readFileSync(absolutePath, "utf8");
|
|
1154
|
+
const verifiedSize = Buffer.byteLength(verifiedContent, "utf8");
|
|
1155
|
+
const verifiedLines = verifiedContent.split(/\r?\n/);
|
|
1156
|
+
const verifiedLineCount = verifiedLines.length;
|
|
1157
|
+
if (verifiedSize === 0 && originalSize > 0) {
|
|
1158
|
+
verifiedContent = null;
|
|
1159
|
+
return `CRITICAL FAILURE: Verification failed. File [${targetPath}] is empty on disk despite success report!`;
|
|
1160
|
+
}
|
|
1161
|
+
let snippet = "";
|
|
1162
|
+
if (verifiedLineCount <= 30) {
|
|
1163
|
+
snippet = verifiedLines.join("\n");
|
|
1164
|
+
} else {
|
|
1165
|
+
const head = verifiedLines.slice(0, 15).join("\n");
|
|
1166
|
+
const tail = verifiedLines.slice(-15).join("\n");
|
|
1167
|
+
snippet = `${head}
|
|
1051
1168
|
|
|
1052
1169
|
... [${verifiedLineCount - 30} lines truncated for history stability] ...
|
|
1053
1170
|
|
|
1054
1171
|
${tail}`;
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1172
|
+
}
|
|
1173
|
+
verifiedContent = null;
|
|
1174
|
+
return `SUCCESS: File [${targetPath}] verified and persisted.
|
|
1058
1175
|
|
|
1059
1176
|
- Stats: [${verifiedLineCount} lines, ${(verifiedSize / 1024).toFixed(1)} KB]
|
|
1060
1177
|
${ancestry}- Content Preview:
|
|
1061
1178
|
${snippet}`;
|
|
1062
|
-
|
|
1063
|
-
|
|
1179
|
+
} catch (err) {
|
|
1180
|
+
return `ERROR: Failed to write file [${targetPath}]: ${err.message}`;
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1064
1183
|
}
|
|
1065
|
-
};
|
|
1184
|
+
});
|
|
1066
1185
|
|
|
1067
1186
|
// src/tools/update_file.js
|
|
1068
1187
|
import fs10 from "fs";
|
|
1069
1188
|
import path11 from "path";
|
|
1070
|
-
var update_file
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1189
|
+
var update_file;
|
|
1190
|
+
var init_update_file = __esm({
|
|
1191
|
+
"src/tools/update_file.js"() {
|
|
1192
|
+
init_arg_parser();
|
|
1193
|
+
update_file = async (args) => {
|
|
1194
|
+
let { path: targetPath, content_to_replace, content_to_add } = parseArgs(args);
|
|
1195
|
+
if (!targetPath) return 'ERROR: Missing "path" argument for update_file.';
|
|
1196
|
+
if (content_to_replace === void 0) return 'ERROR: Missing "content_to_replace" argument.';
|
|
1197
|
+
if (content_to_add === void 0) return 'ERROR: Missing "content_to_add" argument.';
|
|
1198
|
+
const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").trim();
|
|
1199
|
+
content_to_replace = strip(content_to_replace);
|
|
1200
|
+
content_to_add = strip(content_to_add);
|
|
1201
|
+
const absolutePath = path11.resolve(process.cwd(), targetPath);
|
|
1202
|
+
try {
|
|
1203
|
+
if (!fs10.existsSync(absolutePath)) {
|
|
1204
|
+
return `ERROR: File [${targetPath}] does not exist. Use write_file instead.`;
|
|
1205
|
+
}
|
|
1206
|
+
const currentContent = fs10.readFileSync(absolutePath, "utf8");
|
|
1207
|
+
if (!currentContent.includes(content_to_replace)) {
|
|
1208
|
+
return `ERROR: Could not find exact match for the specified "content_to_replace" in [${targetPath}]. Check indentation/whitespace/line breaks(LF or CRLF)/string. Try re-reading the file for latest changes.`;
|
|
1209
|
+
}
|
|
1210
|
+
const startPos = currentContent.indexOf(content_to_replace);
|
|
1211
|
+
const startLine = currentContent.substring(0, startPos).split(/\r?\n/).length;
|
|
1212
|
+
const instances = currentContent.split(content_to_replace).length - 1;
|
|
1213
|
+
const newFileContent = currentContent.split(content_to_replace).join(content_to_add);
|
|
1214
|
+
fs10.writeFileSync(absolutePath, newFileContent, "utf8");
|
|
1215
|
+
const allOriginalLines = currentContent.split(/\r?\n/);
|
|
1216
|
+
const oldLines = content_to_replace.split(/\r?\n/);
|
|
1217
|
+
const newLines = content_to_add.split(/\r?\n/);
|
|
1218
|
+
const endLine = startLine + oldLines.length - 1;
|
|
1219
|
+
let diffText = `SUCCESS: File [${targetPath}] updated. [${instances}] instances replaced.
|
|
1097
1220
|
|
|
1098
1221
|
`;
|
|
1099
|
-
|
|
1222
|
+
diffText += `[DIFF_START]
|
|
1100
1223
|
`;
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1224
|
+
const contextStart = Math.max(0, startLine - 16);
|
|
1225
|
+
for (let i = contextStart; i < startLine - 1; i++) {
|
|
1226
|
+
diffText += `[UI_CONTEXT] ${i + 1}| ${allOriginalLines[i]}
|
|
1104
1227
|
`;
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1228
|
+
}
|
|
1229
|
+
oldLines.forEach((line, i) => {
|
|
1230
|
+
diffText += `-${startLine + i}| ${line}
|
|
1108
1231
|
`;
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1232
|
+
});
|
|
1233
|
+
newLines.forEach((line, i) => {
|
|
1234
|
+
diffText += `+${startLine + i}| ${line}
|
|
1112
1235
|
`;
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1236
|
+
});
|
|
1237
|
+
for (let i = endLine; i < Math.min(allOriginalLines.length, endLine + 15); i++) {
|
|
1238
|
+
diffText += `[UI_CONTEXT] ${i + 1}| ${allOriginalLines[i]}
|
|
1116
1239
|
`;
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1240
|
+
}
|
|
1241
|
+
diffText += `[DIFF_END]`;
|
|
1242
|
+
return diffText;
|
|
1243
|
+
} catch (err) {
|
|
1244
|
+
return `ERROR: Failed to update file [${targetPath}]: ${err.message}`;
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1122
1247
|
}
|
|
1123
|
-
};
|
|
1248
|
+
});
|
|
1124
1249
|
|
|
1125
1250
|
// src/tools/exec_command.js
|
|
1126
1251
|
import { spawn } from "child_process";
|
|
1127
|
-
var exec_command
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1252
|
+
var exec_command;
|
|
1253
|
+
var init_exec_command = __esm({
|
|
1254
|
+
"src/tools/exec_command.js"() {
|
|
1255
|
+
init_arg_parser();
|
|
1256
|
+
exec_command = async (args, options = {}) => {
|
|
1257
|
+
const { command } = parseArgs(args);
|
|
1258
|
+
const { onChunk } = options;
|
|
1259
|
+
if (!command) return 'ERROR: Missing "command" argument for exec_command.';
|
|
1260
|
+
return new Promise((resolve) => {
|
|
1261
|
+
const child = spawn(command, { shell: true, cwd: process.cwd() });
|
|
1262
|
+
let stdout = "";
|
|
1263
|
+
let stderr = "";
|
|
1264
|
+
child.stdout.on("data", (data) => {
|
|
1265
|
+
const chunk = data.toString();
|
|
1266
|
+
stdout += chunk;
|
|
1267
|
+
if (onChunk) onChunk(chunk);
|
|
1268
|
+
});
|
|
1269
|
+
child.stderr.on("data", (data) => {
|
|
1270
|
+
const chunk = data.toString();
|
|
1271
|
+
stderr += chunk;
|
|
1272
|
+
if (onChunk) onChunk(chunk);
|
|
1273
|
+
});
|
|
1274
|
+
child.on("close", (code) => {
|
|
1275
|
+
const result = [];
|
|
1276
|
+
if (stdout) result.push(`STDOUT:
|
|
1148
1277
|
${stdout}`);
|
|
1149
|
-
|
|
1278
|
+
if (stderr) result.push(`STDERR:
|
|
1150
1279
|
${stderr}`);
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1280
|
+
if (code !== 0) result.push(`EXIT CODE: ${code}`);
|
|
1281
|
+
const finalOutput = result.join("\n\n") || "Command executed with no output.";
|
|
1282
|
+
if (code !== 0) {
|
|
1283
|
+
resolve(`FAILURE: Command [${command}] failed.
|
|
1155
1284
|
|
|
1156
1285
|
${finalOutput}`);
|
|
1157
|
-
|
|
1158
|
-
|
|
1286
|
+
} else {
|
|
1287
|
+
resolve(`SUCCESS: Command [${command}] completed.
|
|
1159
1288
|
|
|
1160
1289
|
${finalOutput}`);
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
};
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
child.on("error", (err) => {
|
|
1293
|
+
resolve(`ERROR: Failed to start command [${command}]: ${err.message}`);
|
|
1294
|
+
});
|
|
1295
|
+
});
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1168
1299
|
|
|
1169
1300
|
// src/tools/read_folder.js
|
|
1170
1301
|
import fs11 from "fs";
|
|
1171
1302
|
import path12 from "path";
|
|
1172
|
-
var read_folder
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
const stats = fs11.statSync(absolutePath);
|
|
1180
|
-
if (!stats.isDirectory()) {
|
|
1181
|
-
return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
|
|
1182
|
-
}
|
|
1183
|
-
const totalItems = files.length;
|
|
1184
|
-
const maxDisplay = 100;
|
|
1185
|
-
const displayItems = files.slice(0, maxDisplay);
|
|
1186
|
-
const folderData = [];
|
|
1187
|
-
for (const file of displayItems) {
|
|
1188
|
-
const fPath = path12.join(absolutePath, file);
|
|
1189
|
-
let indicator = "\u{1F4C4}";
|
|
1190
|
-
let info = { name: file, type: "unknown", size: "N/A", mtime: "N/A" };
|
|
1303
|
+
var read_folder;
|
|
1304
|
+
var init_read_folder = __esm({
|
|
1305
|
+
"src/tools/read_folder.js"() {
|
|
1306
|
+
init_arg_parser();
|
|
1307
|
+
read_folder = async (args) => {
|
|
1308
|
+
const { path: targetPath = "." } = parseArgs(args);
|
|
1309
|
+
const absolutePath = path12.resolve(process.cwd(), targetPath);
|
|
1191
1310
|
try {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1311
|
+
if (!fs11.existsSync(absolutePath)) {
|
|
1312
|
+
return `ERROR: Path [${targetPath}] does not exist.`;
|
|
1313
|
+
}
|
|
1314
|
+
const stats = fs11.statSync(absolutePath);
|
|
1315
|
+
if (!stats.isDirectory()) {
|
|
1316
|
+
return `ERROR: Path [${targetPath}] is a file, not a directory. Use view_file instead.`;
|
|
1317
|
+
}
|
|
1318
|
+
const totalItems = files.length;
|
|
1319
|
+
const maxDisplay = 100;
|
|
1320
|
+
const displayItems = files.slice(0, maxDisplay);
|
|
1321
|
+
const folderData = [];
|
|
1322
|
+
for (const file of displayItems) {
|
|
1323
|
+
const fPath = path12.join(absolutePath, file);
|
|
1324
|
+
let indicator = "\u{1F4C4}";
|
|
1325
|
+
let info = { name: file, type: "unknown", size: "N/A", mtime: "N/A" };
|
|
1326
|
+
try {
|
|
1327
|
+
const fStats = fs11.statSync(fPath);
|
|
1328
|
+
info = {
|
|
1329
|
+
name: file,
|
|
1330
|
+
type: fStats.isDirectory() ? "directory" : "file",
|
|
1331
|
+
size: (fStats.size / 1024).toFixed(1) + " KB",
|
|
1332
|
+
mtime: fStats.mtime.toLocaleString()
|
|
1333
|
+
};
|
|
1334
|
+
} catch (e) {
|
|
1335
|
+
info.type = "inaccessible";
|
|
1336
|
+
}
|
|
1337
|
+
folderData.push(info);
|
|
1338
|
+
}
|
|
1339
|
+
const formatted = folderData.map((f) => {
|
|
1340
|
+
const indicator = f.type === "directory" ? "\u{1F4C1}" : f.type === "file" ? "\u{1F4C4}" : "\u2753";
|
|
1341
|
+
if (f.type === "directory") {
|
|
1342
|
+
return `${indicator} ${f.name} - [DIR] - [Modified: ${f.mtime}]`;
|
|
1343
|
+
}
|
|
1344
|
+
return `${indicator} ${f.name} - [Size: ${f.size}] - [Modified: ${f.mtime}]`;
|
|
1345
|
+
}).join("\n");
|
|
1346
|
+
let footer = `
|
|
1212
1347
|
|
|
1213
1348
|
(Total items in folder: ${totalItems})`;
|
|
1214
|
-
|
|
1215
|
-
|
|
1349
|
+
if (totalItems > maxDisplay) {
|
|
1350
|
+
footer = `
|
|
1216
1351
|
|
|
1217
1352
|
\u26A0\uFE0F TRUNCATED: Showing first ${maxDisplay} of ${totalItems} items.`;
|
|
1218
|
-
|
|
1219
|
-
|
|
1353
|
+
}
|
|
1354
|
+
const result = `Detailed folder stats for [${targetPath}]:
|
|
1220
1355
|
|
|
1221
1356
|
${formatted}${footer}`;
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1357
|
+
files.length = 0;
|
|
1358
|
+
displayItems.length = 0;
|
|
1359
|
+
folderData.length = 0;
|
|
1360
|
+
return result;
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
return `ERROR: Failed to read folder [${targetPath}]: ${err.message}`;
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1228
1365
|
}
|
|
1229
|
-
};
|
|
1366
|
+
});
|
|
1230
1367
|
|
|
1231
1368
|
// src/tools/ask_user.js
|
|
1232
|
-
var ask_user
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1369
|
+
var ask_user;
|
|
1370
|
+
var init_ask_user = __esm({
|
|
1371
|
+
"src/tools/ask_user.js"() {
|
|
1372
|
+
init_arg_parser();
|
|
1373
|
+
ask_user = async (args, context) => {
|
|
1374
|
+
const parsed = parseArgs(args);
|
|
1375
|
+
const { question } = parsed;
|
|
1376
|
+
if (!question) return 'ERROR: Missing "question" argument for ask.';
|
|
1377
|
+
if (!context.onAskUser) return "ERROR: onAskUser callback not provided in tool context.";
|
|
1378
|
+
const options = [];
|
|
1379
|
+
Object.keys(parsed).forEach((key) => {
|
|
1380
|
+
if (key.startsWith("option")) {
|
|
1381
|
+
const val = parsed[key];
|
|
1382
|
+
if (typeof val === "string" && val.includes("::")) {
|
|
1383
|
+
const [label, desc] = val.split("::");
|
|
1384
|
+
options.push({
|
|
1385
|
+
id: key,
|
|
1386
|
+
label: label.trim(),
|
|
1387
|
+
description: desc.trim()
|
|
1388
|
+
});
|
|
1389
|
+
} else {
|
|
1390
|
+
options.push({
|
|
1391
|
+
id: key,
|
|
1392
|
+
label: String(val).trim(),
|
|
1393
|
+
description: ""
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
try {
|
|
1399
|
+
const choice = await context.onAskUser(question, options);
|
|
1400
|
+
return `USER CHOOSE: ${choice}`;
|
|
1401
|
+
} catch (err) {
|
|
1402
|
+
return `ERROR: Failed to get user input: ${err.message}`;
|
|
1254
1403
|
}
|
|
1255
|
-
}
|
|
1256
|
-
});
|
|
1257
|
-
try {
|
|
1258
|
-
const choice = await context.onAskUser(question, options);
|
|
1259
|
-
return `USER CHOOSE: ${choice}`;
|
|
1260
|
-
} catch (err) {
|
|
1261
|
-
return `ERROR: Failed to get user input: ${err.message}`;
|
|
1404
|
+
};
|
|
1262
1405
|
}
|
|
1263
|
-
};
|
|
1406
|
+
});
|
|
1264
1407
|
|
|
1265
1408
|
// src/utils/tools.js
|
|
1266
|
-
var TOOL_MAP
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1409
|
+
var TOOL_MAP, dispatchTool;
|
|
1410
|
+
var init_tools = __esm({
|
|
1411
|
+
"src/utils/tools.js"() {
|
|
1412
|
+
init_web_search();
|
|
1413
|
+
init_web_scrape();
|
|
1414
|
+
init_memory();
|
|
1415
|
+
init_chat();
|
|
1416
|
+
init_list_files();
|
|
1417
|
+
init_view_file();
|
|
1418
|
+
init_write_file();
|
|
1419
|
+
init_update_file();
|
|
1420
|
+
init_exec_command();
|
|
1421
|
+
init_read_folder();
|
|
1422
|
+
init_ask_user();
|
|
1423
|
+
TOOL_MAP = {
|
|
1424
|
+
web_search,
|
|
1425
|
+
web_scrape,
|
|
1426
|
+
memory,
|
|
1427
|
+
chat,
|
|
1428
|
+
list_files,
|
|
1429
|
+
view_file,
|
|
1430
|
+
write_file,
|
|
1431
|
+
update_file,
|
|
1432
|
+
exec_command,
|
|
1433
|
+
read_folder,
|
|
1434
|
+
ask: ask_user
|
|
1435
|
+
};
|
|
1436
|
+
dispatchTool = async (toolName, args, context = {}) => {
|
|
1437
|
+
const tool = TOOL_MAP[toolName];
|
|
1438
|
+
if (!tool) {
|
|
1439
|
+
return `ERROR: Tool [${toolName}] not found in registry.`;
|
|
1440
|
+
}
|
|
1441
|
+
try {
|
|
1442
|
+
return await tool(args, context);
|
|
1443
|
+
} catch (err) {
|
|
1444
|
+
return `ERROR: Execution failed for [${toolName}]: ${err.message}`;
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1288
1447
|
}
|
|
1289
|
-
};
|
|
1448
|
+
});
|
|
1290
1449
|
|
|
1291
1450
|
// src/utils/ai.js
|
|
1451
|
+
import { GoogleGenAI, ThinkingLevel } from "@google/genai";
|
|
1292
1452
|
import path13 from "path";
|
|
1293
1453
|
import fs12 from "fs";
|
|
1294
|
-
var client
|
|
1295
|
-
var
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1454
|
+
var client, TERMINATION_SIGNAL, signalTermination, detectToolCalls, initAI, getAIStream;
|
|
1455
|
+
var init_ai = __esm({
|
|
1456
|
+
"src/utils/ai.js"() {
|
|
1457
|
+
init_prompts();
|
|
1458
|
+
init_history();
|
|
1459
|
+
init_usage();
|
|
1460
|
+
init_tools();
|
|
1461
|
+
init_crypto();
|
|
1462
|
+
init_arg_parser();
|
|
1463
|
+
init_paths();
|
|
1464
|
+
client = null;
|
|
1465
|
+
TERMINATION_SIGNAL = false;
|
|
1466
|
+
signalTermination = () => {
|
|
1467
|
+
TERMINATION_SIGNAL = true;
|
|
1468
|
+
};
|
|
1469
|
+
detectToolCalls = (text) => {
|
|
1470
|
+
const results = [];
|
|
1471
|
+
const toolRegex = /(?:\[?\s*tool:functions\.)([a-z0-9_]+)\s*\(([\s\S]*?)\)(?:\s*\]?)/gi;
|
|
1472
|
+
let match;
|
|
1473
|
+
while ((match = toolRegex.exec(text)) !== null) {
|
|
1474
|
+
const fullMatch = match[0];
|
|
1475
|
+
const toolName = match[1];
|
|
1476
|
+
const args = match[2];
|
|
1477
|
+
let openCount = (args.match(/\(/g) || []).length;
|
|
1478
|
+
let closeCount = (args.match(/\)/g) || []).length;
|
|
1479
|
+
let finalArgs = args;
|
|
1480
|
+
let finalFullMatch = fullMatch;
|
|
1481
|
+
if (openCount > closeCount) {
|
|
1482
|
+
const startIdx = match.index + fullMatch.indexOf("(");
|
|
1483
|
+
let balance = 0;
|
|
1484
|
+
let endIdx = -1;
|
|
1485
|
+
for (let i = startIdx; i < text.length; i++) {
|
|
1486
|
+
if (text[i] === "(") balance++;
|
|
1487
|
+
if (text[i] === ")") balance--;
|
|
1488
|
+
if (balance === 0) {
|
|
1489
|
+
endIdx = i;
|
|
1490
|
+
break;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
if (endIdx !== -1) {
|
|
1494
|
+
finalArgs = text.substring(startIdx + 1, endIdx);
|
|
1495
|
+
finalFullMatch = text.substring(match.index, endIdx + 1);
|
|
1496
|
+
toolRegex.lastIndex = endIdx + 1;
|
|
1497
|
+
}
|
|
1321
1498
|
}
|
|
1499
|
+
results.push({
|
|
1500
|
+
fullMatch: finalFullMatch,
|
|
1501
|
+
toolName: toolName.trim(),
|
|
1502
|
+
args: finalArgs.trim()
|
|
1503
|
+
});
|
|
1322
1504
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1505
|
+
return results;
|
|
1506
|
+
};
|
|
1507
|
+
initAI = (apiKey) => {
|
|
1508
|
+
if (!apiKey) return null;
|
|
1509
|
+
client = new GoogleGenAI({ apiKey });
|
|
1510
|
+
return client;
|
|
1511
|
+
};
|
|
1512
|
+
getAIStream = async function* (modelName, history, settings, steeringCallback) {
|
|
1513
|
+
if (!client) throw new Error("AI not initialized");
|
|
1514
|
+
const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats } = settings;
|
|
1515
|
+
const isMemoryEnabled = systemSettings?.memory !== false;
|
|
1516
|
+
const originalText = history[history.length - 1].text;
|
|
1517
|
+
const isFirstPrompt = history.filter((m) => m.role === "user").length === 1;
|
|
1518
|
+
const hasTitleSignal = originalText.includes("[TITLE-UPDATE]");
|
|
1519
|
+
const needTitle = isFirstPrompt || hasTitleSignal;
|
|
1520
|
+
const agentText = originalText.replace(/\[TITLE-UPDATE\]/g, "").trim();
|
|
1521
|
+
let modifiedHistory = [...history.slice(0, -1)];
|
|
1522
|
+
if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > 254e3) {
|
|
1523
|
+
modifiedHistory = getTruncatedHistory(modifiedHistory, 4);
|
|
1327
1524
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
};
|
|
1337
|
-
var initAI = (apiKey) => {
|
|
1338
|
-
if (!apiKey) return null;
|
|
1339
|
-
client = new GoogleGenAI({ apiKey });
|
|
1340
|
-
return client;
|
|
1341
|
-
};
|
|
1342
|
-
var getAIStream = async function* (modelName, history, settings, steeringCallback) {
|
|
1343
|
-
if (!client) throw new Error("AI not initialized");
|
|
1344
|
-
const { profile, thinkingLevel, mode, janitorModel, chatId, systemSettings, sessionStats } = settings;
|
|
1345
|
-
const isMemoryEnabled = systemSettings?.memory !== false;
|
|
1346
|
-
const originalText = history[history.length - 1].text;
|
|
1347
|
-
const isFirstPrompt = history.filter((m) => m.role === "user").length === 1;
|
|
1348
|
-
const hasTitleSignal = originalText.includes("[TITLE-UPDATE]");
|
|
1349
|
-
const needTitle = isFirstPrompt || hasTitleSignal;
|
|
1350
|
-
const agentText = originalText.replace(/\[TITLE-UPDATE\]/g, "").trim();
|
|
1351
|
-
let modifiedHistory = [...history.slice(0, -1)];
|
|
1352
|
-
if (systemSettings?.compression === 0 && (sessionStats?.tokens || 0) > 254e3) {
|
|
1353
|
-
modifiedHistory = getTruncatedHistory(modifiedHistory, 4);
|
|
1354
|
-
}
|
|
1355
|
-
const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
1356
|
-
const otherMemories = Object.entries(tempStorage).filter(([id]) => id !== chatId).flatMap(([_, mems]) => mems).map((mem) => `- ${mem}`).join("\n");
|
|
1357
|
-
const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
|
|
1358
|
-
const mainUserMemories = persistentStorage.map((m) => `- ${m.memory}`).join("\n");
|
|
1359
|
-
const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
|
|
1360
|
-
const isContext50 = (sessionStats.tokens || 0) >= 54e3;
|
|
1361
|
-
const systemInstruction = getSystemInstruction(profile, thinkingLevel, mode, systemSettings, otherMemories, mainUserMemories, isMemoryEnabled, isContext50);
|
|
1362
|
-
const firstUserMsg = `${systemInstruction}
|
|
1525
|
+
const tempStorage = readEncryptedJson(TEMP_MEM_FILE, {});
|
|
1526
|
+
const otherMemories = Object.entries(tempStorage).filter(([id]) => id !== chatId).flatMap(([_, mems]) => mems).map((mem) => `- ${mem}`).join("\n");
|
|
1527
|
+
const persistentStorage = readEncryptedJson(MEMORIES_FILE, []);
|
|
1528
|
+
const mainUserMemories = persistentStorage.map((m) => `- ${m.memory}`).join("\n");
|
|
1529
|
+
const janitorUserMemories = persistentStorage.map((m) => `- [${m.id}]: ${m.memory}`).join("\n");
|
|
1530
|
+
const isContext50 = (sessionStats.tokens || 0) >= 54e3;
|
|
1531
|
+
const systemInstruction = getSystemInstruction(profile, thinkingLevel, mode, systemSettings, otherMemories, mainUserMemories, isMemoryEnabled, isContext50);
|
|
1532
|
+
const firstUserMsg = `${systemInstruction}
|
|
1363
1533
|
|
|
1364
1534
|
USER_PROMPT: ${agentText}`.trim();
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
}
|
|
1377
|
-
if (steeringCallback) {
|
|
1378
|
-
const hint = await steeringCallback();
|
|
1379
|
-
if (hint) {
|
|
1380
|
-
modifiedHistory.push({ role: "user", text: `[STEERING HINT]: ${hint}` });
|
|
1381
|
-
yield { type: "status", content: "Steering Hint Injected." };
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
yield { type: "turn_reset", content: true };
|
|
1385
|
-
const contents = modifiedHistory.filter((msg) => (msg.role === "user" || msg.role === "agent" || msg.role === "system") && !String(msg.id).startsWith("welcome") && !msg.isMeta).map((msg) => ({
|
|
1386
|
-
role: msg.role === "user" || msg.role === "system" ? "user" : "model",
|
|
1387
|
-
parts: [{ text: msg.text }]
|
|
1388
|
-
}));
|
|
1389
|
-
let stream;
|
|
1390
|
-
let success = false;
|
|
1391
|
-
let retryCount = 0;
|
|
1392
|
-
while (retryCount <= MAX_RETRIES && !success) {
|
|
1393
|
-
try {
|
|
1394
|
-
if (!await checkQuota("agent", settings)) {
|
|
1395
|
-
throw new Error("Error: Daily Quota Exausted for Agent");
|
|
1396
|
-
}
|
|
1397
|
-
let targetModel = modelName;
|
|
1398
|
-
if (retryCount === 5) {
|
|
1399
|
-
targetModel = "gemini-3-flash-preview";
|
|
1400
|
-
yield { type: "model_update", content: "Trying with fallback model (v3)" };
|
|
1401
|
-
} else if (retryCount >= 6) {
|
|
1402
|
-
targetModel = "gemini-3.1-flash-lite-preview";
|
|
1403
|
-
yield { type: "model_update", content: "Trying with fallback model (v3.1)" };
|
|
1404
|
-
} else if (retryCount > 0) {
|
|
1405
|
-
yield { type: "model_update", content: null };
|
|
1406
|
-
}
|
|
1407
|
-
stream = await client.models.generateContentStream({
|
|
1408
|
-
model: targetModel,
|
|
1409
|
-
contents,
|
|
1410
|
-
config: {
|
|
1411
|
-
temperature: mode === "Flux" ? 1 : 1.4,
|
|
1412
|
-
thinkingConfig: {
|
|
1413
|
-
includeThoughts: false,
|
|
1414
|
-
thinkingLevel: ThinkingLevel.MINIMAL
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
});
|
|
1418
|
-
success = true;
|
|
1419
|
-
yield { type: "model_update", content: null };
|
|
1420
|
-
} catch (err) {
|
|
1421
|
-
const errMsg = err.status || err.error && err.error.message || String(err);
|
|
1422
|
-
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1423
|
-
const agentErrDir = path13.join(LOGS_DIR, "agent");
|
|
1424
|
-
if (!fs12.existsSync(agentErrDir)) fs12.mkdirSync(agentErrDir, { recursive: true });
|
|
1425
|
-
fs12.appendFileSync(path13.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
|
|
1426
|
-
`);
|
|
1427
|
-
if (retryCount < MAX_RETRIES) {
|
|
1428
|
-
retryCount++;
|
|
1429
|
-
const waitTime = Math.floor(Math.random() * (2e3 - 800 + 1)) + 800;
|
|
1430
|
-
yield { type: "status", content: `Retrying (${retryCount}/${MAX_RETRIES + 1})...` };
|
|
1431
|
-
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
1432
|
-
} else {
|
|
1433
|
-
throw new Error(`Model cannot be reached: ${errMsg}. (Failed ${MAX_RETRIES + 1} times)`);
|
|
1535
|
+
modifiedHistory.push({ role: "user", text: firstUserMsg });
|
|
1536
|
+
let lastUsage = null;
|
|
1537
|
+
const MAX_LOOPS = mode === "Flux" ? 50 : 7;
|
|
1538
|
+
const MAX_RETRIES = 7;
|
|
1539
|
+
yield { type: "status", content: "Working..." };
|
|
1540
|
+
TERMINATION_SIGNAL = false;
|
|
1541
|
+
let fullAgentResponseChunks = [];
|
|
1542
|
+
for (let loop = 0; loop < MAX_LOOPS; loop++) {
|
|
1543
|
+
if (TERMINATION_SIGNAL) {
|
|
1544
|
+
yield { type: "status", content: "Termination Signal Received." };
|
|
1545
|
+
break;
|
|
1434
1546
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
const potentialTool = parts[parts.length - 1].split("(")[0].trim();
|
|
1447
|
-
if (potentialTool && /^[a-z_]+$/.test(potentialTool) && potentialTool !== lastToolSniffed) {
|
|
1448
|
-
lastToolSniffed = potentialTool;
|
|
1449
|
-
yield { type: "status", content: `Working (${potentialTool})...` };
|
|
1547
|
+
if (steeringCallback) {
|
|
1548
|
+
const hint = await steeringCallback();
|
|
1549
|
+
if (hint) {
|
|
1550
|
+
if (modifiedHistory.length > 0 && modifiedHistory[modifiedHistory.length - 1].role === "user") {
|
|
1551
|
+
modifiedHistory[modifiedHistory.length - 1].text += `
|
|
1552
|
+
|
|
1553
|
+
[STEERING HINT]: ${hint}`;
|
|
1554
|
+
} else {
|
|
1555
|
+
modifiedHistory.push({ role: "user", text: `[STEERING HINT]: ${hint}` });
|
|
1556
|
+
}
|
|
1557
|
+
yield { type: "status", content: "Steering Hint Injected." };
|
|
1450
1558
|
}
|
|
1451
1559
|
}
|
|
1452
|
-
}
|
|
1453
|
-
if (chunk.usageMetadata) {
|
|
1454
|
-
lastUsage = chunk.usageMetadata;
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
await incrementUsage("agent");
|
|
1458
|
-
if (lastUsage) {
|
|
1459
|
-
yield { type: "usage", content: lastUsage };
|
|
1460
|
-
}
|
|
1461
|
-
fullAgentResponse += turnText + "\n";
|
|
1462
|
-
const turnTextLower = turnText.toLowerCase();
|
|
1463
|
-
const hasFinish = /\[?\s*(turn\s*:)?\s*finish\s*\]?/i.test(turnTextLower);
|
|
1464
|
-
const hasContinue = /\[?\s*(turn\s*:)?\s*continue\s*\]?/i.test(turnTextLower);
|
|
1465
|
-
const toolCalls = detectToolCalls(turnText);
|
|
1466
|
-
let toolResults = [];
|
|
1467
|
-
const shouldContinue = hasContinue || toolCalls.length > 0;
|
|
1468
|
-
if (toolCalls.length > 0) {
|
|
1469
|
-
for (const toolCall of toolCalls) {
|
|
1470
1560
|
yield { type: "turn_reset", content: true };
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
} else if (toolCall.toolName === "view_file") {
|
|
1480
|
-
const { path: targetPath2, start_line = 1, end_line = 500 } = parseArgs(toolCall.args);
|
|
1481
|
-
let totalLines = "...";
|
|
1482
|
-
let actualEndLine = end_line;
|
|
1561
|
+
const contents = modifiedHistory.filter((msg) => (msg.role === "user" || msg.role === "agent" || msg.role === "system") && !String(msg.id).startsWith("welcome") && !msg.isMeta).map((msg) => ({
|
|
1562
|
+
role: msg.role === "user" || msg.role === "system" ? "user" : "model",
|
|
1563
|
+
parts: [{ text: msg.text }]
|
|
1564
|
+
}));
|
|
1565
|
+
let stream;
|
|
1566
|
+
let success = false;
|
|
1567
|
+
let retryCount = 0;
|
|
1568
|
+
while (retryCount <= MAX_RETRIES && !success) {
|
|
1483
1569
|
try {
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1570
|
+
if (!await checkQuota("agent", settings)) {
|
|
1571
|
+
throw new Error("Error: Daily Quota Exausted for Agent");
|
|
1572
|
+
}
|
|
1573
|
+
let targetModel = modelName;
|
|
1574
|
+
if (retryCount === 5) {
|
|
1575
|
+
targetModel = "gemini-3-flash-preview";
|
|
1576
|
+
yield { type: "model_update", content: "Trying with fallback model (v3)" };
|
|
1577
|
+
} else if (retryCount >= 6) {
|
|
1578
|
+
targetModel = "gemini-3.1-flash-lite-preview";
|
|
1579
|
+
yield { type: "model_update", content: "Trying with fallback model (v3.1)" };
|
|
1580
|
+
} else if (retryCount > 0) {
|
|
1581
|
+
yield { type: "model_update", content: null };
|
|
1582
|
+
}
|
|
1583
|
+
stream = await client.models.generateContentStream({
|
|
1584
|
+
model: targetModel,
|
|
1585
|
+
contents,
|
|
1586
|
+
config: {
|
|
1587
|
+
temperature: mode === "Flux" ? 1 : 1.4,
|
|
1588
|
+
thinkingConfig: {
|
|
1589
|
+
includeThoughts: false,
|
|
1590
|
+
thinkingLevel: ThinkingLevel.MINIMAL
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
});
|
|
1594
|
+
success = true;
|
|
1595
|
+
yield { type: "model_update", content: null };
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
const errMsg = err.status || err.error && err.error.message || String(err);
|
|
1598
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1599
|
+
const agentErrDir = path13.join(LOGS_DIR, "agent");
|
|
1600
|
+
if (!fs12.existsSync(agentErrDir)) fs12.mkdirSync(agentErrDir, { recursive: true });
|
|
1601
|
+
fs12.appendFileSync(path13.join(agentErrDir, "error.log"), `ERROR [${date}]: ${errMsg}
|
|
1602
|
+
`);
|
|
1603
|
+
if (retryCount < MAX_RETRIES) {
|
|
1604
|
+
retryCount++;
|
|
1605
|
+
const waitTime = Math.floor(Math.random() * (2e3 - 800 + 1)) + 800;
|
|
1606
|
+
yield { type: "status", content: `Retrying (${retryCount}/${MAX_RETRIES + 1})...` };
|
|
1607
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
1608
|
+
} else {
|
|
1609
|
+
throw new Error(`Model cannot be reached: ${errMsg}. (Failed ${MAX_RETRIES + 1} times)`);
|
|
1490
1610
|
}
|
|
1491
|
-
} catch (e) {
|
|
1492
1611
|
}
|
|
1493
|
-
label = `\u{1F4C4} READING FILE: ${targetPath2}. LINES ${start_line} - ${actualEndLine} FROM ${totalLines}`.toUpperCase();
|
|
1494
|
-
} else if (toolCall.toolName === "list_files" || toolCall.toolName === "read_folder") {
|
|
1495
|
-
const action = toolCall.toolName === "list_files" ? "LISTING" : "DISCOVERING";
|
|
1496
|
-
label = `\u{1F4C2} ${action} DIRECTORY: ${parseArgs(toolCall.args).path || "."}`.toUpperCase();
|
|
1497
|
-
} else if (toolCall.toolName === "write_file" || toolCall.toolName === "update_file") {
|
|
1498
|
-
const action = toolCall.toolName === "write_file" ? "WRITING" : "PATCHING";
|
|
1499
|
-
label = `\u{1F4BE} ${action} FILE: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
1500
|
-
} else if (toolCall.toolName === "exec_command" || toolCall.toolName === "ask") {
|
|
1501
|
-
label = "";
|
|
1502
|
-
} else {
|
|
1503
|
-
label = `EXECUTING ${toolCall.toolName}`.toUpperCase();
|
|
1504
1612
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1613
|
+
let turnText = "";
|
|
1614
|
+
let lastToolSniffed = null;
|
|
1615
|
+
for await (const chunk of stream) {
|
|
1616
|
+
if (TERMINATION_SIGNAL) break;
|
|
1617
|
+
if (chunk.text) {
|
|
1618
|
+
turnText += chunk.text;
|
|
1619
|
+
yield { type: "text", content: chunk.text };
|
|
1620
|
+
if (turnText.includes("tool:functions.")) {
|
|
1621
|
+
const parts = turnText.split("tool:functions.");
|
|
1622
|
+
const potentialTool = parts[parts.length - 1].split("(")[0].trim();
|
|
1623
|
+
if (potentialTool && /^[a-z_]+$/.test(potentialTool) && potentialTool !== lastToolSniffed) {
|
|
1624
|
+
lastToolSniffed = potentialTool;
|
|
1625
|
+
yield { type: "status", content: `Working (${potentialTool})...` };
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
lastUsage = chunk.usageMetadata;
|
|
1630
|
+
if (lastUsage) {
|
|
1631
|
+
yield { type: "usage", content: lastUsage };
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
await incrementUsage("agent");
|
|
1635
|
+
fullAgentResponseChunks.push(turnText);
|
|
1636
|
+
const textToProcess = turnText.replace(/<think>[\s\S]*?<\/think>/g, "");
|
|
1637
|
+
const turnTextLower = textToProcess.toLowerCase();
|
|
1638
|
+
const hasFinish = /\[\s*(turn\s*:)?\s*finish\s*\]/i.test(turnTextLower);
|
|
1639
|
+
const toolCalls = detectToolCalls(textToProcess);
|
|
1640
|
+
let toolResults = [];
|
|
1641
|
+
const shouldContinue = toolCalls.length > 0;
|
|
1642
|
+
if (toolCalls.length > 0) {
|
|
1643
|
+
for (const toolCall of toolCalls) {
|
|
1644
|
+
yield { type: "turn_reset", content: true };
|
|
1645
|
+
yield { type: "status", content: `Working (${toolCall.toolName})...` };
|
|
1646
|
+
let label = "";
|
|
1647
|
+
if (toolCall.toolName === "web_search") {
|
|
1648
|
+
const { query, limit = 10 } = parseArgs(toolCall.args);
|
|
1649
|
+
label = `\u{1F50D} SEARCHING: "${query}" (${limit})`.toUpperCase();
|
|
1650
|
+
} else if (toolCall.toolName === "web_scrape") {
|
|
1651
|
+
const url = parseArgs(toolCall.args).url || "...";
|
|
1652
|
+
label = `\u{1F4D6} READING SITE: ${url}`.toUpperCase();
|
|
1653
|
+
} else if (toolCall.toolName === "view_file") {
|
|
1654
|
+
const { path: targetPath2, start_line = 1, end_line = 500 } = parseArgs(toolCall.args);
|
|
1655
|
+
let totalLines = "...";
|
|
1656
|
+
let actualEndLine = end_line;
|
|
1657
|
+
try {
|
|
1658
|
+
const absPath = path13.resolve(process.cwd(), targetPath2);
|
|
1659
|
+
if (fs12.existsSync(absPath)) {
|
|
1660
|
+
const content = fs12.readFileSync(absPath, "utf8");
|
|
1661
|
+
const lines = content.split("\n").length;
|
|
1662
|
+
totalLines = lines;
|
|
1663
|
+
actualEndLine = Math.min(end_line, lines);
|
|
1664
|
+
}
|
|
1665
|
+
} catch (e) {
|
|
1666
|
+
}
|
|
1667
|
+
label = `\u{1F4C4} READING FILE: ${targetPath2}. LINES ${start_line} - ${actualEndLine} FROM ${totalLines}`.toUpperCase();
|
|
1668
|
+
} else if (toolCall.toolName === "list_files" || toolCall.toolName === "read_folder") {
|
|
1669
|
+
const action = toolCall.toolName === "list_files" ? "LISTING" : "DISCOVERING";
|
|
1670
|
+
label = `\u{1F4C2} ${action} DIRECTORY: ${parseArgs(toolCall.args).path || "."}`.toUpperCase();
|
|
1671
|
+
} else if (toolCall.toolName === "write_file" || toolCall.toolName === "update_file") {
|
|
1672
|
+
const action = toolCall.toolName === "write_file" ? "WRITING" : "PATCHING";
|
|
1673
|
+
label = `\u{1F4BE} ${action} FILE: ${parseArgs(toolCall.args).path || "..."}`.toUpperCase();
|
|
1674
|
+
} else if (toolCall.toolName === "exec_command" || toolCall.toolName === "ask") {
|
|
1675
|
+
label = "";
|
|
1676
|
+
} else {
|
|
1677
|
+
label = `EXECUTING ${toolCall.toolName}`.toUpperCase();
|
|
1678
|
+
}
|
|
1679
|
+
if (label) {
|
|
1680
|
+
const boxWidth = Math.min(label.length + 4, 85);
|
|
1681
|
+
const boxTop = `\u256D${"\u2500".repeat(boxWidth)}\u256E`;
|
|
1682
|
+
const boxMid = `\u2502 ${label.padEnd(boxWidth - 2).substring(0, boxWidth - 2)} \u2502`;
|
|
1683
|
+
const boxBottom = `\u2570${"\u2500".repeat(boxWidth)}\u256F`;
|
|
1684
|
+
yield { type: "text", content: `
|
|
1511
1685
|
|
|
1512
1686
|
${boxTop}
|
|
1513
1687
|
${boxMid}
|
|
1514
1688
|
${boxBottom}
|
|
1515
1689
|
` };
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1690
|
+
}
|
|
1691
|
+
if (toolCall.toolName === "exec_command") {
|
|
1692
|
+
const { command } = parseArgs(toolCall.args);
|
|
1693
|
+
if (command && settings.systemSettings && settings.systemSettings.allowExternalAccess === false) {
|
|
1694
|
+
const riskyPatterns = [
|
|
1695
|
+
/[a-zA-Z]:[\\\/]/i,
|
|
1696
|
+
// Any drive letter path (C:\, D:/, etc)
|
|
1697
|
+
/^\//,
|
|
1698
|
+
// Root path on Unix
|
|
1699
|
+
/\.\.[\\\/]/,
|
|
1700
|
+
// Parent directory traversal
|
|
1701
|
+
/\/etc\//,
|
|
1702
|
+
/\/var\//,
|
|
1703
|
+
/\/root\//,
|
|
1704
|
+
/\/bin\//,
|
|
1705
|
+
/\/usr\//
|
|
1706
|
+
// Sensitive Linux paths
|
|
1707
|
+
];
|
|
1708
|
+
const currentDrive = path13.resolve(process.cwd()).substring(0, 3).toLowerCase();
|
|
1709
|
+
const isViolating = riskyPatterns.some((pattern) => {
|
|
1710
|
+
if (pattern.source === "[a-zA-Z]:[\\\\\\/]") {
|
|
1711
|
+
const driveMatch = command.match(/[a-zA-Z]:[\\\/]/i);
|
|
1712
|
+
return driveMatch && driveMatch[0].toLowerCase() !== currentDrive;
|
|
1713
|
+
}
|
|
1714
|
+
return pattern.test(command);
|
|
1715
|
+
});
|
|
1716
|
+
if (isViolating) {
|
|
1717
|
+
const denyMsg = `Access Denied. Terminal is prohibited from accessing system drives (C://) or external directories while "External Workspace Access" is disabled.`;
|
|
1718
|
+
toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
|
|
1719
|
+
yield { type: "tool_result", content: `[TOOL_RESULT]: ERROR: ${denyMsg}` };
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
if (settings.onExecStart) settings.onExecStart(command || "Unknown");
|
|
1724
|
+
yield { type: "exec_start" };
|
|
1725
|
+
}
|
|
1726
|
+
const parsedArgs = parseArgs(toolCall.args);
|
|
1727
|
+
const targetPath = parsedArgs.path || parsedArgs.targetPath || null;
|
|
1728
|
+
if (targetPath) {
|
|
1729
|
+
const isExternalOff = settings.systemSettings && settings.systemSettings.allowExternalAccess === false;
|
|
1730
|
+
const absoluteTarget = path13.resolve(targetPath);
|
|
1731
|
+
const absoluteCwd = path13.resolve(process.cwd());
|
|
1732
|
+
if (isExternalOff && !absoluteTarget.startsWith(absoluteCwd)) {
|
|
1733
|
+
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.`;
|
|
1734
|
+
toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
|
|
1735
|
+
yield { type: "tool_result", content: `[TOOL_RESULT]: ERROR: ${denyMsg}` };
|
|
1736
|
+
continue;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
if (settings.onToolApproval) {
|
|
1740
|
+
let shouldPrompt = false;
|
|
1741
|
+
if (toolCall.toolName === "write_file" || toolCall.toolName === "update_file") {
|
|
1742
|
+
shouldPrompt = true;
|
|
1743
|
+
} else if (toolCall.toolName === "exec_command") {
|
|
1744
|
+
shouldPrompt = true;
|
|
1745
|
+
}
|
|
1746
|
+
if (shouldPrompt) {
|
|
1747
|
+
const approval = await settings.onToolApproval(toolCall.toolName, toolCall.args);
|
|
1748
|
+
if (approval === "deny") {
|
|
1749
|
+
if (toolCall.toolName === "exec_command" && settings.onExecEnd) settings.onExecEnd();
|
|
1750
|
+
const denyMsg = `Permission Denied: User rejected the ${toolCall.toolName === "exec_command" ? "terminal execution" : "file edit"}.`;
|
|
1751
|
+
toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
|
|
1752
|
+
yield { type: "tool_result", content: `[TOOL_RESULT]: ERROR: ${denyMsg}` };
|
|
1753
|
+
continue;
|
|
1754
|
+
}
|
|
1539
1755
|
}
|
|
1540
|
-
|
|
1756
|
+
}
|
|
1757
|
+
const result = await dispatchTool(toolCall.toolName, toolCall.args, {
|
|
1758
|
+
chatId,
|
|
1759
|
+
history,
|
|
1760
|
+
onChunk: (chunk) => settings.onExecChunk ? settings.onExecChunk(chunk) : null,
|
|
1761
|
+
onAskUser: settings.onAskUser
|
|
1541
1762
|
});
|
|
1542
|
-
if (
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
yield { type: "tool_result", content: `[TOOL_RESULT]: ERROR: ${denyMsg}` };
|
|
1546
|
-
continue;
|
|
1763
|
+
if (toolCall.toolName === "exec_command" && settings.onExecEnd) {
|
|
1764
|
+
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
1765
|
+
settings.onExecEnd();
|
|
1547
1766
|
}
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
} else if (toolCall.toolName === "exec_command") {
|
|
1570
|
-
shouldPrompt = true;
|
|
1571
|
-
}
|
|
1572
|
-
if (shouldPrompt) {
|
|
1573
|
-
const approval = await settings.onToolApproval(toolCall.toolName, toolCall.args);
|
|
1574
|
-
if (approval === "deny") {
|
|
1575
|
-
if (toolCall.toolName === "exec_command" && settings.onExecEnd) settings.onExecEnd();
|
|
1576
|
-
const denyMsg = `Permission Denied: User rejected the ${toolCall.toolName === "exec_command" ? "terminal execution" : "file edit"}.`;
|
|
1577
|
-
toolResults.push(`[TOOL_RESULT]: ERROR: ${denyMsg}`);
|
|
1578
|
-
yield { type: "tool_result", content: `[TOOL_RESULT]: ERROR: ${denyMsg}` };
|
|
1579
|
-
continue;
|
|
1767
|
+
try {
|
|
1768
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1769
|
+
const isErr = result.startsWith("ERROR:");
|
|
1770
|
+
const logStatus = isErr ? result.trim() : "SUCCESS";
|
|
1771
|
+
const toolHistDir = path13.join(LOGS_DIR, "tools");
|
|
1772
|
+
if (!fs12.existsSync(toolHistDir)) {
|
|
1773
|
+
fs12.mkdirSync(toolHistDir, { recursive: true });
|
|
1774
|
+
}
|
|
1775
|
+
fs12.appendFileSync(path13.join(toolHistDir, "history.log"), `HISTORY [${timestamp}]: ${toolCall.toolName} [${logStatus}]
|
|
1776
|
+
`);
|
|
1777
|
+
} catch (logErr) {
|
|
1778
|
+
}
|
|
1779
|
+
const cleanResultForAI = result.split(/\r?\n/).filter((line) => !line.includes("[UI_CONTEXT]")).join("\n");
|
|
1780
|
+
toolResults.push(`[TOOL_RESULT]: ${cleanResultForAI}`);
|
|
1781
|
+
let uiContent = `[TOOL_RESULT]: ${result}`;
|
|
1782
|
+
if (toolCall.toolName === "view_file" || toolCall.toolName === "web_scrape") {
|
|
1783
|
+
uiContent = `[TOOL_RESULT]: ${label} (Context Locked for UI Clarity)`;
|
|
1784
|
+
}
|
|
1785
|
+
yield { type: "tool_result", content: uiContent };
|
|
1786
|
+
if (toolCall.toolName === "memory" && result.includes("SUCCESS")) {
|
|
1787
|
+
yield { type: "memory_updated" };
|
|
1580
1788
|
}
|
|
1581
1789
|
}
|
|
1790
|
+
yield { type: "status", content: "Working..." };
|
|
1582
1791
|
}
|
|
1583
|
-
const
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
toolResults.push(`[TOOL_RESULT]: ${cleanResultForAI}`);
|
|
1607
|
-
let uiContent = `[TOOL_RESULT]: ${result}`;
|
|
1608
|
-
if (toolCall.toolName === "view_file" || toolCall.toolName === "web_scrape") {
|
|
1609
|
-
uiContent = `[TOOL_RESULT]: ${label} (Context Locked for UI Clarity)`;
|
|
1610
|
-
}
|
|
1611
|
-
yield { type: "tool_result", content: uiContent };
|
|
1612
|
-
if (toolCall.toolName === "memory" && result.includes("SUCCESS")) {
|
|
1613
|
-
yield { type: "memory_updated" };
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
yield { type: "status", content: "Working..." };
|
|
1617
|
-
}
|
|
1618
|
-
const cleanedTurnText = turnText.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/\[?\s*(turn\s*:)?\s*(continue|finish)\s*\]?/gi, "").trim();
|
|
1619
|
-
const isActuallyFinished = hasFinish;
|
|
1620
|
-
if (isActuallyFinished) {
|
|
1621
|
-
const lateHint = await steeringCallback();
|
|
1622
|
-
if (lateHint) {
|
|
1623
|
-
toolResults.push(`[USER STEERING HINT]: ${lateHint}`);
|
|
1624
|
-
yield { type: "status", content: "Steering detected... resuming!" };
|
|
1625
|
-
continue;
|
|
1626
|
-
}
|
|
1627
|
-
yield { type: "status", content: "Finalizing..." };
|
|
1628
|
-
const janitorContents = history.slice(-3).filter((msg) => msg.text && !msg.text.includes("[TOOL_RESULT]") && !msg.text.includes("OBSERVATION:")).map((msg) => ({
|
|
1629
|
-
role: msg.role === "user" ? "user" : "model",
|
|
1630
|
-
parts: [{ text: msg.text.replace(/<think>[\s\S]*?<\/think>/g, "").trim() }]
|
|
1631
|
-
}));
|
|
1632
|
-
const cleanedFullResponse = fullAgentResponse.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1633
|
-
const janitorPrompt = getJanitorInstruction(
|
|
1634
|
-
agentText,
|
|
1635
|
-
cleanedFullResponse,
|
|
1636
|
-
janitorUserMemories,
|
|
1637
|
-
isMemoryEnabled,
|
|
1638
|
-
needTitle
|
|
1639
|
-
);
|
|
1640
|
-
janitorContents.push({ role: "user", parts: [{ text: janitorPrompt }] });
|
|
1641
|
-
let finalSynthesis = "";
|
|
1642
|
-
try {
|
|
1643
|
-
if (!await checkQuota("background", settings)) {
|
|
1644
|
-
console.warn("Quota Exhausted for Background Model. Skipping refinement.");
|
|
1645
|
-
throw new Error("QUOTA_BLOCKED");
|
|
1646
|
-
}
|
|
1647
|
-
const janitorResult = await client.models.generateContent({
|
|
1648
|
-
model: janitorModel || "gemma-4-26b-a4b-it",
|
|
1649
|
-
contents: janitorContents,
|
|
1650
|
-
config: {
|
|
1651
|
-
thinkingConfig: {
|
|
1652
|
-
includeThoughts: false,
|
|
1653
|
-
thinkingLevel: ThinkingLevel.MINIMAL
|
|
1792
|
+
const cleanedTurnText = turnText.replace(/<think>[\s\S]*?<\/think>/g, "").replace(/\[\s*(turn\s*:)?\s*(continue|finish)\s*\]/gi, "").trim();
|
|
1793
|
+
let isActuallyFinished = hasFinish && !shouldContinue;
|
|
1794
|
+
if (isActuallyFinished) {
|
|
1795
|
+
yield { type: "status", content: "Finalizing..." };
|
|
1796
|
+
const janitorContents = history.slice(-3).filter((msg) => msg.text && !msg.text.includes("[TOOL_RESULT]") && !msg.text.includes("OBSERVATION:")).map((msg) => ({
|
|
1797
|
+
role: msg.role === "user" ? "user" : "model",
|
|
1798
|
+
parts: [{ text: msg.text.replace(/<think>[\s\S]*?<\/think>/g, "").trim() }]
|
|
1799
|
+
}));
|
|
1800
|
+
const fullAgentTextRaw = fullAgentResponseChunks.join("\n");
|
|
1801
|
+
const cleanedFullResponse = fullAgentTextRaw.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1802
|
+
const janitorPrompt = getJanitorInstruction(
|
|
1803
|
+
agentText,
|
|
1804
|
+
cleanedFullResponse,
|
|
1805
|
+
janitorUserMemories,
|
|
1806
|
+
isMemoryEnabled,
|
|
1807
|
+
needTitle
|
|
1808
|
+
);
|
|
1809
|
+
janitorContents.push({ role: "user", parts: [{ text: janitorPrompt }] });
|
|
1810
|
+
let finalSynthesis = "";
|
|
1811
|
+
try {
|
|
1812
|
+
if (!await checkQuota("background", settings)) {
|
|
1813
|
+
console.warn("Quota Exhausted for Background Model. Skipping refinement.");
|
|
1814
|
+
throw new Error("QUOTA_BLOCKED");
|
|
1654
1815
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1816
|
+
const janitorResult = await client.models.generateContent({
|
|
1817
|
+
model: janitorModel || "gemma-4-26b-a4b-it",
|
|
1818
|
+
contents: janitorContents,
|
|
1819
|
+
config: {
|
|
1820
|
+
thinkingConfig: {
|
|
1821
|
+
includeThoughts: false,
|
|
1822
|
+
thinkingLevel: ThinkingLevel.MINIMAL
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
});
|
|
1826
|
+
const parts = janitorResult.candidates?.[0]?.content?.parts;
|
|
1827
|
+
if (parts && parts[1]?.text) {
|
|
1828
|
+
finalSynthesis = parts[1].text;
|
|
1829
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1830
|
+
const janitorLogDir = path13.join(LOGS_DIR, "janitor");
|
|
1831
|
+
if (!fs12.existsSync(janitorLogDir)) {
|
|
1832
|
+
fs12.mkdirSync(janitorLogDir, { recursive: true });
|
|
1833
|
+
}
|
|
1834
|
+
fs12.appendFileSync(path13.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: ${finalSynthesis}
|
|
1666
1835
|
`);
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1836
|
+
} else if (parts && parts[0]?.text) finalSynthesis = parts[0].text;
|
|
1837
|
+
else if (janitorResult.response && janitorResult.response.text) finalSynthesis = janitorResult.response.text();
|
|
1838
|
+
else throw new Error("No synthesis generated by Janitor.");
|
|
1839
|
+
await incrementUsage("background");
|
|
1840
|
+
yield { type: "background_increment" };
|
|
1841
|
+
const janitorToolCalls = detectToolCalls(finalSynthesis);
|
|
1842
|
+
for (const janitorToolCall of janitorToolCalls) {
|
|
1843
|
+
const toolContext = { chatId, sessionId: chatId, history };
|
|
1844
|
+
const result = await dispatchTool(janitorToolCall.toolName, janitorToolCall.args, toolContext);
|
|
1845
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1846
|
+
const janitorLogDir = path13.join(LOGS_DIR, "janitor");
|
|
1847
|
+
fs12.appendFileSync(path13.join(janitorLogDir, "debug.log"), `DEBUG [${date}]: RESULT [${janitorToolCall.toolName}]: ${result}
|
|
1679
1848
|
`);
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1849
|
+
if (janitorToolCall.toolName === "memory" && !janitorToolCall.args.includes("action='temp'")) {
|
|
1850
|
+
yield { type: "memory_updated" };
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
} catch (janitorErr) {
|
|
1854
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1855
|
+
const janitorErrDir = path13.join(LOGS_DIR, "janitor");
|
|
1856
|
+
if (!fs12.existsSync(janitorErrDir)) {
|
|
1857
|
+
fs12.mkdirSync(janitorErrDir, { recursive: true });
|
|
1858
|
+
}
|
|
1859
|
+
fs12.appendFileSync(path13.join(janitorErrDir, "error.log"), `ERROR [${date}]: ${janitorErr.message}
|
|
1691
1860
|
`);
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1861
|
+
console.error("Janitor Background Tasks Failed:", janitorErr.message);
|
|
1862
|
+
}
|
|
1863
|
+
const timestamp = `Responded on ${(/* @__PURE__ */ new Date()).toLocaleString()}`;
|
|
1864
|
+
const finalWithTime = `${cleanedFullResponse}
|
|
1696
1865
|
|
|
1697
1866
|
${timestamp}`;
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1867
|
+
if (modifiedHistory.length > 0 && modifiedHistory[modifiedHistory.length - 1].role === "agent") {
|
|
1868
|
+
modifiedHistory[modifiedHistory.length - 1].text = finalWithTime;
|
|
1869
|
+
} else {
|
|
1870
|
+
modifiedHistory.push({ role: "agent", text: finalWithTime });
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
if (isActuallyFinished) break;
|
|
1874
|
+
const nextAgentMsg = cleanedTurnText.trim() || "*Working...*";
|
|
1875
|
+
modifiedHistory.push({ role: "agent", text: nextAgentMsg });
|
|
1876
|
+
const nextUserMsg = toolResults.length > 0 ? toolResults.join("\n") : "[turn: continue]";
|
|
1877
|
+
modifiedHistory.push({ role: "user", text: nextUserMsg });
|
|
1702
1878
|
}
|
|
1703
|
-
|
|
1704
|
-
}
|
|
1705
|
-
if (isActuallyFinished) break;
|
|
1706
|
-
const nextAgentMsg = cleanedTurnText.trim() || "*Working...*";
|
|
1707
|
-
modifiedHistory.push({ role: "agent", text: nextAgentMsg });
|
|
1708
|
-
const nextUserMsg = toolResults.length > 0 ? toolResults.join("\n") : "[turn: continue]";
|
|
1709
|
-
modifiedHistory.push({ role: "user", text: nextUserMsg });
|
|
1879
|
+
yield { type: "status", content: null };
|
|
1880
|
+
};
|
|
1710
1881
|
}
|
|
1711
|
-
|
|
1712
|
-
};
|
|
1882
|
+
});
|
|
1713
1883
|
|
|
1714
1884
|
// src/utils/settings.js
|
|
1715
1885
|
import fs13 from "fs-extra";
|
|
1716
1886
|
import path14 from "path";
|
|
1717
|
-
var DEFAULT_SETTINGS
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1887
|
+
var DEFAULT_SETTINGS, loadSettings, saveSettings;
|
|
1888
|
+
var init_settings = __esm({
|
|
1889
|
+
"src/utils/settings.js"() {
|
|
1890
|
+
init_paths();
|
|
1891
|
+
DEFAULT_SETTINGS = {
|
|
1892
|
+
mode: "Flux",
|
|
1893
|
+
thinkingLevel: "Medium",
|
|
1894
|
+
activeModel: "gemma-4-31b-it",
|
|
1895
|
+
showFullThinking: false,
|
|
1896
|
+
apiTier: "Free",
|
|
1897
|
+
quotas: {
|
|
1898
|
+
agentLimit: 1500,
|
|
1899
|
+
backgroundLimit: 1500,
|
|
1900
|
+
searchLimit: 100,
|
|
1901
|
+
customModelId: "",
|
|
1902
|
+
customLimit: 0
|
|
1903
|
+
},
|
|
1904
|
+
systemSettings: {
|
|
1905
|
+
memory: true,
|
|
1906
|
+
compression: 0,
|
|
1907
|
+
autoExec: false,
|
|
1908
|
+
allowExternalAccess: false,
|
|
1909
|
+
autoDeleteHistory: "7d"
|
|
1910
|
+
},
|
|
1911
|
+
profileData: {
|
|
1912
|
+
name: null,
|
|
1913
|
+
nickname: null,
|
|
1914
|
+
instructions: null
|
|
1915
|
+
}
|
|
1916
|
+
};
|
|
1917
|
+
loadSettings = async () => {
|
|
1918
|
+
try {
|
|
1919
|
+
if (await fs13.exists(SETTINGS_FILE)) {
|
|
1920
|
+
const saved = await fs13.readJson(SETTINGS_FILE);
|
|
1921
|
+
return {
|
|
1922
|
+
...DEFAULT_SETTINGS,
|
|
1923
|
+
...saved,
|
|
1924
|
+
quotas: { ...DEFAULT_SETTINGS.quotas, ...saved.quotas },
|
|
1925
|
+
systemSettings: { ...DEFAULT_SETTINGS.systemSettings, ...saved.systemSettings },
|
|
1926
|
+
profileData: { ...DEFAULT_SETTINGS.profileData, ...saved.profileData }
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
} catch (err) {
|
|
1930
|
+
console.error("Failed to load settings:", err);
|
|
1931
|
+
}
|
|
1932
|
+
return DEFAULT_SETTINGS;
|
|
1933
|
+
};
|
|
1934
|
+
saveSettings = async (settings) => {
|
|
1935
|
+
try {
|
|
1936
|
+
const current = await loadSettings();
|
|
1937
|
+
const updated = { ...current, ...settings };
|
|
1938
|
+
await fs13.ensureDir(path14.dirname(SETTINGS_FILE));
|
|
1939
|
+
await fs13.writeJson(SETTINGS_FILE, updated, { spaces: 2 });
|
|
1940
|
+
return true;
|
|
1941
|
+
} catch (err) {
|
|
1942
|
+
console.error("Failed to save settings:", err);
|
|
1943
|
+
return false;
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1770
1946
|
}
|
|
1771
|
-
};
|
|
1947
|
+
});
|
|
1772
1948
|
|
|
1773
1949
|
// src/components/ResumeModal.jsx
|
|
1774
1950
|
import React7, { useState as useState3, useEffect } from "react";
|
|
@@ -1807,6 +1983,11 @@ function ResumeModal({ onSelect, onDelete, onClose }) {
|
|
|
1807
1983
|
return /* @__PURE__ */ React7.createElement(Box7, { key: id, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: isSelected ? "cyan" : "white" }, isSelected ? "\u276F " : " ", /* @__PURE__ */ React7.createElement(Text7, { bold: isSelected }, chat2.name || id), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " [", id.slice(5), "]")), isSelected && /* @__PURE__ */ React7.createElement(Box7, { marginLeft: "auto" }, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, " (x to delete) ")));
|
|
1808
1984
|
}), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, justifyContent: "center", borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " \u2191\u2193 navigate \u2022 Enter select \u2022 x delete \u2022 Esc close ")));
|
|
1809
1985
|
}
|
|
1986
|
+
var init_ResumeModal = __esm({
|
|
1987
|
+
"src/components/ResumeModal.jsx"() {
|
|
1988
|
+
init_history();
|
|
1989
|
+
}
|
|
1990
|
+
});
|
|
1810
1991
|
|
|
1811
1992
|
// src/components/MemoryModal.jsx
|
|
1812
1993
|
import React8, { useState as useState4, useEffect as useEffect2 } from "react";
|
|
@@ -1840,51 +2021,47 @@ function MemoryModal({ onClose }) {
|
|
|
1840
2021
|
};
|
|
1841
2022
|
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "double", borderColor: "yellow", padding: 1, width: 80 }, /* @__PURE__ */ React8.createElement(Box8, { justifyContent: "center", marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text8, { bold: true, color: "yellow" }, "\u{1F9E0} LONG-TERM MEMORY VAULT")), memories.length === 0 ? /* @__PURE__ */ React8.createElement(Box8, { justifyContent: "center", paddingY: 2 }, /* @__PURE__ */ React8.createElement(Text8, { italic: true, color: "gray" }, "The vault is currently empty...")) : memories.map((mem, idx) => /* @__PURE__ */ React8.createElement(Box8, { key: mem.id, paddingX: 1, backgroundColor: idx === selectedIndex ? "#333" : void 0 }, /* @__PURE__ */ React8.createElement(Text8, { color: idx === selectedIndex ? "yellow" : "white" }, idx === selectedIndex ? "\u276F " : " ", idx + 1, ". ", cleanDisplay(mem.memory)))), /* @__PURE__ */ React8.createElement(Box8, { marginTop: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "\u2191/\u2193 Navigate \u2022 ", /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, "x"), " Delete Memory \u2022 ", /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, "Esc"), " Back to Chat")));
|
|
1842
2023
|
}
|
|
2024
|
+
var init_MemoryModal = __esm({
|
|
2025
|
+
"src/components/MemoryModal.jsx"() {
|
|
2026
|
+
init_crypto();
|
|
2027
|
+
init_paths();
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
1843
2030
|
|
|
1844
2031
|
// src/utils/terminal.js
|
|
1845
|
-
var getTerminalEnv
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2032
|
+
var getTerminalEnv, emojiSpace;
|
|
2033
|
+
var init_terminal = __esm({
|
|
2034
|
+
"src/utils/terminal.js"() {
|
|
2035
|
+
getTerminalEnv = () => {
|
|
2036
|
+
if (process.env.TERM_PROGRAM === "vscode") return "vscode";
|
|
2037
|
+
if (process.env.WT_SESSION) return "wt";
|
|
2038
|
+
return "default";
|
|
2039
|
+
};
|
|
2040
|
+
emojiSpace = (baseSpaces = 2) => {
|
|
2041
|
+
const env = getTerminalEnv();
|
|
2042
|
+
if (env === "wt") {
|
|
2043
|
+
return " ".repeat(Math.max(1, baseSpaces - 1));
|
|
2044
|
+
}
|
|
2045
|
+
if (env === "vscode") {
|
|
2046
|
+
return " ".repeat(baseSpaces);
|
|
2047
|
+
}
|
|
2048
|
+
return " ".repeat(baseSpaces);
|
|
2049
|
+
};
|
|
1857
2050
|
}
|
|
1858
|
-
|
|
1859
|
-
};
|
|
2051
|
+
});
|
|
1860
2052
|
|
|
1861
2053
|
// src/app.jsx
|
|
1862
|
-
var
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
],
|
|
1874
|
-
onSelect: (val) => {
|
|
1875
|
-
if (val === "send") onResolve(data);
|
|
1876
|
-
else onEdit(data);
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
)));
|
|
1880
|
-
var FLUX_LOGO = gradient(["#00ffff", "#0077ff", "#ff00ff"]).multiline(
|
|
1881
|
-
`\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
|
|
1882
|
-
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
1883
|
-
\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2557 \u2588\u2588\u2551
|
|
1884
|
-
\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2557\u2588\u2588\u2551
|
|
1885
|
-
\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u255D \u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2554\u2588\u2588\u2588\u2554\u255D
|
|
1886
|
-
\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D`
|
|
1887
|
-
);
|
|
2054
|
+
var app_exports = {};
|
|
2055
|
+
__export(app_exports, {
|
|
2056
|
+
default: () => App
|
|
2057
|
+
});
|
|
2058
|
+
import React9, { useState as useState5, useEffect as useEffect3, useRef, useMemo } from "react";
|
|
2059
|
+
import { Box as Box9, Text as Text9, useInput as useInput4, useStdout } from "ink";
|
|
2060
|
+
import fs14 from "fs-extra";
|
|
2061
|
+
import { exec } from "child_process";
|
|
2062
|
+
import { MultilineInput } from "ink-multiline-input";
|
|
2063
|
+
import TextInput3 from "ink-text-input";
|
|
2064
|
+
import gradient from "gradient-string";
|
|
1888
2065
|
function App() {
|
|
1889
2066
|
const { stdout } = useStdout();
|
|
1890
2067
|
const [input, setInput] = useState5("");
|
|
@@ -2428,23 +2605,26 @@ Selection: ${val}`,
|
|
|
2428
2605
|
});
|
|
2429
2606
|
}
|
|
2430
2607
|
},
|
|
2431
|
-
async (
|
|
2432
|
-
if (
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2608
|
+
async () => {
|
|
2609
|
+
if (queuedPromptRef.current) {
|
|
2610
|
+
const p = queuedPromptRef.current;
|
|
2611
|
+
setQueuedPrompt(null);
|
|
2612
|
+
queuedPromptRef.current = null;
|
|
2613
|
+
setMessages((prev) => {
|
|
2614
|
+
const index = [...prev].reverse().findIndex((m) => m.text?.includes("[STEERING HINT: QUEUED]"));
|
|
2615
|
+
if (index !== -1) {
|
|
2616
|
+
const actualIndex = prev.length - 1 - index;
|
|
2438
2617
|
const newMsgs = [...prev];
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
}
|
|
2444
|
-
return newMsgs
|
|
2445
|
-
}
|
|
2446
|
-
return
|
|
2447
|
-
}
|
|
2618
|
+
newMsgs[actualIndex] = {
|
|
2619
|
+
...newMsgs[actualIndex],
|
|
2620
|
+
text: newMsgs[actualIndex].text.replace("[STEERING HINT: QUEUED]", "[STEERING HINT: INJECTED]"),
|
|
2621
|
+
color: "cyan"
|
|
2622
|
+
};
|
|
2623
|
+
return newMsgs;
|
|
2624
|
+
}
|
|
2625
|
+
return prev;
|
|
2626
|
+
});
|
|
2627
|
+
return p;
|
|
2448
2628
|
}
|
|
2449
2629
|
return null;
|
|
2450
2630
|
}
|
|
@@ -2497,41 +2677,50 @@ Selection: ${val}`,
|
|
|
2497
2677
|
continue;
|
|
2498
2678
|
}
|
|
2499
2679
|
let chunkText = packet.content;
|
|
2500
|
-
if (chunkText.includes("<think
|
|
2680
|
+
if (chunkText.toLowerCase().includes("<think") && !inThinkMode) {
|
|
2501
2681
|
inThinkMode = true;
|
|
2502
|
-
chunkText = chunkText.replace(
|
|
2682
|
+
chunkText = chunkText.replace(/<think>/gi, "");
|
|
2503
2683
|
currentThinkId = "think-" + Date.now();
|
|
2504
2684
|
setMessages((prev) => [...prev, { id: currentThinkId, role: "think", text: "" }]);
|
|
2505
2685
|
}
|
|
2506
|
-
if (chunkText.includes("</think>")) {
|
|
2507
|
-
const
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
)
|
|
2513
|
-
|
|
2514
|
-
const cleanedAgentPart = (agentPart || "").replace(signalRegex, "");
|
|
2515
|
-
return [...newMsgs, { id: currentAgentId, role: "agent", text: cleanedAgentPart }];
|
|
2516
|
-
});
|
|
2686
|
+
if (chunkText.toLowerCase().includes("</think>")) {
|
|
2687
|
+
const parts = chunkText.split(/<\/think>/gi);
|
|
2688
|
+
const thinkPart = parts[0] || "";
|
|
2689
|
+
const agentPart = parts.slice(1).join("</think>") || "";
|
|
2690
|
+
setMessages((prev) => {
|
|
2691
|
+
const newMsgs = prev.map(
|
|
2692
|
+
(m) => m.id === currentThinkId ? { ...m, text: m.text + thinkPart } : m
|
|
2693
|
+
);
|
|
2517
2694
|
inThinkMode = false;
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
currentAgentId = "agent-" + Date.now();
|
|
2522
|
-
setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: cleanedContent }]);
|
|
2523
|
-
} else {
|
|
2524
|
-
setMessages((prev) => prev.map(
|
|
2525
|
-
(m) => m.id === currentAgentId ? { ...m, text: m.text + cleanedContent } : m
|
|
2526
|
-
));
|
|
2527
|
-
}
|
|
2528
|
-
}
|
|
2695
|
+
currentAgentId = "agent-" + Date.now();
|
|
2696
|
+
return [...newMsgs, { id: currentAgentId, role: "agent", text: agentPart.replace(/<\/?think>/gi, "") }];
|
|
2697
|
+
});
|
|
2529
2698
|
continue;
|
|
2530
2699
|
}
|
|
2531
2700
|
if (inThinkMode && currentThinkId) {
|
|
2532
|
-
setMessages((prev) =>
|
|
2533
|
-
|
|
2534
|
-
|
|
2701
|
+
setMessages((prev) => {
|
|
2702
|
+
let transitioning = false;
|
|
2703
|
+
let transitionContent = "";
|
|
2704
|
+
const newMsgs = prev.map((m) => {
|
|
2705
|
+
if (m.id === currentThinkId) {
|
|
2706
|
+
const newText = m.text + chunkText;
|
|
2707
|
+
if (newText.toLowerCase().includes("</think>")) {
|
|
2708
|
+
transitioning = true;
|
|
2709
|
+
const parts = newText.split(/<\/think>/gi);
|
|
2710
|
+
transitionContent = parts.slice(1).join("</think>") || "";
|
|
2711
|
+
return { ...m, text: parts[0] };
|
|
2712
|
+
}
|
|
2713
|
+
return { ...m, text: newText };
|
|
2714
|
+
}
|
|
2715
|
+
return m;
|
|
2716
|
+
});
|
|
2717
|
+
if (transitioning) {
|
|
2718
|
+
inThinkMode = false;
|
|
2719
|
+
currentAgentId = "agent-" + Date.now();
|
|
2720
|
+
return [...newMsgs, { id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?think>/gi, "") }];
|
|
2721
|
+
}
|
|
2722
|
+
return newMsgs;
|
|
2723
|
+
});
|
|
2535
2724
|
} else if (!inThinkMode) {
|
|
2536
2725
|
const cleanedText = chunkText.replace(/<\/?think>/gi, "").replace(signalRegex, "");
|
|
2537
2726
|
if (!currentAgentId) {
|
|
@@ -2963,7 +3152,9 @@ Selection: ${val}`,
|
|
|
2963
3152
|
onResolve: (val) => {
|
|
2964
3153
|
setResolutionData(null);
|
|
2965
3154
|
setActiveView("chat");
|
|
2966
|
-
|
|
3155
|
+
setTimeout(() => {
|
|
3156
|
+
handleSubmit(val);
|
|
3157
|
+
}, 50);
|
|
2967
3158
|
},
|
|
2968
3159
|
onEdit: (val) => {
|
|
2969
3160
|
setResolutionData(null);
|
|
@@ -3071,26 +3262,89 @@ Selection: ${val}`,
|
|
|
3071
3262
|
}
|
|
3072
3263
|
))));
|
|
3073
3264
|
}
|
|
3265
|
+
var SESSION_START_TIME, CHANGELOG_URL, versionFluxflow, updatedOn, ResolutionModal, FLUX_LOGO;
|
|
3266
|
+
var init_app = __esm({
|
|
3267
|
+
"src/app.jsx"() {
|
|
3268
|
+
init_ChatLayout();
|
|
3269
|
+
init_StatusBar();
|
|
3270
|
+
init_CommandMenu();
|
|
3271
|
+
init_ProfileForm();
|
|
3272
|
+
init_AskUserModal();
|
|
3273
|
+
init_secrets();
|
|
3274
|
+
init_ai();
|
|
3275
|
+
init_settings();
|
|
3276
|
+
init_history();
|
|
3277
|
+
init_ResumeModal();
|
|
3278
|
+
init_MemoryModal();
|
|
3279
|
+
init_usage();
|
|
3280
|
+
init_TerminalBox();
|
|
3281
|
+
init_arg_parser();
|
|
3282
|
+
init_paths();
|
|
3283
|
+
init_terminal();
|
|
3284
|
+
SESSION_START_TIME = Date.now();
|
|
3285
|
+
CHANGELOG_URL = "https://fluxflow-cli.onrender.com/changelog.html";
|
|
3286
|
+
versionFluxflow = "1.2.0";
|
|
3287
|
+
updatedOn = "2026-04-27";
|
|
3288
|
+
ResolutionModal = ({ data, onResolve, onEdit }) => /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 1, width: "100%" }, /* @__PURE__ */ React9.createElement(Text9, { color: "magenta", bold: true, underline: true }, "\u{1F7E3} STEERING HINT RESOLUTION"), /* @__PURE__ */ React9.createElement(Text9, { marginTop: 1 }, "The agent already finished the task (turn: finish) before your hint was consumed."), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1, backgroundColor: "#222", paddingX: 1, width: "100%" }, /* @__PURE__ */ React9.createElement(Text9, { italic: true, color: "gray" }, '"', data, '"')), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, "How would you like to proceed?")), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(
|
|
3289
|
+
CommandMenu,
|
|
3290
|
+
{
|
|
3291
|
+
title: "Select Action",
|
|
3292
|
+
items: [
|
|
3293
|
+
{ label: "Send Anyway", value: "send" },
|
|
3294
|
+
{ label: "Edit Prompt", value: "edit" }
|
|
3295
|
+
],
|
|
3296
|
+
onSelect: (val) => {
|
|
3297
|
+
if (val === "send") onResolve(data);
|
|
3298
|
+
else onEdit(data);
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
)));
|
|
3302
|
+
FLUX_LOGO = gradient(["#00ffff", "#0077ff", "#ff00ff"]).multiline(
|
|
3303
|
+
`\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
|
|
3304
|
+
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
3305
|
+
\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2557 \u2588\u2588\u2551
|
|
3306
|
+
\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2557\u2588\u2588\u2551
|
|
3307
|
+
\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u255D \u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2554\u2588\u2588\u2588\u2554\u255D
|
|
3308
|
+
\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D`
|
|
3309
|
+
);
|
|
3310
|
+
}
|
|
3311
|
+
});
|
|
3074
3312
|
|
|
3075
3313
|
// src/cli.jsx
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
const
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3314
|
+
import { spawn as spawn2 } from "child_process";
|
|
3315
|
+
import { fileURLToPath } from "url";
|
|
3316
|
+
var HEAP_LIMIT = 4096;
|
|
3317
|
+
var isBundled = fileURLToPath(import.meta.url).endsWith(".js");
|
|
3318
|
+
if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-size"))) {
|
|
3319
|
+
const cp = spawn2(process.execPath, [
|
|
3320
|
+
`--max-old-space-size=${HEAP_LIMIT}`,
|
|
3321
|
+
fileURLToPath(import.meta.url),
|
|
3322
|
+
...process.argv.slice(2)
|
|
3323
|
+
], { stdio: "inherit" });
|
|
3324
|
+
cp.on("exit", (code) => process.exit(code || 0));
|
|
3325
|
+
} else {
|
|
3326
|
+
const { default: React10 } = await import("react");
|
|
3327
|
+
const { render } = await import("ink");
|
|
3328
|
+
const { default: App2 } = await Promise.resolve().then(() => (init_app(), app_exports));
|
|
3329
|
+
process.env.NODE_NO_WARNINGS = "1";
|
|
3330
|
+
const silentPatterns = [
|
|
3331
|
+
"cuimp",
|
|
3332
|
+
"Found existing binary",
|
|
3333
|
+
"Binary verified",
|
|
3334
|
+
"curl.exe not found",
|
|
3335
|
+
"Falling back to .bat file",
|
|
3336
|
+
"DeprecationWarning"
|
|
3337
|
+
];
|
|
3338
|
+
const originalLog = console.log;
|
|
3339
|
+
const originalWarn = console.warn;
|
|
3340
|
+
const originalError = console.error;
|
|
3341
|
+
const isNoise = (args) => {
|
|
3342
|
+
const msg = args.map(String).join(" ");
|
|
3343
|
+
return silentPatterns.some((p) => msg.includes(p));
|
|
3344
|
+
};
|
|
3345
|
+
console.log = (...args) => !isNoise(args) && originalLog(...args);
|
|
3346
|
+
console.warn = (...args) => !isNoise(args) && originalWarn(...args);
|
|
3347
|
+
console.error = (...args) => !isNoise(args) && originalError(...args);
|
|
3348
|
+
process.stdout.write("\x1Bc");
|
|
3349
|
+
render(/* @__PURE__ */ React10.createElement(App2, null));
|
|
3350
|
+
}
|