ideacode 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/repl.js +68 -13
- package/package.json +1 -1
package/dist/repl.js
CHANGED
|
@@ -41,6 +41,7 @@ const INITIAL_BANNER_LINES = 12;
|
|
|
41
41
|
const ENABLE_PARALLEL_TOOL_CALLS = process.env.IDEACODE_PARALLEL_TOOL_CALLS !== "0";
|
|
42
42
|
const PARALLEL_SAFE_TOOLS = new Set(["read", "glob", "grep", "web_fetch", "web_search"]);
|
|
43
43
|
const LOADING_TICK_MS = 80;
|
|
44
|
+
const MAX_EMPTY_ASSISTANT_RETRIES = 3;
|
|
44
45
|
const TRUNCATE_NOTE = "\n\n(Output truncated to save context. Use read with offset/limit, grep with a specific pattern, or tail with fewer lines to get more.)";
|
|
45
46
|
function truncateToolResult(content) {
|
|
46
47
|
if (content.length <= MAX_TOOL_RESULT_CHARS)
|
|
@@ -63,11 +64,57 @@ function listFilesWithFilter(cwd, filter) {
|
|
|
63
64
|
return [];
|
|
64
65
|
}
|
|
65
66
|
}
|
|
67
|
+
function stripHeredocBodies(cmdRaw) {
|
|
68
|
+
const lines = cmdRaw.replace(/\r\n/g, "\n").split("\n");
|
|
69
|
+
const out = [];
|
|
70
|
+
let i = 0;
|
|
71
|
+
while (i < lines.length) {
|
|
72
|
+
const line = lines[i] ?? "";
|
|
73
|
+
out.push(line);
|
|
74
|
+
const markerMatch = line.match(/<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1/);
|
|
75
|
+
if (!markerMatch) {
|
|
76
|
+
i += 1;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const marker = markerMatch[2] ?? "";
|
|
80
|
+
i += 1;
|
|
81
|
+
while (i < lines.length) {
|
|
82
|
+
const bodyLine = lines[i] ?? "";
|
|
83
|
+
if (bodyLine.trim() === marker) {
|
|
84
|
+
out.push(bodyLine);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
i += 1;
|
|
88
|
+
}
|
|
89
|
+
i += 1;
|
|
90
|
+
}
|
|
91
|
+
return out.join("\n");
|
|
92
|
+
}
|
|
66
93
|
function summarizeBashCommand(cmdRaw) {
|
|
67
|
-
const
|
|
68
|
-
|
|
94
|
+
const sanitized = stripHeredocBodies(cmdRaw);
|
|
95
|
+
const parts = sanitized
|
|
96
|
+
.split(/\n|&&|\|\||;|\|/g)
|
|
69
97
|
.map((s) => s.trim())
|
|
70
98
|
.filter(Boolean);
|
|
99
|
+
const skipTokens = new Set([
|
|
100
|
+
"if",
|
|
101
|
+
"then",
|
|
102
|
+
"else",
|
|
103
|
+
"elif",
|
|
104
|
+
"fi",
|
|
105
|
+
"for",
|
|
106
|
+
"while",
|
|
107
|
+
"do",
|
|
108
|
+
"done",
|
|
109
|
+
"case",
|
|
110
|
+
"esac",
|
|
111
|
+
"in",
|
|
112
|
+
"function",
|
|
113
|
+
"{",
|
|
114
|
+
"}",
|
|
115
|
+
"(",
|
|
116
|
+
")",
|
|
117
|
+
]);
|
|
71
118
|
const commands = [];
|
|
72
119
|
for (const part of parts) {
|
|
73
120
|
let s = part.replace(/^\(+/, "").trim();
|
|
@@ -84,9 +131,11 @@ function summarizeBashCommand(cmdRaw) {
|
|
|
84
131
|
}
|
|
85
132
|
if (!s)
|
|
86
133
|
continue;
|
|
87
|
-
const token = s.split(/\s+/)[0]
|
|
134
|
+
const token = (s.split(/\s+/)[0] ?? "").replace(/^['"]|['"]$/g, "").toLowerCase();
|
|
88
135
|
if (!/^[A-Za-z0-9_./-]+$/.test(token))
|
|
89
136
|
continue;
|
|
137
|
+
if (skipTokens.has(token))
|
|
138
|
+
continue;
|
|
90
139
|
if (token === "echo")
|
|
91
140
|
continue;
|
|
92
141
|
if (token === "cat" && /<<\s*['"]?EOF/i.test(s))
|
|
@@ -354,7 +403,7 @@ export function Repl({ apiKey, cwd, onQuit }) {
|
|
|
354
403
|
}, [cwd, onQuit]);
|
|
355
404
|
const [loading, setLoading] = useState(false);
|
|
356
405
|
const [loadingLabel, setLoadingLabel] = useState("Thinking…");
|
|
357
|
-
const
|
|
406
|
+
const cursorBlinkOn = true;
|
|
358
407
|
const [showPalette, setShowPalette] = useState(false);
|
|
359
408
|
const [paletteIndex, setPaletteIndex] = useState(0);
|
|
360
409
|
const [showModelSelector, setShowModelSelector] = useState(false);
|
|
@@ -379,15 +428,6 @@ export function Repl({ apiKey, cwd, onQuit }) {
|
|
|
379
428
|
process.stdout.write("\x1b[?1006l\x1b[?1000l");
|
|
380
429
|
};
|
|
381
430
|
}, []);
|
|
382
|
-
useEffect(() => {
|
|
383
|
-
setCursorBlinkOn(true);
|
|
384
|
-
if (loading)
|
|
385
|
-
return;
|
|
386
|
-
const timer = setInterval(() => {
|
|
387
|
-
setCursorBlinkOn((prev) => !prev);
|
|
388
|
-
}, 520);
|
|
389
|
-
return () => clearInterval(timer);
|
|
390
|
-
}, [loading, inputValue, inputCursor]);
|
|
391
431
|
const estimatedTokens = useMemo(() => estimateTokens(messages, undefined), [messages]);
|
|
392
432
|
const contextWindowK = useMemo(() => {
|
|
393
433
|
const ctx = modelList.find((m) => m.id === currentModel)?.context_length;
|
|
@@ -588,6 +628,7 @@ export function Repl({ apiKey, cwd, onQuit }) {
|
|
|
588
628
|
appendLog(colors.muted(" (context compressed to stay under limit)\n"));
|
|
589
629
|
}
|
|
590
630
|
setLoadingLabel("Thinking…");
|
|
631
|
+
let emptyAssistantRetries = 0;
|
|
591
632
|
for (;;) {
|
|
592
633
|
setLoading(true);
|
|
593
634
|
setLoadingLabel("Thinking…");
|
|
@@ -597,6 +638,20 @@ export function Repl({ apiKey, cwd, onQuit }) {
|
|
|
597
638
|
},
|
|
598
639
|
});
|
|
599
640
|
const contentBlocks = response.content ?? [];
|
|
641
|
+
const hasMeaningfulAssistantOutput = contentBlocks.some((block) => block.type === "tool_use" || (block.type === "text" && !!block.text?.trim()));
|
|
642
|
+
if (!hasMeaningfulAssistantOutput) {
|
|
643
|
+
emptyAssistantRetries += 1;
|
|
644
|
+
if (emptyAssistantRetries <= MAX_EMPTY_ASSISTANT_RETRIES) {
|
|
645
|
+
setLoadingLabel(`No output yet, retrying ${emptyAssistantRetries}/${MAX_EMPTY_ASSISTANT_RETRIES}…`);
|
|
646
|
+
appendLog(colors.muted(` ${icons.tool} model returned an empty turn, retrying (${emptyAssistantRetries}/${MAX_EMPTY_ASSISTANT_RETRIES})…`));
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
appendLog(colors.error(`${icons.error} model returned empty output repeatedly. Stopping this turn; you can submit "continue" to resume.`));
|
|
650
|
+
appendLog("");
|
|
651
|
+
setMessages(state);
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
emptyAssistantRetries = 0;
|
|
600
655
|
const toolResults = [];
|
|
601
656
|
const renderToolOutcome = (planned, result, extraIndent = 0) => {
|
|
602
657
|
const ok = !result.startsWith("error:");
|