fluxflow-cli 2.13.4 → 2.14.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.
Files changed (2) hide show
  1. package/dist/fluxflow.js +2144 -1372
  2. package/package.json +2 -2
package/dist/fluxflow.js CHANGED
@@ -40,6 +40,7 @@ __export(paths_exports, {
40
40
  CONTEXT_FILE: () => CONTEXT_FILE,
41
41
  DATA_DIR: () => DATA_DIR,
42
42
  FLUXFLOW_DIR: () => FLUXFLOW_DIR,
43
+ HISTORY_DIR: () => HISTORY_DIR,
43
44
  HISTORY_FILE: () => HISTORY_FILE,
44
45
  LEDGER_FILE: () => LEDGER_FILE,
45
46
  LOGS_DIR: () => LOGS_DIR,
@@ -56,7 +57,7 @@ import os from "os";
56
57
  import path from "path";
57
58
  import fs from "fs";
58
59
  import crypto from "crypto";
59
- var FLUXFLOW_DIR, SETTINGS_FILE, externalDir, DATA_DIR, LOGS_DIR, SECRET_DIR, HISTORY_FILE, USAGE_FILE, MEMORIES_FILE, TEMP_MEM_FILE, TEMP_MEM_CHAT_FILE, BACKUPS_DIR, LEDGER_FILE, ACTIVE_TX_FILE, PATHS_FILE, CONTEXT_FILE, PARSER_DIR;
60
+ var FLUXFLOW_DIR, SETTINGS_FILE, externalDir, DATA_DIR, LOGS_DIR, SECRET_DIR, HISTORY_FILE, HISTORY_DIR, USAGE_FILE, MEMORIES_FILE, TEMP_MEM_FILE, TEMP_MEM_CHAT_FILE, BACKUPS_DIR, LEDGER_FILE, ACTIVE_TX_FILE, PATHS_FILE, CONTEXT_FILE, PARSER_DIR;
60
61
  var init_paths = __esm({
61
62
  "src/utils/paths.js"() {
62
63
  FLUXFLOW_DIR = path.join(os.homedir(), ".fluxflow");
@@ -93,6 +94,7 @@ var init_paths = __esm({
93
94
  LOGS_DIR = path.join(DATA_DIR, "logs");
94
95
  SECRET_DIR = path.join(DATA_DIR, "secret");
95
96
  HISTORY_FILE = path.join(SECRET_DIR, "history.json");
97
+ HISTORY_DIR = path.join(SECRET_DIR, "history");
96
98
  USAGE_FILE = path.join(FLUXFLOW_DIR, "usage.json");
97
99
  MEMORIES_FILE = path.join(SECRET_DIR, "memories.json");
98
100
  TEMP_MEM_FILE = path.join(SECRET_DIR, "memory-temp.json");
@@ -114,7 +116,7 @@ var XOR_KEY, bypass, xorTransform, AES_ALGORITHM, AES_KEY, encryptAes, decryptAe
114
116
  var init_crypto = __esm({
115
117
  "src/utils/crypto.js"() {
116
118
  XOR_KEY = 66;
117
- bypass = false;
119
+ bypass = true;
118
120
  xorTransform = (data) => {
119
121
  const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
120
122
  const result = Buffer.alloc(buffer.length);
@@ -2087,329 +2089,9 @@ var init_build = __esm({
2087
2089
  }
2088
2090
  });
2089
2091
 
2090
- // src/components/MultilineInput.jsx
2091
- import React2, { useState as useState2, useEffect as useEffect2, useMemo, useCallback, useRef } from "react";
2092
- import { Box, Text as Text2, useInput } from "ink";
2093
- function expandTabs(text, tabSize) {
2094
- return text.replace(/\t/g, " ".repeat(tabSize));
2095
- }
2096
- function normalizeLineEndings(text) {
2097
- if (text == null) return "";
2098
- return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2099
- }
2100
- function computeVisualMatrix(value, cursorIndex, wrapWidth, formatText) {
2101
- const textBefore = (value || "").slice(0, cursorIndex);
2102
- const visualCursorIdx = formatText(textBefore).length;
2103
- const fullFormatted = formatText(value || "");
2104
- const literalLines = fullFormatted.split("\n");
2105
- const visualLines = [];
2106
- let currentIdx = 0;
2107
- let cursorLine = 0;
2108
- let cursorCol = 0;
2109
- let foundCursor = false;
2110
- for (let i = 0; i < literalLines.length; i++) {
2111
- const line = literalLines[i];
2112
- if (line.length === 0) {
2113
- if (!foundCursor && visualCursorIdx === currentIdx) {
2114
- cursorLine = visualLines.length;
2115
- cursorCol = 0;
2116
- foundCursor = true;
2117
- }
2118
- visualLines.push({ text: "", globalStart: currentIdx });
2119
- currentIdx += 1;
2120
- continue;
2121
- }
2122
- for (let j = 0; j < line.length; j += wrapWidth) {
2123
- const chunk = line.slice(j, j + wrapWidth);
2124
- const chunkStart = currentIdx + j;
2125
- const chunkEnd = chunkStart + chunk.length;
2126
- if (!foundCursor && visualCursorIdx >= chunkStart && (visualCursorIdx < chunkEnd || visualCursorIdx === chunkEnd && j + wrapWidth >= line.length && i === literalLines.length - 1)) {
2127
- cursorLine = visualLines.length;
2128
- cursorCol = visualCursorIdx - chunkStart;
2129
- foundCursor = true;
2130
- }
2131
- visualLines.push({ text: chunk, globalStart: chunkStart });
2132
- }
2133
- currentIdx += line.length + 1;
2134
- }
2135
- if (!foundCursor) {
2136
- if (visualLines.length === 0) {
2137
- visualLines.push({ text: "", globalStart: 0 });
2138
- } else {
2139
- if (fullFormatted.endsWith("\n")) {
2140
- visualLines.push({ text: "", globalStart: currentIdx });
2141
- cursorLine = visualLines.length - 1;
2142
- cursorCol = 0;
2143
- } else {
2144
- cursorLine = visualLines.length - 1;
2145
- cursorCol = visualLines[cursorLine].text.length;
2146
- }
2147
- }
2148
- }
2149
- return { visualLines, cursorLine, cursorCol };
2150
- }
2151
- var ControlledMultilineInput, MultilineInput;
2152
- var init_MultilineInput = __esm({
2153
- "src/components/MultilineInput.jsx"() {
2154
- ControlledMultilineInput = ({
2155
- value,
2156
- rows,
2157
- maxRows,
2158
- highlightStyle,
2159
- textStyle,
2160
- placeholder = "",
2161
- mask,
2162
- showCursor = true,
2163
- focus = true,
2164
- tabSize = 4,
2165
- cursorIndex = 0,
2166
- highlight,
2167
- columns = 80
2168
- }) => {
2169
- const scrollOffsetRef = useRef(0);
2170
- const wrapWidth = useMemo(() => Math.max(20, columns - 10), [columns]);
2171
- const formatText = useCallback(
2172
- (text, isPlaceholder = false) => {
2173
- const normalized = normalizeLineEndings(text);
2174
- if (!isPlaceholder && mask) {
2175
- return normalized.replace(/[^\n]/g, mask);
2176
- }
2177
- const expanded = expandTabs(normalized, tabSize);
2178
- if (isPlaceholder) return expanded;
2179
- return expanded.replace(/@\[(.*?)\]/g, (match, p1) => {
2180
- const hashIdx = p1.indexOf("#");
2181
- const colonIdx = p1.indexOf(":L");
2182
- let pathOnly = p1;
2183
- let suffix = "";
2184
- if (hashIdx !== -1) {
2185
- pathOnly = p1.slice(0, hashIdx);
2186
- suffix = p1.slice(hashIdx);
2187
- } else if (colonIdx !== -1) {
2188
- pathOnly = p1.slice(0, colonIdx);
2189
- suffix = p1.slice(colonIdx);
2190
- }
2191
- let rel = pathOnly.replace(/\\/g, "/");
2192
- const cwd = (process.cwd() || "").replace(/\\/g, "/");
2193
- if (cwd && rel.toLowerCase().startsWith(cwd.toLowerCase() + "/")) {
2194
- rel = rel.slice(cwd.length + 1);
2195
- } else if (rel.startsWith("./")) {
2196
- rel = rel.slice(2);
2197
- }
2198
- const parts = rel.split("/");
2199
- const basename = parts[parts.length - 1];
2200
- return `[${basename}${suffix}]`;
2201
- });
2202
- },
2203
- [tabSize, mask]
2204
- );
2205
- const { visualLines, cursorLine, cursorCol } = useMemo(() => {
2206
- return computeVisualMatrix(value, cursorIndex, wrapWidth, formatText);
2207
- }, [value, cursorIndex, wrapWidth, formatText]);
2208
- const contentHeight = visualLines.length;
2209
- const visibleRows = useMemo(() => {
2210
- return Math.max(rows ?? maxRows ?? 1, Math.min(maxRows ?? rows ?? 1, contentHeight));
2211
- }, [rows, maxRows, contentHeight]);
2212
- const cursorLineEnd = cursorLine + 1;
2213
- const viewportEnd = scrollOffsetRef.current + visibleRows;
2214
- let newScrollOffset = scrollOffsetRef.current;
2215
- if (cursorLineEnd <= scrollOffsetRef.current) {
2216
- newScrollOffset = Math.max(0, cursorLineEnd - 1);
2217
- } else if (cursorLineEnd > viewportEnd) {
2218
- newScrollOffset = cursorLineEnd - visibleRows;
2219
- } else if (contentHeight) {
2220
- if (contentHeight < visibleRows) {
2221
- newScrollOffset = 0;
2222
- } else if (contentHeight < viewportEnd) {
2223
- newScrollOffset = contentHeight - visibleRows;
2224
- }
2225
- }
2226
- scrollOffsetRef.current = newScrollOffset;
2227
- const visibleLines = useMemo(() => {
2228
- return visualLines.slice(newScrollOffset, newScrollOffset + visibleRows);
2229
- }, [visualLines, newScrollOffset, visibleRows]);
2230
- const [blink, setBlink] = useState2(true);
2231
- useEffect2(() => {
2232
- setBlink(true);
2233
- if (!focus || !showCursor) return;
2234
- const timer = setInterval(() => {
2235
- setBlink((prev) => !prev);
2236
- }, 530);
2237
- return () => clearInterval(timer);
2238
- }, [focus, showCursor, value, cursorIndex]);
2239
- const cursorStyle = useMemo(() => ({
2240
- ...textStyle,
2241
- color: showCursor && focus && blink ? "white" : void 0,
2242
- bold: showCursor && focus && blink,
2243
- inverse: showCursor && focus && blink
2244
- }), [textStyle, showCursor, focus, blink]);
2245
- return /* @__PURE__ */ React2.createElement(Box, { height: visibleRows, width: wrapWidth, overflow: "hidden", flexDirection: "column", flexGrow: 0, flexShrink: 0 }, visibleLines.map((lineObj, idx) => {
2246
- const globalLineIdx = newScrollOffset + idx;
2247
- const isCursorLine = globalLineIdx === cursorLine && focus && showCursor;
2248
- if (!isCursorLine) {
2249
- return /* @__PURE__ */ React2.createElement(Text2, { key: globalLineIdx, ...textStyle, wrap: "truncate" }, lineObj.text || (placeholder && value.length === 0 ? formatText(placeholder, true) : " "));
2250
- }
2251
- const text = lineObj.text;
2252
- const left = text.slice(0, cursorCol);
2253
- const charAtCursor = text[cursorCol] || " ";
2254
- const right = text.slice(cursorCol + 1);
2255
- return /* @__PURE__ */ React2.createElement(Text2, { key: globalLineIdx, ...textStyle, wrap: "truncate" }, /* @__PURE__ */ React2.createElement(Text2, null, left), /* @__PURE__ */ React2.createElement(Text2, { ...cursorStyle }, charAtCursor), /* @__PURE__ */ React2.createElement(Text2, null, right));
2256
- }));
2257
- };
2258
- MultilineInput = ({
2259
- value,
2260
- onChange,
2261
- onSubmit,
2262
- keyBindings,
2263
- showCursor = true,
2264
- highlightPastedText = false,
2265
- focus = true,
2266
- columns = 80,
2267
- useCustomInput = (inputHandler, isActive) => useInput(inputHandler, { isActive }),
2268
- ...controlledProps
2269
- }) => {
2270
- const [cursorIndex, setCursorIndex] = useState2(value.length);
2271
- const [pasteLength, setPasteLength] = useState2(0);
2272
- const cursorIndexRef = useRef(value.length);
2273
- const valueRef = useRef(value);
2274
- const pasteLengthRef = useRef(0);
2275
- const lastArrowTimeRef = useRef(0);
2276
- cursorIndexRef.current = cursorIndex;
2277
- valueRef.current = value;
2278
- pasteLengthRef.current = pasteLength;
2279
- useEffect2(() => {
2280
- if (cursorIndexRef.current > value.length) {
2281
- cursorIndexRef.current = value.length;
2282
- setCursorIndex(value.length);
2283
- }
2284
- }, [value]);
2285
- useCustomInput((input, key) => {
2286
- if (input === "\x1B[I" || input === "\x1B[O" || input === "[I" || input === "[O") {
2287
- return;
2288
- }
2289
- const isArrowKey = key.upArrow || key.downArrow || key.leftArrow || key.rightArrow;
2290
- if (isArrowKey) {
2291
- const now = Date.now();
2292
- if (now - lastArrowTimeRef.current < 33) {
2293
- return;
2294
- }
2295
- lastArrowTimeRef.current = now;
2296
- }
2297
- const curIdx = cursorIndexRef.current;
2298
- const val = valueRef.current;
2299
- const wrapWidth = Math.max(20, columns - 10);
2300
- const submitKey = keyBindings?.submit ?? ((k) => k.return && k.ctrl);
2301
- const newlineKey = keyBindings?.newline ?? ((k) => k.return);
2302
- if (submitKey(key)) {
2303
- onSubmit?.(val);
2304
- return;
2305
- } else if (newlineKey(key)) {
2306
- const newValue = val.slice(0, curIdx) + "\n" + val.slice(curIdx);
2307
- onChange(newValue);
2308
- cursorIndexRef.current = curIdx + 1;
2309
- setCursorIndex(curIdx + 1);
2310
- setPasteLength(0);
2311
- return;
2312
- }
2313
- if (key.tab || key.shift && key.tab || key.ctrl && input === "c") {
2314
- return;
2315
- }
2316
- const identity = (t) => t;
2317
- if (key.upArrow || key.downArrow) {
2318
- if (showCursor) {
2319
- const { visualLines, cursorLine, cursorCol } = computeVisualMatrix(val, curIdx, wrapWidth, identity);
2320
- const targetLine = key.upArrow ? cursorLine - 1 : cursorLine + 1;
2321
- if (targetLine >= 0 && targetLine < visualLines.length) {
2322
- const targetLineObj = visualLines[targetLine];
2323
- const targetCol = Math.min(cursorCol, targetLineObj.text.length);
2324
- const newIndex = targetLineObj.globalStart + targetCol;
2325
- cursorIndexRef.current = newIndex;
2326
- setCursorIndex(newIndex);
2327
- setPasteLength(0);
2328
- } else if (key.upArrow && cursorLine === 0) {
2329
- cursorIndexRef.current = 0;
2330
- setCursorIndex(0);
2331
- setPasteLength(0);
2332
- } else if (key.downArrow && cursorLine === visualLines.length - 1) {
2333
- const lastLineObj = visualLines[visualLines.length - 1];
2334
- const newIndex = lastLineObj.globalStart + lastLineObj.text.length;
2335
- cursorIndexRef.current = newIndex;
2336
- setCursorIndex(newIndex);
2337
- setPasteLength(0);
2338
- }
2339
- }
2340
- } else if (key.leftArrow) {
2341
- if (showCursor) {
2342
- const newIndex = Math.max(0, curIdx - 1);
2343
- cursorIndexRef.current = newIndex;
2344
- setCursorIndex(newIndex);
2345
- setPasteLength(0);
2346
- }
2347
- } else if (key.rightArrow) {
2348
- if (showCursor) {
2349
- const newIndex = Math.min(val.length, curIdx + 1);
2350
- cursorIndexRef.current = newIndex;
2351
- setCursorIndex(newIndex);
2352
- setPasteLength(0);
2353
- }
2354
- } else if (key.backspace) {
2355
- if (curIdx > 0) {
2356
- const newValue = val.slice(0, curIdx - 1) + val.slice(curIdx);
2357
- onChange(newValue);
2358
- cursorIndexRef.current = curIdx - 1;
2359
- setCursorIndex(curIdx - 1);
2360
- setPasteLength(0);
2361
- }
2362
- } else if (key.delete) {
2363
- if (curIdx < val.length) {
2364
- const newValue = val.slice(0, curIdx) + val.slice(curIdx + 1);
2365
- onChange(newValue);
2366
- setPasteLength(0);
2367
- }
2368
- } else if (key.home || key.end) {
2369
- if (showCursor) {
2370
- const { visualLines, cursorLine } = computeVisualMatrix(val, curIdx, wrapWidth, identity);
2371
- const currentLineObj = visualLines[cursorLine];
2372
- if (currentLineObj) {
2373
- let newIndex;
2374
- if (key.home) {
2375
- newIndex = currentLineObj.globalStart;
2376
- } else if (key.end) {
2377
- newIndex = currentLineObj.globalStart + currentLineObj.text.length;
2378
- }
2379
- cursorIndexRef.current = newIndex;
2380
- setCursorIndex(newIndex);
2381
- setPasteLength(0);
2382
- }
2383
- }
2384
- } else {
2385
- if (input) {
2386
- const newValue = val.slice(0, curIdx) + input + val.slice(curIdx);
2387
- onChange(newValue);
2388
- const newIndex = curIdx + input.length;
2389
- cursorIndexRef.current = newIndex;
2390
- setCursorIndex(newIndex);
2391
- setPasteLength(input.length > 1 ? input.length : 0);
2392
- }
2393
- }
2394
- }, focus);
2395
- return /* @__PURE__ */ React2.createElement(
2396
- ControlledMultilineInput,
2397
- {
2398
- ...controlledProps,
2399
- value,
2400
- cursorIndex,
2401
- showCursor,
2402
- focus,
2403
- columns
2404
- }
2405
- );
2406
- };
2407
- }
2408
- });
2409
-
2410
2092
  // src/utils/text.js
2411
2093
  import os2 from "os";
2412
- var wrapText, formatTokens, truncatePath, parsePatchPairs, applyPatches, generateHighFidelityDiff, parseMessageToBlocks, TOOL_LABELS, cleanSignals;
2094
+ var wrapText, formatTokens, truncatePath, parsePatchPairs, applyPatches, generateHighFidelityDiff, parseLineInfo, getSimilarity, alignChangeGroup, blocksCache, streamingBlocksCache, MAX_CACHE_SIZE, parseMessageToBlocks, TOOL_LABELS, REGEX_INITIAL_THINK, REGEX_INITIAL_TOOL, REGEX_SYSTEM, REGEX_THINK, REGEX_ANSWER, REGEX_TOOL_RES, REGEX_SUCCESS_ERROR, REGEX_TURN_1, REGEX_END, REGEX_TURN_2, REGEX_TURN_3, REGEX_OPEN_BRACKET, REGEX_RESPONDED, REGEX_PROMPTED, REGEX_ARROWS, REGEX_ARROWS_L, REGEX_ARROWS_U, REGEX_ARROWS_D, REGEX_ARROWS_LR, REGEX_TERMINAL, REGEX_TOOLS, cleanSignals, clearBlocksCache;
2413
2095
  var init_text = __esm({
2414
2096
  "src/utils/text.js"() {
2415
2097
  init_paths();
@@ -2455,616 +2137,1382 @@ var init_text = __esm({
2455
2137
  }
2456
2138
  }
2457
2139
  } else {
2458
- currentLine += token;
2459
- currentVisibleLength += tokenVisibleLength;
2140
+ currentLine += token;
2141
+ currentVisibleLength += tokenVisibleLength;
2142
+ }
2143
+ });
2144
+ if (currentLine.trimEnd().length > 0 || currentLine === indent) {
2145
+ finalLines.push(currentLine.trimEnd());
2146
+ }
2147
+ });
2148
+ return finalLines.join("\n");
2149
+ };
2150
+ formatTokens = (tokens) => {
2151
+ if (!tokens && tokens !== 0) return "0.0k";
2152
+ const num = typeof tokens === "string" ? parseFloat(tokens) : tokens;
2153
+ if (num >= 1e6) {
2154
+ return `${(num / 1e6).toFixed(1)}m`;
2155
+ } else if (num >= 1e3) {
2156
+ return `${(num / 1e3).toFixed(1)}k`;
2157
+ }
2158
+ return num.toString();
2159
+ };
2160
+ truncatePath = (p, maxLength = 40) => {
2161
+ let data_dir = DATA_DIR.replaceAll("\\\\", "\\");
2162
+ p = p.replace(os2.homedir(), "~").replace(data_dir, "FluxFlow").replaceAll("\\", "/");
2163
+ if (!p || p.length <= maxLength) return p;
2164
+ const half = Math.floor((maxLength - 3) / 2);
2165
+ return p.substring(0, half) + "..." + p.substring(p.length - half).replaceAll("\\", "/");
2166
+ };
2167
+ parsePatchPairs = (args) => {
2168
+ const patchPairs = [];
2169
+ const indices = /* @__PURE__ */ new Set();
2170
+ Object.keys(args).forEach((key) => {
2171
+ const m = key.match(/^(replaceContent|newContent|content_to_replace|content_to_add)(\d+)?$/);
2172
+ if (m) {
2173
+ const index = m[2] ? parseInt(m[2]) : 1;
2174
+ indices.add(index);
2175
+ }
2176
+ });
2177
+ const sortedIndices = Array.from(indices).sort((a, b) => a - b);
2178
+ for (const i of sortedIndices) {
2179
+ let r, n;
2180
+ if (i === 1) {
2181
+ r = args.replaceContent1 ?? (args.content_to_replace ?? args.replaceContent);
2182
+ n = args.newContent1 ?? (args.content_to_add ?? args.newContent);
2183
+ } else {
2184
+ r = args[`replaceContent${i}`] ?? args[`content_to_replace${i}`];
2185
+ n = args[`newContent${i}`] ?? args[`content_to_add${i}`];
2186
+ }
2187
+ if (r !== void 0 && n !== void 0) {
2188
+ patchPairs.push({ replace: r, new: n });
2189
+ } else if (r !== void 0 || n !== void 0) {
2190
+ return { error: `Mismatched replacement pair for index ${i}. Both replacement and new content must be provided.` };
2191
+ }
2192
+ }
2193
+ return { patchPairs };
2194
+ };
2195
+ applyPatches = (content, patches) => {
2196
+ let currentFileContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2197
+ const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2198
+ const getIndent = (line) => line.match(/^\s*/)[0];
2199
+ const getMinIndent = (text) => {
2200
+ const lines = text.split("\n").filter((l) => l.trim() !== "");
2201
+ if (lines.length === 0) return "";
2202
+ let min = getIndent(lines[0]);
2203
+ for (const line of lines) {
2204
+ const indent = getIndent(line);
2205
+ if (indent.length < min.length) min = indent;
2206
+ }
2207
+ return min;
2208
+ };
2209
+ const adjustIndentation = (newText, originalMatch, leadingContext = "") => {
2210
+ if (!newText || originalMatch === void 0) return newText;
2211
+ const getIndentStyle = (text) => {
2212
+ const lines = text.split("\n").filter((l) => l.trim() !== "");
2213
+ if (lines.length === 0) return { char: " ", size: 4 };
2214
+ const firstIndent = lines[0].match(/^\s*/)[0];
2215
+ if (firstIndent.includes(" ")) return { char: " ", size: 1 };
2216
+ const indents = lines.map((l) => l.match(/^\s*/)[0].length).filter((l) => l > 0);
2217
+ if (indents.length === 0) return { char: " ", size: firstIndent.length || 4 };
2218
+ const gcd = (a, b) => b ? gcd(b, a % b) : a;
2219
+ const step = indents.reduce((a, b) => gcd(a, b));
2220
+ return { char: " ", size: step || 4 };
2221
+ };
2222
+ const fileStyle = getIndentStyle(originalMatch);
2223
+ const modelStyle = getIndentStyle(newText);
2224
+ const matchMinIndent = getMinIndent(originalMatch).length;
2225
+ const leadingIndent = (leadingContext.match(/^\s*/) || [""])[0].length;
2226
+ const targetBaseIndentRaw = leadingIndent + matchMinIndent;
2227
+ const targetUnits = targetBaseIndentRaw / fileStyle.size;
2228
+ const modelBaseUnits = getMinIndent(newText).length / modelStyle.size;
2229
+ const deltaUnits = targetUnits - modelBaseUnits;
2230
+ const newLines = newText.split("\n");
2231
+ return newLines.map((line, i) => {
2232
+ if (line.trim() === "" && i !== 0) return "";
2233
+ const currentLineUnits = line.match(/^\s*/)[0].length / modelStyle.size;
2234
+ const finalUnits = Math.max(0, currentLineUnits + deltaUnits);
2235
+ let unitCount = finalUnits;
2236
+ if (i === 0) {
2237
+ const leadingUnits = leadingIndent / fileStyle.size;
2238
+ unitCount = Math.max(0, finalUnits - leadingUnits);
2239
+ }
2240
+ return fileStyle.char.repeat(unitCount * fileStyle.size) + line.trimStart();
2241
+ }).join("\n");
2242
+ };
2243
+ const patchMatches = [];
2244
+ for (let i = 0; i < patches.length; i++) {
2245
+ const pair = patches[i];
2246
+ const content_to_replace = strip(pair.replace || "");
2247
+ const content_to_add = strip(pair.new || "");
2248
+ if (content_to_replace === "" && content_to_add === "") {
2249
+ patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Empty replace and add content.` });
2250
+ continue;
2251
+ }
2252
+ const exactPattern = content_to_replace.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2253
+ let matchRegex = null;
2254
+ if (content_to_replace !== "" && currentFileContent.includes(content_to_replace)) {
2255
+ matchRegex = new RegExp(exactPattern, "g");
2256
+ } else {
2257
+ const fuzzyLines = content_to_replace.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => line.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s*"));
2258
+ if (fuzzyLines.length > 0) {
2259
+ const fuzzyPattern = fuzzyLines.join("\\s*");
2260
+ try {
2261
+ matchRegex = new RegExp(fuzzyPattern, "g");
2262
+ } catch (e) {
2263
+ matchRegex = new RegExp(exactPattern, "g");
2264
+ }
2265
+ } else {
2266
+ matchRegex = new RegExp(exactPattern, "g");
2267
+ }
2268
+ }
2269
+ const matches = [...currentFileContent.matchAll(matchRegex)];
2270
+ if (matches.length === 0) {
2271
+ patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Could not find match.` });
2272
+ continue;
2273
+ }
2274
+ if (matches.length > 1) {
2275
+ patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Found ${matches.length} matches (must be unique).` });
2276
+ continue;
2277
+ }
2278
+ patchMatches.push({
2279
+ index: i,
2280
+ success: true,
2281
+ startPos: matches[0].index,
2282
+ firstMatchContent: matches[0][0],
2283
+ content_to_add
2284
+ });
2285
+ }
2286
+ const successful = patchMatches.filter((m) => m.success).sort((a, b) => a.startPos - b.startPos);
2287
+ for (let j = 0; j < successful.length - 1; j++) {
2288
+ const curr = successful[j];
2289
+ const next = successful[j + 1];
2290
+ if (curr.startPos + curr.firstMatchContent.length > next.startPos) {
2291
+ curr.success = false;
2292
+ curr.error = `Block ${curr.index + 1}: Overlaps with another block.`;
2293
+ next.success = false;
2294
+ next.error = `Block ${next.index + 1}: Overlaps with another block.`;
2295
+ }
2296
+ }
2297
+ const resultsMap = /* @__PURE__ */ new Map();
2298
+ let finalContent = currentFileContent;
2299
+ let charOffset = 0;
2300
+ let lineOffset = 0;
2301
+ const toApply = patchMatches.filter((m) => m.success).sort((a, b) => a.startPos - b.startPos);
2302
+ for (const match of toApply) {
2303
+ const originalStartPos = match.startPos;
2304
+ const originalStartLine = currentFileContent.substring(0, originalStartPos).split("\n").length;
2305
+ const finalStartPos = originalStartPos + charOffset;
2306
+ const finalStartLine = originalStartLine + lineOffset;
2307
+ const lineStart = finalContent.lastIndexOf("\n", finalStartPos) + 1;
2308
+ const leadingContext = finalContent.substring(lineStart, finalStartPos);
2309
+ const finalReplacement = adjustIndentation(match.content_to_add, match.firstMatchContent, leadingContext);
2310
+ const allLines = finalContent.split("\n");
2311
+ const contextBefore = [];
2312
+ for (let j = Math.max(0, finalStartLine - 4); j < finalStartLine - 1; j++) {
2313
+ contextBefore.push({ num: j + 1, text: allLines[j] });
2314
+ }
2315
+ const patchOldLines = match.firstMatchContent.split("\n");
2316
+ const contextAfter = [];
2317
+ const patchEndLineIdx = finalStartLine + patchOldLines.length - 1;
2318
+ for (let j = patchEndLineIdx; j < Math.min(allLines.length, patchEndLineIdx + 3); j++) {
2319
+ contextAfter.push({ num: j + 1, text: allLines[j] });
2320
+ }
2321
+ resultsMap.set(match.index, {
2322
+ success: true,
2323
+ oldContent: match.firstMatchContent,
2324
+ newContent: finalReplacement,
2325
+ originalStartLine,
2326
+ finalStartLine,
2327
+ contextBefore,
2328
+ contextAfter
2329
+ });
2330
+ finalContent = finalContent.substring(0, finalStartPos) + finalReplacement + finalContent.substring(finalStartPos + match.firstMatchContent.length);
2331
+ charOffset += finalReplacement.length - match.firstMatchContent.length;
2332
+ lineOffset += finalReplacement.split("\n").length - match.firstMatchContent.split("\n").length;
2333
+ }
2334
+ const results = [];
2335
+ for (let i = 0; i < patches.length; i++) {
2336
+ if (resultsMap.has(i)) {
2337
+ results.push(resultsMap.get(i));
2338
+ } else {
2339
+ const match = patchMatches.find((m) => m.index === i);
2340
+ results.push({
2341
+ success: false,
2342
+ error: match ? match.error : `Block ${i + 1}: Unknown error.`
2343
+ });
2344
+ }
2345
+ }
2346
+ return { content: finalContent, results };
2347
+ };
2348
+ generateHighFidelityDiff = (originalContent, finalContent, patchResults, threshold = 8) => {
2349
+ if (!patchResults || patchResults.length === 0) return "";
2350
+ const allLinesOriginal = originalContent.split(/\r?\n/);
2351
+ const allLinesFinal = finalContent.split(/\r?\n/);
2352
+ let diffText = `[DIFF_START]
2353
+ `;
2354
+ const separatorLine = "\u2550".repeat(88);
2355
+ let currentFinalLineIdx = 0;
2356
+ let lastSuccessfulHunk = null;
2357
+ const sortedResults = patchResults.filter((res) => res.success).sort((a, b) => a.originalStartLine - b.originalStartLine);
2358
+ sortedResults.forEach((res, idx) => {
2359
+ const startLineFinal = res.finalStartLine !== void 0 ? res.finalStartLine : res.originalStartLine;
2360
+ if (lastSuccessfulHunk === null) {
2361
+ const contextStart = Math.max(0, startLineFinal - 4);
2362
+ currentFinalLineIdx = contextStart;
2363
+ while (currentFinalLineIdx < startLineFinal - 1) {
2364
+ diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2365
+ `;
2366
+ currentFinalLineIdx++;
2367
+ }
2368
+ } else {
2369
+ const prev = lastSuccessfulHunk;
2370
+ const prevOriginalEnd = prev.originalStartLine + prev.oldContent.split("\n").length - 1;
2371
+ const gap = res.originalStartLine - prevOriginalEnd - 1;
2372
+ if (gap >= threshold) {
2373
+ let afterLimit = Math.min(allLinesFinal.length, currentFinalLineIdx + 3);
2374
+ while (currentFinalLineIdx < afterLimit) {
2375
+ diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2376
+ `;
2377
+ currentFinalLineIdx++;
2378
+ }
2379
+ diffText += `[UI_CONTEXT] ${separatorLine}
2380
+ `;
2381
+ const beforeStart = Math.max(currentFinalLineIdx, startLineFinal - 4);
2382
+ currentFinalLineIdx = beforeStart;
2383
+ while (currentFinalLineIdx < startLineFinal - 1) {
2384
+ diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2385
+ `;
2386
+ currentFinalLineIdx++;
2387
+ }
2388
+ } else {
2389
+ while (currentFinalLineIdx < startLineFinal - 1) {
2390
+ diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2391
+ `;
2392
+ currentFinalLineIdx++;
2393
+ }
2460
2394
  }
2395
+ }
2396
+ const oldLines = res.oldContent.split("\n");
2397
+ oldLines.forEach((line, i) => {
2398
+ diffText += `-${res.originalStartLine + i}|${line}
2399
+ `;
2461
2400
  });
2462
- if (currentLine.trimEnd().length > 0 || currentLine === indent) {
2463
- finalLines.push(currentLine.trimEnd());
2401
+ let hunkEndInFinal = currentFinalLineIdx;
2402
+ if (res.finalStartLine !== void 0) {
2403
+ hunkEndInFinal = res.finalStartLine - 1 + (res.newContent ? res.newContent.split("\n").length : 0);
2404
+ } else {
2405
+ const originalResyncLineIdx = res.originalStartLine + oldLines.length - 1;
2406
+ const resyncAnchorText = allLinesOriginal[originalResyncLineIdx] || null;
2407
+ if (resyncAnchorText !== null) {
2408
+ const lookAheadLimit = idx < sortedResults.length - 1 ? (sortedResults[idx + 1].originalStartLine || allLinesFinal.length) + 10 : allLinesFinal.length;
2409
+ for (let s = currentFinalLineIdx; s < lookAheadLimit; s++) {
2410
+ if (allLinesFinal[s] === resyncAnchorText) {
2411
+ hunkEndInFinal = s;
2412
+ break;
2413
+ }
2414
+ if (s === allLinesFinal.length - 1) hunkEndInFinal = allLinesFinal.length;
2415
+ }
2416
+ } else {
2417
+ hunkEndInFinal = allLinesFinal.length;
2418
+ }
2419
+ }
2420
+ while (currentFinalLineIdx < hunkEndInFinal) {
2421
+ diffText += `+${currentFinalLineIdx + 1}|${allLinesFinal[currentFinalLineIdx] || ""}
2422
+ `;
2423
+ currentFinalLineIdx++;
2464
2424
  }
2425
+ lastSuccessfulHunk = res;
2465
2426
  });
2466
- return finalLines.join("\n");
2467
- };
2468
- formatTokens = (tokens) => {
2469
- if (!tokens && tokens !== 0) return "0.0k";
2470
- const num = typeof tokens === "string" ? parseFloat(tokens) : tokens;
2471
- if (num >= 1e6) {
2472
- return `${(num / 1e6).toFixed(1)}m`;
2473
- } else if (num >= 1e3) {
2474
- return `${(num / 1e3).toFixed(1)}k`;
2427
+ if (lastSuccessfulHunk !== null) {
2428
+ let limit = Math.min(allLinesFinal.length, currentFinalLineIdx + 3);
2429
+ while (currentFinalLineIdx < limit) {
2430
+ diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2431
+ `;
2432
+ currentFinalLineIdx++;
2433
+ }
2475
2434
  }
2476
- return num.toString();
2435
+ diffText += `[DIFF_END]`;
2436
+ return diffText;
2477
2437
  };
2478
- truncatePath = (p, maxLength = 40) => {
2479
- let data_dir = DATA_DIR.replaceAll("\\\\", "\\");
2480
- p = p.replace(os2.homedir(), "~").replace(data_dir, "FluxFlow").replaceAll("\\", "/");
2481
- if (!p || p.length <= maxLength) return p;
2482
- const half = Math.floor((maxLength - 3) / 2);
2483
- return p.substring(0, half) + "..." + p.substring(p.length - half).replaceAll("\\", "/");
2438
+ parseLineInfo = (l) => {
2439
+ if (!l) return null;
2440
+ const clean = l.replace("[UI_CONTEXT]", "").replace(/\r/g, "");
2441
+ const isR = clean.startsWith("-");
2442
+ const isA = clean.startsWith("+");
2443
+ let rest = isR || isA ? clean.substring(1) : clean;
2444
+ rest = rest.trim();
2445
+ const splitIdx = rest.indexOf("|");
2446
+ const num = splitIdx !== -1 ? rest.substring(0, splitIdx).trim() : "";
2447
+ const content = splitIdx !== -1 ? rest.substring(splitIdx + 1) : rest;
2448
+ return { isR, isA, num, content };
2484
2449
  };
2485
- parsePatchPairs = (args) => {
2486
- const patchPairs = [];
2487
- const indices = /* @__PURE__ */ new Set();
2488
- Object.keys(args).forEach((key) => {
2489
- const m = key.match(/^(replaceContent|newContent|content_to_replace|content_to_add)(\d+)?$/);
2490
- if (m) {
2491
- const index = m[2] ? parseInt(m[2]) : 1;
2492
- indices.add(index);
2450
+ getSimilarity = (s1, s2) => {
2451
+ if (!s1 && !s2) return 1;
2452
+ if (!s1 || !s2) return 0;
2453
+ const l1 = s1.length;
2454
+ const l2 = s2.length;
2455
+ const dp = Array.from({ length: l1 + 1 }, () => Array(l2 + 1).fill(0));
2456
+ for (let i = 0; i <= l1; i++) dp[i][0] = i;
2457
+ for (let j = 0; j <= l2; j++) dp[0][j] = j;
2458
+ for (let i = 1; i <= l1; i++) {
2459
+ for (let j = 1; j <= l2; j++) {
2460
+ if (s1[i - 1] === s2[j - 1]) {
2461
+ dp[i][j] = dp[i - 1][j - 1];
2462
+ } else {
2463
+ dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1;
2464
+ }
2465
+ }
2466
+ }
2467
+ const maxLen = Math.max(l1, l2);
2468
+ if (maxLen === 0) return 1;
2469
+ return 1 - dp[l1][l2] / maxLen;
2470
+ };
2471
+ alignChangeGroup = (group) => {
2472
+ const removals = [];
2473
+ const additions = [];
2474
+ group.forEach((item, index) => {
2475
+ if (item.parsed.isR) {
2476
+ removals.push({ index, content: item.parsed.content });
2477
+ } else if (item.parsed.isA) {
2478
+ additions.push({ index, content: item.parsed.content });
2493
2479
  }
2494
2480
  });
2495
- const sortedIndices = Array.from(indices).sort((a, b) => a - b);
2496
- for (const i of sortedIndices) {
2497
- let r, n;
2498
- if (i === 1) {
2499
- r = args.replaceContent1 ?? (args.content_to_replace ?? args.replaceContent);
2500
- n = args.newContent1 ?? (args.content_to_add ?? args.newContent);
2481
+ const N = removals.length;
2482
+ const M = additions.length;
2483
+ if (N === 0 || M === 0) return;
2484
+ const dp = Array.from({ length: N + 1 }, () => Array(M + 1).fill(0));
2485
+ const choices = Array.from({ length: N + 1 }, () => Array(M + 1).fill(""));
2486
+ for (let i2 = 1; i2 <= N; i2++) choices[i2][0] = "up";
2487
+ for (let j2 = 1; j2 <= M; j2++) choices[0][j2] = "left";
2488
+ const simMatrix = Array.from({ length: N }, () => Array(M).fill(0));
2489
+ for (let i2 = 0; i2 < N; i2++) {
2490
+ for (let j2 = 0; j2 < M; j2++) {
2491
+ simMatrix[i2][j2] = getSimilarity(removals[i2].content.trim(), additions[j2].content.trim());
2492
+ }
2493
+ }
2494
+ for (let i2 = 1; i2 <= N; i2++) {
2495
+ for (let j2 = 1; j2 <= M; j2++) {
2496
+ const matchScore = simMatrix[i2 - 1][j2 - 1];
2497
+ const score = matchScore >= 0.2 ? matchScore : -10;
2498
+ const diag = dp[i2 - 1][j2 - 1] + score;
2499
+ const up = dp[i2 - 1][j2];
2500
+ const left = dp[i2][j2 - 1];
2501
+ if (diag >= up && diag >= left) {
2502
+ dp[i2][j2] = diag;
2503
+ choices[i2][j2] = "diag";
2504
+ } else if (up >= left) {
2505
+ dp[i2][j2] = up;
2506
+ choices[i2][j2] = "up";
2507
+ } else {
2508
+ dp[i2][j2] = left;
2509
+ choices[i2][j2] = "left";
2510
+ }
2511
+ }
2512
+ }
2513
+ let i = N;
2514
+ let j = M;
2515
+ while (i > 0 || j > 0) {
2516
+ if (choices[i][j] === "diag") {
2517
+ const matchScore = simMatrix[i - 1][j - 1];
2518
+ if (matchScore >= 0.2) {
2519
+ const rIdx = removals[i - 1].index;
2520
+ const aIdx = additions[j - 1].index;
2521
+ group[rIdx].pairContent = group[aIdx].parsed.content;
2522
+ group[aIdx].pairContent = group[rIdx].parsed.content;
2523
+ }
2524
+ i--;
2525
+ j--;
2526
+ } else if (choices[i][j] === "up") {
2527
+ i--;
2501
2528
  } else {
2502
- r = args[`replaceContent${i}`] ?? args[`content_to_replace${i}`];
2503
- n = args[`newContent${i}`] ?? args[`content_to_add${i}`];
2504
- }
2505
- if (r !== void 0 && n !== void 0) {
2506
- patchPairs.push({ replace: r, new: n });
2507
- } else if (r !== void 0 || n !== void 0) {
2508
- return { error: `Mismatched replacement pair for index ${i}. Both replacement and new content must be provided.` };
2529
+ j--;
2509
2530
  }
2510
2531
  }
2511
- return { patchPairs };
2512
2532
  };
2513
- applyPatches = (content, patches) => {
2514
- let currentFileContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2515
- const strip = (t) => t.replace(/^```[\w]*\n?/, "").replace(/```\s*$/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2516
- const getIndent = (line) => line.match(/^\s*/)[0];
2517
- const getMinIndent = (text) => {
2518
- const lines = text.split("\n").filter((l) => l.trim() !== "");
2519
- if (lines.length === 0) return "";
2520
- let min = getIndent(lines[0]);
2521
- for (const line of lines) {
2522
- const indent = getIndent(line);
2523
- if (indent.length < min.length) min = indent;
2533
+ blocksCache = /* @__PURE__ */ new Map();
2534
+ streamingBlocksCache = /* @__PURE__ */ new Map();
2535
+ MAX_CACHE_SIZE = 200;
2536
+ parseMessageToBlocks = (msg, columns) => {
2537
+ if (!msg) return { completed: [], active: [] };
2538
+ const cacheKey = `${msg.id}-${msg.text?.length || 0}-${columns}-${msg.isStreaming}`;
2539
+ if (!msg.isStreaming && blocksCache.has(cacheKey)) {
2540
+ return blocksCache.get(cacheKey);
2541
+ }
2542
+ const text = cleanSignals(msg.text || "");
2543
+ const streamCacheKey = `${msg.id}-${columns}`;
2544
+ let cachedBlocks = /* @__PURE__ */ new Map();
2545
+ if (msg.isStreaming) {
2546
+ const cached = streamingBlocksCache.get(streamCacheKey);
2547
+ if (cached && text.startsWith(cached.text)) {
2548
+ cachedBlocks = cached.blocksMap;
2524
2549
  }
2525
- return min;
2550
+ }
2551
+ const getBlock = (key, type, textContent, extra = {}) => {
2552
+ const existing = cachedBlocks.get(key);
2553
+ if (existing && existing.text === textContent && existing.type === type && !!existing.isActiveBlock === !!extra.isActiveBlock && !!existing.isStreaming === !!extra.isStreaming && existing.pairContent === extra.pairContent) {
2554
+ return existing;
2555
+ }
2556
+ return {
2557
+ key,
2558
+ msg,
2559
+ type,
2560
+ text: textContent,
2561
+ ...extra
2562
+ };
2526
2563
  };
2527
- const adjustIndentation = (newText, originalMatch, leadingContext = "") => {
2528
- if (!newText || originalMatch === void 0) return newText;
2529
- const getIndentStyle = (text) => {
2530
- const lines = text.split("\n").filter((l) => l.trim() !== "");
2531
- if (lines.length === 0) return { char: " ", size: 4 };
2532
- const firstIndent = lines[0].match(/^\s*/)[0];
2533
- if (firstIndent.includes(" ")) return { char: " ", size: 1 };
2534
- const indents = lines.map((l) => l.match(/^\s*/)[0].length).filter((l) => l > 0);
2535
- if (indents.length === 0) return { char: " ", size: firstIndent.length || 4 };
2536
- const gcd = (a, b) => b ? gcd(b, a % b) : a;
2537
- const step = indents.reduce((a, b) => gcd(a, b));
2538
- return { char: " ", size: step || 4 };
2564
+ if (text.includes("- Content Preview:")) {
2565
+ const mainParts = text.split("- Content Preview:");
2566
+ const headerText = mainParts[0] || "";
2567
+ const contentPart = mainParts[1] || "";
2568
+ const footerMarker = "[SYSTEM] Check the content preview for verification [/SYSTEM]";
2569
+ const contentAndFooter = contentPart.split(footerMarker);
2570
+ const content = contentAndFooter[0]?.trim() || "";
2571
+ const footer = contentAndFooter[1] ? `${footerMarker}${contentAndFooter[1]}` : "";
2572
+ const codeLines = content.split("\n").map((l) => l.replace(/\r$/, ""));
2573
+ const gutterWidth = String(codeLines.length).length;
2574
+ const completedBlocks2 = [];
2575
+ let activeBlock2 = null;
2576
+ codeLines.forEach((line, idx) => {
2577
+ const isLast = idx === codeLines.length - 1;
2578
+ const block = getBlock(`${msg.id || Date.now()}-write-line-${idx}`, "write-line", line, {
2579
+ gutterWidth,
2580
+ lineNum: idx + 1,
2581
+ isFirstLine: idx === 0,
2582
+ isLastLine: isLast
2583
+ });
2584
+ if (isLast && msg.isStreaming) {
2585
+ activeBlock2 = block;
2586
+ } else {
2587
+ completedBlocks2.push(block);
2588
+ }
2589
+ });
2590
+ return {
2591
+ completed: completedBlocks2,
2592
+ active: activeBlock2 ? [activeBlock2] : []
2539
2593
  };
2540
- const fileStyle = getIndentStyle(originalMatch);
2541
- const modelStyle = getIndentStyle(newText);
2542
- const matchMinIndent = getMinIndent(originalMatch).length;
2543
- const leadingIndent = (leadingContext.match(/^\s*/) || [""])[0].length;
2544
- const targetBaseIndentRaw = leadingIndent + matchMinIndent;
2545
- const targetUnits = targetBaseIndentRaw / fileStyle.size;
2546
- const modelBaseUnits = getMinIndent(newText).length / modelStyle.size;
2547
- const deltaUnits = targetUnits - modelBaseUnits;
2548
- const newLines = newText.split("\n");
2549
- return newLines.map((line, i) => {
2550
- if (line.trim() === "" && i !== 0) return "";
2551
- const currentLineUnits = line.match(/^\s*/)[0].length / modelStyle.size;
2552
- const finalUnits = Math.max(0, currentLineUnits + deltaUnits);
2553
- let unitCount = finalUnits;
2554
- if (i === 0) {
2555
- const leadingUnits = leadingIndent / fileStyle.size;
2556
- unitCount = Math.max(0, finalUnits - leadingUnits);
2594
+ }
2595
+ if (text.includes("[DIFF_START]")) {
2596
+ const match = text.match(/\[DIFF_START\]([\s\S]*?)(?:\[DIFF_END\]|$)/);
2597
+ const diffBody = match ? match[1].trim() : "";
2598
+ const diffLines = diffBody.split("\n").map((l) => l.replace(/\r$/, ""));
2599
+ const parsedLines = diffLines.map((line) => {
2600
+ return {
2601
+ line,
2602
+ parsed: parseLineInfo(line),
2603
+ pairContent: null
2604
+ };
2605
+ });
2606
+ let currentGroup = [];
2607
+ for (let i = 0; i < parsedLines.length; i++) {
2608
+ const item = parsedLines[i];
2609
+ if (item.parsed && (item.parsed.isR || item.parsed.isA)) {
2610
+ currentGroup.push(item);
2611
+ } else {
2612
+ if (currentGroup.length > 0) {
2613
+ alignChangeGroup(currentGroup);
2614
+ currentGroup = [];
2615
+ }
2557
2616
  }
2558
- return fileStyle.char.repeat(unitCount * fileStyle.size) + line.trimStart();
2559
- }).join("\n");
2560
- };
2561
- const patchMatches = [];
2562
- for (let i = 0; i < patches.length; i++) {
2563
- const pair = patches[i];
2564
- const content_to_replace = strip(pair.replace || "");
2565
- const content_to_add = strip(pair.new || "");
2566
- if (content_to_replace === "" && content_to_add === "") {
2567
- patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Empty replace and add content.` });
2568
- continue;
2569
2617
  }
2570
- const exactPattern = content_to_replace.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2571
- let matchRegex = null;
2572
- if (content_to_replace !== "" && currentFileContent.includes(content_to_replace)) {
2573
- matchRegex = new RegExp(exactPattern, "g");
2574
- } else {
2575
- const fuzzyLines = content_to_replace.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => line.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s*"));
2576
- if (fuzzyLines.length > 0) {
2577
- const fuzzyPattern = fuzzyLines.join("\\s*");
2578
- try {
2579
- matchRegex = new RegExp(fuzzyPattern, "g");
2580
- } catch (e) {
2581
- matchRegex = new RegExp(exactPattern, "g");
2618
+ if (currentGroup.length > 0) {
2619
+ alignChangeGroup(currentGroup);
2620
+ }
2621
+ const completedBlocks2 = [];
2622
+ let activeBlock2 = null;
2623
+ diffLines.forEach((line, i) => {
2624
+ const isLast = i === diffLines.length - 1;
2625
+ const block = getBlock(`${msg.id || Date.now()}-diff-${i}`, "diff-line", line, {
2626
+ isFirstLine: i === 0,
2627
+ isLastLine: isLast,
2628
+ pairContent: parsedLines[i].pairContent
2629
+ });
2630
+ if (isLast && msg.isStreaming) {
2631
+ activeBlock2 = block;
2632
+ } else {
2633
+ completedBlocks2.push(block);
2634
+ }
2635
+ });
2636
+ return {
2637
+ completed: completedBlocks2,
2638
+ active: activeBlock2 ? [activeBlock2] : []
2639
+ };
2640
+ }
2641
+ if (msg.role === "system" || msg.isLogo || msg.isHelpRecord || msg.isTerminalRecord || msg.isHomeWarning || msg.isImageStats || msg.isAskRecord || msg.isAboutRecord || msg.isUpdateNotification || msg.role === "user") {
2642
+ return {
2643
+ completed: [getBlock(`${msg.id || Date.now()}-full`, "full-message", text)],
2644
+ active: []
2645
+ };
2646
+ }
2647
+ const completedBlocks = [];
2648
+ let activeBlock = null;
2649
+ if (msg.role === "think") {
2650
+ completedBlocks.push(getBlock(`${msg.id}-header`, "think-header", ""));
2651
+ const lines = text.split("\n");
2652
+ lines.forEach((line, idx) => {
2653
+ const isLast = idx === lines.length - 1;
2654
+ const block = getBlock(`${msg.id}-${idx}`, "think-line", line, isLast && msg.isStreaming ? { isActiveBlock: true } : {});
2655
+ if (isLast && msg.isStreaming) {
2656
+ activeBlock = block;
2657
+ } else {
2658
+ completedBlocks.push(block);
2659
+ }
2660
+ });
2661
+ if (!msg.isStreaming) {
2662
+ completedBlocks.push({
2663
+ key: `${msg.id}-footer-padding`,
2664
+ msg,
2665
+ type: "think-footer-padding",
2666
+ text: ""
2667
+ });
2668
+ }
2669
+ } else {
2670
+ const lines = text.split("\n");
2671
+ let inTable = false;
2672
+ let tableLines = [];
2673
+ let inCodeBlock = false;
2674
+ let codeLines = [];
2675
+ lines.forEach((line, idx) => {
2676
+ const isLast = idx === lines.length - 1;
2677
+ const isTableRow = line.trim().startsWith("|");
2678
+ const isCodeBlockMarker = line.trim().startsWith("```");
2679
+ if (inCodeBlock) {
2680
+ codeLines.push(line);
2681
+ if (isCodeBlockMarker || isLast) {
2682
+ inCodeBlock = !isCodeBlockMarker;
2683
+ if (!inCodeBlock || isLast) {
2684
+ const block = getBlock(`${msg.id}-code-${idx}`, "agent-line", codeLines.join("\n"), isLast && msg.isStreaming && inCodeBlock ? { isActiveBlock: true } : {});
2685
+ if (isLast && msg.isStreaming && inCodeBlock) {
2686
+ activeBlock = block;
2687
+ } else {
2688
+ completedBlocks.push(block);
2689
+ }
2690
+ codeLines = [];
2691
+ }
2692
+ }
2693
+ } else if (isCodeBlockMarker) {
2694
+ inCodeBlock = true;
2695
+ codeLines.push(line);
2696
+ if (isLast) {
2697
+ const block = getBlock(`${msg.id}-code-${idx}`, "agent-line", codeLines.join("\n"), msg.isStreaming ? { isActiveBlock: true } : {});
2698
+ if (msg.isStreaming) {
2699
+ activeBlock = block;
2700
+ } else {
2701
+ completedBlocks.push(block);
2702
+ }
2703
+ }
2704
+ } else if (isTableRow) {
2705
+ inTable = true;
2706
+ tableLines.push(line);
2707
+ if (isLast) {
2708
+ if (msg.isStreaming) {
2709
+ activeBlock = getBlock(`${msg.id}-table-${idx}`, "table", tableLines.join("\n"), { isStreaming: true, isActiveBlock: true });
2710
+ } else {
2711
+ completedBlocks.push(getBlock(`${msg.id}-table-${idx}`, "table", tableLines.join("\n"), { isStreaming: false }));
2712
+ }
2582
2713
  }
2583
2714
  } else {
2584
- matchRegex = new RegExp(exactPattern, "g");
2715
+ if (inTable) {
2716
+ completedBlocks.push(getBlock(`${msg.id}-table-${idx}`, "table", tableLines.join("\n"), { isStreaming: false }));
2717
+ inTable = false;
2718
+ tableLines = [];
2719
+ }
2720
+ const block = getBlock(`${msg.id}-${idx}`, "agent-line", line, isLast && msg.isStreaming ? { isActiveBlock: true } : {});
2721
+ if (isLast && msg.isStreaming) {
2722
+ activeBlock = block;
2723
+ } else {
2724
+ completedBlocks.push(block);
2725
+ }
2585
2726
  }
2586
- }
2587
- const matches = [...currentFileContent.matchAll(matchRegex)];
2588
- if (matches.length === 0) {
2589
- patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Could not find match.` });
2590
- continue;
2591
- }
2592
- if (matches.length > 1) {
2593
- patchMatches.push({ index: i, success: false, error: `Block ${i + 1}: Found ${matches.length} matches (must be unique).` });
2594
- continue;
2595
- }
2596
- patchMatches.push({
2597
- index: i,
2598
- success: true,
2599
- startPos: matches[0].index,
2600
- firstMatchContent: matches[0][0],
2601
- content_to_add
2602
2727
  });
2603
- }
2604
- const successful = patchMatches.filter((m) => m.success).sort((a, b) => a.startPos - b.startPos);
2605
- for (let j = 0; j < successful.length - 1; j++) {
2606
- const curr = successful[j];
2607
- const next = successful[j + 1];
2608
- if (curr.startPos + curr.firstMatchContent.length > next.startPos) {
2609
- curr.success = false;
2610
- curr.error = `Block ${curr.index + 1}: Overlaps with another block.`;
2611
- next.success = false;
2612
- next.error = `Block ${next.index + 1}: Overlaps with another block.`;
2728
+ if (!msg.isStreaming && msg.workedDuration) {
2729
+ completedBlocks.push(getBlock(`${msg.id}-worked-duration`, "worked-duration", ""));
2613
2730
  }
2614
2731
  }
2615
- const resultsMap = /* @__PURE__ */ new Map();
2616
- let finalContent = currentFileContent;
2617
- let charOffset = 0;
2618
- let lineOffset = 0;
2619
- const toApply = patchMatches.filter((m) => m.success).sort((a, b) => a.startPos - b.startPos);
2620
- for (const match of toApply) {
2621
- const originalStartPos = match.startPos;
2622
- const originalStartLine = currentFileContent.substring(0, originalStartPos).split("\n").length;
2623
- const finalStartPos = originalStartPos + charOffset;
2624
- const finalStartLine = originalStartLine + lineOffset;
2625
- const lineStart = finalContent.lastIndexOf("\n", finalStartPos) + 1;
2626
- const leadingContext = finalContent.substring(lineStart, finalStartPos);
2627
- const finalReplacement = adjustIndentation(match.content_to_add, match.firstMatchContent, leadingContext);
2628
- const allLines = finalContent.split("\n");
2629
- const contextBefore = [];
2630
- for (let j = Math.max(0, finalStartLine - 4); j < finalStartLine - 1; j++) {
2631
- contextBefore.push({ num: j + 1, text: allLines[j] });
2632
- }
2633
- const patchOldLines = match.firstMatchContent.split("\n");
2634
- const contextAfter = [];
2635
- const patchEndLineIdx = finalStartLine + patchOldLines.length - 1;
2636
- for (let j = patchEndLineIdx; j < Math.min(allLines.length, patchEndLineIdx + 3); j++) {
2637
- contextAfter.push({ num: j + 1, text: allLines[j] });
2732
+ const result = {
2733
+ completed: completedBlocks,
2734
+ active: activeBlock ? [activeBlock] : []
2735
+ };
2736
+ if (!msg.isStreaming) {
2737
+ blocksCache.set(cacheKey, result);
2738
+ if (blocksCache.size > MAX_CACHE_SIZE) {
2739
+ const firstKey = blocksCache.keys().next().value;
2740
+ blocksCache.delete(firstKey);
2638
2741
  }
2639
- resultsMap.set(match.index, {
2640
- success: true,
2641
- oldContent: match.firstMatchContent,
2642
- newContent: finalReplacement,
2643
- originalStartLine,
2644
- finalStartLine,
2645
- contextBefore,
2646
- contextAfter
2742
+ streamingBlocksCache.delete(streamCacheKey);
2743
+ } else {
2744
+ const blocksMap = /* @__PURE__ */ new Map();
2745
+ completedBlocks.forEach((b) => blocksMap.set(b.key, b));
2746
+ if (activeBlock) {
2747
+ blocksMap.set(activeBlock.key, activeBlock);
2748
+ }
2749
+ streamingBlocksCache.set(streamCacheKey, {
2750
+ text,
2751
+ blocksMap
2647
2752
  });
2648
- finalContent = finalContent.substring(0, finalStartPos) + finalReplacement + finalContent.substring(finalStartPos + match.firstMatchContent.length);
2649
- charOffset += finalReplacement.length - match.firstMatchContent.length;
2650
- lineOffset += finalReplacement.split("\n").length - match.firstMatchContent.split("\n").length;
2651
- }
2652
- const results = [];
2653
- for (let i = 0; i < patches.length; i++) {
2654
- if (resultsMap.has(i)) {
2655
- results.push(resultsMap.get(i));
2656
- } else {
2657
- const match = patchMatches.find((m) => m.index === i);
2658
- results.push({
2659
- success: false,
2660
- error: match ? match.error : `Block ${i + 1}: Unknown error.`
2661
- });
2753
+ if (streamingBlocksCache.size > MAX_CACHE_SIZE) {
2754
+ const firstKey = streamingBlocksCache.keys().next().value;
2755
+ streamingBlocksCache.delete(firstKey);
2662
2756
  }
2663
2757
  }
2664
- return { content: finalContent, results };
2758
+ return result;
2665
2759
  };
2666
- generateHighFidelityDiff = (originalContent, finalContent, patchResults, threshold = 8) => {
2667
- if (!patchResults || patchResults.length === 0) return "";
2668
- const allLinesOriginal = originalContent.split(/\r?\n/);
2669
- const allLinesFinal = finalContent.split(/\r?\n/);
2670
- let diffText = `[DIFF_START]
2671
- `;
2672
- const separatorLine = "\u2550".repeat(88);
2673
- let currentFinalLineIdx = 0;
2674
- let lastSuccessfulHunk = null;
2675
- const sortedResults = patchResults.filter((res) => res.success).sort((a, b) => a.originalStartLine - b.originalStartLine);
2676
- sortedResults.forEach((res, idx) => {
2677
- const startLineFinal = res.finalStartLine !== void 0 ? res.finalStartLine : res.originalStartLine;
2678
- if (lastSuccessfulHunk === null) {
2679
- const contextStart = Math.max(0, startLineFinal - 4);
2680
- currentFinalLineIdx = contextStart;
2681
- while (currentFinalLineIdx < startLineFinal - 1) {
2682
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2683
- `;
2684
- currentFinalLineIdx++;
2760
+ TOOL_LABELS = {
2761
+ "write_file": "WriteFile",
2762
+ "update_file": "UpdateFile",
2763
+ "read_folder": "ReadFolder",
2764
+ "view_file": "ViewFile",
2765
+ "exec_command": "ExecuteCommand",
2766
+ "web_search": "WebSearch",
2767
+ "web_scrape": "ReadSite",
2768
+ "search_keyword": "SearchKeyword",
2769
+ "write_pdf": "CreatePDF",
2770
+ "write_docx": "CreateDocument",
2771
+ "generate_image": "GenerateImage",
2772
+ // PascalCase Support
2773
+ "WriteFile": "WriteFile",
2774
+ "PatchFile": "PatchFile",
2775
+ "ReadFolder": "ReadFolder",
2776
+ "ReadFile": "ReadFile",
2777
+ "Run": "RunCommand",
2778
+ "WebSearch": "WebSearch",
2779
+ "WebScrape": "WebScrape",
2780
+ "SearchKeyword": "SearchKeyword",
2781
+ "WritePDF": "WritePDF",
2782
+ "WriteDoc": "WriteDoc",
2783
+ "Memory": "Memory",
2784
+ "Chat": "Chat",
2785
+ "GenerateImage": "GenerateImage"
2786
+ };
2787
+ REGEX_INITIAL_THINK = /<\/think>(\r?\n){2}/gi;
2788
+ REGEX_INITIAL_TOOL = /(\r?\n){2}(?=\[?(?:tool:functions|tool\.functions|\s*turn\s*:))/gi;
2789
+ REGEX_SYSTEM = /\[SYSTEM\][\s\S]*?\[\/SYSTEM\]/gi;
2790
+ REGEX_THINK = /<(think|thought)>[\s\S]*?(?:<\/(think|thought)>|$)/gi;
2791
+ REGEX_ANSWER = /\[ANSWER\][\s\S]*?(?:\[\/ANSWER\]|$)/gi;
2792
+ REGEX_TOOL_RES = /\[TOOL RESULT\]:?\s*/gi;
2793
+ REGEX_SUCCESS_ERROR = /^\s*(SUCCESS|ERROR):.*(\r?\n)?/gm;
2794
+ REGEX_TURN_1 = /\[\s*turn\s*:\s*(continue|finish)\s*\]/gi;
2795
+ REGEX_END = /\[\[END\]\]/gi;
2796
+ REGEX_TURN_2 = /\[\s*turn\s*:?.*?$/gi;
2797
+ REGEX_TURN_3 = /\n\s*turn\s*:?.*?$/gi;
2798
+ REGEX_OPEN_BRACKET = /\[\s*$/gi;
2799
+ REGEX_RESPONDED = /\n\nResponded on .*/g;
2800
+ REGEX_PROMPTED = /\n\n\[Prompted on: .*\]/g;
2801
+ REGEX_ARROWS = /(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)/gi;
2802
+ REGEX_ARROWS_L = /(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)/gi;
2803
+ REGEX_ARROWS_U = /(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)/gi;
2804
+ REGEX_ARROWS_D = /(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)/gi;
2805
+ REGEX_ARROWS_LR = /(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi;
2806
+ REGEX_TERMINAL = /@\[TerminalName:.*?, ProcessId:.*?\]/gi;
2807
+ REGEX_TOOLS = /\b(write_file|update_file|read_folder|view_file|exec_command|web_search|web_scrape|search_keyword|write_pdf|write_docx|generate_image)\b/gi;
2808
+ cleanSignals = (text) => {
2809
+ if (!text) return text;
2810
+ let result = text.replace(REGEX_INITIAL_THINK, "</think>").replace(REGEX_INITIAL_TOOL, "");
2811
+ const trigger = "tool:functions.";
2812
+ if (result.toLowerCase().includes(trigger)) {
2813
+ while (true) {
2814
+ const lowerResult = result.toLowerCase();
2815
+ let triggerIdx = lowerResult.indexOf(trigger);
2816
+ if (triggerIdx === -1) break;
2817
+ let startIdx = triggerIdx;
2818
+ let hasOuterBracket = false;
2819
+ let k = triggerIdx - 1;
2820
+ while (k >= 0 && /\s/.test(result[k])) k--;
2821
+ if (k >= 0 && result[k] === "[") {
2822
+ startIdx = k;
2823
+ hasOuterBracket = true;
2685
2824
  }
2686
- } else {
2687
- const prev = lastSuccessfulHunk;
2688
- const prevOriginalEnd = prev.originalStartLine + prev.oldContent.split("\n").length - 1;
2689
- const gap = res.originalStartLine - prevOriginalEnd - 1;
2690
- if (gap >= threshold) {
2691
- let afterLimit = Math.min(allLinesFinal.length, currentFinalLineIdx + 3);
2692
- while (currentFinalLineIdx < afterLimit) {
2693
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2694
- `;
2695
- currentFinalLineIdx++;
2825
+ let balance = 0;
2826
+ let foundStart = false;
2827
+ let inString = null;
2828
+ let j = triggerIdx;
2829
+ while (j < result.length) {
2830
+ const char = result[j];
2831
+ if (!inString && (char === "'" || char === '"' || char === "`")) {
2832
+ inString = char;
2833
+ } else if (inString && char === inString && result[j - 1] !== "\\") {
2834
+ inString = null;
2696
2835
  }
2697
- diffText += `[UI_CONTEXT] ${separatorLine}
2698
- `;
2699
- const beforeStart = Math.max(currentFinalLineIdx, startLineFinal - 4);
2700
- currentFinalLineIdx = beforeStart;
2701
- while (currentFinalLineIdx < startLineFinal - 1) {
2702
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2703
- `;
2704
- currentFinalLineIdx++;
2836
+ if (!inString) {
2837
+ if (char === "(") {
2838
+ balance++;
2839
+ foundStart = true;
2840
+ } else if (char === ")") {
2841
+ balance--;
2842
+ }
2705
2843
  }
2706
- } else {
2707
- while (currentFinalLineIdx < startLineFinal - 1) {
2708
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2709
- `;
2710
- currentFinalLineIdx++;
2844
+ if (foundStart && balance === 0 && !inString) {
2845
+ let endIdx = j;
2846
+ if (hasOuterBracket) {
2847
+ let m = j + 1;
2848
+ while (m < result.length && /\s/.test(result[m])) m++;
2849
+ if (m < result.length && result[m] === "]") {
2850
+ endIdx = m;
2851
+ }
2852
+ }
2853
+ result = result.substring(0, startIdx) + result.substring(endIdx + 1);
2854
+ break;
2855
+ }
2856
+ j++;
2857
+ if (j === result.length) {
2858
+ result = result.substring(0, startIdx);
2859
+ return result;
2711
2860
  }
2712
2861
  }
2713
2862
  }
2714
- const oldLines = res.oldContent.split("\n");
2715
- oldLines.forEach((line, i) => {
2716
- diffText += `-${res.originalStartLine + i}|${line}
2717
- `;
2863
+ }
2864
+ return result.replaceAll(REGEX_SYSTEM, "").replaceAll(REGEX_THINK, "").replace(REGEX_ANSWER, "").replaceAll(REGEX_TOOL_RES, "").replaceAll(REGEX_SUCCESS_ERROR, "").replaceAll(REGEX_TURN_1, "").replaceAll(REGEX_END, "").replaceAll(REGEX_TURN_2, "").replaceAll(REGEX_TURN_3, "").replaceAll(REGEX_OPEN_BRACKET, "").replaceAll(REGEX_RESPONDED, "").replaceAll(REGEX_PROMPTED, "").replaceAll(REGEX_ARROWS, "\u2192").replaceAll(REGEX_ARROWS_L, "\u2190").replaceAll(REGEX_ARROWS_U, "\u2191").replaceAll(REGEX_ARROWS_D, "\u2193").replaceAll(REGEX_ARROWS_LR, "\u2194").replaceAll(REGEX_TERMINAL, "").replaceAll(REGEX_TOOLS, (match) => TOOL_LABELS[match.toLowerCase()] || match).trim();
2865
+ };
2866
+ clearBlocksCache = () => {
2867
+ blocksCache.clear();
2868
+ streamingBlocksCache.clear();
2869
+ };
2870
+ }
2871
+ });
2872
+
2873
+ // src/components/MultilineInput.jsx
2874
+ import React2, { useState as useState2, useEffect as useEffect2, useMemo, useCallback, useRef } from "react";
2875
+ import { Box, Text as Text2, useInput } from "ink";
2876
+ function expandTabs(text, tabSize) {
2877
+ return text.replace(/\t/g, " ".repeat(tabSize));
2878
+ }
2879
+ function normalizeLineEndings(text) {
2880
+ if (text == null) return "";
2881
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2882
+ }
2883
+ function formattedToRaw(formattedIdx, pasteBlocks, value) {
2884
+ let rawIdx = formattedIdx;
2885
+ const sortedBlocks = [...pasteBlocks].sort((a, b) => a.start - b.start);
2886
+ let currentFormattedOffset = 0;
2887
+ for (const block of sortedBlocks) {
2888
+ const formattedStart = block.start - currentFormattedOffset;
2889
+ const lines = normalizeLineEndings(block.text).split("\n").length;
2890
+ const chars = block.text.length;
2891
+ const placeholderLength = (lines > 3 ? `[Pasted ${lines} lines]` : `[Pasted ${chars} chars]`).length;
2892
+ const formattedEnd = formattedStart + placeholderLength;
2893
+ if (formattedIdx <= formattedStart) {
2894
+ break;
2895
+ } else if (formattedIdx < formattedEnd) {
2896
+ rawIdx = block.end;
2897
+ break;
2898
+ } else {
2899
+ const delta = block.text.length - placeholderLength;
2900
+ rawIdx += delta;
2901
+ currentFormattedOffset += delta;
2902
+ }
2903
+ }
2904
+ return Math.min(rawIdx, value.length);
2905
+ }
2906
+ function computeVisualMatrix(value, cursorIndex, wrapWidth, formatText, pasteBlocks = []) {
2907
+ const textBefore = (value || "").slice(0, cursorIndex);
2908
+ let visualCursorIdx = formatText(textBefore).length;
2909
+ let fullFormatted = formatText(value || "");
2910
+ const sortedBlocks = [...pasteBlocks].sort((a, b) => b.start - a.start);
2911
+ for (const block of sortedBlocks) {
2912
+ const formattedStart = formatText(value.slice(0, block.start)).length;
2913
+ const formattedPasted = formatText(block.text);
2914
+ const formattedEnd = formattedStart + formattedPasted.length;
2915
+ const lines = normalizeLineEndings(block.text).split("\n").length;
2916
+ const chars = block.text.length;
2917
+ const placeholderText = lines > 3 ? `[Pasted ${lines} lines]` : `[Pasted ${chars} chars]`;
2918
+ fullFormatted = fullFormatted.slice(0, formattedStart) + placeholderText + fullFormatted.slice(formattedEnd);
2919
+ if (visualCursorIdx > formattedStart && visualCursorIdx < formattedEnd) {
2920
+ visualCursorIdx = formattedStart + placeholderText.length;
2921
+ } else if (visualCursorIdx >= formattedEnd) {
2922
+ visualCursorIdx = visualCursorIdx - (formattedEnd - formattedStart) + placeholderText.length;
2923
+ }
2924
+ }
2925
+ const literalLines = fullFormatted.split("\n");
2926
+ const visualLines = [];
2927
+ let currentIdx = 0;
2928
+ let cursorLine = 0;
2929
+ let cursorCol = 0;
2930
+ let foundCursor = false;
2931
+ for (let i = 0; i < literalLines.length; i++) {
2932
+ const line = literalLines[i];
2933
+ if (line.length === 0) {
2934
+ if (!foundCursor && visualCursorIdx === currentIdx) {
2935
+ cursorLine = visualLines.length;
2936
+ cursorCol = 0;
2937
+ foundCursor = true;
2938
+ }
2939
+ visualLines.push({ text: "", globalStart: formattedToRaw(currentIdx, pasteBlocks, value), formattedStart: currentIdx });
2940
+ currentIdx += 1;
2941
+ continue;
2942
+ }
2943
+ const wrapped = wrapText(line, wrapWidth);
2944
+ const wrappedLines = wrapped.split("\n");
2945
+ let lastMatchEnd = 0;
2946
+ const chunks = [];
2947
+ for (let j = 0; j < wrappedLines.length; j++) {
2948
+ const wLine = wrappedLines[j];
2949
+ const trimmed = wLine.trim();
2950
+ if (trimmed.length === 0) {
2951
+ const spaceLength = wLine.length || 1;
2952
+ chunks.push({
2953
+ text: wLine,
2954
+ start: lastMatchEnd,
2955
+ length: spaceLength
2718
2956
  });
2719
- let hunkEndInFinal = currentFinalLineIdx;
2720
- if (res.finalStartLine !== void 0) {
2721
- hunkEndInFinal = res.finalStartLine - 1 + (res.newContent ? res.newContent.split("\n").length : 0);
2722
- } else {
2723
- const originalResyncLineIdx = res.originalStartLine + oldLines.length - 1;
2724
- const resyncAnchorText = allLinesOriginal[originalResyncLineIdx] || null;
2725
- if (resyncAnchorText !== null) {
2726
- const lookAheadLimit = idx < sortedResults.length - 1 ? (sortedResults[idx + 1].originalStartLine || allLinesFinal.length) + 10 : allLinesFinal.length;
2727
- for (let s = currentFinalLineIdx; s < lookAheadLimit; s++) {
2728
- if (allLinesFinal[s] === resyncAnchorText) {
2729
- hunkEndInFinal = s;
2730
- break;
2731
- }
2732
- if (s === allLinesFinal.length - 1) hunkEndInFinal = allLinesFinal.length;
2957
+ lastMatchEnd += spaceLength;
2958
+ } else {
2959
+ const idx = line.indexOf(trimmed, lastMatchEnd);
2960
+ if (idx !== -1) {
2961
+ const start = lastMatchEnd;
2962
+ const nextTrimmed = j < wrappedLines.length - 1 ? wrappedLines[j + 1].trim() : "";
2963
+ let end = line.length;
2964
+ if (nextTrimmed.length > 0) {
2965
+ const nextIdx = line.indexOf(nextTrimmed, idx + trimmed.length);
2966
+ if (nextIdx !== -1) {
2967
+ end = nextIdx;
2733
2968
  }
2734
- } else {
2735
- hunkEndInFinal = allLinesFinal.length;
2736
2969
  }
2737
- }
2738
- while (currentFinalLineIdx < hunkEndInFinal) {
2739
- diffText += `+${currentFinalLineIdx + 1}|${allLinesFinal[currentFinalLineIdx] || ""}
2740
- `;
2741
- currentFinalLineIdx++;
2742
- }
2743
- lastSuccessfulHunk = res;
2744
- });
2745
- if (lastSuccessfulHunk !== null) {
2746
- let limit = Math.min(allLinesFinal.length, currentFinalLineIdx + 3);
2747
- while (currentFinalLineIdx < limit) {
2748
- diffText += `[UI_CONTEXT] ${currentFinalLineIdx + 1} |${allLinesFinal[currentFinalLineIdx] || ""}
2749
- `;
2750
- currentFinalLineIdx++;
2970
+ chunks.push({
2971
+ text: line.slice(start, end),
2972
+ start,
2973
+ length: end - start
2974
+ });
2975
+ lastMatchEnd = end;
2976
+ } else {
2977
+ chunks.push({
2978
+ text: wLine,
2979
+ start: lastMatchEnd,
2980
+ length: wLine.length
2981
+ });
2982
+ lastMatchEnd += wLine.length;
2751
2983
  }
2752
2984
  }
2753
- diffText += `[DIFF_END]`;
2754
- return diffText;
2755
- };
2756
- parseMessageToBlocks = (msg, columns) => {
2757
- const text = cleanSignals(msg.text || "");
2758
- if (text.includes("- Content Preview:")) {
2759
- const mainParts = text.split("- Content Preview:");
2760
- const headerText = mainParts[0] || "";
2761
- const contentPart = mainParts[1] || "";
2762
- const footerMarker = "[SYSTEM] Check the content preview for verification [/SYSTEM]";
2763
- const contentAndFooter = contentPart.split(footerMarker);
2764
- const content = contentAndFooter[0]?.trim() || "";
2765
- const footer = contentAndFooter[1] ? `${footerMarker}${contentAndFooter[1]}` : "";
2766
- const codeLines = content.split("\n").map((l) => l.replace(/\r$/, ""));
2767
- const gutterWidth = String(codeLines.length).length;
2768
- const completedBlocks2 = [];
2769
- let activeBlock2 = null;
2770
- codeLines.forEach((line, idx) => {
2771
- const isLast = idx === codeLines.length - 1;
2772
- const block = {
2773
- key: `${msg.id || Date.now()}-write-line-${idx}`,
2774
- msg,
2775
- type: "write-line",
2776
- text: line,
2777
- gutterWidth,
2778
- lineNum: idx + 1,
2779
- isFirstLine: idx === 0,
2780
- isLastLine: isLast
2781
- };
2782
- if (isLast && msg.isStreaming) {
2783
- activeBlock2 = block;
2784
- } else {
2785
- completedBlocks2.push(block);
2786
- }
2787
- });
2788
- return {
2789
- completed: completedBlocks2,
2790
- active: activeBlock2 ? [activeBlock2] : []
2791
- };
2985
+ }
2986
+ for (let idx = 0; idx < chunks.length; idx++) {
2987
+ const chunkObj = chunks[idx];
2988
+ const chunk = chunkObj.text;
2989
+ const chunkStart = currentIdx + chunkObj.start;
2990
+ const chunkEnd = chunkStart + chunkObj.length;
2991
+ if (!foundCursor && visualCursorIdx >= chunkStart && (visualCursorIdx < chunkEnd || visualCursorIdx === chunkEnd && idx === chunks.length - 1)) {
2992
+ cursorLine = visualLines.length;
2993
+ cursorCol = visualCursorIdx - chunkStart;
2994
+ foundCursor = true;
2792
2995
  }
2793
- if (text.includes("[DIFF_START]")) {
2794
- const match = text.match(/\[DIFF_START\]([\s\S]*?)(?:\[DIFF_END\]|$)/);
2795
- const diffBody = match ? match[1].trim() : "";
2796
- const diffLines = diffBody.split("\n").map((l) => l.replace(/\r$/, ""));
2797
- const completedBlocks2 = [];
2798
- let activeBlock2 = null;
2799
- diffLines.forEach((line, i) => {
2800
- const isLast = i === diffLines.length - 1;
2801
- const block = {
2802
- key: `${msg.id || Date.now()}-diff-${i}`,
2803
- msg,
2804
- type: "diff-line",
2805
- text: line,
2806
- isFirstLine: i === 0,
2807
- isLastLine: isLast
2808
- };
2809
- if (isLast && msg.isStreaming) {
2810
- activeBlock2 = block;
2811
- } else {
2812
- completedBlocks2.push(block);
2813
- }
2996
+ visualLines.push({
2997
+ text: chunk,
2998
+ globalStart: formattedToRaw(chunkStart, pasteBlocks, value),
2999
+ formattedStart: chunkStart
3000
+ });
3001
+ }
3002
+ currentIdx += line.length + 1;
3003
+ }
3004
+ if (!foundCursor) {
3005
+ if (visualLines.length === 0) {
3006
+ visualLines.push({ text: "", globalStart: 0, formattedStart: 0 });
3007
+ } else {
3008
+ if (fullFormatted.endsWith("\n")) {
3009
+ visualLines.push({
3010
+ text: "",
3011
+ globalStart: formattedToRaw(currentIdx, pasteBlocks, value),
3012
+ formattedStart: currentIdx
2814
3013
  });
2815
- return {
2816
- completed: completedBlocks2,
2817
- active: activeBlock2 ? [activeBlock2] : []
2818
- };
3014
+ cursorLine = visualLines.length - 1;
3015
+ cursorCol = 0;
3016
+ } else {
3017
+ cursorLine = visualLines.length - 1;
3018
+ cursorCol = visualLines[cursorLine].text.length;
2819
3019
  }
2820
- if (msg.role === "system" || msg.isLogo || msg.isHelpRecord || msg.isTerminalRecord || msg.isHomeWarning || msg.isImageStats || msg.isAskRecord || msg.isAboutRecord || msg.isUpdateNotification || msg.role === "user") {
2821
- return {
2822
- completed: [{
2823
- key: `${msg.id || Date.now()}-full`,
2824
- msg,
2825
- type: "full-message",
2826
- text
2827
- }],
2828
- active: []
2829
- };
3020
+ }
3021
+ }
3022
+ return { visualLines, cursorLine, cursorCol };
3023
+ }
3024
+ var ControlledMultilineInput, MultilineInput;
3025
+ var init_MultilineInput = __esm({
3026
+ "src/components/MultilineInput.jsx"() {
3027
+ init_text();
3028
+ ControlledMultilineInput = ({
3029
+ value,
3030
+ rows,
3031
+ maxRows,
3032
+ highlightStyle,
3033
+ textStyle,
3034
+ placeholder = "",
3035
+ mask,
3036
+ showCursor = true,
3037
+ focus = true,
3038
+ tabSize = 4,
3039
+ cursorIndex = 0,
3040
+ highlight,
3041
+ columns = 80,
3042
+ pasteBlocks = []
3043
+ }) => {
3044
+ const scrollOffsetRef = useRef(0);
3045
+ const wrapWidth = useMemo(() => Math.max(20, columns - 10), [columns]);
3046
+ const formatText = useCallback(
3047
+ (text, isPlaceholder = false) => {
3048
+ const normalized = normalizeLineEndings(text);
3049
+ if (!isPlaceholder && mask) {
3050
+ return normalized.replace(/[^\n]/g, mask);
3051
+ }
3052
+ const expanded = expandTabs(normalized, tabSize);
3053
+ if (isPlaceholder) return expanded;
3054
+ return expanded.replace(/@\[(.*?)\]/g, (match, p1) => {
3055
+ const hashIdx = p1.indexOf("#");
3056
+ const colonIdx = p1.indexOf(":L");
3057
+ let pathOnly = p1;
3058
+ let suffix = "";
3059
+ if (hashIdx !== -1) {
3060
+ pathOnly = p1.slice(0, hashIdx);
3061
+ suffix = p1.slice(hashIdx);
3062
+ } else if (colonIdx !== -1) {
3063
+ pathOnly = p1.slice(0, colonIdx);
3064
+ suffix = p1.slice(colonIdx);
3065
+ }
3066
+ let rel = pathOnly.replace(/\\/g, "/");
3067
+ const cwd = (process.cwd() || "").replace(/\\/g, "/");
3068
+ if (cwd && rel.toLowerCase().startsWith(cwd.toLowerCase() + "/")) {
3069
+ rel = rel.slice(cwd.length + 1);
3070
+ } else if (rel.startsWith("./")) {
3071
+ rel = rel.slice(2);
3072
+ }
3073
+ const parts = rel.split("/");
3074
+ const basename = parts[parts.length - 1];
3075
+ return `[${basename}${suffix}]`;
3076
+ });
3077
+ },
3078
+ [tabSize, mask]
3079
+ );
3080
+ const { visualLines, cursorLine, cursorCol } = useMemo(() => {
3081
+ return computeVisualMatrix(value, cursorIndex, wrapWidth, formatText, pasteBlocks);
3082
+ }, [value, cursorIndex, wrapWidth, formatText, pasteBlocks]);
3083
+ const contentHeight = visualLines.length;
3084
+ const visibleRows = useMemo(() => {
3085
+ return Math.max(rows ?? maxRows ?? 1, Math.min(maxRows ?? rows ?? 1, contentHeight));
3086
+ }, [rows, maxRows, contentHeight]);
3087
+ const cursorLineEnd = cursorLine + 1;
3088
+ const viewportEnd = scrollOffsetRef.current + visibleRows;
3089
+ let newScrollOffset = scrollOffsetRef.current;
3090
+ if (cursorLineEnd <= scrollOffsetRef.current) {
3091
+ newScrollOffset = Math.max(0, cursorLineEnd - 1);
3092
+ } else if (cursorLineEnd > viewportEnd) {
3093
+ newScrollOffset = cursorLineEnd - visibleRows;
3094
+ } else if (contentHeight) {
3095
+ if (contentHeight < visibleRows) {
3096
+ newScrollOffset = 0;
3097
+ } else if (contentHeight < viewportEnd) {
3098
+ newScrollOffset = contentHeight - visibleRows;
3099
+ }
2830
3100
  }
2831
- const completedBlocks = [];
2832
- let activeBlock = null;
2833
- if (msg.role === "think") {
2834
- completedBlocks.push({
2835
- key: `${msg.id}-header`,
2836
- msg,
2837
- type: "think-header",
2838
- text: ""
2839
- });
2840
- const lines = text.split("\n");
2841
- lines.forEach((line, idx) => {
2842
- const isLast = idx === lines.length - 1;
2843
- const block = {
2844
- key: `${msg.id}-${idx}`,
2845
- msg,
2846
- type: "think-line",
2847
- text: line
2848
- };
2849
- if (isLast && msg.isStreaming) {
2850
- block.isActiveBlock = true;
2851
- activeBlock = block;
3101
+ scrollOffsetRef.current = newScrollOffset;
3102
+ const visibleLines = useMemo(() => {
3103
+ return visualLines.slice(newScrollOffset, newScrollOffset + visibleRows);
3104
+ }, [visualLines, newScrollOffset, visibleRows]);
3105
+ const [blink, setBlink] = useState2(true);
3106
+ useEffect2(() => {
3107
+ setBlink(true);
3108
+ if (!focus || !showCursor) return;
3109
+ const timer = setInterval(() => {
3110
+ setBlink((prev) => !prev);
3111
+ }, 530);
3112
+ return () => clearInterval(timer);
3113
+ }, [focus, showCursor, value, cursorIndex]);
3114
+ const cursorStyle = useMemo(() => ({
3115
+ ...textStyle,
3116
+ color: showCursor && focus && blink ? "white" : void 0,
3117
+ bold: showCursor && focus && blink,
3118
+ inverse: showCursor && focus && blink
3119
+ }), [textStyle, showCursor, focus, blink]);
3120
+ const renderLineText = (text, isCursor, col, cStyle) => {
3121
+ if (!text) {
3122
+ const emptyText = placeholder && value.length === 0 ? formatText(placeholder, true) : "";
3123
+ if (isCursor) {
3124
+ const charAtCursor = emptyText[0] || " ";
3125
+ const right = emptyText.slice(1);
3126
+ return /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { ...cStyle }, charAtCursor), /* @__PURE__ */ React2.createElement(Text2, { color: "gray", dimColor: true }, right));
3127
+ }
3128
+ return /* @__PURE__ */ React2.createElement(Text2, { color: "gray", dimColor: true }, emptyText || " ");
3129
+ }
3130
+ const regex2 = /(\[Pasted \d+ (?:lines|chars)\])/g;
3131
+ const parts = text.split(regex2);
3132
+ let currentOffset = 0;
3133
+ const rendered = [];
3134
+ for (let i = 0; i < parts.length; i++) {
3135
+ const part = parts[i];
3136
+ if (part === "") continue;
3137
+ const isPlaceholder = part.match(/^\[Pasted \d+ (?:lines|chars)\]$/);
3138
+ const partLength = part.length;
3139
+ const partEnd = currentOffset + partLength;
3140
+ if (isCursor && col >= currentOffset && col < partEnd) {
3141
+ const localCol = col - currentOffset;
3142
+ const left = part.slice(0, localCol);
3143
+ const charAtCursor = part[localCol] || " ";
3144
+ const right = part.slice(localCol + 1);
3145
+ rendered.push(
3146
+ /* @__PURE__ */ React2.createElement(Text2, { key: i }, /* @__PURE__ */ React2.createElement(Text2, { color: isPlaceholder ? "magenta" : void 0 }, left), /* @__PURE__ */ React2.createElement(Text2, { ...cStyle }, charAtCursor), /* @__PURE__ */ React2.createElement(Text2, { color: isPlaceholder ? "magenta" : void 0 }, right))
3147
+ );
2852
3148
  } else {
2853
- completedBlocks.push(block);
3149
+ rendered.push(
3150
+ /* @__PURE__ */ React2.createElement(Text2, { key: i, color: isPlaceholder ? "magenta" : void 0 }, part)
3151
+ );
2854
3152
  }
2855
- });
2856
- if (!msg.isStreaming) {
2857
- completedBlocks.push({
2858
- key: `${msg.id}-footer-padding`,
2859
- msg,
2860
- type: "think-footer-padding",
2861
- text: ""
2862
- });
3153
+ currentOffset = partEnd;
3154
+ }
3155
+ if (isCursor && col >= text.length) {
3156
+ rendered.push(
3157
+ /* @__PURE__ */ React2.createElement(Text2, { key: "cursor-end", ...cStyle }, " ")
3158
+ );
3159
+ }
3160
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, rendered);
3161
+ };
3162
+ return /* @__PURE__ */ React2.createElement(Box, { height: visibleRows, width: wrapWidth + 1, overflow: "hidden", flexDirection: "column", flexGrow: 0, flexShrink: 0 }, visibleLines.map((lineObj, idx) => {
3163
+ const globalLineIdx = newScrollOffset + idx;
3164
+ const isCursorLine = globalLineIdx === cursorLine && focus && showCursor;
3165
+ return /* @__PURE__ */ React2.createElement(Text2, { key: globalLineIdx, ...textStyle, wrap: "truncate" }, renderLineText(lineObj.text, isCursorLine, cursorCol, cursorStyle));
3166
+ }));
3167
+ };
3168
+ MultilineInput = ({
3169
+ value,
3170
+ onChange,
3171
+ onSubmit,
3172
+ keyBindings,
3173
+ showCursor = true,
3174
+ highlightPastedText = false,
3175
+ focus = true,
3176
+ columns = 80,
3177
+ useCustomInput = (inputHandler, isActive) => useInput(inputHandler, { isActive }),
3178
+ onPasteStateChange,
3179
+ ...controlledProps
3180
+ }) => {
3181
+ const [cursorIndex, setCursorIndex] = useState2(value.length);
3182
+ const [pasteLength, setPasteLength] = useState2(0);
3183
+ const [pasteBlocks, setPasteBlocks] = useState2([]);
3184
+ const cursorIndexRef = useRef(value.length);
3185
+ const valueRef = useRef(value);
3186
+ const pasteLengthRef = useRef(0);
3187
+ const pasteBlocksRef = useRef([]);
3188
+ const pasteBufferRef = useRef("");
3189
+ const pasteBufferStartRef = useRef(-1);
3190
+ const pasteTimerRef = useRef(null);
3191
+ const lastArrowTimeRef = useRef(0);
3192
+ cursorIndexRef.current = cursorIndex;
3193
+ valueRef.current = value;
3194
+ pasteLengthRef.current = pasteLength;
3195
+ pasteBlocksRef.current = pasteBlocks;
3196
+ useEffect2(() => {
3197
+ if (cursorIndexRef.current > value.length) {
3198
+ cursorIndexRef.current = value.length;
3199
+ setCursorIndex(value.length);
2863
3200
  }
2864
- } else {
2865
- const lines = text.split("\n");
2866
- let inTable = false;
2867
- let tableLines = [];
2868
- let inCodeBlock = false;
2869
- let codeLines = [];
2870
- lines.forEach((line, idx) => {
2871
- const isLast = idx === lines.length - 1;
2872
- const isTableRow = line.trim().startsWith("|");
2873
- const isCodeBlockMarker = line.trim().startsWith("```");
2874
- if (inCodeBlock) {
2875
- codeLines.push(line);
2876
- if (isCodeBlockMarker || isLast) {
2877
- inCodeBlock = !isCodeBlockMarker;
2878
- if (!inCodeBlock || isLast) {
2879
- const block = {
2880
- key: `${msg.id}-code-${idx}`,
2881
- msg,
2882
- type: "agent-line",
2883
- text: codeLines.join("\n")
3201
+ if (!value) {
3202
+ setPasteBlocks([]);
3203
+ setPasteLength(0);
3204
+ } else {
3205
+ setPasteBlocks((prev) => prev.filter((b) => b.end <= value.length && b.start <= value.length));
3206
+ }
3207
+ }, [value]);
3208
+ useEffect2(() => {
3209
+ onPasteStateChange?.(pasteBlocks.length > 0);
3210
+ }, [pasteBlocks, onPasteStateChange]);
3211
+ const finalizePasteTransaction = () => {
3212
+ const accumulated = pasteBufferRef.current;
3213
+ const start = pasteBufferStartRef.current;
3214
+ const end = start + accumulated.length;
3215
+ const val = valueRef.current;
3216
+ const newValue = val.slice(0, start) + accumulated + val.slice(start);
3217
+ onChange(newValue);
3218
+ cursorIndexRef.current = end;
3219
+ setCursorIndex(end);
3220
+ setPasteLength(accumulated.length > 1 ? accumulated.length : 0);
3221
+ const lines = normalizeLineEndings(accumulated).split("\n").length;
3222
+ const chars = accumulated.length;
3223
+ if (chars > 50 && (lines > 3 || lines <= 3 && chars > 200)) {
3224
+ const newBlock = {
3225
+ start,
3226
+ end,
3227
+ text: accumulated
3228
+ };
3229
+ setPasteBlocks((prev) => {
3230
+ const delta = accumulated.length;
3231
+ const adjusted = prev.map((block) => {
3232
+ if (start <= block.start) {
3233
+ return {
3234
+ ...block,
3235
+ start: block.start + delta,
3236
+ end: block.end + delta
2884
3237
  };
2885
- if (isLast && msg.isStreaming && inCodeBlock) {
2886
- block.isActiveBlock = true;
2887
- activeBlock = block;
2888
- } else {
2889
- completedBlocks.push(block);
2890
- }
2891
- codeLines = [];
2892
3238
  }
2893
- }
2894
- } else if (isCodeBlockMarker) {
2895
- inCodeBlock = true;
2896
- codeLines.push(line);
2897
- if (isLast) {
2898
- const block = {
2899
- key: `${msg.id}-code-${idx}`,
2900
- msg,
2901
- type: "agent-line",
2902
- text: codeLines.join("\n")
3239
+ return block;
3240
+ });
3241
+ return [...adjusted, newBlock];
3242
+ });
3243
+ }
3244
+ pasteBufferRef.current = "";
3245
+ pasteBufferStartRef.current = -1;
3246
+ pasteTimerRef.current = null;
3247
+ };
3248
+ const flushPasteTransaction = () => {
3249
+ if (pasteTimerRef.current) {
3250
+ clearTimeout(pasteTimerRef.current);
3251
+ finalizePasteTransaction();
3252
+ }
3253
+ };
3254
+ useCustomInput((input, key) => {
3255
+ if (input === "\x1B[I" || input === "\x1B[O" || input === "[I" || input === "[O") {
3256
+ return;
3257
+ }
3258
+ let cleanInput = input;
3259
+ let isBracketedStart = false;
3260
+ let isBracketedEnd = false;
3261
+ if (cleanInput && typeof cleanInput === "string") {
3262
+ if (cleanInput.includes("\x1B[200~")) {
3263
+ isBracketedStart = true;
3264
+ cleanInput = cleanInput.replace(/\x1b\[200~/g, "");
3265
+ }
3266
+ if (cleanInput.includes("\x1B[201~")) {
3267
+ isBracketedEnd = true;
3268
+ cleanInput = cleanInput.replace(/\x1b\[201~/g, "");
3269
+ }
3270
+ }
3271
+ const curIdx = cursorIndexRef.current;
3272
+ const val = valueRef.current;
3273
+ const currentPasteBlocks = pasteBlocksRef.current;
3274
+ const wrapWidth = Math.max(20, columns - 10);
3275
+ const adjustPasteBlocksOnEdit = (editStart, delta) => {
3276
+ if (currentPasteBlocks.length === 0) return;
3277
+ const updated = currentPasteBlocks.map((block) => {
3278
+ if (editStart <= block.start) {
3279
+ return {
3280
+ ...block,
3281
+ start: block.start + delta,
3282
+ end: block.end + delta
2903
3283
  };
2904
- if (msg.isStreaming) {
2905
- block.isActiveBlock = true;
2906
- activeBlock = block;
2907
- } else {
2908
- completedBlocks.push(block);
2909
- }
2910
- }
2911
- } else if (isTableRow) {
2912
- inTable = true;
2913
- tableLines.push(line);
2914
- if (isLast) {
2915
- if (msg.isStreaming) {
2916
- activeBlock = {
2917
- key: `${msg.id}-table-${idx}`,
2918
- msg,
2919
- type: "table",
2920
- text: tableLines.join("\n"),
2921
- isStreaming: true,
2922
- isActiveBlock: true
2923
- };
2924
- } else {
2925
- completedBlocks.push({
2926
- key: `${msg.id}-table-${idx}`,
2927
- msg,
2928
- type: "table",
2929
- text: tableLines.join("\n"),
2930
- isStreaming: false
2931
- });
2932
- }
2933
3284
  }
2934
- } else {
2935
- if (inTable) {
2936
- completedBlocks.push({
2937
- key: `${msg.id}-table-${idx}`,
2938
- msg,
2939
- type: "table",
2940
- text: tableLines.join("\n"),
2941
- isStreaming: false
2942
- });
2943
- inTable = false;
2944
- tableLines = [];
3285
+ if (editStart > block.start && editStart < block.end) {
3286
+ return null;
2945
3287
  }
2946
- const block = {
2947
- key: `${msg.id}-${idx}`,
2948
- msg,
2949
- type: "agent-line",
2950
- text: line
2951
- };
2952
- if (isLast && msg.isStreaming) {
2953
- block.isActiveBlock = true;
2954
- activeBlock = block;
2955
- } else {
2956
- completedBlocks.push(block);
3288
+ return block;
3289
+ }).filter(Boolean);
3290
+ setPasteBlocks(updated);
3291
+ };
3292
+ const adjustIndex = (idx) => {
3293
+ for (const block of currentPasteBlocks) {
3294
+ if (idx > block.start && idx < block.end) {
3295
+ return block.start;
2957
3296
  }
2958
3297
  }
2959
- });
2960
- if (!msg.isStreaming && msg.workedDuration) {
2961
- completedBlocks.push({
2962
- key: `${msg.id}-worked-duration`,
2963
- msg,
2964
- type: "worked-duration",
2965
- text: ""
2966
- });
3298
+ return idx;
3299
+ };
3300
+ if (key.ctrl && (cleanInput === "o" || cleanInput === "")) {
3301
+ setPasteBlocks([]);
3302
+ return;
2967
3303
  }
2968
- }
2969
- return {
2970
- completed: completedBlocks,
2971
- active: activeBlock ? [activeBlock] : []
2972
- };
2973
- };
2974
- TOOL_LABELS = {
2975
- "write_file": "WriteFile",
2976
- "update_file": "UpdateFile",
2977
- "read_folder": "ReadFolder",
2978
- "view_file": "ViewFile",
2979
- "exec_command": "ExecuteCommand",
2980
- "web_search": "WebSearch",
2981
- "web_scrape": "ReadSite",
2982
- "search_keyword": "SearchKeyword",
2983
- "write_pdf": "CreatePDF",
2984
- "write_docx": "CreateDocument",
2985
- "generate_image": "GenerateImage",
2986
- // PascalCase Support
2987
- "WriteFile": "WriteFile",
2988
- "PatchFile": "PatchFile",
2989
- "ReadFolder": "ReadFolder",
2990
- "ReadFile": "ReadFile",
2991
- "Run": "RunCommand",
2992
- "WebSearch": "WebSearch",
2993
- "WebScrape": "WebScrape",
2994
- "SearchKeyword": "SearchKeyword",
2995
- "WritePDF": "WritePDF",
2996
- "WriteDoc": "WriteDoc",
2997
- "Memory": "Memory",
2998
- "Chat": "Chat",
2999
- "GenerateImage": "GenerateImage"
3000
- };
3001
- cleanSignals = (text) => {
3002
- if (!text) return text;
3003
- let result = text.replace(/<\/think>(\r?\n){2}/gi, "</think>").replace(/(\r?\n){2}(?=\[?(?:tool:functions|tool\.functions|\s*turn\s*:))/gi, "");
3004
- const trigger = "tool:functions.";
3005
- while (true) {
3006
- const lowerResult = result.toLowerCase();
3007
- let triggerIdx = lowerResult.indexOf(trigger);
3008
- if (triggerIdx === -1) break;
3009
- let startIdx = triggerIdx;
3010
- let hasOuterBracket = false;
3011
- let k = triggerIdx - 1;
3012
- while (k >= 0 && /\s/.test(result[k])) k--;
3013
- if (k >= 0 && result[k] === "[") {
3014
- startIdx = k;
3015
- hasOuterBracket = true;
3304
+ const isArrowKey = key.upArrow || key.downArrow || key.leftArrow || key.rightArrow;
3305
+ if (isArrowKey) {
3306
+ flushPasteTransaction();
3307
+ const now = Date.now();
3308
+ if (now - lastArrowTimeRef.current < 33) {
3309
+ return;
3310
+ }
3311
+ lastArrowTimeRef.current = now;
3016
3312
  }
3017
- let balance = 0;
3018
- let foundStart = false;
3019
- let inString = null;
3020
- let j = triggerIdx;
3021
- while (j < result.length) {
3022
- const char = result[j];
3023
- if (!inString && (char === "'" || char === '"' || char === "`")) {
3024
- inString = char;
3025
- } else if (inString && char === inString && result[j - 1] !== "\\") {
3026
- inString = null;
3313
+ const submitKey = keyBindings?.submit ?? ((k) => k.return && k.ctrl);
3314
+ const newlineKey = keyBindings?.newline ?? ((k) => k.return);
3315
+ if (submitKey(key)) {
3316
+ flushPasteTransaction();
3317
+ onSubmit?.(val);
3318
+ return;
3319
+ } else if (newlineKey(key)) {
3320
+ flushPasteTransaction();
3321
+ adjustPasteBlocksOnEdit(curIdx, 1);
3322
+ const newValue = val.slice(0, curIdx) + "\n" + val.slice(curIdx);
3323
+ onChange(newValue);
3324
+ cursorIndexRef.current = curIdx + 1;
3325
+ setCursorIndex(curIdx + 1);
3326
+ setPasteLength(0);
3327
+ return;
3328
+ }
3329
+ if (key.tab || key.shift && key.tab || key.ctrl && cleanInput === "c") {
3330
+ return;
3331
+ }
3332
+ const identity = (t) => t;
3333
+ if (key.upArrow || key.downArrow) {
3334
+ flushPasteTransaction();
3335
+ if (showCursor) {
3336
+ const { visualLines, cursorLine, cursorCol } = computeVisualMatrix(val, curIdx, wrapWidth, identity, currentPasteBlocks);
3337
+ const targetLine = key.upArrow ? cursorLine - 1 : cursorLine + 1;
3338
+ if (targetLine >= 0 && targetLine < visualLines.length) {
3339
+ const targetLineObj = visualLines[targetLine];
3340
+ const targetCol = Math.min(cursorCol, targetLineObj.text.length);
3341
+ const targetFormattedIdx = targetLineObj.formattedStart + targetCol;
3342
+ let newIndex = formattedToRaw(targetFormattedIdx, currentPasteBlocks, val);
3343
+ newIndex = adjustIndex(newIndex);
3344
+ cursorIndexRef.current = newIndex;
3345
+ setCursorIndex(newIndex);
3346
+ setPasteLength(0);
3347
+ } else if (key.upArrow && cursorLine === 0) {
3348
+ cursorIndexRef.current = 0;
3349
+ setCursorIndex(0);
3350
+ setPasteLength(0);
3351
+ } else if (key.downArrow && cursorLine === visualLines.length - 1) {
3352
+ const lastLineObj = visualLines[visualLines.length - 1];
3353
+ const targetFormattedIdx = lastLineObj.formattedStart + lastLineObj.text.length;
3354
+ let newIndex = formattedToRaw(targetFormattedIdx, currentPasteBlocks, val);
3355
+ newIndex = adjustIndex(newIndex);
3356
+ cursorIndexRef.current = newIndex;
3357
+ setCursorIndex(newIndex);
3358
+ setPasteLength(0);
3359
+ }
3027
3360
  }
3028
- if (!inString) {
3029
- if (char === "(") {
3030
- balance++;
3031
- foundStart = true;
3032
- } else if (char === ")") {
3033
- balance--;
3361
+ } else if (key.leftArrow) {
3362
+ flushPasteTransaction();
3363
+ if (showCursor) {
3364
+ let newIndex = Math.max(0, curIdx - 1);
3365
+ const activeBlock = currentPasteBlocks.find((b) => curIdx === b.end);
3366
+ if (activeBlock) {
3367
+ newIndex = activeBlock.start;
3034
3368
  }
3369
+ cursorIndexRef.current = newIndex;
3370
+ setCursorIndex(newIndex);
3371
+ setPasteLength(0);
3372
+ }
3373
+ } else if (key.rightArrow) {
3374
+ flushPasteTransaction();
3375
+ if (showCursor) {
3376
+ let newIndex = Math.min(val.length, curIdx + 1);
3377
+ const activeBlock = currentPasteBlocks.find((b) => curIdx === b.start);
3378
+ if (activeBlock) {
3379
+ newIndex = activeBlock.end;
3380
+ }
3381
+ cursorIndexRef.current = newIndex;
3382
+ setCursorIndex(newIndex);
3383
+ setPasteLength(0);
3384
+ }
3385
+ } else if (key.backspace) {
3386
+ flushPasteTransaction();
3387
+ const targetBlockIndex = currentPasteBlocks.findIndex((b) => curIdx === b.end);
3388
+ if (targetBlockIndex !== -1) {
3389
+ const targetBlock = currentPasteBlocks[targetBlockIndex];
3390
+ const delta = -(targetBlock.end - targetBlock.start);
3391
+ const newValue = val.slice(0, targetBlock.start) + val.slice(targetBlock.end);
3392
+ onChange(newValue);
3393
+ cursorIndexRef.current = targetBlock.start;
3394
+ setCursorIndex(targetBlock.start);
3395
+ setPasteLength(0);
3396
+ const updatedBlocks = currentPasteBlocks.filter((_, idx) => idx !== targetBlockIndex).map((block) => {
3397
+ if (block.start >= targetBlock.end) {
3398
+ return {
3399
+ ...block,
3400
+ start: block.start + delta,
3401
+ end: block.end + delta
3402
+ };
3403
+ }
3404
+ return block;
3405
+ });
3406
+ setPasteBlocks(updatedBlocks);
3407
+ } else if (curIdx > 0) {
3408
+ adjustPasteBlocksOnEdit(curIdx - 1, -1);
3409
+ const newValue = val.slice(0, curIdx - 1) + val.slice(curIdx);
3410
+ onChange(newValue);
3411
+ cursorIndexRef.current = curIdx - 1;
3412
+ setCursorIndex(curIdx - 1);
3413
+ setPasteLength(0);
3035
3414
  }
3036
- if (foundStart && balance === 0 && !inString) {
3037
- let endIdx = j;
3038
- if (hasOuterBracket) {
3039
- let m = j + 1;
3040
- while (m < result.length && /\s/.test(result[m])) m++;
3041
- if (m < result.length && result[m] === "]") {
3042
- endIdx = m;
3415
+ } else if (key.delete) {
3416
+ flushPasteTransaction();
3417
+ const targetBlockIndex = currentPasteBlocks.findIndex((b) => curIdx === b.start);
3418
+ if (targetBlockIndex !== -1) {
3419
+ const targetBlock = currentPasteBlocks[targetBlockIndex];
3420
+ const delta = -(targetBlock.end - targetBlock.start);
3421
+ const newValue = val.slice(0, targetBlock.start) + val.slice(targetBlock.end);
3422
+ onChange(newValue);
3423
+ setPasteLength(0);
3424
+ const updatedBlocks = currentPasteBlocks.filter((_, idx) => idx !== targetBlockIndex).map((block) => {
3425
+ if (block.start >= targetBlock.end) {
3426
+ return {
3427
+ ...block,
3428
+ start: block.start + delta,
3429
+ end: block.end + delta
3430
+ };
3043
3431
  }
3432
+ return block;
3433
+ });
3434
+ setPasteBlocks(updatedBlocks);
3435
+ } else {
3436
+ adjustPasteBlocksOnEdit(curIdx, -1);
3437
+ if (curIdx < val.length) {
3438
+ const newValue = val.slice(0, curIdx) + val.slice(curIdx + 1);
3439
+ onChange(newValue);
3440
+ setPasteLength(0);
3044
3441
  }
3045
- result = result.substring(0, startIdx) + result.substring(endIdx + 1);
3046
- break;
3047
3442
  }
3048
- j++;
3049
- if (j === result.length) {
3050
- result = result.substring(0, startIdx);
3051
- return result;
3443
+ } else if (key.home || key.end) {
3444
+ flushPasteTransaction();
3445
+ if (showCursor) {
3446
+ const { visualLines, cursorLine } = computeVisualMatrix(val, curIdx, wrapWidth, identity, currentPasteBlocks);
3447
+ const currentLineObj = visualLines[cursorLine];
3448
+ if (currentLineObj) {
3449
+ let newIndex;
3450
+ if (key.home) {
3451
+ newIndex = formattedToRaw(currentLineObj.formattedStart, currentPasteBlocks, val);
3452
+ } else if (key.end) {
3453
+ newIndex = formattedToRaw(currentLineObj.formattedStart + currentLineObj.text.length, currentPasteBlocks, val);
3454
+ }
3455
+ newIndex = adjustIndex(newIndex);
3456
+ cursorIndexRef.current = newIndex;
3457
+ setCursorIndex(newIndex);
3458
+ setPasteLength(0);
3459
+ }
3460
+ }
3461
+ } else {
3462
+ if (cleanInput !== "" || isBracketedStart || isBracketedEnd) {
3463
+ const isPaste = isBracketedStart || isBracketedEnd || cleanInput.length > 1 || pasteTimerRef.current !== null;
3464
+ if (isPaste) {
3465
+ if (pasteTimerRef.current) {
3466
+ clearTimeout(pasteTimerRef.current);
3467
+ pasteBufferRef.current += cleanInput;
3468
+ } else {
3469
+ pasteBufferStartRef.current = curIdx;
3470
+ pasteBufferRef.current = cleanInput;
3471
+ }
3472
+ if (isBracketedEnd) {
3473
+ pasteTimerRef.current = null;
3474
+ finalizePasteTransaction();
3475
+ } else {
3476
+ pasteTimerRef.current = setTimeout(() => {
3477
+ finalizePasteTransaction();
3478
+ }, 100);
3479
+ }
3480
+ } else {
3481
+ adjustPasteBlocksOnEdit(curIdx, cleanInput.length);
3482
+ const newValue = val.slice(0, curIdx) + cleanInput + val.slice(curIdx);
3483
+ onChange(newValue);
3484
+ const newIndex = curIdx + cleanInput.length;
3485
+ cursorIndexRef.current = newIndex;
3486
+ setCursorIndex(newIndex);
3487
+ setPasteLength(0);
3488
+ }
3052
3489
  }
3053
3490
  }
3054
- }
3055
- return result.replaceAll(/\[SYSTEM\][\s\S]*?\[\/SYSTEM\]/gi, "").replaceAll(/<(think|thought)>[\s\S]*?(?:<\/(think|thought)>|$)/gi, "").replace(/\[ANSWER\][\s\S]*?(?:\[\/ANSWER\]|$)/gi, "").replaceAll(/\[TOOL RESULT\]:?\s*/gi, "").split("\n").filter((line) => !line.trim().startsWith("SUCCESS:") && !line.trim().startsWith("ERROR:")).join("\n").replaceAll(/\[\s*turn\s*:\s*(continue|finish)\s*\]/gi, "").replaceAll(/\[\[END\]\]/gi, "").replaceAll(/\[\s*turn\s*:?.*?$/gi, "").replaceAll(/\n\s*turn\s*:?.*?$/gi, "").replaceAll(/\[\s*$/gi, "").replaceAll(/\n\nResponded on .*/g, "").replaceAll(/\n\n\[Prompted on: .*\]/g, "").replaceAll(/(\$?\\?\/?\\rightarrow\$?|\$\\rightarrow\$)/gi, "\u2192").replaceAll(/(\$?\\?\/?\\leftarrow\$?|\$\\leftarrow\$)/gi, "\u2190").replaceAll(/(\$?\\?\/?\\uparrow\$?|\$\\uparrow\$)/gi, "\u2191").replaceAll(/(\$?\\?\/?\\downarrow\$?|\$\\downarrow\$)/gi, "\u2193").replaceAll(/(\$?\\?\/?\\leftrightarrow\$?|\$\\leftrightarrow\$)/gi, "\u2194").replaceAll(/@\[TerminalName:.*?, ProcessId:.*?\]/gi, "").replaceAll(/\b(write_file|update_file|read_folder|view_file|exec_command|web_search|web_scrape|search_keyword|write_pdf|write_docx|generate_image)\b/gi, (match) => TOOL_LABELS[match.toLowerCase()] || match).trim();
3491
+ }, focus);
3492
+ return /* @__PURE__ */ React2.createElement(
3493
+ ControlledMultilineInput,
3494
+ {
3495
+ ...controlledProps,
3496
+ value,
3497
+ cursorIndex,
3498
+ showCursor,
3499
+ focus,
3500
+ columns,
3501
+ pasteBlocks
3502
+ }
3503
+ );
3056
3504
  };
3057
3505
  }
3058
3506
  });
3059
3507
 
3060
3508
  // src/components/TerminalBox.jsx
3061
- import React3 from "react";
3062
- import { Box as Box2, Text as Text3 } from "ink";
3509
+ import React3, { useState as useState3 } from "react";
3510
+ import { Box as Box2, Text as Text3, useInput as useInput2 } from "ink";
3063
3511
  var TerminalBox;
3064
3512
  var init_TerminalBox = __esm({
3065
3513
  "src/components/TerminalBox.jsx"() {
3066
3514
  init_text();
3067
- TerminalBox = React3.memo(({ command, output, completed = false, isFocused = false, columns = 80, isPty = false }) => {
3515
+ TerminalBox = React3.memo(({ command, output, completed = false, isFocused = false, columns = 80, isPty = false, terminalHeight = 24 }) => {
3068
3516
  const processPTY = (text) => {
3069
3517
  if (!text) return "";
3070
3518
  const lines = [[]];
@@ -3188,6 +3636,18 @@ var init_TerminalBox = __esm({
3188
3636
  };
3189
3637
  const cleanOutput = processPTY(output).replace(/\n{3,}/g, "\n\n");
3190
3638
  const displayOutput = isPty ? cleanOutput : cleanOutput ? wrapText(cleanOutput, columns - 6) : "";
3639
+ const [isExpanded, setIsExpanded] = useState3(false);
3640
+ useInput2((input, key) => {
3641
+ if (isFocused && key.ctrl && (input === "o" || input === "")) {
3642
+ setIsExpanded((prev) => !prev);
3643
+ }
3644
+ }, { isActive: isFocused });
3645
+ const rawLines = displayOutput ? displayOutput.split("\n") : [];
3646
+ const limit = Math.max(5, completed ? terminalHeight - 10 : terminalHeight - 20);
3647
+ const hasCollapsibleContent = rawLines.length > limit;
3648
+ const collapsedCount = rawLines.length - limit;
3649
+ const visibleLines = hasCollapsibleContent && !isExpanded ? rawLines.slice(rawLines.length - limit) : rawLines;
3650
+ const renderedOutput = visibleLines.join("\n");
3191
3651
  return /* @__PURE__ */ React3.createElement(
3192
3652
  Box2,
3193
3653
  {
@@ -3205,7 +3665,7 @@ var init_TerminalBox = __esm({
3205
3665
  width: "100%"
3206
3666
  },
3207
3667
  /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React3.createElement(Box2, { flexShrink: 1, paddingRight: 2 }, /* @__PURE__ */ React3.createElement(Text3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "gray", bold: true }, completed ? "\u{1F3C1} FINISHED:" : "\u26A1 EXECUTING:", " "), /* @__PURE__ */ React3.createElement(Text3, { color: "white" }, command))), isPty && /* @__PURE__ */ React3.createElement(Box2, { flexShrink: 0, paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "gray", bold: true }, "ADVANCE"))),
3208
- displayOutput ? /* @__PURE__ */ React3.createElement(Box2, { marginTop: completed ? 0 : 1, backgroundColor: isPty ? void 0 : "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: completed ? "gray" : void 0 }, displayOutput)) : !completed && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, backgroundColor: isPty ? void 0 : "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "white", italic: true }, "Waiting for output...")),
3668
+ displayOutput ? /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginTop: 0, backgroundColor: isPty ? void 0 : "#0a0a0a", paddingX: 1 }, hasCollapsibleContent && !isExpanded && /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "magenta" }, "...", collapsedCount, " lines collapsed... Press CTRL + O to expand.")), /* @__PURE__ */ React3.createElement(Text3, { color: completed ? "gray" : void 0 }, renderedOutput)) : !completed && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, backgroundColor: isPty ? void 0 : "#0a0a0a", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "white", italic: true }, "Waiting for output...")),
3209
3669
  /* @__PURE__ */ React3.createElement(Box2, { justifyContent: "space-between", marginTop: 1 }, !completed ? /* @__PURE__ */ React3.createElement(Text3, { color: "gray", italic: true }, isFocused ? "Press TAB to unfocus, then double-press ESC to terminate." : "Double-press ESC to terminate if hanging.") : /* @__PURE__ */ React3.createElement(Box2, null), /* @__PURE__ */ React3.createElement(Text3, { color: "gray", bold: true }, completed ? "\u25CF ARCHIVED" : isFocused ? "\u25B6 TERMINAL FOCUSED" : "\u25CF LIVE (Press TAB to focus)"))
3210
3670
  );
3211
3671
  });
@@ -3482,78 +3942,17 @@ ${coloredArt[7]}`;
3482
3942
  });
3483
3943
 
3484
3944
  // src/components/ChatLayout.jsx
3485
- import React4, { useState as useState3, useEffect as useEffect3, useRef as useRef2 } from "react";
3945
+ import React4, { useState as useState4, useEffect as useEffect3, useRef as useRef2 } from "react";
3486
3946
  import { Box as Box3, Text as Text4 } from "ink";
3487
3947
  import { diffWordsWithSpace } from "diff";
3488
- var useStreamingText, formatThinkText, parseMathSymbols, renderLatexText, InlineMarkdown, TableRenderer, MarkdownText, parseLineInfo, DiffLine, DiffBlock, CodeRenderer, formatThinkingDuration, MessageItem, BlockItem, ChatLayout;
3948
+ var useStreamingText, formatThinkText, REGEX_MD_TOKENS, REGEX_LATEX_FRAC, REGEX_LATEX_STYLE, parseMathSymbols, renderLatexText, InlineMarkdown, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, formatThinkingDuration, MessageItem, BlockItem, ChatLayout;
3489
3949
  var init_ChatLayout = __esm({
3490
3950
  "src/components/ChatLayout.jsx"() {
3491
3951
  init_TerminalBox();
3492
3952
  init_text();
3493
3953
  init_terminal();
3494
3954
  useStreamingText = (targetText, isStreaming, isActiveBlock) => {
3495
- const [displayedText, setDisplayedText] = useState3(isActiveBlock && isStreaming ? "" : targetText);
3496
- const targetTextRef = useRef2(targetText);
3497
- useEffect3(() => {
3498
- targetTextRef.current = targetText;
3499
- }, [targetText]);
3500
- useEffect3(() => {
3501
- if (!isActiveBlock) {
3502
- setDisplayedText(targetText);
3503
- return;
3504
- }
3505
- if (!isStreaming && displayedText === targetText) {
3506
- return;
3507
- }
3508
- const interval = setInterval(() => {
3509
- setDisplayedText((current) => {
3510
- const target = targetTextRef.current;
3511
- if (current.length >= target.length) {
3512
- if (!isStreaming) {
3513
- clearInterval(interval);
3514
- }
3515
- return current;
3516
- }
3517
- if (!target.startsWith(current)) {
3518
- return target;
3519
- }
3520
- const remaining = target.substring(current.length);
3521
- const words = remaining.split(/(\s+)/);
3522
- if (words.length <= 1) {
3523
- if (!isStreaming) {
3524
- clearInterval(interval);
3525
- }
3526
- return target;
3527
- }
3528
- const currentWordsCount = current.split(/\s+/).filter(Boolean).length;
3529
- const targetWordsCount = target.split(/\s+/).filter(Boolean).length;
3530
- const diff = targetWordsCount - currentWordsCount;
3531
- let wordsToAdd = 1;
3532
- if (diff > 15) {
3533
- wordsToAdd = 4;
3534
- } else if (diff > 8) {
3535
- wordsToAdd = 3;
3536
- } else if (diff > 3) {
3537
- wordsToAdd = 2;
3538
- }
3539
- let addedText = "";
3540
- let wordCount = 0;
3541
- for (let i = 0; i < words.length; i++) {
3542
- const w = words[i];
3543
- addedText += w;
3544
- if (/\S/.test(w)) {
3545
- wordCount++;
3546
- if (wordCount >= wordsToAdd) {
3547
- break;
3548
- }
3549
- }
3550
- }
3551
- return current + addedText;
3552
- });
3553
- }, 100);
3554
- return () => clearInterval(interval);
3555
- }, [isStreaming, isActiveBlock, targetText, displayedText]);
3556
- return displayedText;
3955
+ return targetText;
3557
3956
  };
3558
3957
  formatThinkText = (cleaned, columns = 80) => {
3559
3958
  if (!cleaned) return null;
@@ -3581,14 +3980,17 @@ var init_ChatLayout = __esm({
3581
3980
  return /* @__PURE__ */ React4.createElement(MarkdownText, { key: i, text: cleanPart, color: "gray", columns: availableWidth, italic: true });
3582
3981
  }));
3583
3982
  };
3983
+ REGEX_MD_TOKENS = /(```[\s\S]*?```|`[^`]+`|@\[.*?\]|\*\*.*?\*\*|\*.*?\*|\$.*?\$|\[.*?\]\s*\(.*?\)|\[.*?\]\s*\[.*?\]|https?:\/\/[^\s]+)/g;
3984
+ REGEX_LATEX_FRAC = /\\frac\s*\{([^{}]*)\}\s*\{([^{}]*)\}/g;
3985
+ REGEX_LATEX_STYLE = /(\\(?:mathbf|textbf|textit|underline|texttt)\{[^{}]*\})/g;
3584
3986
  parseMathSymbols = (content) => {
3585
3987
  return content.replace(/\\multiply|\\mul|\\times/g, "\xD7").replace(/\\div/g, "\xF7").replace(/\\cdot/g, "\u22C5").replace(/\\infty/g, "\u221E").replace(/\\pm/g, "\xB1").replace(/\\leq/g, "\u2264").replace(/\\geq/g, "\u2265").replace(/\\neq/g, "\u2260").replace(/\\sqrt\s*\{([^}]+)\}/g, "\u221A($1)").replace(/\\sqrt\s*(\w+|\d+)/g, "\u221A($1)").replace(/\\alpha/g, "\u03B1").replace(/\\beta/g, "\u03B2").replace(/\\theta/g, "\u03B8").replace(/\\pi/g, "\u03C0").replace(/\\approx/g, "\u2248").replace(/\\Delta/g, "\u0394").replace(/\\sigma/g, "\u03C3").replace(/\\sum/g, "\u03A3").replace(/\\prod/g, "\u03A0").replace(/\\rightarrow|\\to/g, "\u2192").replace(/\\left\b|\\right\b/g, "").replace(/\\left\(|\\right\)/g, (match) => match.includes("left") ? "(" : ")").replace(/\\left\[|\\right\]/g, (match) => match.includes("left") ? "[" : "]").replace(/\\\{|\\\}/g, (match) => match.includes("{") ? "{" : "}").replace(/\\text\s*\{([^}]+)\}/g, "$1").replace(/\\text\s+(\w+)/g, "$1").replace(/\\%/g, "%");
3586
3988
  };
3587
3989
  renderLatexText = (content, key) => {
3588
3990
  if (!content) return null;
3589
- let formatted = content.replace(/\\frac\s*\{([^{}]*)\}\s*\{([^{}]*)\}/g, "($1/$2)");
3991
+ let formatted = content.replace(REGEX_LATEX_FRAC, "($1/$2)");
3590
3992
  formatted = parseMathSymbols(formatted);
3591
- const parts = formatted.split(/(\\(?:mathbf|textbf|textit|underline|texttt)\{[^{}]*\})/g);
3993
+ const parts = formatted.split(REGEX_LATEX_STYLE);
3592
3994
  return /* @__PURE__ */ React4.createElement(React4.Fragment, { key }, parts.map((p, idx) => {
3593
3995
  if (p.startsWith("\\")) {
3594
3996
  const match = p.match(/\\(\w+)\{([^{}]*)\}/);
@@ -3607,7 +4009,7 @@ var init_ChatLayout = __esm({
3607
4009
  };
3608
4010
  InlineMarkdown = React4.memo(({ text, color, italic }) => {
3609
4011
  if (!text) return null;
3610
- const parts = text.split(/(```[\s\S]*?```|`[^`]+`|@\[.*?\]|\*\*.*?\*\*|\*.*?\*|\$.*?\$|\[.*?\]\s*\(.*?\)|\[.*?\]\s*\[.*?\]|https?:\/\/[^\s]+)/g);
4012
+ const parts = text.split(REGEX_MD_TOKENS);
3611
4013
  return /* @__PURE__ */ React4.createElement(Text4, { color, wrap: "anywhere", italic }, parts.map((part, j) => {
3612
4014
  if (!part) return null;
3613
4015
  if (part.startsWith("```") && part.endsWith("```")) {
@@ -3738,40 +4140,47 @@ var init_ChatLayout = __esm({
3738
4140
  flushBuffers("final");
3739
4141
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 2 }, result);
3740
4142
  });
3741
- parseLineInfo = (l) => {
3742
- if (!l) return null;
3743
- const clean = l.replace("[UI_CONTEXT]", "").replace(/\r/g, "");
3744
- const isR = clean.startsWith("-");
3745
- const isA = clean.startsWith("+");
3746
- let rest = isR || isA ? clean.substring(1) : clean;
3747
- rest = rest.trim();
3748
- const splitIdx = rest.indexOf("|");
3749
- const num = splitIdx !== -1 ? rest.substring(0, splitIdx).trim() : "";
3750
- const content = splitIdx !== -1 ? rest.substring(splitIdx + 1) : rest;
3751
- return { isR, isA, num, content };
3752
- };
3753
4143
  DiffLine = React4.memo(({ line, pairContent, parentText, columns = 80 }) => {
3754
4144
  const isContext = line.includes("[UI_CONTEXT]");
3755
4145
  const cleanLine = line.replace("[UI_CONTEXT]", "");
3756
4146
  if (isContext && cleanLine.includes("\u2550")) {
3757
- return /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "\u2550".repeat(Math.max(10, columns - 4))));
4147
+ return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "\u2550".repeat(Math.max(10, columns - 4))));
3758
4148
  }
3759
4149
  const parsedCurrent = parseLineInfo(line);
3760
4150
  if (!parsedCurrent) {
3761
4151
  return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, wrapText(cleanLine, columns - 14))));
3762
4152
  }
3763
4153
  const { isR: isRemoval, isA: isAddition, num: lineNum, content } = parsedCurrent;
3764
- const innerBgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : void 0;
3765
4154
  let finalPairContent = pairContent;
3766
4155
  if (!finalPairContent && parentText && (isRemoval || isAddition)) {
3767
- const cleanParent = parentText.replace(/\[DIFF_START\]|\[DIFF_END\]/g, "").trim();
3768
- const diffLines = cleanParent.split("\n");
3769
- const pairLine = diffLines.find((l) => {
3770
- const p = parseLineInfo(l);
3771
- return p && p.num === lineNum && p.isR !== isRemoval;
4156
+ const match = parentText.match(/\[DIFF_START\]([\s\S]*?)(?:\[DIFF_END\]|$)/);
4157
+ const diffBody = match ? match[1].trim() : "";
4158
+ const diffLines = diffBody.split("\n").map((l) => l.replace(/\r$/, ""));
4159
+ const parsedLines = diffLines.map((l) => {
4160
+ return {
4161
+ line: l,
4162
+ parsed: parseLineInfo(l),
4163
+ pairContent: null
4164
+ };
3772
4165
  });
3773
- if (pairLine) {
3774
- finalPairContent = parseLineInfo(pairLine).content;
4166
+ let currentGroup = [];
4167
+ for (let idx = 0; idx < parsedLines.length; idx++) {
4168
+ const item = parsedLines[idx];
4169
+ if (item.parsed && (item.parsed.isR || item.parsed.isA)) {
4170
+ currentGroup.push(item);
4171
+ } else {
4172
+ if (currentGroup.length > 0) {
4173
+ alignChangeGroup(currentGroup);
4174
+ currentGroup = [];
4175
+ }
4176
+ }
4177
+ }
4178
+ if (currentGroup.length > 0) {
4179
+ alignChangeGroup(currentGroup);
4180
+ }
4181
+ const matchedItem = parsedLines.find((item) => item.parsed && item.parsed.num === lineNum && item.parsed.isR === isRemoval);
4182
+ if (matchedItem) {
4183
+ finalPairContent = matchedItem.pairContent;
3775
4184
  }
3776
4185
  }
3777
4186
  let words = [];
@@ -3786,7 +4195,7 @@ var init_ChatLayout = __esm({
3786
4195
  }
3787
4196
  const hasInlineChange = words.some((part) => isRemoval && part.removed || isAddition && part.added);
3788
4197
  const isPureUnpairedBlock = !finalPairContent && (isRemoval || isAddition);
3789
- const hasRealChange = hasInlineChange || isPureUnpairedBlock;
4198
+ const innerBgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : void 0;
3790
4199
  const finalNumColor = isRemoval || isAddition ? isRemoval ? "#d96868" : "#68d98c" : "gray";
3791
4200
  const finalPrefixColor = isRemoval ? "#ff4d4d" : "#4dff88";
3792
4201
  const displayPrefix = isRemoval ? "-" : isAddition ? "+" : " ";
@@ -3796,7 +4205,7 @@ var init_ChatLayout = __esm({
3796
4205
  return /* @__PURE__ */ React4.createElement(Text4, { color: blockColor }, wrapText(content, columns - 14));
3797
4206
  }
3798
4207
  if (!(isRemoval || isAddition) || words.length === 0 || !hasInlineChange) {
3799
- const textColor = isRemoval ? "#b34d4d" : isAddition ? "#4db36b" : "gray";
4208
+ const textColor = isRemoval ? "#885555" : isAddition ? "#558866" : "gray";
3800
4209
  return /* @__PURE__ */ React4.createElement(Text4, { color: textColor }, wrapText(content, columns - 14));
3801
4210
  }
3802
4211
  return /* @__PURE__ */ React4.createElement(Text4, { wrap: "anywhere" }, words.map((part, idx) => {
@@ -3807,7 +4216,7 @@ var init_ChatLayout = __esm({
3807
4216
  return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#ff3333" }, part.value);
3808
4217
  }
3809
4218
  if (part.added) return null;
3810
- return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#b34d4d" }, part.value);
4219
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#885555" }, part.value);
3811
4220
  }
3812
4221
  if (isAddition) {
3813
4222
  const isSurroundedByAddition = words[idx - 1]?.added || words[idx + 1]?.added;
@@ -3815,7 +4224,7 @@ var init_ChatLayout = __esm({
3815
4224
  return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#33ff66" }, part.value);
3816
4225
  }
3817
4226
  if (part.removed) return null;
3818
- return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#4db36b" }, part.value);
4227
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#558866" }, part.value);
3819
4228
  }
3820
4229
  return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "gray" }, part.value);
3821
4230
  }));
@@ -3826,7 +4235,37 @@ var init_ChatLayout = __esm({
3826
4235
  const match = text.match(/\[DIFF_START\]([\s\S]*?)\[DIFF_END\]/);
3827
4236
  const diffBody = match ? match[1].trim() : "";
3828
4237
  const diffLines = diffBody.split("\n");
3829
- return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 3, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingY: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " "))), diffLines.map((line, i) => /* @__PURE__ */ React4.createElement(DiffLine, { key: i, line, parentText: text, columns: columns - 3 })), /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " ")))));
4238
+ const parsedLines = diffLines.map((line) => {
4239
+ return {
4240
+ line,
4241
+ parsed: parseLineInfo(line),
4242
+ pairContent: null
4243
+ };
4244
+ });
4245
+ let currentGroup = [];
4246
+ for (let i = 0; i < parsedLines.length; i++) {
4247
+ const item = parsedLines[i];
4248
+ if (item.parsed && (item.parsed.isR || item.parsed.isA)) {
4249
+ currentGroup.push(item);
4250
+ } else {
4251
+ if (currentGroup.length > 0) {
4252
+ alignChangeGroup(currentGroup);
4253
+ currentGroup = [];
4254
+ }
4255
+ }
4256
+ }
4257
+ if (currentGroup.length > 0) {
4258
+ alignChangeGroup(currentGroup);
4259
+ }
4260
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 3, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingY: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " "))), parsedLines.map((item, i) => /* @__PURE__ */ React4.createElement(
4261
+ DiffLine,
4262
+ {
4263
+ key: i,
4264
+ line: item.line,
4265
+ pairContent: item.pairContent,
4266
+ columns: columns - 3
4267
+ }
4268
+ )), /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " ")))));
3830
4269
  });
3831
4270
  CodeRenderer = React4.memo(({ text, columns = 80 }) => {
3832
4271
  if (!text) return null;
@@ -3887,7 +4326,7 @@ var init_ChatLayout = __esm({
3887
4326
  paddingRight: 0,
3888
4327
  width: "100%"
3889
4328
  },
3890
- /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", bold: true }, "\u{1F4BB} ", lang.toUpperCase() || "CODE")),
4329
+ /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray", bold: true }, "\u25B6_ ", lang.toUpperCase() || "CODE")),
3891
4330
  /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: "100%" }, codeLines.map((line, idx) => /* @__PURE__ */ React4.createElement(Box3, { key: idx, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: gutterWidth + 2, flexShrink: 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, String(idx + 1).padStart(gutterWidth, " "), " ")), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "#fcfca4ff" }, line)))))
3892
4331
  );
3893
4332
  }
@@ -4214,12 +4653,35 @@ var init_ChatLayout = __esm({
4214
4653
  // src/components/StatusBar.jsx
4215
4654
  import React5 from "react";
4216
4655
  import { Box as Box4, Text as Text5 } from "ink";
4656
+ import { useState as useState5, useEffect as useEffect4 } from "react";
4217
4657
  var StatusBar, StatusBar_default;
4218
4658
  var init_StatusBar = __esm({
4219
4659
  "src/components/StatusBar.jsx"() {
4220
4660
  init_text();
4221
4661
  StatusBar = React5.memo(({ mode, thinkingLevel, tokens = "0.0k", tokensTotal = "0.0k", chatId = "NEW-SESSION", isMemoryEnabled = true, apiTier = "Free", aiProvider = "Google" }) => {
4222
4662
  const modeIcon = mode === "Flux" ? "" : "";
4663
+ const [memoryUsage, setMemoryUsage] = useState5(0);
4664
+ const [memoryLimit, setMemoryLimit] = useState5(0);
4665
+ const [memoryUnit, setMemoryUnit] = useState5("MB");
4666
+ useEffect4(() => {
4667
+ const getMemoryInfo = () => {
4668
+ const usage = process.memoryUsage();
4669
+ const isGB = usage.heapTotal / (1024 * 1024) >= 1024;
4670
+ const currentUnit = isGB ? "GB" : "MB";
4671
+ const formatToNumber = (bytes, toGB) => {
4672
+ const converted = bytes / (1024 * 1024 * (toGB ? 1024 : 1));
4673
+ return toGB ? parseFloat(converted.toFixed(2)) : Math.round(converted);
4674
+ };
4675
+ setMemoryUnit(currentUnit);
4676
+ setMemoryLimit(formatToNumber(usage.heapTotal, isGB));
4677
+ setMemoryUsage(formatToNumber(usage.heapUsed, isGB));
4678
+ };
4679
+ getMemoryInfo();
4680
+ const interval = setInterval(() => {
4681
+ getMemoryInfo();
4682
+ }, 3e3);
4683
+ return () => clearInterval(interval);
4684
+ }, []);
4223
4685
  let maxLimit = 256e3;
4224
4686
  if (aiProvider === "DeepSeek" || aiProvider === "Google" && apiTier === "Paid") {
4225
4687
  maxLimit = 4e5;
@@ -4234,7 +4696,7 @@ var init_StatusBar = __esm({
4234
4696
  },
4235
4697
  /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Box4, { marginRight: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, modeIcon, " ", mode.toUpperCase())), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, thinkingLevel.toUpperCase())), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "MEM: "), /* @__PURE__ */ React5.createElement(Text5, { color: "white", bold: true }, isMemoryEnabled ? "ON" : "OFF"))),
4236
4698
  /* @__PURE__ */ React5.createElement(Box4, { flexGrow: 1, justifyContent: "center", paddingX: 2 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white", italic: true }, " ", truncatePath(process.cwd(), 35))),
4237
- /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white" }, " ", formatTokens(tokensTotal), " ", /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, (tokens / maxLimit * 100).toFixed(0), "%"))), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginLeft: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", italic: true }, " ", chatId), (apiTier === "Custom" || apiTier === "Paid") && /* @__PURE__ */ React5.createElement(Text5, { color: "white" }, " | ", /* @__PURE__ */ React5.createElement(Text5, { color: "gray", bold: true }, "CUSTOM"))))
4699
+ /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "white" }, " ", formatTokens(tokensTotal), " ", /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, (tokens / maxLimit * 100).toFixed(0), "%"))), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginX: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "grey", bold: true }, memoryUsage, "/", memoryLimit, " ", memoryUnit)), /* @__PURE__ */ React5.createElement(Text5, { color: "gray", dimColor: true }, "\u2503 "), /* @__PURE__ */ React5.createElement(Box4, { marginLeft: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "gray", italic: true }, " ", chatId), (apiTier === "Custom" || apiTier === "Paid") && /* @__PURE__ */ React5.createElement(Text5, { color: "white" }, " | ", /* @__PURE__ */ React5.createElement(Text5, { color: "gray", bold: true }, "CUSTOM"))))
4238
4700
  );
4239
4701
  });
4240
4702
  StatusBar_default = StatusBar;
@@ -4453,7 +4915,6 @@ var init_main_tools = __esm({
4453
4915
  TOOL_PROTOCOL = (mode, osDetected, isMultiModal, aiProvider) => `
4454
4916
  -- TOOL DEFINITIONS --
4455
4917
  Internal tools. **MUST use the EXACT syntax** [tool:functions.ToolName(args)]. **NO OTHER SYNTAX/MARKERS/BOUNDARY ALLOWED**
4456
- NO TOOL CALL INSIDE THINKING
4457
4918
 
4458
4919
  **TOOL USAGE POLICY:**
4459
4920
  - **MAX 3 TOOL CALLS PER TURN${mode === "Flux" ? " (EXCEPTION FOR Todo TOOL: 3+ CALLS ALLOWED, Run: Limit 1 OR 2 CONSECUTIVE Run)" : ""}. Next Turn, verify tool results, plan next**
@@ -4471,8 +4932,8 @@ ${mode === "Flux" ? `- WORKSPACE TOOLS (path = relative to CWD & WILL BE FIRST A
4471
4932
  3. [tool:functions.FileMap(path="path/file")]. Shows file structure, functions, class, import/export, variable
4472
4933
  4. [tool:functions.PatchFile(path="...", replaceContent1="full line/block", newContent1="...", ...MAX 6)]. Surgical Patch. **Multiple patch on same file/path? Use replaceContent2, newContent2 etc >>> multiple spams**. Unsure? ReadFile >> guessing. **MUST VERIFY DIFF**
4473
4934
  5. [tool:functions.WriteFile(path="...", content="...")]. Creates/Overwrites. File Exist? PatchFile > WriteFile. Verify Imports
4474
- 6. [tool:functions.SearchKeyword(keyword="...", file="optional")]. Global project search. If 'file' is provided, searches only that file. Finds definitions/logic without reading every file. Usage: Can search for relevent lines/logic area to read specifically for edit
4475
- 7. [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `${isPtyAvailable ? "Interactive " : ""}WINDOWS POWERSHELL ONLY` : `${isPtyAvailable ? "Interactive " : ""}WINDOWS CMD ONLY` : `${isPtyAvailable ? "Interactive " : ""}BASH`} command. Destructive/Irreversible ops -> Ask user
4935
+ 6. [tool:functions.SearchKeyword(keyword="...", file="optional", subString="true/false optional")]. Global project search. If 'file' is provided, searches only that file. Finds definitions/logic without reading every file. Usage: Can search for relevent lines/logic area to read specifically for edit
4936
+ 7. [tool:functions.Run(command="...")]. Runs ${osDetected === "Windows" ? isPsAvailable() ? `WINDOWS POWERSHELL ONLY` : `WINDOWS CMD ONLY` : `BASH`} command. Destructive/Irreversible ops -> Ask user
4476
4937
  8. [tool:functions.Todo(method="create/append/get", tasks=[ARRAY OF STRINGS], markDone=[ARRAY OF TASK STRINGS])]. Task List, Markdown IN ARRAY NOT ALLOWED. USAGE: ANALYZE USER REQUEST **IF** MULTIPLE TASK \u2192 BREAK DOWN TASK \u2192 CREATE TODO **BEFORE** DIVING IN. 'tasks' & 'markDone' OPTIONAL PARAMETERS WITH method 'get'. USE 'get' method WITH 'markDone' to mark task completed`.trim() : `- CREATIVE TOOLS (path = relative to CWD & WILL BE FIRST ARGUMENT, path separator: '/') -
4477
4938
  1. [tool:functions.WritePDF(path="...", content="...", orientation="...")]. PROACTIVE A4 PAGE BREAKS MUST IN CSS. HTML/CSS for PREMIUM layout
4478
4939
  2. [tool:functions.WriteDoc(path="...", content="...")]. A4 Word document
@@ -5130,9 +5591,10 @@ ${finalOutput}`);
5130
5591
  });
5131
5592
 
5132
5593
  // src/components/SettingsMenu.jsx
5133
- import React7, { useState as useState4 } from "react";
5134
- import { Box as Box6, Text as Text7, useInput as useInput2 } from "ink";
5594
+ import React7, { useState as useState6, useEffect as useEffect5 } from "react";
5595
+ import { Box as Box6, Text as Text7, useInput as useInput3 } from "ink";
5135
5596
  import TextInput from "ink-text-input";
5597
+ import v8 from "v8";
5136
5598
  function SettingsMenu({
5137
5599
  systemSettings,
5138
5600
  setSystemSettings,
@@ -5144,11 +5606,34 @@ function SettingsMenu({
5144
5606
  setMessages,
5145
5607
  aiProvider
5146
5608
  }) {
5147
- const [activeColumn, setActiveColumn] = useState4("categories");
5148
- const [selectedCategoryIndex, setSelectedCategoryIndex] = useState4(0);
5149
- const [selectedItemIndex, setSelectedItemIndex] = useState4(0);
5150
- const [editingItem, setEditingItem] = useState4(null);
5151
- const [editValue, setEditValue] = useState4("");
5609
+ const [activeColumn, setActiveColumn] = useState6("categories");
5610
+ const [selectedCategoryIndex, setSelectedCategoryIndex] = useState6(0);
5611
+ const [selectedItemIndex, setSelectedItemIndex] = useState6(0);
5612
+ const [editingItem, setEditingItem] = useState6(null);
5613
+ const [editValue, setEditValue] = useState6("");
5614
+ const [currentMemory, setCurrentMemory] = useState6(0);
5615
+ const [maxMemory, setMaxMemory] = useState6(0);
5616
+ const [memoryUnit, setMemoryUnit] = useState6("MB");
5617
+ useEffect5(() => {
5618
+ const maxLimitBytes = v8.getHeapStatistics().heap_size_limit;
5619
+ const isGB = maxLimitBytes >= 1024 * 1024 * 1024;
5620
+ const unitLabel = isGB ? "GB" : "MB";
5621
+ const divisor = isGB ? 1024 * 1024 * 1024 : 1024 * 1024;
5622
+ setMaxMemory(parseFloat((maxLimitBytes / divisor).toFixed(2)));
5623
+ setMemoryUnit(unitLabel);
5624
+ const getMemoryStats = () => {
5625
+ const usage = process.memoryUsage();
5626
+ const targetBytes = usage.rss;
5627
+ const converted = targetBytes / divisor;
5628
+ const formattedCurrent = isGB ? parseFloat(converted.toFixed(2)) : Math.round(converted);
5629
+ setCurrentMemory(formattedCurrent);
5630
+ };
5631
+ getMemoryStats();
5632
+ const interval = setInterval(() => {
5633
+ getMemoryStats();
5634
+ }, 5e3);
5635
+ return () => clearInterval(interval);
5636
+ }, []);
5152
5637
  const getCategoryItems = (catId) => {
5153
5638
  switch (catId) {
5154
5639
  case "memory":
@@ -5186,7 +5671,7 @@ function SettingsMenu({
5186
5671
  };
5187
5672
  const currentCatId = CATEGORIES[selectedCategoryIndex].id;
5188
5673
  const currentItems = getCategoryItems(currentCatId);
5189
- useInput2((input, key) => {
5674
+ useInput3((input, key) => {
5190
5675
  if (editingItem) {
5191
5676
  if (key.escape) {
5192
5677
  setEditingItem(null);
@@ -5408,7 +5893,10 @@ function SettingsMenu({
5408
5893
  });
5409
5894
  if (currentCatId === "other") {
5410
5895
  elements.push(
5411
- /* @__PURE__ */ React7.createElement(Box6, { key: "pty-notice", marginTop: 18, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "white" }, isPtyAvailable ? "\u2713 Advance Interactive Terminal Supported" : "\u26A0 Interactive Terminal is Limited"))
5896
+ /* @__PURE__ */ React7.createElement(Box6, { key: "pty-notice", marginTop: 17, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "white" }, isPtyAvailable ? "\u2713 Advance Interactive Terminal Supported" : "\u26A0 Interactive Terminal is Limited"))
5897
+ );
5898
+ elements.push(
5899
+ /* @__PURE__ */ React7.createElement(Box6, { key: "memory-load-2026", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "Memory Load: ", currentMemory, "/", maxMemory, " ", memoryUnit))
5412
5900
  );
5413
5901
  }
5414
5902
  if (hasConflict) {
@@ -5451,13 +5939,13 @@ var init_SettingsMenu = __esm({
5451
5939
  });
5452
5940
 
5453
5941
  // src/components/ProfileForm.jsx
5454
- import React8, { useState as useState5, useEffect as useEffect4 } from "react";
5942
+ import React8, { useState as useState7, useEffect as useEffect6 } from "react";
5455
5943
  import { Box as Box7, Text as Text8 } from "ink";
5456
5944
  import TextInput2 from "ink-text-input";
5457
5945
  function ProfileForm({ initialData, onSave, onCancel }) {
5458
- const [step, setStep] = useState5(0);
5459
- const [currentInput, setCurrentInput] = useState5("");
5460
- const [profile, setProfile] = useState5(() => ({
5946
+ const [step, setStep] = useState7(0);
5947
+ const [currentInput, setCurrentInput] = useState7("");
5948
+ const [profile, setProfile] = useState7(() => ({
5461
5949
  name: initialData?.name || "",
5462
5950
  nickname: initialData?.nickname || "",
5463
5951
  instructions: initialData?.instructions || ""
@@ -5467,7 +5955,7 @@ function ProfileForm({ initialData, onSave, onCancel }) {
5467
5955
  { key: "nickname", label: "Enter a Nickname (Agent will use this): " },
5468
5956
  { key: "instructions", label: "System Instructions (Persona overrides): " }
5469
5957
  ];
5470
- useEffect4(() => {
5958
+ useEffect6(() => {
5471
5959
  const currentKey = steps[step].key;
5472
5960
  setCurrentInput(profile[currentKey] || "");
5473
5961
  }, [step, profile]);
@@ -5515,19 +6003,19 @@ var init_ProfileForm = __esm({
5515
6003
  });
5516
6004
 
5517
6005
  // src/components/AskUserModal.jsx
5518
- import React9, { useState as useState6 } from "react";
5519
- import { Box as Box8, Text as Text9, useInput as useInput3 } from "ink";
6006
+ import React9, { useState as useState8 } from "react";
6007
+ import { Box as Box8, Text as Text9, useInput as useInput4 } from "ink";
5520
6008
  import TextInput3 from "ink-text-input";
5521
6009
  var AskUserModal, AskUserModal_default;
5522
6010
  var init_AskUserModal = __esm({
5523
6011
  "src/components/AskUserModal.jsx"() {
5524
6012
  init_terminal();
5525
6013
  AskUserModal = ({ question, options, onResolve }) => {
5526
- const [isSuggestingElse, setIsSuggestingElse] = useState6(false);
5527
- const [customInput, setCustomInput] = useState6("");
5528
- const [selectedIndex, setSelectedIndex] = useState6(0);
6014
+ const [isSuggestingElse, setIsSuggestingElse] = useState8(false);
6015
+ const [customInput, setCustomInput] = useState8("");
6016
+ const [selectedIndex, setSelectedIndex] = useState8(0);
5529
6017
  const allOptions = [...options, { id: "CUSTOM", label: "Suggest something else...", description: "Provide a custom response" }];
5530
- useInput3((input, key) => {
6018
+ useInput4((input, key) => {
5531
6019
  if (isSuggestingElse) return;
5532
6020
  if (key.leftArrow || key.upArrow) {
5533
6021
  setSelectedIndex((prev) => Math.max(0, prev - 1));
@@ -5757,8 +6245,8 @@ ${projectContextBlock}
5757
6245
 
5758
6246
  -- FORMATTING --
5759
6247
  - GFM Supported
5760
- - NO CHAT **AFTER** FIRING TOOLS IN THIS TURN
5761
- - End final response with summary of changes made and files edited
6248
+ - NO CHAT **AFTER** FIRING TOOLS IN CURRENT TURN
6249
+ - Task Complete & Results Verified? End response with summary of changes made and files edited
5762
6250
  - Basic LaTeX${mode === "Flux" ? "" : ". Kaomojis"}
5763
6251
  === END SYSTEM PROMPT ===`.trim();
5764
6252
  };
@@ -5996,27 +6484,61 @@ var init_history = __esm({
5996
6484
  return nextLock;
5997
6485
  };
5998
6486
  loadHistory = async () => {
6487
+ await fs7.ensureDir(HISTORY_DIR);
6488
+ let history = {};
5999
6489
  if (await fs7.pathExists(HISTORY_FILE)) {
6000
6490
  try {
6001
- return readEncryptedJson(HISTORY_FILE, {});
6491
+ history = readEncryptedJson(HISTORY_FILE, {});
6002
6492
  } catch (e) {
6003
- return {};
6493
+ history = {};
6004
6494
  }
6005
6495
  }
6006
- return {};
6496
+ for (const id in history) {
6497
+ const chatFile = path6.join(HISTORY_DIR, `${id}.json`);
6498
+ Object.defineProperty(history[id], "messages", {
6499
+ get: () => {
6500
+ if (fs7.existsSync(chatFile)) {
6501
+ try {
6502
+ return readEncryptedJson(chatFile, []);
6503
+ } catch (e) {
6504
+ return [];
6505
+ }
6506
+ }
6507
+ return [];
6508
+ },
6509
+ set: (msgs) => {
6510
+ try {
6511
+ writeEncryptedJson(chatFile, msgs);
6512
+ } catch (e) {
6513
+ }
6514
+ },
6515
+ enumerable: false,
6516
+ configurable: true
6517
+ });
6518
+ }
6519
+ return history;
6007
6520
  };
6008
6521
  saveChat = async (id, name, messages) => {
6009
6522
  return withLock(async () => {
6523
+ await fs7.ensureDir(HISTORY_DIR);
6010
6524
  const history = await loadHistory();
6011
6525
  const existingChat = history[id];
6012
6526
  const persistentMessages = (messages || []).filter((m) => !m.isUpdateNotification && !m.isMeta);
6013
6527
  const finalName = name || (existingChat ? existingChat.name : `Session ${id.slice(-6)}`);
6528
+ const chatFile = path6.join(HISTORY_DIR, `${id}.json`);
6529
+ writeEncryptedJson(chatFile, persistentMessages);
6014
6530
  history[id] = {
6015
6531
  name: finalName,
6016
- messages: persistentMessages,
6017
6532
  updatedAt: Date.now()
6018
6533
  };
6019
- writeEncryptedJson(HISTORY_FILE, history);
6534
+ const indexHistory = {};
6535
+ for (const chatId in history) {
6536
+ indexHistory[chatId] = {
6537
+ name: history[chatId].name,
6538
+ updatedAt: history[chatId].updatedAt
6539
+ };
6540
+ }
6541
+ writeEncryptedJson(HISTORY_FILE, indexHistory);
6020
6542
  });
6021
6543
  };
6022
6544
  saveChatTitle = async (id, title) => {
@@ -6026,16 +6548,30 @@ var init_history = __esm({
6026
6548
  history[id].name = title;
6027
6549
  history[id].updatedAt = Date.now();
6028
6550
  } else {
6029
- history[id] = { name: title, messages: [], updatedAt: Date.now() };
6551
+ history[id] = { name: title, updatedAt: Date.now() };
6552
+ }
6553
+ const indexHistory = {};
6554
+ for (const chatId in history) {
6555
+ indexHistory[chatId] = {
6556
+ name: history[chatId].name,
6557
+ updatedAt: history[chatId].updatedAt
6558
+ };
6030
6559
  }
6031
- writeEncryptedJson(HISTORY_FILE, history);
6560
+ writeEncryptedJson(HISTORY_FILE, indexHistory);
6032
6561
  });
6033
6562
  };
6034
6563
  deleteChat = async (id) => {
6035
6564
  return withLock(async () => {
6036
6565
  const history = await loadHistory();
6037
6566
  delete history[id];
6038
- writeEncryptedJson(HISTORY_FILE, history);
6567
+ const indexHistory = {};
6568
+ for (const chatId in history) {
6569
+ indexHistory[chatId] = {
6570
+ name: history[chatId].name,
6571
+ updatedAt: history[chatId].updatedAt
6572
+ };
6573
+ }
6574
+ writeEncryptedJson(HISTORY_FILE, indexHistory);
6039
6575
  if (await fs7.pathExists(CONTEXT_FILE)) {
6040
6576
  try {
6041
6577
  const contextData = readEncryptedJson(CONTEXT_FILE, []);
@@ -6057,6 +6593,13 @@ var init_history = __esm({
6057
6593
  writeEncryptedJson(TEMP_MEM_CHAT_FILE, cache);
6058
6594
  }
6059
6595
  await RevertManager.deleteChatBackups(id);
6596
+ const chatFile = path6.join(HISTORY_DIR, `${id}.json`);
6597
+ if (await fs7.pathExists(chatFile)) {
6598
+ try {
6599
+ await fs7.remove(chatFile);
6600
+ } catch (e) {
6601
+ }
6602
+ }
6060
6603
  return history;
6061
6604
  });
6062
6605
  };
@@ -7759,8 +8302,10 @@ var init_search_keyword = __esm({
7759
8302
  "src/tools/search_keyword.js"() {
7760
8303
  init_arg_parser();
7761
8304
  search_keyword = async (args) => {
7762
- const { keyword, file } = parseArgs(args);
8305
+ const { keyword, file, subString } = parseArgs(args);
7763
8306
  if (!keyword) return 'ERROR: Missing "keyword" argument.';
8307
+ const matchSubstring = subString === true || subString === "true" || subString === 1 || subString === "1" || subString === "true";
8308
+ const wordRegex = new RegExp(`(?<![\\w])${keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w])`, "i");
7764
8309
  const excludes = [
7765
8310
  "node_modules",
7766
8311
  ".git",
@@ -7800,7 +8345,8 @@ var init_search_keyword = __esm({
7800
8345
  const lines = content.split(/\r?\n/);
7801
8346
  const fileMatches = [];
7802
8347
  for (let i = 0; i < lines.length; i++) {
7803
- if (lines[i].includes(keyword)) {
8348
+ const matched = matchSubstring ? lines[i].toLowerCase().includes(keyword.toLowerCase()) : wordRegex.test(lines[i]);
8349
+ if (matched) {
7804
8350
  const displayPath = fileObj.relativePath.replace(/\\/g, "/");
7805
8351
  fileMatches.push(`${displayPath} \u2192 ${i + 1}`);
7806
8352
  }
@@ -7816,9 +8362,9 @@ var init_search_keyword = __esm({
7816
8362
  global.gc();
7817
8363
  }
7818
8364
  if (matches.length === 0) {
7819
- return `Found 0 matches for keyword: "${keyword}"${file ? ` in file: ${file}` : ". Try to specify files"}`;
8365
+ return `Found 0 matches for keyword: "${keyword}"${file ? ` in file: ${file}` : ". Try to specify files"} ${matchSubstring ? "(subString mode)" : ""}`;
7820
8366
  }
7821
- let output = `Found ${matches.length} matches:
8367
+ let output = `Found ${matches.length} matches ${matchSubstring ? "(subString mode)" : ""}:
7822
8368
 
7823
8369
  `;
7824
8370
  output += matches.join("\n");
@@ -8796,7 +9342,7 @@ var init_editor = __esm({
8796
9342
  import { GoogleGenAI, ThinkingLevel, HarmBlockThreshold, HarmCategory } from "@google/genai";
8797
9343
  import path19 from "path";
8798
9344
  import fs20 from "fs";
8799
- var client, globalSettings, colorMainWords, TERMINATION_SIGNAL, MULTIMODAL_MODELS, isModelMultimodal, getCleanGroupedLength, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getNVIDIAStream, getOpenRouterStream, signalTermination, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, compressHistory, deleteChatSummary, getAIStream;
9345
+ var client, globalSettings, colorMainWords, TERMINATION_SIGNAL, MULTIMODAL_MODELS, isModelMultimodal, getCleanGroupedLength, stripAnsi2, fetchWithBackoff, getDeepSeekStream, getNVIDIAStream, getOpenRouterStream, signalTermination, TOOL_LABELS2, getToolDetail, runJanitorTask, getActiveToolContext, getContextSafeText, contextSafeReplace, getSanitizedText, translateKimiToolCalls, detectToolCalls, initAI, generateSimpleContent, consolidatePastMemories, compressHistory, deleteChatSummary, getAIStream;
8800
9346
  var init_ai = __esm({
8801
9347
  async "src/utils/ai.js"() {
8802
9348
  await init_prompts();
@@ -8816,7 +9362,7 @@ var init_ai = __esm({
8816
9362
  colorMainWords = (label2) => {
8817
9363
  if (!label2) return label2;
8818
9364
  return label2.replace(/(?:(\x1b\[\d+m))?([✔✗✖🔍📖→➕↻•])(?:(\x1b\[\d+m))?\s*\b(Created|Read|Edited|Viewed|Auto-Read|List|Generated|Written|Searched|Get Map|Write Canceled|Edit Canceled|Write Cancelled|Edit Denied|Visited|Updated|Reviewed)\b/ig, (match, ansiBefore, icon, ansiAfter, word) => {
8819
- return `${ansiBefore || ""}${icon}${ansiAfter || ""} \x1B[95m${word}\x1B[0m`;
9365
+ return `${ansiBefore || ""}${icon}${ansiAfter || ""} \x1B[95m${word}\x1B[0m`;
8820
9366
  });
8821
9367
  };
8822
9368
  TERMINATION_SIGNAL = false;
@@ -8836,13 +9382,11 @@ var init_ai = __esm({
8836
9382
  "moonshotai/kimi-k2.6",
8837
9383
  // NVIDIA vision models
8838
9384
  "moonshotai/kimi-k2.6",
9385
+ "stepfun-ai/step-3.7-flash",
9386
+ "google/gemma-4-31b-it",
9387
+ "mistralai/mistral-medium-3.5-128b"
8839
9388
  // Google models
8840
- "gemma-4-31b-it",
8841
- "gemini-2.5-flash",
8842
- "gemini-3-flash-preview",
8843
- "gemini-3.5-flash",
8844
- "gemini-3.1-flash-lite",
8845
- "gemini-3.1-pro-preview"
9389
+ // No need. All models on Gemini API is Multimodal
8846
9390
  ];
8847
9391
  isModelMultimodal = (model) => {
8848
9392
  if (!model) return false;
@@ -8906,7 +9450,7 @@ var init_ai = __esm({
8906
9450
  }
8907
9451
  return fetch(url, options);
8908
9452
  };
8909
- getDeepSeekStream = async function* (apiKey, model, contents, systemInstruction, thinkingLevel, mode, isMultiModal, signal, temperature = 0.9) {
9453
+ getDeepSeekStream = async function* (apiKey, model, contents, systemInstruction, thinkingLevel, mode, isMultiModal, signal, temperature = 0.99) {
8910
9454
  const messages = [];
8911
9455
  if (systemInstruction) {
8912
9456
  messages.push({ role: "system", content: systemInstruction });
@@ -9038,7 +9582,7 @@ var init_ai = __esm({
9038
9582
  }
9039
9583
  }
9040
9584
  };
9041
- getNVIDIAStream = async function* (apiKey, model, contents, systemInstruction, thinkingLevel, mode, isMultiModal = false, signal, temperature = 0.8) {
9585
+ getNVIDIAStream = async function* (apiKey, model, contents, systemInstruction, thinkingLevel, mode, isMultiModal = false, signal, temperature = 0.99) {
9042
9586
  const messages = [];
9043
9587
  if (systemInstruction) {
9044
9588
  messages.push({ role: "system", content: systemInstruction });
@@ -9054,7 +9598,7 @@ var init_ai = __esm({
9054
9598
  const mimeType = part.inlineData.mimeType;
9055
9599
  const data = part.inlineData.data;
9056
9600
  const isImage = mimeType.startsWith("image/");
9057
- if (isImage) {
9601
+ if (isImage && MULTIMODAL_MODELS.includes(model)) {
9058
9602
  msgContent.push({
9059
9603
  type: "image_url",
9060
9604
  image_url: {
@@ -9078,7 +9622,7 @@ var init_ai = __esm({
9078
9622
  "High": "High",
9079
9623
  "xHigh": "High"
9080
9624
  };
9081
- const apiLevel = thinkingLevelMap[thinkingLevel] || "Standard";
9625
+ const apiLevel = thinkingLevelMap[thinkingLevel] || "High";
9082
9626
  const isThinking = apiLevel !== "Fast";
9083
9627
  const isKimi = model.includes("kimi");
9084
9628
  const isGemma = model.includes("gemma");
@@ -9086,6 +9630,15 @@ var init_ai = __esm({
9086
9630
  const isGlm = model.includes("glm");
9087
9631
  const isMistral = model.includes("mistral");
9088
9632
  const isMinimax = model.includes("minimax");
9633
+ const isGPT = model.includes("gpt");
9634
+ const GPT_THINKING_LEVELS = {
9635
+ "Fast": "low",
9636
+ "Low": "low",
9637
+ "Medium": "medium",
9638
+ "Standard": "medium",
9639
+ "High": "high",
9640
+ "xHigh": "high"
9641
+ };
9089
9642
  const maxTokens = isMinimax || isDeepSeek ? 16384 : 32768;
9090
9643
  const body = {
9091
9644
  model,
@@ -9093,7 +9646,8 @@ var init_ai = __esm({
9093
9646
  max_tokens: maxTokens,
9094
9647
  stream: true,
9095
9648
  stream_options: { include_usage: true },
9096
- temperature
9649
+ temperature,
9650
+ ...isGPT && { thinking: GPT_THINKING_LEVELS[thinkingLevel] || "high" }
9097
9651
  };
9098
9652
  if (isKimi) {
9099
9653
  body.chat_template_kwargs = { thinking: isThinking };
@@ -9190,7 +9744,7 @@ var init_ai = __esm({
9190
9744
  }
9191
9745
  }
9192
9746
  };
9193
- getOpenRouterStream = async function* (apiKey, model, contents, systemInstruction, thinkingLevel, mode, isMultiModal, signal, temperature = 0.5) {
9747
+ getOpenRouterStream = async function* (apiKey, model, contents, systemInstruction, thinkingLevel, mode, isMultiModal, signal, temperature = 0.95) {
9194
9748
  const messages = [];
9195
9749
  if (systemInstruction) {
9196
9750
  messages.push({ role: "system", content: systemInstruction });
@@ -9437,7 +9991,7 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
9437
9991
  mode,
9438
9992
  false,
9439
9993
  null,
9440
- 0.4
9994
+ 0.75
9441
9995
  );
9442
9996
  const iterator2 = stream[Symbol.asyncIterator]();
9443
9997
  const firstResult2 = await iterator2.next();
@@ -9453,7 +10007,7 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
9453
10007
  mode,
9454
10008
  false,
9455
10009
  null,
9456
- 0.4
10010
+ 0.75
9457
10011
  );
9458
10012
  const iterator2 = stream[Symbol.asyncIterator]();
9459
10013
  const firstResult2 = await iterator2.next();
@@ -9469,7 +10023,7 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
9469
10023
  mode,
9470
10024
  false,
9471
10025
  null,
9472
- 0.4
10026
+ 0.75
9473
10027
  );
9474
10028
  const iterator2 = stream[Symbol.asyncIterator]();
9475
10029
  const firstResult2 = await iterator2.next();
@@ -9481,7 +10035,7 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
9481
10035
  config: {
9482
10036
  systemInstruction: janitorPrompt,
9483
10037
  maxOutputTokens: 512,
9484
- temperature: 0.4,
10038
+ temperature: 0.75,
9485
10039
  safetySettings: [
9486
10040
  { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
9487
10041
  { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
@@ -9778,9 +10332,69 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
9778
10332
  getSanitizedText = (text) => {
9779
10333
  return getContextSafeText(text, true);
9780
10334
  };
10335
+ translateKimiToolCalls = (text) => {
10336
+ if (!text) return text;
10337
+ const PASCAL_MAP = {
10338
+ "patchfile": "PatchFile",
10339
+ "writefile": "WriteFile",
10340
+ "readfile": "ReadFile",
10341
+ "viewfile": "ReadFile",
10342
+ "run": "Run",
10343
+ "execcommand": "Run",
10344
+ "searchkeyword": "SearchKeyword",
10345
+ "websearch": "WebSearch",
10346
+ "webscrape": "WebScrape",
10347
+ "readfolder": "ReadFolder",
10348
+ "writepdf": "WritePDF",
10349
+ "writedoc": "WriteDoc",
10350
+ "writedocx": "WriteDoc",
10351
+ "filemap": "FileMap",
10352
+ "generateimage": "GenerateImage",
10353
+ "todo": "Todo",
10354
+ "ask": "Ask"
10355
+ };
10356
+ const toPascalCase = (str) => {
10357
+ return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
10358
+ };
10359
+ const kimiRegex = /<\|\s*tool_call_begin\s*\|>\s*(?:(?:tool|functions)\b[\s._]*)*([a-zA-Z0-9_]+)(?::\d+)?\s*<\|\s*tool_call_argument_begin\s*\|>([\s\S]*?)<\|\s*tool_call_end\s*\|>/gi;
10360
+ let result = text.replace(kimiRegex, (match, toolName, argsJsonStr) => {
10361
+ let parsedArgs = "";
10362
+ try {
10363
+ const argsObj = JSON.parse(argsJsonStr.trim());
10364
+ if (argsObj && typeof argsObj === "object") {
10365
+ const argPairs = Object.entries(argsObj).map(([key, val]) => {
10366
+ const stringVal = typeof val === "string" ? val : JSON.stringify(val);
10367
+ return `${key}=${JSON.stringify(stringVal)}`;
10368
+ });
10369
+ parsedArgs = argPairs.join(", ");
10370
+ }
10371
+ } catch (e) {
10372
+ const pairs = [];
10373
+ const pairRegex = /"([^"]+)"\s*:\s*(?:"([^"]*)"|(\d+)|true|false|null)/g;
10374
+ let pMatch;
10375
+ while ((pMatch = pairRegex.exec(argsJsonStr)) !== null) {
10376
+ const key = pMatch[1];
10377
+ const val = pMatch[2] !== void 0 ? pMatch[2] : pMatch[0].split(":").slice(1).join(":").trim();
10378
+ pairs.push(`${key}=${JSON.stringify(val)}`);
10379
+ }
10380
+ if (pairs.length > 0) {
10381
+ parsedArgs = pairs.join(", ");
10382
+ } else {
10383
+ parsedArgs = argsJsonStr.trim();
10384
+ }
10385
+ }
10386
+ const cleanKey = toolName.toLowerCase().replace(/_/g, "");
10387
+ const normToolName = PASCAL_MAP[cleanKey] || toPascalCase(toolName);
10388
+ return `[tool:functions.${normToolName}(${parsedArgs})]`;
10389
+ });
10390
+ result = result.replace(/<\|\s*tool_calls_section_begin\s*\|>/gi, "");
10391
+ result = result.replace(/<\|\s*tool_calls_section_end\s*\|>/gi, "");
10392
+ return result;
10393
+ };
9781
10394
  detectToolCalls = (text) => {
9782
10395
  if (!text) return [];
9783
- const cleanText = text.replace(/(?:<(think|thought|thoughts)>|\[(think|thought|thoughts)\])[\s\S]*?(?:<\/(think|thought|thoughts)>|\[\/(think|thought|thoughts)\]|$)/gi, "");
10396
+ const translatedText = translateKimiToolCalls(text);
10397
+ const cleanText = translatedText.replace(/(?:<(think|thought|thoughts)>|\[(think|thought|thoughts)\])[\s\S]*?(?:<\/(think|thought|thoughts)>|\[\/(think|thought|thoughts)\]|$)/gi, "");
9784
10398
  const results = [];
9785
10399
  const toolRegex = /\[\s*tool:functions\.([a-z0-9_]+)\s*\(/gi;
9786
10400
  let match;
@@ -9841,7 +10455,7 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
9841
10455
  client = new GoogleGenAI({ apiKey });
9842
10456
  return client;
9843
10457
  };
9844
- generateSimpleContent = async (settings, model, contents, systemInstruction, thinkingLevel = "Fast", temperature = 0.4) => {
10458
+ generateSimpleContent = async (settings, model, contents, systemInstruction, thinkingLevel = "Fast", temperature = 0.75) => {
9845
10459
  const { aiProvider = "Google", apiKey, mode } = settings;
9846
10460
  let fullText = "";
9847
10461
  let usageMetadata = null;
@@ -9859,7 +10473,6 @@ ${originalTextProcessed.length > USER_CONTEXT_LENGTH ? "... (truncated) ...\n\n"
9859
10473
  contents: normalizedContents,
9860
10474
  config: {
9861
10475
  systemInstruction,
9862
- maxOutputTokens: 2048,
9863
10476
  temperature,
9864
10477
  thinkingConfig: { includeThoughts: false, thinkingLevel: ThinkingLevel.MINIMAL }
9865
10478
  }
@@ -10695,7 +11308,9 @@ ${boxMid}`) };
10695
11308
  if (taggedContextBlocks.length > 0) {
10696
11309
  taggedContextStr = "[TAGGED CONTEXT]\n" + taggedContextBlocks.join("\n\n") + "\n[/TAGGED CONTEXT]\n";
10697
11310
  }
11311
+ const osDetected = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
10698
11312
  const firstUserMsg = `[SYSTEM METADATA (PRIORITY: DYNAMIC), Chat Context >> Metadata] Time: ${dateTimeStr}
11313
+ OS: ${osDetected}
10699
11314
  CWD: ${process.cwd()}${isPlayground ? " [PLAYGROUND MODE]" : ""}${cwdMismatch ? ` (WARNING: CWD Mismatch! Previous Path: ${lastCwd})` : ""}
10700
11315
  **DIRECTORY STRUCTURE**
10701
11316
  ${dirStructure}${memoryPrompt}${ideBlock}
@@ -10774,6 +11389,7 @@ ${activeSummaryBlock}${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" &&
10774
11389
  let isStutteringLoop = false;
10775
11390
  let isGeneralLoop = false;
10776
11391
  let isInitialAttempt = true;
11392
+ let lastLoopCheckLen = 0;
10777
11393
  let accumulatedContext = "";
10778
11394
  let dedupeBuffer = "";
10779
11395
  let isDedupeActive = false;
@@ -10878,7 +11494,7 @@ ${activeSummaryBlock}${thinkingLevel !== "Fast" && thinkingLevel !== "xHigh" &&
10878
11494
  } else if (retryCount > 0) {
10879
11495
  yield { type: "model_update", content: null };
10880
11496
  }
10881
- currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? "GEM" : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt, aiProvider, isMultiModal);
11497
+ currentSystemInstruction = getSystemInstruction(profile, !(targetModel || "gemma").toLowerCase().startsWith("gemma") ? "GEM" : thinkingLevel, mode, systemSettings, isMemoryEnabled, isFirstPrompt, aiProvider, aiProvider === "Google" ? true : isMultiModal);
10882
11498
  const lastUserMsg = contents[contents.length - 1];
10883
11499
  if (isBridgeConnected() & loop > 0) {
10884
11500
  yield { type: "status", content: "Checking Code..." };
@@ -10926,7 +11542,7 @@ ${ideErr} [/ERROR]`;
10926
11542
  mode,
10927
11543
  isMultiModal,
10928
11544
  abortController.signal,
10929
- 0.5
11545
+ 0.95
10930
11546
  );
10931
11547
  } else if (aiProvider === "DeepSeek") {
10932
11548
  stream = getDeepSeekStream(
@@ -10938,7 +11554,7 @@ ${ideErr} [/ERROR]`;
10938
11554
  mode,
10939
11555
  isMultiModal,
10940
11556
  abortController.signal,
10941
- 0.9
11557
+ 0.99
10942
11558
  );
10943
11559
  } else if (aiProvider === "NVIDIA") {
10944
11560
  stream = getNVIDIAStream(
@@ -10950,7 +11566,7 @@ ${ideErr} [/ERROR]`;
10950
11566
  mode,
10951
11567
  isMultiModal,
10952
11568
  abortController.signal,
10953
- 0.8
11569
+ 0.99
10954
11570
  );
10955
11571
  } else {
10956
11572
  const apiCallPromise = client.models.generateContentStream({
@@ -10959,7 +11575,7 @@ ${ideErr} [/ERROR]`;
10959
11575
  config: {
10960
11576
  systemInstruction: currentSystemInstruction,
10961
11577
  mediaResolution: "MEDIA_RESOLUTION_MEDIUM",
10962
- temperature: 1.05,
11578
+ temperature: 1,
10963
11579
  safetySettings: [
10964
11580
  { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
10965
11581
  { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
@@ -11037,9 +11653,13 @@ ${ideErr} [/ERROR]`;
11037
11653
  if (!isBufferingToolCall) {
11038
11654
  const toolIdx = remaining.indexOf("[tool");
11039
11655
  const endIdx = remaining.indexOf("[[END]]");
11656
+ const kimiSectionIdx = remaining.indexOf("<|tool_calls_section_begin|>");
11657
+ const kimiCallIdx = remaining.indexOf("<|tool_call_begin|>");
11040
11658
  const indices = [
11041
11659
  { type: "tool", idx: toolIdx, start: "[tool", end: "]" },
11042
- { type: "end", idx: endIdx, start: "[[END]]", end: "[[END]]" }
11660
+ { type: "end", idx: endIdx, start: "[[END]]", end: "[[END]]" },
11661
+ { type: "kimi_section", idx: kimiSectionIdx, start: "<|tool_calls_section_begin|>", end: "<|tool_calls_section_end|>" },
11662
+ { type: "kimi_call", idx: kimiCallIdx, start: "<|tool_call_begin|>", end: "<|tool_call_end|>" }
11043
11663
  ].filter((i) => i.idx !== -1).sort((a, b) => a.idx - b.idx);
11044
11664
  if (indices.length > 0) {
11045
11665
  const match2 = indices[0];
@@ -11051,13 +11671,17 @@ ${ideErr} [/ERROR]`;
11051
11671
  toolCallBuffer = "";
11052
11672
  remaining = remaining.substring(match2.idx);
11053
11673
  } else {
11054
- const potentialStarts = ["[tool", "[[END]]"];
11674
+ const potentialStarts = ["[tool", "[[END]]", "<|tool_calls_section_begin|>", "<|tool_call_begin|>"];
11055
11675
  let splitPoint = -1;
11056
11676
  for (const start of potentialStarts) {
11057
11677
  for (let len = start.length - 1; len > 0; len--) {
11058
11678
  if (remaining.endsWith(start.substring(0, len))) {
11059
11679
  splitPoint = remaining.length - len;
11060
- activeBufferType = potentialStarts.indexOf(start) === 0 ? "tool" : "end";
11680
+ const idx = potentialStarts.indexOf(start);
11681
+ if (idx === 0) activeBufferType = "tool";
11682
+ else if (idx === 1) activeBufferType = "end";
11683
+ else if (idx === 2) activeBufferType = "kimi_section";
11684
+ else activeBufferType = "kimi_call";
11061
11685
  break;
11062
11686
  }
11063
11687
  }
@@ -11076,7 +11700,6 @@ ${ideErr} [/ERROR]`;
11076
11700
  }
11077
11701
  }
11078
11702
  } else {
11079
- const endTag = activeBufferType === "tool" ? "]" : "[[END]]";
11080
11703
  const combined = toolCallBuffer + remaining;
11081
11704
  if (activeBufferType === "tool") {
11082
11705
  const protocolPrefix = "[tool:functions.";
@@ -11090,6 +11713,7 @@ ${ideErr} [/ERROR]`;
11090
11713
  }
11091
11714
  }
11092
11715
  let endIdx = -1;
11716
+ let endTag = "]";
11093
11717
  if (activeBufferType === "tool") {
11094
11718
  let balance = 0;
11095
11719
  let inString = null;
@@ -11127,19 +11751,27 @@ ${ideErr} [/ERROR]`;
11127
11751
  }
11128
11752
  }
11129
11753
  } else {
11754
+ if (activeBufferType === "end") endTag = "[[END]]";
11755
+ else if (activeBufferType === "kimi_section") endTag = "<|tool_calls_section_end|>";
11756
+ else if (activeBufferType === "kimi_call") endTag = "<|tool_call_end|>";
11130
11757
  endIdx = combined.indexOf(endTag);
11131
11758
  }
11132
11759
  if (endIdx !== -1) {
11133
- const fullMatch = combined.substring(0, endIdx + 1);
11134
- msgs.push({ type: "text", content: fullMatch });
11760
+ const endLen = endTag.length;
11761
+ if (!activeBufferType.startsWith("kimi")) {
11762
+ const fullMatch = combined.substring(0, endIdx + endLen);
11763
+ msgs.push({ type: "text", content: fullMatch });
11764
+ }
11135
11765
  toolCallBuffer = "";
11136
11766
  isBufferingToolCall = false;
11137
11767
  activeBufferType = null;
11138
- remaining = combined.substring(endIdx + 1);
11768
+ remaining = combined.substring(endIdx + endLen);
11139
11769
  } else {
11140
- const MAX_BUFFER = 512;
11770
+ const MAX_BUFFER = activeBufferType.startsWith("kimi") ? 8192 : 512;
11141
11771
  if (combined.length > MAX_BUFFER) {
11142
- msgs.push({ type: "text", content: combined });
11772
+ if (!activeBufferType.startsWith("kimi")) {
11773
+ msgs.push({ type: "text", content: combined });
11774
+ }
11143
11775
  toolCallBuffer = "";
11144
11776
  isBufferingToolCall = false;
11145
11777
  } else {
@@ -11237,7 +11869,6 @@ ${ideErr} [/ERROR]`;
11237
11869
  for (const m of msgs) yield m;
11238
11870
  }
11239
11871
  }
11240
- const signalSafeText3 = getSanitizedText(turnText);
11241
11872
  const toolContext = getActiveToolContext(turnText);
11242
11873
  if (toolContext.inside) {
11243
11874
  if (!lastToolEventTime) lastToolEventTime = Date.now();
@@ -11308,78 +11939,60 @@ ${ideErr} [/ERROR]`;
11308
11939
  }
11309
11940
  }
11310
11941
  }
11311
- const contextSafeText = getContextSafeText(turnText, false);
11312
- const thinkBlocks = contextSafeText.match(/(?:<think>|\[think\])([\s\S]*?)(?:<\/think>|\[\/think\]|$)/gi) || [];
11313
- const thinkContent = thinkBlocks.join("").trim();
11314
- const sentences = thinkContent.split(/[.!?]\s+/);
11315
- const uniqueSentences = new Set(sentences);
11316
- const repetitionRatio = sentences.length > 10 ? (sentences.length - uniqueSentences.size) / sentences.length : 0;
11317
- const wordCount = thinkContent.split(/\s+/).filter((w) => w.length > 0).length;
11318
- let repetitionThresholdThinking = 0.4;
11319
- let repetitionThresholdResponse = 0.6;
11320
- let isOverVerboseThinking = false;
11321
- if ((targetModel || "").toLowerCase().startsWith("gemma")) {
11322
- const thinkingCaps = {
11323
- "low": 256,
11324
- "medium": 768,
11325
- "high": 2048,
11326
- "max": 4096,
11327
- "xhigh": 4096
11328
- };
11329
- const cap = thinkingCaps[thinkingLevel?.toLowerCase()] || 2500;
11330
- isOverVerboseThinking = wordCount > cap;
11331
- }
11332
- if (repetitionRatio > repetitionThresholdThinking || isOverVerboseThinking) {
11333
- const reason = repetitionRatio > repetitionThresholdThinking ? "Reasoning Loop Detected" : "Thinking Budget Exceeded";
11334
- yield { type: "status", content: `${reason}. Re-centering...` };
11335
- isThinkingLoop = true;
11336
- await new Promise((resolve) => setTimeout(resolve, 3e3));
11337
- break;
11338
- }
11339
- const responseContent = signalSafeText3.trim();
11340
- const respSentences = responseContent.split(/[.!?]\s+/);
11341
- const uniqueRespSentences = new Set(respSentences);
11342
- const respRepetitionRatio = respSentences.length > 10 ? (respSentences.length - uniqueRespSentences.size) / respSentences.length : 0;
11343
- if (respRepetitionRatio > repetitionThresholdResponse) {
11344
- yield { type: "status", content: `Response Loop Detected. Re-centering...` };
11345
- isThinkingLoop = false;
11346
- isGeneralLoop = true;
11347
- await new Promise((resolve) => setTimeout(resolve, 3e3));
11348
- break;
11349
- }
11350
- const allWords = contextSafeText.toLowerCase().split(/\s+/).filter((w) => w.length > 0);
11351
- let stutterDetected = false;
11352
- if (allWords.length > 5) {
11353
- for (let p = 1; p <= 15; p++) {
11354
- const R = Math.max(3, Math.ceil(8 / p));
11355
- if (allWords.length < p * R) continue;
11356
- let isRepeating = true;
11357
- const pattern = allWords.slice(allWords.length - p);
11358
- const patternStr = pattern.join(" ");
11359
- for (let r = 1; r < R; r++) {
11360
- const prevPattern = allWords.slice(allWords.length - p * (r + 1), allWords.length - p * r);
11361
- if (prevPattern.join(" ") !== patternStr) {
11362
- isRepeating = false;
11363
- break;
11364
- }
11365
- }
11366
- if (isRepeating) {
11367
- stutterDetected = true;
11368
- break;
11369
- }
11942
+ if (turnText.length - lastLoopCheckLen > 150) {
11943
+ lastLoopCheckLen = turnText.length;
11944
+ const contextSafeText = getContextSafeText(turnText, false);
11945
+ const thinkBlocks = contextSafeText.match(/(?:<think>|\[think\])([\s\S]*?)(?:<\/think>|\[\/think\]|$)/gi) || [];
11946
+ const thinkContent = thinkBlocks.join("").trim();
11947
+ const sentences = thinkContent.split(/[.!?]\s+/);
11948
+ const uniqueSentences = new Set(sentences);
11949
+ const repetitionRatio = sentences.length > 10 ? (sentences.length - uniqueSentences.size) / sentences.length : 0;
11950
+ const wordCount = thinkContent.split(/\s+/).filter((w) => w.length > 0).length;
11951
+ let repetitionThresholdThinking = 0.4;
11952
+ let repetitionThresholdResponse = 0.6;
11953
+ let isOverVerboseThinking = false;
11954
+ if ((targetModel || "").toLowerCase().startsWith("gemma")) {
11955
+ const thinkingCaps = {
11956
+ "low": 256,
11957
+ "medium": 768,
11958
+ "high": 2048,
11959
+ "max": 4096,
11960
+ "xhigh": 4096
11961
+ };
11962
+ const cap = thinkingCaps[thinkingLevel?.toLowerCase()] || 2500;
11963
+ isOverVerboseThinking = wordCount > cap;
11370
11964
  }
11371
- }
11372
- if (!stutterDetected) {
11373
- const cleanChars = contextSafeText.toLowerCase().replace(/[^a-z0-9]/gi, "");
11374
- if (cleanChars.length >= 10) {
11375
- for (let p = 1; p <= 10; p++) {
11376
- const R = Math.max(4, Math.ceil(12 / p));
11377
- if (cleanChars.length < p * R) continue;
11378
- const pattern = cleanChars.substring(cleanChars.length - p);
11965
+ if (repetitionRatio > repetitionThresholdThinking || isOverVerboseThinking) {
11966
+ const reason = repetitionRatio > repetitionThresholdThinking ? "Reasoning Loop Detected" : "Thinking Budget Exceeded";
11967
+ yield { type: "status", content: `${reason}. Re-centering...` };
11968
+ isThinkingLoop = true;
11969
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
11970
+ break;
11971
+ }
11972
+ const signalSafeText3 = getSanitizedText(turnText);
11973
+ const responseContent = signalSafeText3.trim();
11974
+ const respSentences = responseContent.split(/[.!?]\s+/);
11975
+ const uniqueRespSentences = new Set(respSentences);
11976
+ const respRepetitionRatio = respSentences.length > 10 ? (respSentences.length - uniqueRespSentences.size) / respSentences.length : 0;
11977
+ if (respRepetitionRatio > repetitionThresholdResponse) {
11978
+ yield { type: "status", content: `Response Loop Detected. Re-centering...` };
11979
+ isThinkingLoop = false;
11980
+ isGeneralLoop = true;
11981
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
11982
+ break;
11983
+ }
11984
+ const allWords = contextSafeText.toLowerCase().split(/\s+/).filter((w) => w.length > 0);
11985
+ let stutterDetected = false;
11986
+ if (allWords.length > 5) {
11987
+ for (let p = 1; p <= 15; p++) {
11988
+ const R = Math.max(3, Math.ceil(8 / p));
11989
+ if (allWords.length < p * R) continue;
11379
11990
  let isRepeating = true;
11991
+ const pattern = allWords.slice(allWords.length - p);
11992
+ const patternStr = pattern.join(" ");
11380
11993
  for (let r = 1; r < R; r++) {
11381
- const prevPattern = cleanChars.substring(cleanChars.length - p * (r + 1), cleanChars.length - p * r);
11382
- if (prevPattern !== pattern) {
11994
+ const prevPattern = allWords.slice(allWords.length - p * (r + 1), allWords.length - p * r);
11995
+ if (prevPattern.join(" ") !== patternStr) {
11383
11996
  isRepeating = false;
11384
11997
  break;
11385
11998
  }
@@ -11390,13 +12003,35 @@ ${ideErr} [/ERROR]`;
11390
12003
  }
11391
12004
  }
11392
12005
  }
11393
- }
11394
- if (stutterDetected) {
11395
- yield { type: "status", content: `Stuttering Detected. Re-centering...` };
11396
- isThinkingLoop = false;
11397
- isStutteringLoop = true;
11398
- await new Promise((resolve) => setTimeout(resolve, 3e3));
11399
- break;
12006
+ if (!stutterDetected) {
12007
+ const cleanChars = contextSafeText.toLowerCase().replace(/[^a-z0-9]/gi, "");
12008
+ if (cleanChars.length >= 10) {
12009
+ for (let p = 1; p <= 10; p++) {
12010
+ const R = Math.max(4, Math.ceil(12 / p));
12011
+ if (cleanChars.length < p * R) continue;
12012
+ const pattern = cleanChars.substring(cleanChars.length - p);
12013
+ let isRepeating = true;
12014
+ for (let r = 1; r < R; r++) {
12015
+ const prevPattern = cleanChars.substring(cleanChars.length - p * (r + 1), cleanChars.length - p * r);
12016
+ if (prevPattern !== pattern) {
12017
+ isRepeating = false;
12018
+ break;
12019
+ }
12020
+ }
12021
+ if (isRepeating) {
12022
+ stutterDetected = true;
12023
+ break;
12024
+ }
12025
+ }
12026
+ }
12027
+ }
12028
+ if (stutterDetected) {
12029
+ yield { type: "status", content: `Stuttering Detected. Re-centering...` };
12030
+ isThinkingLoop = false;
12031
+ isStutteringLoop = true;
12032
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
12033
+ break;
12034
+ }
11400
12035
  }
11401
12036
  const toolActionableText = turnText.replace(/(?:<(think|thought|thoughts)>|\[(think|thought|thoughts)\])[\s\S]*?(?:<\/(think|thought|thoughts)>|\[\/(think|thought|thoughts)\]|$)/gi, "");
11402
12037
  const allToolsFound = detectToolCalls(toolActionableText);
@@ -12548,13 +13183,13 @@ Error Log can be found in ${path19.join(LOGS_DIR, "agent", "error.log")}`);
12548
13183
  });
12549
13184
 
12550
13185
  // src/components/ResumeModal.jsx
12551
- import React10, { useState as useState7, useEffect as useEffect5 } from "react";
12552
- import { Box as Box9, Text as Text10, useInput as useInput4 } from "ink";
13186
+ import React10, { useState as useState9, useEffect as useEffect7 } from "react";
13187
+ import { Box as Box9, Text as Text10, useInput as useInput5 } from "ink";
12553
13188
  function ResumeModal({ onSelect, onDelete, onClose }) {
12554
- const [history, setHistory] = useState7({});
12555
- const [keys, setKeys] = useState7([]);
12556
- const [selectedIndex, setSelectedIndex] = useState7(0);
12557
- useEffect5(() => {
13189
+ const [history, setHistory] = useState9({});
13190
+ const [keys, setKeys] = useState9([]);
13191
+ const [selectedIndex, setSelectedIndex] = useState9(0);
13192
+ useEffect7(() => {
12558
13193
  const fetchHistory = async () => {
12559
13194
  const h = await loadHistory();
12560
13195
  setHistory(h);
@@ -12562,7 +13197,7 @@ function ResumeModal({ onSelect, onDelete, onClose }) {
12562
13197
  };
12563
13198
  fetchHistory();
12564
13199
  }, []);
12565
- useInput4((input, key) => {
13200
+ useInput5((input, key) => {
12566
13201
  if (key.escape) onClose();
12567
13202
  if (key.upArrow) setSelectedIndex((prev) => Math.max(0, prev - 1));
12568
13203
  if (key.downArrow) setSelectedIndex((prev) => Math.min(keys.length - 1, prev + 1));
@@ -12640,14 +13275,14 @@ var init_ResumeModal = __esm({
12640
13275
  });
12641
13276
 
12642
13277
  // src/components/MemoryModal.jsx
12643
- import React11, { useState as useState8, useEffect as useEffect6 } from "react";
12644
- import { Box as Box10, Text as Text11, useInput as useInput5, useStdout } from "ink";
13278
+ import React11, { useState as useState10, useEffect as useEffect8 } from "react";
13279
+ import { Box as Box10, Text as Text11, useInput as useInput6, useStdout } from "ink";
12645
13280
  function MemoryModal({ onClose }) {
12646
13281
  const { stdout } = useStdout();
12647
13282
  const columns = stdout?.columns || 80;
12648
- const [memories, setMemories] = useState8([]);
12649
- const [selectedIndex, setSelectedIndex] = useState8(0);
12650
- const [isMemoryOn, setIsMemoryOn] = useState8(true);
13283
+ const [memories, setMemories] = useState10([]);
13284
+ const [selectedIndex, setSelectedIndex] = useState10(0);
13285
+ const [isMemoryOn, setIsMemoryOn] = useState10(true);
12651
13286
  const loadMemories = () => {
12652
13287
  const data = readEncryptedJson(MEMORIES_FILE, []);
12653
13288
  setMemories(data);
@@ -12659,10 +13294,10 @@ function MemoryModal({ onClose }) {
12659
13294
  setIsMemoryOn(true);
12660
13295
  }
12661
13296
  };
12662
- useEffect6(() => {
13297
+ useEffect8(() => {
12663
13298
  loadMemories();
12664
13299
  }, []);
12665
- useInput5((input, key) => {
13300
+ useInput6((input, key) => {
12666
13301
  if (key.escape) onClose();
12667
13302
  if (key.upArrow) setSelectedIndex((prev) => Math.max(0, prev - 1));
12668
13303
  if (key.downArrow) setSelectedIndex((prev) => Math.min(memories.length - 1, prev + 1));
@@ -12761,7 +13396,7 @@ var init_MemoryModal = __esm({
12761
13396
  });
12762
13397
 
12763
13398
  // src/components/UpdateProcessor.jsx
12764
- import React12, { useState as useState9, useEffect as useEffect7 } from "react";
13399
+ import React12, { useState as useState11, useEffect as useEffect9 } from "react";
12765
13400
  import { Box as Box11, Text as Text12 } from "ink";
12766
13401
  import { spawn as spawn2 } from "child_process";
12767
13402
  var pty2, SPINNER_FRAMES, UpdateProcessor, UpdateProcessor_default;
@@ -12776,17 +13411,17 @@ var init_UpdateProcessor = __esm({
12776
13411
  }
12777
13412
  SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
12778
13413
  UpdateProcessor = ({ latest, current, settings, onClose, onUpdateSettings, onSuccess }) => {
12779
- const [status, setStatus] = useState9("initializing");
12780
- const [log, setLog] = useState9("");
12781
- const [error, setError] = useState9(null);
12782
- const [tick, setTick] = useState9(0);
12783
- useEffect7(() => {
13414
+ const [status, setStatus] = useState11("initializing");
13415
+ const [log, setLog] = useState11("");
13416
+ const [error, setError] = useState11(null);
13417
+ const [tick, setTick] = useState11(0);
13418
+ useEffect9(() => {
12784
13419
  const interval = setInterval(() => {
12785
13420
  setTick((t) => (t + 1) % 1e3);
12786
13421
  }, 33);
12787
13422
  return () => clearInterval(interval);
12788
13423
  }, []);
12789
- useEffect7(() => {
13424
+ useEffect9(() => {
12790
13425
  let child;
12791
13426
  const runUpdate = async () => {
12792
13427
  const manager = settings.updateManager || "npm";
@@ -12919,12 +13554,12 @@ var init_UpdateProcessor = __esm({
12919
13554
  });
12920
13555
 
12921
13556
  // src/components/ParserDownloadModal.jsx
12922
- import React13, { useState as useState10, useEffect as useEffect8 } from "react";
12923
- import { Box as Box12, Text as Text13, useInput as useInput6 } from "ink";
13557
+ import React13, { useState as useState12, useEffect as useEffect10 } from "react";
13558
+ import { Box as Box12, Text as Text13, useInput as useInput7 } from "ink";
12924
13559
  function ParserDownloadModal({ onClose }) {
12925
- const [selectedIndex, setSelectedIndex] = useState10(0);
12926
- const [status, setStatus] = useState10({});
12927
- useEffect8(() => {
13560
+ const [selectedIndex, setSelectedIndex] = useState12(0);
13561
+ const [status, setStatus] = useState12({});
13562
+ useEffect10(() => {
12928
13563
  const initialStatus = {};
12929
13564
  EXTENSIONS.forEach((item) => {
12930
13565
  if (isParserInstalled(item.file)) {
@@ -12935,7 +13570,7 @@ function ParserDownloadModal({ onClose }) {
12935
13570
  });
12936
13571
  setStatus(initialStatus);
12937
13572
  }, []);
12938
- useInput6(async (input, key) => {
13573
+ useInput7(async (input, key) => {
12939
13574
  if (key.escape) onClose();
12940
13575
  if (key.upArrow) setSelectedIndex((prev) => Math.max(0, prev - 1));
12941
13576
  if (key.downArrow) setSelectedIndex((prev) => Math.min(EXTENSIONS.length - 1, prev + 1));
@@ -13425,11 +14060,11 @@ var init_dist = __esm({
13425
14060
  });
13426
14061
 
13427
14062
  // src/components/RevertModal.jsx
13428
- import React14, { useState as useState11 } from "react";
13429
- import { Box as Box14, Text as Text15, useInput as useInput7 } from "ink";
14063
+ import React14, { useState as useState13 } from "react";
14064
+ import { Box as Box14, Text as Text15, useInput as useInput8 } from "ink";
13430
14065
  function RevertModal({ prompts, onSelect, onClose }) {
13431
- const [selectedIndex, setSelectedIndex] = useState11(0);
13432
- useInput7((input, key) => {
14066
+ const [selectedIndex, setSelectedIndex] = useState13(0);
14067
+ useInput8((input, key) => {
13433
14068
  if (key.escape) onClose();
13434
14069
  if (key.upArrow) setSelectedIndex((prev) => Math.max(0, prev - 1));
13435
14070
  if (key.downArrow) setSelectedIndex((prev) => Math.min(prompts.length - 1, prev + 1));
@@ -13551,8 +14186,8 @@ __export(app_exports, {
13551
14186
  default: () => App
13552
14187
  });
13553
14188
  import os4 from "os";
13554
- import React15, { useState as useState12, useEffect as useEffect9, useRef as useRef3, useMemo as useMemo2 } from "react";
13555
- import { Box as Box15, Text as Text16, useInput as useInput8, useStdout as useStdout2, Static } from "ink";
14189
+ import React15, { useState as useState14, useEffect as useEffect11, useRef as useRef3, useMemo as useMemo2 } from "react";
14190
+ import { Box as Box15, Text as Text16, useInput as useInput9, useStdout as useStdout2, Static } from "ink";
13556
14191
  import fs22 from "fs-extra";
13557
14192
  import path20 from "path";
13558
14193
  import { exec as exec2 } from "child_process";
@@ -13561,24 +14196,24 @@ import TextInput4 from "ink-text-input";
13561
14196
  import SelectInput2 from "ink-select-input";
13562
14197
  import gradient2 from "gradient-string";
13563
14198
  function App({ args = [] }) {
13564
- const [confirmExit, setConfirmExit] = useState12(false);
13565
- const [exitCountdown, setExitCountdown] = useState12(10);
14199
+ const [confirmExit, setConfirmExit] = useState14(false);
14200
+ const [exitCountdown, setExitCountdown] = useState14(10);
13566
14201
  const { stdout } = useStdout2();
13567
- const [input, setInput] = useState12("");
13568
- const [inputKey, setInputKey] = useState12(0);
13569
- const [isExpanded, setIsExpanded] = useState12(false);
13570
- const [mode, setMode] = useState12("Flux");
13571
- const [terminalSize, setTerminalSize] = useState12({
14202
+ const [input, setInput] = useState14("");
14203
+ const [inputKey, setInputKey] = useState14(0);
14204
+ const [isExpanded, setIsExpanded] = useState14(false);
14205
+ const [mode, setMode] = useState14("Flux");
14206
+ const [terminalSize, setTerminalSize] = useState14({
13572
14207
  columns: stdout?.columns || 80,
13573
14208
  rows: stdout?.rows || 24
13574
14209
  });
13575
- const [selectedIndex, setSelectedIndex] = useState12(0);
13576
- const [isFilePickerDismissed, setIsFilePickerDismissed] = useState12(false);
13577
- const [showBridgePromo, setShowBridgePromo] = useState12(false);
13578
- const [promoSelectedIndex, setPromoSelectedIndex] = useState12(0);
14210
+ const [selectedIndex, setSelectedIndex] = useState14(0);
14211
+ const [isFilePickerDismissed, setIsFilePickerDismissed] = useState14(false);
14212
+ const [showBridgePromo, setShowBridgePromo] = useState14(false);
14213
+ const [promoSelectedIndex, setPromoSelectedIndex] = useState14(0);
13579
14214
  const suggestionOffsetRef = useRef3(0);
13580
14215
  const persistedModelRef = useRef3(null);
13581
- useEffect9(() => {
14216
+ useEffect11(() => {
13582
14217
  const ideName = getIDEName();
13583
14218
  const isIDE = !["Terminal", "Windows Terminal"].includes(ideName) || !!process.env.VSC_TERMINAL_URL;
13584
14219
  const graceTimer = setTimeout(() => {
@@ -13753,7 +14388,7 @@ function App({ args = [] }) {
13753
14388
  }
13754
14389
  }
13755
14390
  };
13756
- useEffect9(() => {
14391
+ useEffect11(() => {
13757
14392
  const handleResize = () => {
13758
14393
  stdout.write("\x1B[2J\x1B[3J\x1B[H");
13759
14394
  setTerminalSize({
@@ -13766,18 +14401,18 @@ function App({ args = [] }) {
13766
14401
  stdout.off("resize", handleResize);
13767
14402
  };
13768
14403
  }, [stdout]);
13769
- const [thinkingLevel, setThinkingLevel] = useState12("Medium");
13770
- const [aiProvider, setAiProvider] = useState12("Google");
13771
- const [setupStep, setSetupStep] = useState12(0);
13772
- const [latestVer, setLatestVer] = useState12(null);
13773
- const [showFullThinking, setShowFullThinking] = useState12(false);
13774
- const [activeModel, setActiveModel] = useState12("gemma-4-31b-it");
13775
- const [janitorModel, setJanitorModel] = useState12("gemma-4-26b-a4b-it");
13776
- const [isInitializing, setIsInitializing] = useState12(true);
13777
- const [isAppFocused, setIsAppFocused] = useState12(true);
14404
+ const [thinkingLevel, setThinkingLevel] = useState14("Medium");
14405
+ const [aiProvider, setAiProvider] = useState14("Google");
14406
+ const [setupStep, setSetupStep] = useState14(0);
14407
+ const [latestVer, setLatestVer] = useState14(null);
14408
+ const [showFullThinking, setShowFullThinking] = useState14(false);
14409
+ const [activeModel, setActiveModel] = useState14("gemma-4-31b-it");
14410
+ const [janitorModel, setJanitorModel] = useState14("gemma-4-26b-a4b-it");
14411
+ const [isInitializing, setIsInitializing] = useState14(true);
14412
+ const [isAppFocused, setIsAppFocused] = useState14(true);
13778
14413
  const lastFocusEventTime = useRef3(0);
13779
- const [apiKey, setApiKey] = useState12(null);
13780
- const [tempKey, setTempKey] = useState12("");
14414
+ const [apiKey, setApiKey] = useState14(null);
14415
+ const [tempKey, setTempKey] = useState14("");
13781
14416
  const addShiftEnterBinding = async (ideName) => {
13782
14417
  const kbPath = getKeybindingsPath(ideName);
13783
14418
  if (!kbPath) return;
@@ -13828,35 +14463,35 @@ function App({ args = [] }) {
13828
14463
  });
13829
14464
  }
13830
14465
  };
13831
- const [activeView, setActiveView] = useState12("chat");
13832
- const [apiTier, setApiTier] = useState12("Free");
13833
- const [quotas, setQuotas] = useState12({ limitMode: "Daily", agentLimit: 99999999, tokenLimit: 99999999999999, backgroundLimit: 999999, searchLimit: 100, customModelId: "", customLimit: 0 });
13834
- const [inputConfig, setInputConfig] = useState12(null);
13835
- const [systemSettings, setSystemSettings] = useState12({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
13836
- const [profileData, setProfileData] = useState12({ name: null, nickname: null, instructions: null });
13837
- const [imageSettings, setImageSettings] = useState12({ keyType: "Default", quality: "Low-High", apiKey: "" });
13838
- const [sessionStats, setSessionStats] = useState12({ tokens: 0 });
13839
- const [sessionAgentCalls, setSessionAgentCalls] = useState12(0);
13840
- const [sessionBackgroundCalls, setSessionBackgroundCalls] = useState12(0);
13841
- const [sessionTotalTokens, setSessionTotalTokens] = useState12(0);
13842
- const [chatTokens, setChatTokens] = useState12(0);
14466
+ const [activeView, setActiveView] = useState14("chat");
14467
+ const [apiTier, setApiTier] = useState14("Free");
14468
+ const [quotas, setQuotas] = useState14({ limitMode: "Daily", agentLimit: 99999999, tokenLimit: 99999999999999, backgroundLimit: 999999, searchLimit: 100, customModelId: "", customLimit: 0 });
14469
+ const [inputConfig, setInputConfig] = useState14(null);
14470
+ const [systemSettings, setSystemSettings] = useState14({ memory: true, compression: 0, autoExec: false, autoDeleteHistory: "7d", autoUpdate: false, updateManager: "npm", customUpdateCommand: "" });
14471
+ const [profileData, setProfileData] = useState14({ name: null, nickname: null, instructions: null });
14472
+ const [imageSettings, setImageSettings] = useState14({ keyType: "Default", quality: "Low-High", apiKey: "" });
14473
+ const [sessionStats, setSessionStats] = useState14({ tokens: 0 });
14474
+ const [sessionAgentCalls, setSessionAgentCalls] = useState14(0);
14475
+ const [sessionBackgroundCalls, setSessionBackgroundCalls] = useState14(0);
14476
+ const [sessionTotalTokens, setSessionTotalTokens] = useState14(0);
14477
+ const [chatTokens, setChatTokens] = useState14(0);
13843
14478
  const chatTokenStartRef = useRef3(0);
13844
- const [sessionTotalCachedTokens, setSessionTotalCachedTokens] = useState12(0);
13845
- const [sessionTotalCandidateTokens, setSessionTotalCandidateTokens] = useState12(0);
13846
- const [sessionToolSuccess, setSessionToolSuccess] = useState12(0);
13847
- const [sessionToolFailure, setSessionToolFailure] = useState12(0);
13848
- const [sessionToolDenied, setSessionToolDenied] = useState12(0);
13849
- const [sessionApiTime, setSessionApiTime] = useState12(0);
13850
- const [sessionToolTime, setSessionToolTime] = useState12(0);
13851
- const [sessionImageCount, setSessionImageCount] = useState12(0);
13852
- const [sessionImageCredits, setSessionImageCredits] = useState12(0);
13853
- const [dailyUsage, setDailyUsage] = useState12(null);
13854
- const [monthlyUsage, setMonthlyUsage] = useState12(null);
13855
- const [customPeriodUsage, setCustomPeriodUsage] = useState12(null);
13856
- const [statsMode, setStatsMode] = useState12("daily");
14479
+ const [sessionTotalCachedTokens, setSessionTotalCachedTokens] = useState14(0);
14480
+ const [sessionTotalCandidateTokens, setSessionTotalCandidateTokens] = useState14(0);
14481
+ const [sessionToolSuccess, setSessionToolSuccess] = useState14(0);
14482
+ const [sessionToolFailure, setSessionToolFailure] = useState14(0);
14483
+ const [sessionToolDenied, setSessionToolDenied] = useState14(0);
14484
+ const [sessionApiTime, setSessionApiTime] = useState14(0);
14485
+ const [sessionToolTime, setSessionToolTime] = useState14(0);
14486
+ const [sessionImageCount, setSessionImageCount] = useState14(0);
14487
+ const [sessionImageCredits, setSessionImageCredits] = useState14(0);
14488
+ const [dailyUsage, setDailyUsage] = useState14(null);
14489
+ const [monthlyUsage, setMonthlyUsage] = useState14(null);
14490
+ const [customPeriodUsage, setCustomPeriodUsage] = useState14(null);
14491
+ const [statsMode, setStatsMode] = useState14("daily");
13857
14492
  const PLAYGROUND_CHAT_ID = "flow-playground";
13858
- const [chatId, setChatId] = useState12(args.includes("--playground") ? PLAYGROUND_CHAT_ID : generateChatId());
13859
- useEffect9(() => {
14493
+ const [chatId, setChatId] = useState14(args.includes("--playground") ? PLAYGROUND_CHAT_ID : generateChatId());
14494
+ useEffect11(() => {
13860
14495
  const nextTokens = sessionTotalTokens - chatTokenStartRef.current;
13861
14496
  setChatTokens(nextTokens);
13862
14497
  if (chatId) {
@@ -13864,7 +14499,7 @@ function App({ args = [] }) {
13864
14499
  });
13865
14500
  }
13866
14501
  }, [sessionTotalTokens, chatId, sessionStats.tokens]);
13867
- useEffect9(() => {
14502
+ useEffect11(() => {
13868
14503
  if (activeView === "apiTier") {
13869
14504
  const load = async () => {
13870
14505
  const d = await getDailyUsage();
@@ -13877,17 +14512,17 @@ function App({ args = [] }) {
13877
14512
  load();
13878
14513
  }
13879
14514
  }, [activeView, quotas.resetDay]);
13880
- const [activeCommand, setActiveCommand] = useState12(null);
13881
- const [execOutput, setExecOutput] = useState12("");
13882
- const [isTerminalFocused, setIsTerminalFocused] = useState12(false);
13883
- const [tick, setTick] = useState12(0);
14515
+ const [activeCommand, setActiveCommand] = useState14(null);
14516
+ const [execOutput, setExecOutput] = useState14("");
14517
+ const [isTerminalFocused, setIsTerminalFocused] = useState14(false);
14518
+ const [tick, setTick] = useState14(0);
13884
14519
  const isFirstRender = useRef3(true);
13885
14520
  const isSecondRender = useRef3(true);
13886
14521
  const isThirdRender = useRef3(true);
13887
14522
  const prevProviderRef = useRef3(aiProvider);
13888
14523
  const originalAllowExternalAccessRef = useRef3(false);
13889
14524
  const originalMemoryRef = useRef3(true);
13890
- useEffect9(() => {
14525
+ useEffect11(() => {
13891
14526
  if (prevProviderRef.current !== aiProvider) {
13892
14527
  prevProviderRef.current = aiProvider;
13893
14528
  const hasStandard = aiProvider === "DeepSeek" || aiProvider === "NVIDIA";
@@ -13900,7 +14535,7 @@ function App({ args = [] }) {
13900
14535
  }
13901
14536
  }
13902
14537
  }, [aiProvider, activeModel, thinkingLevel]);
13903
- useEffect9(() => {
14538
+ useEffect11(() => {
13904
14539
  if (!apiKey) return;
13905
14540
  if (isFirstRender.current) {
13906
14541
  isFirstRender.current = false;
@@ -13974,15 +14609,15 @@ function App({ args = [] }) {
13974
14609
  }, []);
13975
14610
  const activeCommandRef = useRef3(null);
13976
14611
  const execOutputRef = useRef3("");
13977
- useEffect9(() => {
14612
+ useEffect11(() => {
13978
14613
  activeCommandRef.current = activeCommand;
13979
14614
  }, [activeCommand]);
13980
- useEffect9(() => {
14615
+ useEffect11(() => {
13981
14616
  execOutputRef.current = execOutput;
13982
14617
  }, [execOutput]);
13983
- const [autoAcceptWrites, setAutoAcceptWrites] = useState12(false);
13984
- const [pendingApproval, setPendingApproval] = useState12(null);
13985
- const [pendingAsk, setPendingAsk] = useState12(null);
14618
+ const [autoAcceptWrites, setAutoAcceptWrites] = useState14(false);
14619
+ const [pendingApproval, setPendingApproval] = useState14(null);
14620
+ const [pendingAsk, setPendingAsk] = useState14(null);
13986
14621
  const resetPendingApproval = (decision) => {
13987
14622
  setPendingApproval(null);
13988
14623
  setActiveView("chat");
@@ -14001,9 +14636,10 @@ function App({ args = [] }) {
14001
14636
  if (ms < 1e3) return `${ms}ms`;
14002
14637
  return formatDuration(Math.floor(ms / 1e3));
14003
14638
  };
14004
- const [statusText, setStatusText] = useState12(null);
14005
- const [wittyPhrase, setWittyPhrase] = useState12("");
14006
- useEffect9(() => {
14639
+ const [statusText, setStatusText] = useState14(null);
14640
+ const [wittyPhrase, setWittyPhrase] = useState14("");
14641
+ const [hasPasteBlock, setHasPasteBlock] = useState14(false);
14642
+ useEffect11(() => {
14007
14643
  let interval;
14008
14644
  if (statusText) {
14009
14645
  const updatePhrase = () => {
@@ -14017,19 +14653,20 @@ function App({ args = [] }) {
14017
14653
  }
14018
14654
  return () => clearInterval(interval);
14019
14655
  }, [statusText]);
14020
- const [isSpinnerActive, setIsSpinnerActive] = useState12(true);
14021
- const [isProcessing, setIsProcessing] = useState12(false);
14022
- const [isCompressing, setIsCompressing] = useState12(false);
14023
- const [escPressed, setEscPressed] = useState12(false);
14024
- const [escTimer, setEscTimer] = useState12(null);
14025
- const [escPressCount, setEscPressCount] = useState12(0);
14026
- const [recentPrompts, setRecentPrompts] = useState12([]);
14656
+ const [isSpinnerActive, setIsSpinnerActive] = useState14(true);
14657
+ const [isProcessing, setIsProcessing] = useState14(false);
14658
+ const [isCompressing, setIsCompressing] = useState14(false);
14659
+ const [escPressed, setEscPressed] = useState14(false);
14660
+ const [escTimer, setEscTimer] = useState14(null);
14661
+ const [escPressCount, setEscPressCount] = useState14(0);
14662
+ const [recentPrompts, setRecentPrompts] = useState14([]);
14027
14663
  const escDoubleTimerRef = useRef3(null);
14028
- const [queuedPrompt, setQueuedPrompt] = useState12(null);
14029
- const [resolutionData, setResolutionData] = useState12(null);
14030
- const [tempModelOverride, setTempModelOverride] = useState12(null);
14031
- useEffect9(() => setEscPressCount(0), [input]);
14032
- const [messages, setMessages] = useState12(() => {
14664
+ const didSignalTerminationRef = useRef3(false);
14665
+ const [queuedPrompt, setQueuedPrompt] = useState14(null);
14666
+ const [resolutionData, setResolutionData] = useState14(null);
14667
+ const [tempModelOverride, setTempModelOverride] = useState14(null);
14668
+ useEffect11(() => setEscPressCount(0), [input]);
14669
+ const [messages, rawSetMessages] = useState14(() => {
14033
14670
  const logoMsg = { id: "logo-" + Date.now(), role: "system", isLogo: true, isMeta: true };
14034
14671
  const isHomeDir = process.cwd() === os4.homedir();
14035
14672
  const isSystemDir = (() => {
@@ -14064,12 +14701,25 @@ function App({ args = [] }) {
14064
14701
  }
14065
14702
  return msgs;
14066
14703
  });
14704
+ const setMessages = (value) => {
14705
+ rawSetMessages((prev) => {
14706
+ const next = typeof value === "function" ? value(prev) : value;
14707
+ if (next.length > 1) {
14708
+ const last = next[next.length - 1];
14709
+ const secondLast = next[next.length - 2];
14710
+ if (last?.text?.includes("Request Cancelled") && secondLast?.text?.includes("Request Cancelled")) {
14711
+ return next.slice(0, -1);
14712
+ }
14713
+ }
14714
+ return next;
14715
+ });
14716
+ };
14067
14717
  const queuedPromptRef = useRef3(null);
14068
- const [btwResponse, setBtwResponse] = useState12("");
14069
- const [showBtwBox, setShowBtwBox] = useState12(false);
14718
+ const [btwResponse, setBtwResponse] = useState14("");
14719
+ const [showBtwBox, setShowBtwBox] = useState14(false);
14070
14720
  const btwResponseRef = useRef3("");
14071
14721
  const btwClosedRef = useRef3(null);
14072
- useEffect9(() => {
14722
+ useEffect11(() => {
14073
14723
  if (messages.length === 0) return;
14074
14724
  const lastMsg = messages[messages.length - 1];
14075
14725
  if (lastMsg && (lastMsg.role === "agent" || lastMsg.role === "assistant")) {
@@ -14087,59 +14737,105 @@ function App({ args = [] }) {
14087
14737
  }
14088
14738
  }
14089
14739
  }, [messages]);
14090
- const [completedIndex, setCompletedIndex] = useState12(messages.length);
14091
- const [clearKey, setClearKey] = useState12(0);
14740
+ const [completedIndex, setCompletedIndex] = useState14(messages.length);
14741
+ const [clearKey, setClearKey] = useState14(0);
14742
+ const lastCompletedBlocksRef = useRef3([]);
14743
+ const cachedHistoryRef = useRef3({
14744
+ completedIndex: 0,
14745
+ columns: 0,
14746
+ historicalBlocks: [],
14747
+ seenSelections: /* @__PURE__ */ new Set()
14748
+ });
14092
14749
  const parsedBlocks = useMemo2(() => {
14093
- const completed = [];
14094
- const active = [];
14095
14750
  const columns = terminalSize.columns || 80;
14096
- const completedMsgs = messages.slice(0, completedIndex);
14097
- const activeMsgs = messages.slice(completedIndex);
14098
- const seenAskSelections = /* @__PURE__ */ new Set();
14099
- const filterDuplicates = (msgList) => {
14100
- return msgList.filter((msg) => {
14101
- if (msg.isAskRecord) {
14102
- const selectionMatch = msg.text?.match(/Selection: (.*)/);
14103
- const selection = selectionMatch ? selectionMatch[1].trim() : "";
14104
- if (selection) {
14105
- if (seenAskSelections.has(selection)) {
14106
- return false;
14107
- }
14751
+ const SELECTION_REGEX = /Selection: (.*)/;
14752
+ let historicalBlocks = [];
14753
+ let seenAskSelections = /* @__PURE__ */ new Set();
14754
+ const isResize = cachedHistoryRef.current.columns !== columns;
14755
+ const isClear = completedIndex < cachedHistoryRef.current.completedIndex;
14756
+ if (isResize || isClear) {
14757
+ const completedMsgs = messages.slice(0, completedIndex);
14758
+ for (let i = 0; i < completedMsgs.length; i++) {
14759
+ const msg = completedMsgs[i];
14760
+ if (msg.isAskRecord && msg.text) {
14761
+ const match = msg.text.match(SELECTION_REGEX);
14762
+ if (match && match[1].trim()) {
14763
+ const selection = match[1].trim();
14764
+ if (seenAskSelections.has(selection)) continue;
14108
14765
  seenAskSelections.add(selection);
14109
14766
  }
14110
14767
  }
14111
- return true;
14112
- });
14113
- };
14114
- const uniqueCompleted = filterDuplicates(completedMsgs);
14115
- const uniqueActive = filterDuplicates(activeMsgs);
14116
- uniqueCompleted.forEach((msg) => {
14117
- const parsed = parseMessageToBlocks(msg, columns);
14118
- completed.push(...parsed.completed);
14119
- completed.push(...parsed.active);
14120
- });
14121
- uniqueActive.forEach((msg) => {
14768
+ const parsed = parseMessageToBlocks(msg, columns);
14769
+ for (let j = 0; j < parsed.completed.length; j++) historicalBlocks.push(parsed.completed[j]);
14770
+ for (let j = 0; j < parsed.active.length; j++) historicalBlocks.push(parsed.active[j]);
14771
+ }
14772
+ cachedHistoryRef.current = {
14773
+ completedIndex,
14774
+ columns,
14775
+ historicalBlocks,
14776
+ seenSelections: new Set(seenAskSelections)
14777
+ };
14778
+ } else {
14779
+ historicalBlocks = cachedHistoryRef.current.historicalBlocks;
14780
+ seenAskSelections = cachedHistoryRef.current.seenSelections;
14781
+ if (completedIndex > cachedHistoryRef.current.completedIndex) {
14782
+ historicalBlocks = [...historicalBlocks];
14783
+ seenAskSelections = new Set(seenAskSelections);
14784
+ const newMsgs = messages.slice(cachedHistoryRef.current.completedIndex, completedIndex);
14785
+ for (let i = 0; i < newMsgs.length; i++) {
14786
+ const msg = newMsgs[i];
14787
+ if (msg.isAskRecord && msg.text) {
14788
+ const match = msg.text.match(SELECTION_REGEX);
14789
+ if (match && match[1].trim()) {
14790
+ const selection = match[1].trim();
14791
+ if (seenAskSelections.has(selection)) continue;
14792
+ seenAskSelections.add(selection);
14793
+ }
14794
+ }
14795
+ const parsed = parseMessageToBlocks(msg, columns);
14796
+ for (let j = 0; j < parsed.completed.length; j++) historicalBlocks.push(parsed.completed[j]);
14797
+ for (let j = 0; j < parsed.active.length; j++) historicalBlocks.push(parsed.active[j]);
14798
+ }
14799
+ cachedHistoryRef.current = {
14800
+ completedIndex,
14801
+ columns,
14802
+ historicalBlocks,
14803
+ seenSelections: seenAskSelections
14804
+ };
14805
+ }
14806
+ }
14807
+ const activeMsgs = messages.slice(completedIndex);
14808
+ const streamingCompletedBlocks = [];
14809
+ const activeBlocks = [];
14810
+ for (let i = 0; i < activeMsgs.length; i++) {
14811
+ const msg = activeMsgs[i];
14812
+ if (msg.isAskRecord && msg.text) {
14813
+ const match = msg.text.match(SELECTION_REGEX);
14814
+ if (match && match[1].trim()) {
14815
+ const selection = match[1].trim();
14816
+ if (seenAskSelections.has(selection)) continue;
14817
+ }
14818
+ }
14122
14819
  const parsed = parseMessageToBlocks(msg, columns);
14123
- completed.push(...parsed.completed);
14124
- active.push(...parsed.active);
14125
- });
14126
- const MAX_BLOCKS = 5e9;
14127
- const slicedCompleted = completed.slice(Math.max(0, completed.length - MAX_BLOCKS));
14128
- if (slicedCompleted.length >= 75e3) {
14129
- slicedCompleted.push({
14130
- key: "memory-warning-block",
14820
+ for (let j = 0; j < parsed.completed.length; j++) streamingCompletedBlocks.push(parsed.completed[j]);
14821
+ for (let j = 0; j < parsed.active.length; j++) activeBlocks.push(parsed.active[j]);
14822
+ }
14823
+ const finalCompleted = historicalBlocks.concat(streamingCompletedBlocks);
14824
+ if (finalCompleted.length >= 75e3) {
14825
+ finalCompleted.push({
14826
+ key: `memory-warning-block-${finalCompleted.length}`,
14131
14827
  msg: {
14132
14828
  role: "system",
14133
14829
  text: `\u26A0\uFE0F MEMORY WARNING: CHAT IS GETTING VERY LONG`,
14134
- subText: `This session has reached ${slicedCompleted.length} blocks. To maintain optimal performance and prevent high memory usage, it is highly recommended to save and start a clean chat with /clear.`,
14830
+ subText: `This session has reached ${finalCompleted.length} blocks. To maintain optimal performance and prevent high memory usage, it is highly recommended to save and start a clean chat with /clear.`,
14135
14831
  isHomeWarning: true
14136
14832
  },
14137
14833
  type: "full-message"
14138
14834
  });
14139
14835
  }
14140
14836
  return {
14141
- completed: slicedCompleted,
14142
- active
14837
+ completed: finalCompleted,
14838
+ active: activeBlocks
14143
14839
  };
14144
14840
  }, [messages, completedIndex, terminalSize.columns]);
14145
14841
  const isTerminalWaitingForInput = useMemo2(() => {
@@ -14147,7 +14843,7 @@ function App({ args = [] }) {
14147
14843
  const lastChunk = execOutput.trim();
14148
14844
  return lastChunk.endsWith("?") || lastChunk.endsWith(":") || /\[[yYnN/]+\]\s*$/.test(lastChunk) || /\([yYnN]\)\s*$/.test(lastChunk);
14149
14845
  }, [activeCommand, execOutput]);
14150
- useInput8((inputText, key) => {
14846
+ useInput9((inputText, key) => {
14151
14847
  if (inputText === "\x1B[I" || inputText === "\x1B[O" || inputText === "[I" || inputText === "[O") {
14152
14848
  return;
14153
14849
  }
@@ -14246,16 +14942,11 @@ function App({ args = [] }) {
14246
14942
  return;
14247
14943
  }
14248
14944
  if (isProcessing || activeCommand) {
14249
- if (!escPressed) {
14250
- setEscPressed(true);
14251
- if (escTimer) clearTimeout(escTimer);
14252
- setEscTimer(setTimeout(() => setEscPressed(false), 3e3));
14253
- } else {
14254
- signalTermination();
14255
- terminateActiveCommand();
14256
- setEscPressed(false);
14257
- if (escTimer) clearTimeout(escTimer);
14258
- }
14945
+ didSignalTerminationRef.current = true;
14946
+ signalTermination();
14947
+ terminateActiveCommand();
14948
+ setEscPressed(false);
14949
+ if (escTimer) clearTimeout(escTimer);
14259
14950
  } else {
14260
14951
  if (activeView === "revert") {
14261
14952
  setActiveView("chat");
@@ -14333,7 +15024,7 @@ function App({ args = [] }) {
14333
15024
  setInput((prev) => prev.replace(/\\\r?$/, "").replace(/\r?$/, "") + "\n");
14334
15025
  }
14335
15026
  });
14336
- useEffect9(() => {
15027
+ useEffect11(() => {
14337
15028
  process.stdout.write("\x1B[?1004h");
14338
15029
  const onData = (data) => {
14339
15030
  const str = data.toString();
@@ -14351,7 +15042,7 @@ function App({ args = [] }) {
14351
15042
  process.stdin.off("data", onData);
14352
15043
  };
14353
15044
  }, []);
14354
- useEffect9(() => {
15045
+ useEffect11(() => {
14355
15046
  async function init() {
14356
15047
  try {
14357
15048
  const pkg = JSON.parse(fs22.readFileSync(path20.join(process.cwd(), "package.json"), "utf8"));
@@ -14570,7 +15261,7 @@ function App({ args = [] }) {
14570
15261
  }
14571
15262
  init();
14572
15263
  }, []);
14573
- useEffect9(() => {
15264
+ useEffect11(() => {
14574
15265
  let timer;
14575
15266
  if (confirmExit) {
14576
15267
  setExitCountdown(10);
@@ -14588,7 +15279,7 @@ function App({ args = [] }) {
14588
15279
  if (timer) clearInterval(timer);
14589
15280
  };
14590
15281
  }, [confirmExit]);
14591
- useEffect9(() => {
15282
+ useEffect11(() => {
14592
15283
  if (!isInitializing) {
14593
15284
  const modelToSave = parsedArgs.model && activeModel === parsedArgs.model ? persistedModelRef.current : activeModel;
14594
15285
  let settingsToSave = systemSettings;
@@ -14638,7 +15329,7 @@ function App({ args = [] }) {
14638
15329
  }
14639
15330
  };
14640
15331
  const lastSavedTimeRef = useRef3(SESSION_START_TIME);
14641
- useEffect9(() => {
15332
+ useEffect11(() => {
14642
15333
  if (activeView === "exit") {
14643
15334
  const flush = async () => {
14644
15335
  const now = Date.now();
@@ -14656,7 +15347,7 @@ function App({ args = [] }) {
14656
15347
  return () => clearTimeout(timer);
14657
15348
  }
14658
15349
  }, [activeView]);
14659
- useEffect9(() => {
15350
+ useEffect11(() => {
14660
15351
  const interval = setInterval(async () => {
14661
15352
  if (!isInitializing) {
14662
15353
  const now = Date.now();
@@ -14666,7 +15357,7 @@ function App({ args = [] }) {
14666
15357
  lastSavedTimeRef.current += deltaSecs * 1e3;
14667
15358
  }
14668
15359
  }
14669
- }, 1500);
15360
+ }, 2e3);
14670
15361
  return () => clearInterval(interval);
14671
15362
  }, [isInitializing]);
14672
15363
  const COMMANDS = [
@@ -14682,32 +15373,6 @@ function App({ args = [] }) {
14682
15373
  { cmd: "/export", desc: "Export current chat in a .txt file" },
14683
15374
  { cmd: "/chats", desc: "List all chat sessions" },
14684
15375
  { cmd: "/btw", desc: "Ask a question without intefering with ongoing tasks" },
14685
- // {
14686
- // cmd: '/image', desc: 'Generate images using Pollinations', subs: [
14687
- // {
14688
- // cmd: 'setup', desc: 'Configure defaults', subs: [
14689
- // {
14690
- // cmd: 'key', desc: 'Set API key strategy', subs: [
14691
- // { cmd: 'default', desc: 'Default (Quota: Dynamic 25 max/hr)' },
14692
- // { cmd: 'custom', desc: 'Custom Key' }
14693
- // ]
14694
- // },
14695
- // {
14696
- // cmd: 'quality', desc: 'Set default quality', subs: [
14697
- // { cmd: 'low', desc: imageSettings?.keyType === 'Custom' ? '(0.001/img)' : '(1/img)' },
14698
- // { cmd: 'low-high', desc: imageSettings?.keyType === 'Custom' ? '(0.002/img)' : '(2/img)' },
14699
- // { cmd: 'medium', desc: imageSettings?.keyType === 'Custom' ? '(0.008/img)' : '(8/img)' },
14700
- // { cmd: 'medium-high', desc: imageSettings?.keyType === 'Custom' ? '(0.01/img)' : '(10/img)' },
14701
- // { cmd: 'high', desc: imageSettings?.keyType === 'Custom' ? '(0.045/img)' : '(45/img)' },
14702
- // { cmd: 'ultra', desc: imageSettings?.keyType === 'Custom' ? '(0.0488/img)' : '(49/img)' },
14703
- // { cmd: 'premium', desc: imageSettings?.keyType === 'Custom' ? '(0.1/img)' : '(100/img)' }
14704
- // ]
14705
- // }
14706
- // ]
14707
- // },
14708
- // { cmd: 'stats', desc: 'Show remaining credits or Pollinations balance status' }
14709
- // ]
14710
- // },
14711
15376
  {
14712
15377
  cmd: "/mode",
14713
15378
  desc: "Toggle Flux/Flow modes",
@@ -14749,7 +15414,7 @@ function App({ args = [] }) {
14749
15414
  },
14750
15415
  {
14751
15416
  cmd: "/model",
14752
- desc: "Switch Model for Agent",
15417
+ desc: "Select Agent Model",
14753
15418
  subs: aiProvider === "OpenRouter" ? apiTier === "Free" ? [
14754
15419
  {
14755
15420
  cmd: "google/gemma-4-31b-it:free",
@@ -14761,11 +15426,11 @@ function App({ args = [] }) {
14761
15426
  },
14762
15427
  {
14763
15428
  cmd: "qwen/qwen3-coder:free",
14764
- desc: ""
15429
+ desc: "Text Only"
14765
15430
  },
14766
15431
  {
14767
15432
  cmd: "z-ai/glm-4.5-air:free",
14768
- desc: ""
15433
+ desc: "Text Only"
14769
15434
  }
14770
15435
  ] : [
14771
15436
  {
@@ -14794,19 +15459,19 @@ function App({ args = [] }) {
14794
15459
  },
14795
15460
  {
14796
15461
  cmd: "deepseek/deepseek-v4-pro",
14797
- desc: ""
15462
+ desc: "Text Only"
14798
15463
  },
14799
15464
  {
14800
15465
  cmd: "deepseek/deepseek-v4-flash",
14801
- desc: ""
15466
+ desc: "Text Only"
14802
15467
  },
14803
15468
  {
14804
15469
  cmd: "xiaomi/mimo-v2.5-pro",
14805
- desc: ""
15470
+ desc: "Text Only"
14806
15471
  },
14807
15472
  {
14808
15473
  cmd: "z-ai/glm-5",
14809
- desc: ""
15474
+ desc: "Text Only"
14810
15475
  },
14811
15476
  {
14812
15477
  cmd: "openai/gpt-5.2-codex",
@@ -14827,106 +15492,122 @@ function App({ args = [] }) {
14827
15492
  ] : aiProvider === "DeepSeek" ? [
14828
15493
  {
14829
15494
  cmd: "deepseek-v4-flash",
14830
- desc: "Fast & Efficient"
15495
+ desc: "Fast & Efficient (Text Only)"
14831
15496
  },
14832
15497
  {
14833
15498
  cmd: "deepseek-v4-pro",
14834
- desc: "High-Intelligence Reasoning"
15499
+ desc: "High-Intelligence Reasoning (Text Only)"
14835
15500
  }
14836
15501
  ] : aiProvider === "NVIDIA" ? [
15502
+ // --- Kimi (Moonshot AI) ---
14837
15503
  {
14838
15504
  cmd: "moonshotai/kimi-k2.6",
14839
15505
  desc: "Multimodal"
14840
15506
  },
15507
+ // --- DeepSeek Family ---
14841
15508
  {
14842
- cmd: "google/gemma-4-31b-it",
14843
- desc: ""
15509
+ cmd: "deepseek-ai/deepseek-v4-flash",
15510
+ desc: "Text Only"
14844
15511
  },
14845
15512
  {
14846
- cmd: "stepfun-ai/step-3.7-flash",
14847
- desc: ""
15513
+ cmd: "deepseek-ai/deepseek-v4-pro",
15514
+ desc: "Text Only"
14848
15515
  },
15516
+ // --- StepFun ---
14849
15517
  {
14850
- cmd: "minimaxai/minimax-m2.7",
14851
- desc: ""
15518
+ cmd: "stepfun-ai/step-3.7-flash",
15519
+ desc: "Multimodal"
14852
15520
  },
15521
+ // --- Gemma Family (Google) ---
14853
15522
  {
14854
- cmd: "deepseek-ai/deepseek-v4-flash",
14855
- desc: ""
15523
+ cmd: "google/gemma-4-31b-it",
15524
+ desc: "Multimodal"
14856
15525
  },
14857
15526
  {
14858
- cmd: "deepseek-ai/deepseek-v4-pro",
14859
- desc: ""
15527
+ cmd: "google/diffusiongemma-26b-a4b-it",
15528
+ desc: "Mega Fast [Experimental]"
14860
15529
  },
15530
+ // --- Mistral ---
14861
15531
  {
14862
15532
  cmd: "mistralai/mistral-medium-3.5-128b",
14863
- desc: ""
15533
+ desc: "Multimodal"
15534
+ },
15535
+ // --- GPT Open Source Series (OpenAI) ---
15536
+ {
15537
+ cmd: "openai/gpt-oss-20b",
15538
+ desc: "Text Only"
15539
+ },
15540
+ {
15541
+ cmd: "openai/gpt-oss-120b",
15542
+ desc: "Text Only"
14864
15543
  },
15544
+ // --- GLM (Zhipu AI) ---
14865
15545
  {
14866
15546
  cmd: "z-ai/glm-5.1",
14867
- desc: ""
15547
+ desc: "Text Only"
14868
15548
  },
15549
+ // --- MiniMax Family ---
14869
15550
  {
14870
- cmd: "google/diffusiongemma-26b-a4b-it",
14871
- desc: "Mega Fast [Experimental]"
15551
+ cmd: "minimaxai/minimax-m2.7",
15552
+ desc: "Text Only"
14872
15553
  },
14873
15554
  {
14874
15555
  cmd: "minimaxai/minimax-m3",
14875
- desc: ""
15556
+ desc: "Text Only"
14876
15557
  }
14877
15558
  ] : apiTier === "Free" ? [
14878
15559
  {
14879
15560
  cmd: "gemma-4-26b-a4b-it",
14880
- desc: "Standard & Faster"
15561
+ desc: "Standard & Faster (Multimodal)"
14881
15562
  },
14882
15563
  {
14883
15564
  cmd: "gemma-4-31b-it",
14884
- desc: "Standard Default"
15565
+ desc: "Standard Default (Multimodal)"
14885
15566
  },
14886
15567
  {
14887
15568
  cmd: "gemini-2.5-flash-lite",
14888
- desc: "Fast & Cheap (Limited Free Quota)"
15569
+ desc: "Fast & Cheap (Multimodal) [Limited Free Quota]"
14889
15570
  },
14890
15571
  {
14891
15572
  cmd: "gemini-2.5-flash",
14892
- desc: "Fast & Reliable (Limited Free Quota)"
15573
+ desc: "Fast & Reliable (Multimodal) [Limited Free Quota]"
14893
15574
  },
14894
15575
  {
14895
15576
  cmd: "gemini-3-flash-preview",
14896
- desc: "Fast & Lightweight (Limited Free Quota)"
15577
+ desc: "Fast & Lightweight (Multimodal) [Limited Free Quota]"
14897
15578
  },
14898
15579
  {
14899
15580
  cmd: "gemini-3.5-flash",
14900
- desc: "Flash Latest (Limited Free Quota) [Instability Issues]"
15581
+ desc: "Flash Latest (Multimodal) [Limited Free Quota] Instability Issues"
14901
15582
  }
14902
15583
  ] : [
14903
15584
  {
14904
15585
  cmd: "gemini-2.5-flash-lite",
14905
- desc: "Fast & Cheap"
15586
+ desc: "Fast & Cheap (Multimodal)"
14906
15587
  },
14907
15588
  {
14908
15589
  cmd: "gemini-2.5-flash",
14909
- desc: "Fast & Reliable"
15590
+ desc: "Fast & Reliable (Multimodal)"
14910
15591
  },
14911
15592
  {
14912
15593
  cmd: "gemini-2.5-pro",
14913
- desc: "Last gen Pro reasoning"
15594
+ desc: "Last gen Pro reasoning (Multimodal)"
14914
15595
  },
14915
15596
  {
14916
15597
  cmd: "gemini-3.1-flash-lite",
14917
- desc: "Ultra-Fast & Lite"
15598
+ desc: "Ultra-Fast & Lite (Multimodal)"
14918
15599
  },
14919
15600
  {
14920
15601
  cmd: "gemini-3-flash-preview",
14921
- desc: "Default, Fast & Lightweight"
15602
+ desc: "Default, Fast & Lightweight (Multimodal)"
14922
15603
  },
14923
15604
  {
14924
15605
  cmd: "gemini-3.5-flash",
14925
- desc: "Flash Latest [Instability Issues]"
15606
+ desc: "Flash Latest (Multimodal) [Instability Issues]"
14926
15607
  },
14927
15608
  {
14928
15609
  cmd: "gemini-3.1-pro-preview",
14929
- desc: "Pro Reasoning"
15610
+ desc: "Pro Reasoning (Multimodal)"
14930
15611
  }
14931
15612
  ]
14932
15613
  },
@@ -14943,7 +15624,7 @@ function App({ args = [] }) {
14943
15624
  cmd: "/fluxflow",
14944
15625
  desc: "Project management",
14945
15626
  subs: [
14946
- { cmd: "init", desc: "Create FluxFlow.md template" }
15627
+ { cmd: "init", desc: "Create empty FluxFlow.md template" }
14947
15628
  ]
14948
15629
  },
14949
15630
  {
@@ -14958,8 +15639,8 @@ function App({ args = [] }) {
14958
15639
  cmd: "/update",
14959
15640
  desc: "Check/Install updates",
14960
15641
  subs: [
14961
- { cmd: "check", desc: "Check for new version" },
14962
- { cmd: "latest", desc: "Install latest release" }
15642
+ { cmd: "latest", desc: "Install latest release" },
15643
+ { cmd: "check", desc: "Check for new version" }
14963
15644
  ]
14964
15645
  }
14965
15646
  ];
@@ -14977,6 +15658,7 @@ function App({ args = [] }) {
14977
15658
  setInputKey((prev) => prev + 1);
14978
15659
  return;
14979
15660
  }
15661
+ didSignalTerminationRef.current = false;
14980
15662
  const normalizedValue = value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trimEnd();
14981
15663
  if (normalizedValue.endsWith("\\")) {
14982
15664
  setInput(normalizedValue.slice(0, -1) + "\n");
@@ -15038,6 +15720,7 @@ ${cleanText}`, color: "magenta" }];
15038
15720
  const target = h[targetId] || Object.values(h).find((h2) => h2.name.toLowerCase() === targetId.toLowerCase());
15039
15721
  if (target) {
15040
15722
  stdout.write("\x1B[2J\x1B[3J\x1B[H");
15723
+ clearBlocksCache();
15041
15724
  setChatId(targetId);
15042
15725
  const savedData = await loadChatContext(targetId);
15043
15726
  chatTokenStartRef.current = sessionTotalTokens - savedData.total;
@@ -15112,12 +15795,16 @@ ${cleanText}`, color: "magenta" }];
15112
15795
  case "/clear": {
15113
15796
  if (stdout) {
15114
15797
  stdout.write("\x1B[2J\x1B[3J\x1B[H");
15798
+ if (stdout.isTTY) {
15799
+ stdout.write("\x1B[?2004h");
15800
+ }
15115
15801
  }
15116
15802
  setMessages([
15117
15803
  { id: "logo-" + Date.now(), role: "system", isLogo: true, isMeta: true }
15118
15804
  ]);
15119
15805
  setCompletedIndex(1);
15120
15806
  setClearKey((prev) => prev + 1);
15807
+ clearBlocksCache();
15121
15808
  if (parsedArgs.playground) {
15122
15809
  parsedArgs.playground = false;
15123
15810
  deleteChat(PLAYGROUND_CHAT_ID).catch(() => {
@@ -15163,6 +15850,14 @@ ${cleanText}`, color: "magenta" }];
15163
15850
  setIsExpanded(false);
15164
15851
  setChatTokens(0);
15165
15852
  chatTokenStartRef.current = sessionTotalTokens;
15853
+ setTimeout(() => {
15854
+ if (global.gc) {
15855
+ try {
15856
+ global.gc();
15857
+ } catch (e) {
15858
+ }
15859
+ }
15860
+ }, 500);
15166
15861
  break;
15167
15862
  }
15168
15863
  case "/revert": {
@@ -15178,6 +15873,14 @@ ${cleanText}`, color: "magenta" }];
15178
15873
  });
15179
15874
  }
15180
15875
  });
15876
+ setTimeout(() => {
15877
+ if (global.gc) {
15878
+ try {
15879
+ global.gc();
15880
+ } catch (e) {
15881
+ }
15882
+ }
15883
+ }, 500);
15181
15884
  break;
15182
15885
  }
15183
15886
  case "/mode": {
@@ -15784,6 +16487,26 @@ ${timestamp}` };
15784
16487
  return [...prev, userMessage];
15785
16488
  });
15786
16489
  const streamChat = async () => {
16490
+ let didAppendCancel = false;
16491
+ const appendCancelMessage = () => {
16492
+ if (didAppendCancel) return;
16493
+ didAppendCancel = true;
16494
+ setMessages((prev) => {
16495
+ const lastMsg = prev[prev.length - 1];
16496
+ if (lastMsg && lastMsg.text && lastMsg.text.includes("Request Cancelled")) {
16497
+ return prev;
16498
+ }
16499
+ const updatedPrev = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
16500
+ const newMsgs = [...updatedPrev, {
16501
+ id: "cancel-" + Date.now(),
16502
+ role: "system",
16503
+ text: "\n\n\x1B[33m\u2139 Request Cancelled\x1B[0m",
16504
+ isMeta: true
16505
+ }];
16506
+ setCompletedIndex(newMsgs.length);
16507
+ return newMsgs;
16508
+ });
16509
+ };
15787
16510
  let hasFiredJanitor = false;
15788
16511
  setIsProcessing(true);
15789
16512
  setIsExpanded(false);
@@ -15976,7 +16699,14 @@ Selection: ${val}`,
15976
16699
  let toolCallBalance = 0;
15977
16700
  let inToolCallString = null;
15978
16701
  const signalRegex = /\[?\s*turn\s*:\s*.*?\s*\]?/gi;
16702
+ const bullyTheBug = path20.join(DATA_DIR, "padding");
15979
16703
  for await (const packet of stream) {
16704
+ if (packet.type === "interactive_turn_finished") {
16705
+ fs22.writeFileSync(bullyTheBug, "pad_0xa\n");
16706
+ } else {
16707
+ fs22.appendFileSync(bullyTheBug, "\r");
16708
+ parsedBlocks.completed = [];
16709
+ }
15980
16710
  if (isFirstPacket && packet.type === "text") {
15981
16711
  apiStart = Date.now();
15982
16712
  isFirstPacket = false;
@@ -15987,17 +16717,7 @@ Selection: ${val}`,
15987
16717
  sendStatus(packet.content);
15988
16718
  }
15989
16719
  if (packet.content === "Request Cancelled") {
15990
- setMessages((prev) => {
15991
- const updatedPrev = prev.map((m) => m.isStreaming ? { ...m, isStreaming: false } : m);
15992
- const newMsgs = [...updatedPrev, {
15993
- id: "cancel-" + Date.now(),
15994
- role: "system",
15995
- text: "\n\n\x1B[33m\u2139 Request Cancelled\x1B[0m",
15996
- isMeta: true
15997
- }];
15998
- setCompletedIndex(newMsgs.length);
15999
- return newMsgs;
16000
- });
16720
+ appendCancelMessage();
16001
16721
  }
16002
16722
  continue;
16003
16723
  }
@@ -16036,6 +16756,14 @@ Selection: ${val}`,
16036
16756
  setCompletedIndex(newMsgs.length);
16037
16757
  return newMsgs;
16038
16758
  });
16759
+ setTimeout(() => {
16760
+ if (global.gc) {
16761
+ try {
16762
+ global.gc();
16763
+ } catch (e) {
16764
+ }
16765
+ }
16766
+ }, 100);
16039
16767
  continue;
16040
16768
  }
16041
16769
  if (packet.type === "interactive_turn_finished") {
@@ -16244,29 +16972,31 @@ Selection: ${val}`,
16244
16972
  }
16245
16973
  if (inThinkMode && currentThinkId) {
16246
16974
  setMessages((prev) => {
16975
+ const next = [...prev];
16247
16976
  let transitioning = false;
16248
16977
  let transitionContent = "";
16249
- const newMsgs = prev.map((m) => {
16250
- if (m.id === currentThinkId) {
16251
- const newText = m.text + chunkText;
16978
+ for (let i = next.length - 1; i >= 0; i--) {
16979
+ if (next[i].id === currentThinkId) {
16980
+ const newText = next[i].text + chunkText;
16252
16981
  if (newText.toLowerCase().includes("</think>")) {
16253
16982
  transitioning = true;
16254
16983
  const parts = newText.split(/<\/think>/gi);
16255
16984
  transitionContent = parts.slice(1).join("</think>") || "";
16256
- const startTime = m.startTime || parseInt(m.id.split("-")[1]) || Date.now();
16985
+ const startTime = next[i].startTime || parseInt(String(next[i].id).split("-")[1]) || Date.now();
16257
16986
  const duration = Date.now() - startTime;
16258
- return { ...m, text: parts[0], isStreaming: false, duration };
16987
+ next[i] = { ...next[i], text: parts[0], isStreaming: false, duration };
16988
+ } else {
16989
+ next[i] = { ...next[i], text: newText, isStreaming: true };
16259
16990
  }
16260
- return { ...m, text: newText, isStreaming: true };
16991
+ break;
16261
16992
  }
16262
- return m;
16263
- });
16993
+ }
16264
16994
  if (transitioning) {
16265
16995
  inThinkMode = false;
16266
16996
  currentAgentId = "agent-" + Date.now();
16267
- return [...newMsgs, { id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?(think|thought)>/gi, ""), isStreaming: true }];
16997
+ next.push({ id: currentAgentId, role: "agent", text: transitionContent.replace(/<\/?(think|thought)>/gi, ""), isStreaming: true });
16268
16998
  }
16269
- return newMsgs;
16999
+ return next;
16270
17000
  });
16271
17001
  } else if (!inThinkMode) {
16272
17002
  const chunkLower2 = chunkText.toLowerCase();
@@ -16277,9 +17007,16 @@ Selection: ${val}`,
16277
17007
  currentAgentId = "agent-" + Date.now();
16278
17008
  setMessages((prev) => [...prev, { id: currentAgentId, role: "agent", text: chunkText, isStreaming: true }]);
16279
17009
  } else {
16280
- setMessages((prev) => prev.map(
16281
- (m) => m.id === currentAgentId ? { ...m, text: m.text + chunkText, isStreaming: true } : m
16282
- ));
17010
+ setMessages((prev) => {
17011
+ const next = [...prev];
17012
+ for (let i = next.length - 1; i >= 0; i--) {
17013
+ if (next[i].id === currentAgentId) {
17014
+ next[i] = { ...next[i], text: next[i].text + chunkText, isStreaming: true };
17015
+ break;
17016
+ }
17017
+ }
17018
+ return next;
17019
+ });
16283
17020
  }
16284
17021
  }
16285
17022
  }
@@ -16293,6 +17030,9 @@ Selection: ${val}`,
16293
17030
  } finally {
16294
17031
  setIsProcessing(false);
16295
17032
  setStatusText(null);
17033
+ if (didSignalTerminationRef.current) {
17034
+ appendCancelMessage();
17035
+ }
16296
17036
  if (!hasFiredJanitor) {
16297
17037
  if (process.stdout.isTTY) {
16298
17038
  process.stdout.write("\x1B]0;FluxFlow | Idle\x07");
@@ -16384,7 +17124,7 @@ Selection: ${val}`,
16384
17124
  }
16385
17125
  return [];
16386
17126
  }, [input, isFilePickerDismissed]);
16387
- useEffect9(() => {
17127
+ useEffect11(() => {
16388
17128
  setSelectedIndex(0);
16389
17129
  }, [suggestions]);
16390
17130
  const CustomMenuItem = ({ label: label2, isSelected }) => {
@@ -16881,6 +17621,7 @@ Selection: ${val}`,
16881
17621
  stdout.write("\x1B[2J\x1B[3J\x1B[H");
16882
17622
  }
16883
17623
  setClearKey((prev) => prev + 1);
17624
+ clearBlocksCache();
16884
17625
  const targetIdx = messages.findLastIndex(
16885
17626
  (m) => m.role === "user" && m.text && (m.text.startsWith(targetPrompt) || m.text.includes(targetPrompt))
16886
17627
  );
@@ -16933,6 +17674,7 @@ Selection: ${val}`,
16933
17674
  const h = await loadHistory();
16934
17675
  if (h[id]) {
16935
17676
  stdout.write("\x1B[2J\x1B[3J\x1B[H");
17677
+ clearBlocksCache();
16936
17678
  setChatId(id);
16937
17679
  const savedData = await loadChatContext(id);
16938
17680
  chatTokenStartRef.current = sessionTotalTokens - savedData.total;
@@ -17147,7 +17889,7 @@ Selection: ${val}`,
17147
17889
  }
17148
17890
  )));
17149
17891
  default:
17150
- return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" }, showBtwBox && btwResponse && /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "grey", paddingX: 2, paddingY: 1, width: "100%", marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Box15, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React15.createElement(Text16, { color: "white", bold: true, underline: true }, "INQUIRY RESPONSE"), /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "[ ESC to Close ]")), /* @__PURE__ */ React15.createElement(Box15, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React15.createElement(CodeRenderer, { text: btwResponse, columns: terminalSize.columns - 6 }))), /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginBottom: 0, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, null, statusText ? /* @__PURE__ */ React15.createElement(Box15, { gap: 1 }, /* @__PURE__ */ React15.createElement(dist_default, { colors: ["#001a1a", "#080510", "#12021c"] }, /* @__PURE__ */ React15.createElement(build_default, null)), /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true, italic: true }, statusText)) : /* @__PURE__ */ React15.createElement(Text16, { color: "gray", italic: true }, input.length > 0 && escPressCount ? "Press ESC again to clear input" : "Waiting for input...")), /* @__PURE__ */ React15.createElement(Box15, null, wittyPhrase && /* @__PURE__ */ React15.createElement(Text16, { color: "gray", italic: true }, wittyPhrase, " "), /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, "[ "), /* @__PURE__ */ React15.createElement(Text16, { color: "white" }, tempModelOverride || activeModel), /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, " ]"))), /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { width: "100%", height: 1, overflow: "hidden" }, /* @__PURE__ */ React15.createElement(Text16, { color: "#555555" }, "\u2584".repeat(Math.max(1, terminalSize.columns)))), /* @__PURE__ */ React15.createElement(
17892
+ return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", marginTop: 1, flexShrink: 0, width: "100%" }, showBtwBox && btwResponse && /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "grey", paddingX: 2, paddingY: 1, width: "100%", marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Box15, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React15.createElement(Text16, { color: "white", bold: true, underline: true }, "INQUIRY RESPONSE"), /* @__PURE__ */ React15.createElement(Text16, { color: "gray" }, "[ ESC to Close ]")), /* @__PURE__ */ React15.createElement(Box15, { marginTop: 1, width: "100%" }, /* @__PURE__ */ React15.createElement(CodeRenderer, { text: btwResponse, columns: terminalSize.columns - 6 }))), /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginBottom: 0, justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, null, statusText ? /* @__PURE__ */ React15.createElement(Box15, { gap: 1 }, /* @__PURE__ */ React15.createElement(dist_default, { colors: ["#001a1a", "#080510", "#12021c"] }, /* @__PURE__ */ React15.createElement(build_default, null)), /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true, italic: true }, statusText)) : /* @__PURE__ */ React15.createElement(Text16, { color: "gray", italic: true }, input.length > 0 && escPressCount ? "Press ESC again to clear input" : hasPasteBlock ? "Press CTRL + O to expand" : "Waiting for input...")), /* @__PURE__ */ React15.createElement(Box15, null, wittyPhrase && /* @__PURE__ */ React15.createElement(Text16, { color: "gray", italic: true }, wittyPhrase, " "), /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, "[ "), /* @__PURE__ */ React15.createElement(Text16, { color: "white" }, tempModelOverride || activeModel), /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, " ]"))), /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { width: "100%", height: 1, overflow: "hidden" }, /* @__PURE__ */ React15.createElement(Text16, { color: "#555555" }, "\u2584".repeat(Math.max(1, terminalSize.columns)))), /* @__PURE__ */ React15.createElement(
17151
17893
  Box15,
17152
17894
  {
17153
17895
  backgroundColor: "#555555",
@@ -17160,6 +17902,7 @@ Selection: ${val}`,
17160
17902
  MultilineInput,
17161
17903
  {
17162
17904
  key: `input-${inputKey}`,
17905
+ onPasteStateChange: setHasPasteBlock,
17163
17906
  focus: !isTerminalFocused && !isCompressing,
17164
17907
  showCursor: isAppFocused && !isCompressing,
17165
17908
  lastFocusEventTime: lastFocusEventTime.current,
@@ -17203,7 +17946,7 @@ Selection: ${val}`,
17203
17946
  aiProvider,
17204
17947
  version: versionFluxflow
17205
17948
  }
17206
- )), activeCommand && /* @__PURE__ */ React15.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(TerminalBox, { command: activeCommand, output: execOutput, isFocused: isTerminalFocused, isPty: isActiveCommandPty }))), isInitializing ? /* @__PURE__ */ React15.createElement(Box15, { borderStyle: "double", borderColor: "grey", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React15.createElement(Text16, { color: "white" }, "Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React15.createElement(Box15, { borderStyle: "round", borderColor: "white", padding: 0, flexDirection: "column", flexShrink: 0, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, "API KEY REQUIRED")), /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, flexDirection: "column" }, setupStep === 0 ? /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Text16, { color: "white" }, "Select your Preferred Provider:"), /* @__PURE__ */ React15.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(
17949
+ )), activeCommand && /* @__PURE__ */ React15.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(TerminalBox, { command: activeCommand, output: execOutput, isFocused: isTerminalFocused, isPty: isActiveCommandPty, terminalHeight: terminalSize.rows }))), isInitializing ? /* @__PURE__ */ React15.createElement(Box15, { borderStyle: "double", borderColor: "grey", padding: 1, flexShrink: 0 }, /* @__PURE__ */ React15.createElement(Text16, { color: "white" }, "Starting Flux Flow...")) : !apiKey ? /* @__PURE__ */ React15.createElement(Box15, { borderStyle: "round", borderColor: "white", padding: 0, flexDirection: "column", flexShrink: 0, width: "100%" }, /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text16, { color: "gray", bold: true }, "API KEY REQUIRED")), /* @__PURE__ */ React15.createElement(Box15, { paddingX: 1, flexDirection: "column" }, setupStep === 0 ? /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Text16, { color: "white" }, "Select your Preferred Provider:"), /* @__PURE__ */ React15.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(
17207
17950
  CommandMenu,
17208
17951
  {
17209
17952
  items: [
@@ -17573,7 +18316,22 @@ var init_app = __esm({
17573
18316
  // src/cli.jsx
17574
18317
  import { spawn as spawn3 } from "child_process";
17575
18318
  import { fileURLToPath as fileURLToPath2 } from "url";
17576
- var HEAP_LIMIT = 6144;
18319
+ import os5 from "os";
18320
+ var totalSystemRamBytes = os5.totalmem();
18321
+ var totalSystemRamMB = totalSystemRamBytes / (1024 * 1024);
18322
+ var SAFETY_MARGIN = 0.5;
18323
+ var calculatedLimit = Math.floor(totalSystemRamMB * SAFETY_MARGIN);
18324
+ var _rawArgs = process.argv.slice(2);
18325
+ var _allocIdx = _rawArgs.indexOf("--allocation");
18326
+ var _allocValue = _allocIdx !== -1 ? parseInt(_rawArgs[_allocIdx + 1], 10) : NaN;
18327
+ var _maxAllowed = Math.floor(totalSystemRamMB * 0.75);
18328
+ var HEAP_LIMIT = !isNaN(_allocValue) && _allocValue > 0 ? Math.min(_allocValue, _maxAllowed) : Math.max(1536, Math.min(32768, calculatedLimit));
18329
+ if (!Number.isNaN(_allocValue)) {
18330
+ console.log("\n[MEMORY] Using custom memory allocation: " + _allocValue + " MB" + (_allocValue > _maxAllowed ? " (Max allowed: " + _maxAllowed + "MB)" : ""));
18331
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
18332
+ } else {
18333
+ console.log("\n[MEMORY] Using auto-detected memory allocation: " + calculatedLimit + " MB");
18334
+ }
17577
18335
  var isBundled = fileURLToPath2(import.meta.url).endsWith(".js");
17578
18336
  if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-size"))) {
17579
18337
  const cp = spawn3(process.execPath, [
@@ -17608,6 +18366,7 @@ if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-siz
17608
18366
  --thinking <Fast|Low|Medium|High|xHigh> Set startup thinking level
17609
18367
  --memory <on|off> Toggle memory system
17610
18368
  --resume <session_id> Resume a previous session
18369
+ --allocation <mb> Override Node.js max-old-space-size in MB (default: auto)
17611
18370
  --package <npm|pnpm|yarn|bun> Set package manager for updates
17612
18371
  --auto-del <1d|7d|30d> Set history auto-deletion timeframe
17613
18372
  --auto-exec <on|off> Toggle permission for autonomous command execution
@@ -17697,7 +18456,7 @@ if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-siz
17697
18456
  }
17698
18457
  const promptPackageManager = async () => {
17699
18458
  const React17 = (await import("react")).default;
17700
- const { useState: useState13 } = React17;
18459
+ const { useState: useState15 } = React17;
17701
18460
  const { render: render2, Box: Box16, Text: Text17 } = await import("ink");
17702
18461
  const SelectInput3 = (await import("ink-select-input")).default;
17703
18462
  const TextInput5 = (await import("ink-text-input")).default;
@@ -17714,8 +18473,8 @@ if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-siz
17714
18473
  };
17715
18474
  let unmountFn;
17716
18475
  const PromptComponent = () => {
17717
- const [step, setStep] = useState13("select");
17718
- const [customCommand2, setCustomCommand] = useState13("");
18476
+ const [step, setStep] = useState15("select");
18477
+ const [customCommand2, setCustomCommand] = useState15("");
17719
18478
  const handleSelect = (item) => {
17720
18479
  if (item.value === "custom") {
17721
18480
  setStep("custom");
@@ -17816,7 +18575,20 @@ if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-siz
17816
18575
  if (process.stdout.isTTY) {
17817
18576
  process.stdout.write("\x1B]0;FluxFlow\x07");
17818
18577
  process.stdout.write("\x1B]633;P;TerminalTitle=FluxFlow\x07");
18578
+ process.stdout.write("\x1B[?2004h");
17819
18579
  }
18580
+ const disableBracketedPaste = () => {
18581
+ if (process.stdout.isTTY) {
18582
+ process.stdout.write("\x1B[?2004l");
18583
+ }
18584
+ };
18585
+ process.on("exit", disableBracketedPaste);
18586
+ ["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
18587
+ process.once(sig, () => {
18588
+ disableBracketedPaste();
18589
+ process.exit(0);
18590
+ });
18591
+ });
17820
18592
  if (args.includes("--playground")) {
17821
18593
  const originalCwd = process.cwd();
17822
18594
  process.argv.push("--original-cwd", originalCwd);