codeep 1.0.25 → 1.0.28
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/app.js +9 -1
- package/dist/components/Message.js +9 -1
- package/dist/config/index.js +1 -1
- package/dist/utils/agent.js +8 -22
- package/dist/utils/tools.js +91 -6
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -291,7 +291,15 @@ export const App = () => {
|
|
|
291
291
|
});
|
|
292
292
|
},
|
|
293
293
|
onThinking: (text) => {
|
|
294
|
-
|
|
294
|
+
// Strip <think> and <tool_call> tags from thinking text
|
|
295
|
+
const cleanText = text
|
|
296
|
+
.replace(/<think>[\s\S]*?<\/think>/gi, '')
|
|
297
|
+
.replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, '')
|
|
298
|
+
.replace(/<toolcall>[\s\S]*?<\/toolcall>/gi, '')
|
|
299
|
+
.trim();
|
|
300
|
+
if (cleanText) {
|
|
301
|
+
setAgentThinking(prev => prev + cleanText);
|
|
302
|
+
}
|
|
295
303
|
},
|
|
296
304
|
abortSignal: controller.signal,
|
|
297
305
|
});
|
|
@@ -19,7 +19,15 @@ export function getCodeBlock(index) {
|
|
|
19
19
|
}
|
|
20
20
|
export const MessageView = memo(({ role, content }) => {
|
|
21
21
|
if (role === 'user') {
|
|
22
|
-
|
|
22
|
+
// For long user messages, truncate display but keep full content for processing
|
|
23
|
+
const maxDisplayLength = 500;
|
|
24
|
+
const isLong = content.length > maxDisplayLength;
|
|
25
|
+
const displayContent = isLong
|
|
26
|
+
? content.substring(0, maxDisplayLength) + '...'
|
|
27
|
+
: content;
|
|
28
|
+
// Replace multiple newlines with single space for cleaner display
|
|
29
|
+
const cleanContent = displayContent.replace(/\n+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
30
|
+
return (_jsxs(Box, { marginY: 1, flexDirection: "column", children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: "#f02a30", bold: true, children: '> ' }), _jsx(Text, { children: cleanContent })] }), isLong && (_jsxs(Text, { color: "gray", dimColor: true, children: [" (", content.length, " characters total)"] }))] }));
|
|
23
31
|
}
|
|
24
32
|
if (role === 'system') {
|
|
25
33
|
return (_jsx(Box, { marginY: 1, justifyContent: "center", children: _jsx(Text, { italic: true, children: content }) }));
|
package/dist/config/index.js
CHANGED
|
@@ -68,7 +68,7 @@ export const config = new Conf({
|
|
|
68
68
|
agentMaxFixAttempts: 3,
|
|
69
69
|
agentMaxIterations: 100,
|
|
70
70
|
agentMaxDuration: 20, // minutes
|
|
71
|
-
agentApiTimeout:
|
|
71
|
+
agentApiTimeout: 180000, // 180 seconds base timeout for agent (dynamically adjusted)
|
|
72
72
|
protocol: 'openai',
|
|
73
73
|
plan: 'lite',
|
|
74
74
|
language: 'en',
|
package/dist/utils/agent.js
CHANGED
|
@@ -75,6 +75,7 @@ function getAgentSystemPrompt(projectContext) {
|
|
|
75
75
|
8. Use execute_command ONLY for: npm, git, composer, pip, cargo (build/package managers)
|
|
76
76
|
9. When the task is complete, respond with a summary WITHOUT any tool calls
|
|
77
77
|
10. IMPORTANT: After finishing, your response must NOT include any tool calls - just provide a summary
|
|
78
|
+
11. IGNORE the .codeep folder - it contains internal configuration, do NOT read or modify it
|
|
78
79
|
|
|
79
80
|
## Self-Verification
|
|
80
81
|
After you make changes, the system will automatically run build and tests.
|
|
@@ -142,6 +143,7 @@ When you need to use a tool, respond with:
|
|
|
142
143
|
5. Use execute_command ONLY for: npm, git, composer, pip, cargo (build/package managers)
|
|
143
144
|
6. Always read files before editing
|
|
144
145
|
7. When done, respond WITHOUT tool calls
|
|
146
|
+
8. IGNORE the .codeep folder - it contains internal configuration, do NOT read or modify it
|
|
145
147
|
|
|
146
148
|
## Project: ${projectContext.name} (${projectContext.type})
|
|
147
149
|
${projectContext.structure}
|
|
@@ -205,7 +207,7 @@ async function agentChat(messages, systemPrompt, onChunk, abortSignal, dynamicTi
|
|
|
205
207
|
tools: getOpenAITools(),
|
|
206
208
|
tool_choice: 'auto',
|
|
207
209
|
temperature: config.get('temperature'),
|
|
208
|
-
max_tokens: config.get('maxTokens'),
|
|
210
|
+
max_tokens: Math.max(config.get('maxTokens'), 16384), // Ensure enough tokens for large file generation
|
|
209
211
|
};
|
|
210
212
|
}
|
|
211
213
|
else {
|
|
@@ -216,7 +218,7 @@ async function agentChat(messages, systemPrompt, onChunk, abortSignal, dynamicTi
|
|
|
216
218
|
messages: messages,
|
|
217
219
|
tools: getAnthropicTools(),
|
|
218
220
|
temperature: config.get('temperature'),
|
|
219
|
-
max_tokens: config.get('maxTokens'),
|
|
221
|
+
max_tokens: Math.max(config.get('maxTokens'), 16384), // Ensure enough tokens for large file generation
|
|
220
222
|
};
|
|
221
223
|
}
|
|
222
224
|
const response = await fetch(endpoint, {
|
|
@@ -234,24 +236,15 @@ async function agentChat(messages, systemPrompt, onChunk, abortSignal, dynamicTi
|
|
|
234
236
|
throw new Error(`API error: ${response.status} - ${errorText}`);
|
|
235
237
|
}
|
|
236
238
|
const data = await response.json();
|
|
237
|
-
// Debug: log raw API response
|
|
238
|
-
console.error(`[DEBUG] Raw API response:`, JSON.stringify(data, null, 2).substring(0, 1000));
|
|
239
239
|
if (protocol === 'openai') {
|
|
240
240
|
const message = data.choices?.[0]?.message;
|
|
241
241
|
const content = message?.content || '';
|
|
242
242
|
const rawToolCalls = message?.tool_calls || [];
|
|
243
|
-
// Debug: log raw tool calls from API
|
|
244
|
-
console.error(`[DEBUG] Raw tool_calls from API:`, JSON.stringify(rawToolCalls, null, 2));
|
|
245
243
|
const toolCalls = parseOpenAIToolCalls(rawToolCalls);
|
|
246
|
-
// Debug: log parsed tool calls
|
|
247
|
-
console.error(`[DEBUG] Parsed tool calls:`, JSON.stringify(toolCalls, null, 2));
|
|
248
244
|
// If no native tool calls, try parsing from content (some models return text-based)
|
|
249
245
|
if (toolCalls.length === 0 && content) {
|
|
250
|
-
console.error(`[DEBUG] No native tool calls, checking content for text-based calls...`);
|
|
251
|
-
console.error(`[DEBUG] Content preview:`, content.substring(0, 500));
|
|
252
246
|
const textToolCalls = parseToolCalls(content);
|
|
253
247
|
if (textToolCalls.length > 0) {
|
|
254
|
-
console.error(`[DEBUG] Found ${textToolCalls.length} text-based tool calls`);
|
|
255
248
|
return { content, toolCalls: textToolCalls, usedNativeTools: false };
|
|
256
249
|
}
|
|
257
250
|
}
|
|
@@ -524,6 +517,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
524
517
|
let result;
|
|
525
518
|
let consecutiveTimeouts = 0;
|
|
526
519
|
const maxTimeoutRetries = 3;
|
|
520
|
+
const maxConsecutiveTimeouts = 9; // Allow more consecutive timeouts before giving up
|
|
527
521
|
const baseTimeout = config.get('agentApiTimeout');
|
|
528
522
|
try {
|
|
529
523
|
while (iteration < opts.maxIterations) {
|
|
@@ -540,7 +534,6 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
540
534
|
}
|
|
541
535
|
// Check abort signal
|
|
542
536
|
if (opts.abortSignal?.aborted) {
|
|
543
|
-
console.error(`[DEBUG] Agent aborted at iteration ${iteration}, signal:`, opts.abortSignal.aborted);
|
|
544
537
|
result = {
|
|
545
538
|
success: false,
|
|
546
539
|
iterations: iteration,
|
|
@@ -551,11 +544,9 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
551
544
|
return result;
|
|
552
545
|
}
|
|
553
546
|
iteration++;
|
|
554
|
-
console.error(`[DEBUG] Starting iteration ${iteration}/${opts.maxIterations}, actions: ${actions.length}`);
|
|
555
547
|
opts.onIteration?.(iteration, `Iteration ${iteration}/${opts.maxIterations}`);
|
|
556
548
|
// Calculate dynamic timeout based on task complexity
|
|
557
549
|
const dynamicTimeout = calculateDynamicTimeout(prompt, iteration, baseTimeout);
|
|
558
|
-
console.error(`[DEBUG] Using dynamic timeout: ${dynamicTimeout}ms (base: ${baseTimeout}ms, iteration: ${iteration})`);
|
|
559
550
|
// Get AI response with retry logic for timeouts
|
|
560
551
|
let chatResponse;
|
|
561
552
|
let retryCount = 0;
|
|
@@ -563,7 +554,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
563
554
|
try {
|
|
564
555
|
chatResponse = await agentChat(messages, systemPrompt, opts.onThinking, opts.abortSignal, dynamicTimeout * (1 + retryCount * 0.5) // Increase timeout on retry
|
|
565
556
|
);
|
|
566
|
-
consecutiveTimeouts = 0; // Reset on success
|
|
557
|
+
consecutiveTimeouts = 0; // Reset consecutive count on success
|
|
567
558
|
break;
|
|
568
559
|
}
|
|
569
560
|
catch (error) {
|
|
@@ -583,23 +574,21 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
583
574
|
if (err.name === 'TimeoutError') {
|
|
584
575
|
retryCount++;
|
|
585
576
|
consecutiveTimeouts++;
|
|
586
|
-
console.error(`[DEBUG] Timeout occurred (retry ${retryCount}/${maxTimeoutRetries}, consecutive: ${consecutiveTimeouts})`);
|
|
587
577
|
opts.onIteration?.(iteration, `API timeout, retrying (${retryCount}/${maxTimeoutRetries})...`);
|
|
588
578
|
if (retryCount >= maxTimeoutRetries) {
|
|
589
579
|
// Too many retries for this iteration
|
|
590
|
-
if (consecutiveTimeouts >=
|
|
580
|
+
if (consecutiveTimeouts >= maxConsecutiveTimeouts) {
|
|
591
581
|
// Too many consecutive timeouts overall, give up
|
|
592
582
|
result = {
|
|
593
583
|
success: false,
|
|
594
584
|
iterations: iteration,
|
|
595
585
|
actions,
|
|
596
586
|
finalResponse: 'Agent stopped due to repeated API timeouts',
|
|
597
|
-
error: `API timed out ${consecutiveTimeouts} times consecutively.
|
|
587
|
+
error: `API timed out ${consecutiveTimeouts} times consecutively. Try increasing the timeout in settings or simplifying the task.`,
|
|
598
588
|
};
|
|
599
589
|
return result;
|
|
600
590
|
}
|
|
601
591
|
// Skip this iteration and try next
|
|
602
|
-
console.error(`[DEBUG] Max retries reached, skipping to next iteration`);
|
|
603
592
|
messages.push({
|
|
604
593
|
role: 'user',
|
|
605
594
|
content: 'The previous request timed out. Please continue with the task, using simpler responses if needed.'
|
|
@@ -628,7 +617,6 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
628
617
|
}
|
|
629
618
|
// If no tool calls, check if model wants to continue or is really done
|
|
630
619
|
if (toolCalls.length === 0) {
|
|
631
|
-
console.error(`[DEBUG] No tool calls at iteration ${iteration}`);
|
|
632
620
|
// Remove <think>...</think> tags from response (some models include thinking)
|
|
633
621
|
finalResponse = content.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
|
634
622
|
// Check if model indicates it wants to continue (incomplete response)
|
|
@@ -643,7 +631,6 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
643
631
|
// by looking for incomplete actions (e.g., write_file without content)
|
|
644
632
|
const hasIncompleteWork = iteration < 10 && wantsToContinue && finalResponse.length < 500;
|
|
645
633
|
if (hasIncompleteWork) {
|
|
646
|
-
console.error(`[DEBUG] Model wants to continue, prompting for next action`);
|
|
647
634
|
messages.push({ role: 'assistant', content });
|
|
648
635
|
messages.push({
|
|
649
636
|
role: 'user',
|
|
@@ -652,7 +639,6 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
652
639
|
continue;
|
|
653
640
|
}
|
|
654
641
|
// Model is done
|
|
655
|
-
console.error(`[DEBUG] Agent finished at iteration ${iteration}`);
|
|
656
642
|
break;
|
|
657
643
|
}
|
|
658
644
|
// Add assistant response to history
|
package/dist/utils/tools.js
CHANGED
|
@@ -210,25 +210,29 @@ export function parseOpenAIToolCalls(toolCalls) {
|
|
|
210
210
|
if (!toolName)
|
|
211
211
|
continue;
|
|
212
212
|
let parameters = {};
|
|
213
|
+
const rawArgs = tc.function?.arguments || '{}';
|
|
213
214
|
try {
|
|
214
|
-
parameters = JSON.parse(
|
|
215
|
+
parameters = JSON.parse(rawArgs);
|
|
215
216
|
}
|
|
216
217
|
catch (e) {
|
|
217
218
|
// JSON parsing failed - likely truncated response
|
|
218
|
-
|
|
219
|
-
|
|
219
|
+
// Try to extract what we can from partial JSON
|
|
220
|
+
const partialParams = extractPartialToolParams(toolName, rawArgs);
|
|
221
|
+
if (partialParams) {
|
|
222
|
+
parameters = partialParams;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
220
227
|
}
|
|
221
228
|
// Validate required parameters for specific tools
|
|
222
229
|
if (toolName === 'write_file' && (!parameters.path || parameters.content === undefined)) {
|
|
223
|
-
console.error(`[DEBUG] Skipping write_file with missing path or content`);
|
|
224
230
|
continue;
|
|
225
231
|
}
|
|
226
232
|
if (toolName === 'read_file' && !parameters.path) {
|
|
227
|
-
console.error(`[DEBUG] Skipping read_file with missing path`);
|
|
228
233
|
continue;
|
|
229
234
|
}
|
|
230
235
|
if (toolName === 'edit_file' && (!parameters.path || parameters.old_text === undefined || parameters.new_text === undefined)) {
|
|
231
|
-
console.error(`[DEBUG] Skipping edit_file with missing parameters`);
|
|
232
236
|
continue;
|
|
233
237
|
}
|
|
234
238
|
parsed.push({
|
|
@@ -239,6 +243,87 @@ export function parseOpenAIToolCalls(toolCalls) {
|
|
|
239
243
|
}
|
|
240
244
|
return parsed;
|
|
241
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Extract parameters from truncated/partial JSON for tool calls
|
|
248
|
+
* This is a fallback when JSON.parse fails due to API truncation
|
|
249
|
+
*/
|
|
250
|
+
function extractPartialToolParams(toolName, rawArgs) {
|
|
251
|
+
try {
|
|
252
|
+
// For write_file, try to extract path and content
|
|
253
|
+
if (toolName === 'write_file') {
|
|
254
|
+
const pathMatch = rawArgs.match(/"path"\s*:\s*"([^"]+)"/);
|
|
255
|
+
const contentMatch = rawArgs.match(/"content"\s*:\s*"([\s\S]*?)(?:"|$)/);
|
|
256
|
+
if (pathMatch && contentMatch) {
|
|
257
|
+
// Unescape the content
|
|
258
|
+
let content = contentMatch[1];
|
|
259
|
+
content = content
|
|
260
|
+
.replace(/\\n/g, '\n')
|
|
261
|
+
.replace(/\\t/g, '\t')
|
|
262
|
+
.replace(/\\r/g, '\r')
|
|
263
|
+
.replace(/\\"/g, '"')
|
|
264
|
+
.replace(/\\\\/g, '\\');
|
|
265
|
+
// If content appears truncated (doesn't end properly), add a comment
|
|
266
|
+
if (!content.endsWith('\n') && !content.endsWith('}') && !content.endsWith(';') && !content.endsWith('>')) {
|
|
267
|
+
content += '\n<!-- Content may be truncated -->\n';
|
|
268
|
+
}
|
|
269
|
+
return { path: pathMatch[1], content };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// For read_file, just need path
|
|
273
|
+
if (toolName === 'read_file') {
|
|
274
|
+
const pathMatch = rawArgs.match(/"path"\s*:\s*"([^"]+)"/);
|
|
275
|
+
if (pathMatch) {
|
|
276
|
+
return { path: pathMatch[1] };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// For list_files, just need path
|
|
280
|
+
if (toolName === 'list_files') {
|
|
281
|
+
const pathMatch = rawArgs.match(/"path"\s*:\s*"([^"]+)"/);
|
|
282
|
+
if (pathMatch) {
|
|
283
|
+
return { path: pathMatch[1] };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// For create_directory, just need path
|
|
287
|
+
if (toolName === 'create_directory') {
|
|
288
|
+
const pathMatch = rawArgs.match(/"path"\s*:\s*"([^"]+)"/);
|
|
289
|
+
if (pathMatch) {
|
|
290
|
+
return { path: pathMatch[1] };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// For edit_file, need path, old_text, new_text
|
|
294
|
+
if (toolName === 'edit_file') {
|
|
295
|
+
const pathMatch = rawArgs.match(/"path"\s*:\s*"([^"]+)"/);
|
|
296
|
+
const oldTextMatch = rawArgs.match(/"old_text"\s*:\s*"([\s\S]*?)(?:"|$)/);
|
|
297
|
+
const newTextMatch = rawArgs.match(/"new_text"\s*:\s*"([\s\S]*?)(?:"|$)/);
|
|
298
|
+
if (pathMatch && oldTextMatch && newTextMatch) {
|
|
299
|
+
return {
|
|
300
|
+
path: pathMatch[1],
|
|
301
|
+
old_text: oldTextMatch[1].replace(/\\n/g, '\n').replace(/\\"/g, '"'),
|
|
302
|
+
new_text: newTextMatch[1].replace(/\\n/g, '\n').replace(/\\"/g, '"'),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// For execute_command
|
|
307
|
+
if (toolName === 'execute_command') {
|
|
308
|
+
const commandMatch = rawArgs.match(/"command"\s*:\s*"([^"]+)"/);
|
|
309
|
+
if (commandMatch) {
|
|
310
|
+
const argsMatch = rawArgs.match(/"args"\s*:\s*\[([\s\S]*?)\]/);
|
|
311
|
+
let args = [];
|
|
312
|
+
if (argsMatch) {
|
|
313
|
+
const argStrings = argsMatch[1].match(/"([^"]+)"/g);
|
|
314
|
+
if (argStrings) {
|
|
315
|
+
args = argStrings.map(s => s.replace(/"/g, ''));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return { command: commandMatch[1], args };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
242
327
|
/**
|
|
243
328
|
* Parse tool calls from Anthropic response
|
|
244
329
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|