coder-agent 2.9.8 → 2.9.10
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/agent.js +77 -7
- package/dist/memory.js +20 -1
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -83,6 +83,30 @@ function extractDiagnostics(text) {
|
|
|
83
83
|
}
|
|
84
84
|
return diagnostics;
|
|
85
85
|
}
|
|
86
|
+
function extractFilePaths(text) {
|
|
87
|
+
const pathRegex = /(?:\.\/|\.\.\\|[a-zA-Z]:\\)?(?:[a-zA-Z0-9_\-\.]+[\\\/])+[a-zA-Z0-9_\-\.]+\.[a-zA-Z0-9]+/g;
|
|
88
|
+
const fileRegex = /\b[a-zA-Z0-9_\-\.]+\.[a-zA-Z0-9]+\b/g;
|
|
89
|
+
const matches = new Set();
|
|
90
|
+
let match;
|
|
91
|
+
while ((match = pathRegex.exec(text)) !== null) {
|
|
92
|
+
matches.add(match[0]);
|
|
93
|
+
}
|
|
94
|
+
while ((match = fileRegex.exec(text)) !== null) {
|
|
95
|
+
matches.add(match[0]);
|
|
96
|
+
}
|
|
97
|
+
const paths = [];
|
|
98
|
+
for (const m of matches) {
|
|
99
|
+
const trimmed = m.trim();
|
|
100
|
+
if (!trimmed)
|
|
101
|
+
continue;
|
|
102
|
+
if (/^\d/.test(trimmed))
|
|
103
|
+
continue;
|
|
104
|
+
if (trimmed.includes("://") || trimmed.startsWith("http"))
|
|
105
|
+
continue;
|
|
106
|
+
paths.push(trimmed);
|
|
107
|
+
}
|
|
108
|
+
return paths;
|
|
109
|
+
}
|
|
86
110
|
function getToolBadge(name) {
|
|
87
111
|
const blue = chalk.hex('#0a84ff');
|
|
88
112
|
const amber = chalk.hex('#ff9f0a');
|
|
@@ -253,7 +277,7 @@ function hasRepeatingCycle(history) {
|
|
|
253
277
|
const n = history.length;
|
|
254
278
|
for (let len = 1; len <= 4; len++) {
|
|
255
279
|
if (n >= len * 2) {
|
|
256
|
-
const minRepeats =
|
|
280
|
+
const minRepeats = 2;
|
|
257
281
|
if (n >= len * minRepeats) {
|
|
258
282
|
let isLoop = true;
|
|
259
283
|
const lastBlock = history.slice(n - len);
|
|
@@ -680,6 +704,49 @@ export class Agent {
|
|
|
680
704
|
}
|
|
681
705
|
enrichedPrompt += "\n\n=== Enriched Code Context (Auto-Parsed) ===\n" + contexts.join("\n\n") + "\n===========================================";
|
|
682
706
|
}
|
|
707
|
+
// Auto-resolve any file paths explicitly mentioned in the user message
|
|
708
|
+
try {
|
|
709
|
+
const potentialPaths = extractFilePaths(userMessage);
|
|
710
|
+
const autoResolvedContexts = [];
|
|
711
|
+
for (const rawPath of potentialPaths) {
|
|
712
|
+
// Skip if already in diagnostics to avoid duplication
|
|
713
|
+
if (diagnostics.some(d => d.resource.toLowerCase().includes(rawPath.toLowerCase()))) {
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
const filePath = normalizeFilePath(rawPath);
|
|
717
|
+
try {
|
|
718
|
+
const stats = await fs.stat(filePath);
|
|
719
|
+
if (stats.isFile()) {
|
|
720
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
721
|
+
const lines = content.split(/\r?\n/);
|
|
722
|
+
const fileContentText = lines.length > 800
|
|
723
|
+
? lines.slice(0, 800).join("\n") + "\n... [Content truncated: first 800 lines shown] ..."
|
|
724
|
+
: content;
|
|
725
|
+
autoResolvedContexts.push(`File: ${rawPath}\n\`\`\`\n${fileContentText}\n\`\`\``);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
catch {
|
|
729
|
+
try {
|
|
730
|
+
// Try resolving relative path if absolute resolution failed
|
|
731
|
+
const relativePath = path.relative("/", filePath).replace(/^[a-zA-Z]:/, "").replace(/^\\+|^[//]+/, "");
|
|
732
|
+
const stats = await fs.stat(relativePath);
|
|
733
|
+
if (stats.isFile()) {
|
|
734
|
+
const content = await fs.readFile(relativePath, "utf-8");
|
|
735
|
+
const lines = content.split(/\r?\n/);
|
|
736
|
+
const fileContentText = lines.length > 800
|
|
737
|
+
? lines.slice(0, 800).join("\n") + "\n... [Content truncated: first 800 lines shown] ..."
|
|
738
|
+
: content;
|
|
739
|
+
autoResolvedContexts.push(`File: ${rawPath}\n\`\`\`\n${fileContentText}\n\`\`\``);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
catch { }
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (autoResolvedContexts.length > 0) {
|
|
746
|
+
enrichedPrompt += "\n\n=== Auto-Resolved File Context ===\n" + autoResolvedContexts.join("\n\n") + "\n===================================";
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
catch { }
|
|
683
750
|
this.memory.add({ role: "user", content: enrichedPrompt });
|
|
684
751
|
let iterations = 0;
|
|
685
752
|
const MAX_ITERATIONS = 12;
|
|
@@ -860,12 +927,15 @@ export class Agent {
|
|
|
860
927
|
console.log(chalk.hex('#ff453a')('\n✕ Loop intervention failed: Coder is stuck in an execution loop. Exiting to prompt.'));
|
|
861
928
|
break;
|
|
862
929
|
}
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
930
|
+
const originalGoal = this.memory.getAll().find(m => m.role === "user")?.content || userMessage;
|
|
931
|
+
const warningMessage = `⚠️ [LOOP DETECTED & CONTEXT RESET] You got stuck in a repeating thinking/execution loop. To break the loop, all intermediate repetitive chat context has been discarded.
|
|
932
|
+
|
|
933
|
+
Here is your original goal:
|
|
934
|
+
"${originalGoal}"
|
|
935
|
+
|
|
936
|
+
Please start fresh. Re-evaluate your strategy, check other files, run different commands, or ask the user for clarification directly. Do NOT repeat the same failed tool calls.`;
|
|
937
|
+
console.log(chalk.hex('#ff9f0a')('\n⚠ Loop detected! Compressing memory and resetting context window...'));
|
|
938
|
+
this.memory.resetToInitialPrompt(warningMessage);
|
|
869
939
|
stateHistory.length = 0; // Reset history to allow a fresh start
|
|
870
940
|
}
|
|
871
941
|
else {
|
package/dist/memory.js
CHANGED
|
@@ -15,7 +15,7 @@ CORE OPERATING PRINCIPLES
|
|
|
15
15
|
- What is the correct file/folder structure?
|
|
16
16
|
- What dependencies are required and at what versions?
|
|
17
17
|
- What edge cases exist in this domain?
|
|
18
|
-
If the target file
|
|
18
|
+
If the target file is clear, or if its contents are already provided in the prompt context (e.g. under "Auto-Resolved File Context"), skip writing a plan, reasoning preamble, or explanation. Proceed directly to executing the edit tools (like patch_file or write_file) in the very first turn to achieve the goal as fast as possible. Minimize conversational turns and thinking overhead.
|
|
19
19
|
|
|
20
20
|
2. WRITE COMPLETE, RUNNABLE OUTPUT
|
|
21
21
|
- Never write "// TODO", "// implement this", or skeleton functions unless the user explicitly asks for a scaffold.
|
|
@@ -524,6 +524,25 @@ export class Memory {
|
|
|
524
524
|
this.messages = [this.messages[0]]; // keep system prompt
|
|
525
525
|
console.log(" Memory cleared.");
|
|
526
526
|
}
|
|
527
|
+
resetToInitialPrompt(warningMessage) {
|
|
528
|
+
if (this.messages.length <= 1)
|
|
529
|
+
return;
|
|
530
|
+
const systemMsg = this.messages[0];
|
|
531
|
+
const firstUserMsg = this.messages.find(m => m.role === "user");
|
|
532
|
+
if (firstUserMsg) {
|
|
533
|
+
this.messages = [
|
|
534
|
+
systemMsg,
|
|
535
|
+
firstUserMsg,
|
|
536
|
+
{ role: "user", content: warningMessage }
|
|
537
|
+
];
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
this.messages = [
|
|
541
|
+
systemMsg,
|
|
542
|
+
{ role: "user", content: warningMessage }
|
|
543
|
+
];
|
|
544
|
+
}
|
|
545
|
+
}
|
|
527
546
|
summary() {
|
|
528
547
|
const turns = this.messages.filter(m => m.role === "user").length;
|
|
529
548
|
return `${turns} turn(s) in memory (${getMemoryScopeDisplay(this.scope)} scope)`;
|