codeep 1.2.69 → 1.2.70
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/api/index.js +2 -2
- package/dist/renderer/agentExecution.js +19 -21
- package/dist/utils/agent.js +37 -19
- package/dist/utils/agentChat.js +2 -2
- package/dist/utils/shell.js +2 -2
- package/dist/utils/toolExecution.js +2 -2
- package/dist/utils/verify.js +14 -13
- package/package.json +1 -1
package/dist/api/index.js
CHANGED
|
@@ -293,7 +293,7 @@ async function chatOpenAI(message, history, model, apiKey, onChunk, abortSignal)
|
|
|
293
293
|
const timeoutId = setTimeout(() => { timedOut = true; controller.abort(); }, timeout);
|
|
294
294
|
// Listen to external abort signal if provided (user cancel)
|
|
295
295
|
if (abortSignal) {
|
|
296
|
-
abortSignal.addEventListener('abort', () => controller.abort());
|
|
296
|
+
abortSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
297
297
|
}
|
|
298
298
|
// Build headers based on auth type
|
|
299
299
|
const headers = {
|
|
@@ -413,7 +413,7 @@ async function chatAnthropic(message, history, model, apiKey, onChunk, abortSign
|
|
|
413
413
|
const timeoutId = setTimeout(() => { timedOut = true; controller.abort(); }, timeout);
|
|
414
414
|
// Listen to external abort signal if provided (user cancel)
|
|
415
415
|
if (abortSignal) {
|
|
416
|
-
abortSignal.addEventListener('abort', () => controller.abort());
|
|
416
|
+
abortSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
417
417
|
}
|
|
418
418
|
// Build headers based on auth type
|
|
419
419
|
const headers = {
|
|
@@ -9,6 +9,16 @@ import { chat } from '../api/index.js';
|
|
|
9
9
|
import { runAgent } from '../utils/agent.js';
|
|
10
10
|
import { config, autoSaveSession } from '../config/index.js';
|
|
11
11
|
import { getGitStatus } from '../utils/git.js';
|
|
12
|
+
function getActionType(toolName) {
|
|
13
|
+
return toolName.includes('write') ? 'write' :
|
|
14
|
+
toolName.includes('edit') ? 'edit' :
|
|
15
|
+
toolName.includes('read') ? 'read' :
|
|
16
|
+
toolName.includes('delete') ? 'delete' :
|
|
17
|
+
toolName.includes('list') ? 'list' :
|
|
18
|
+
toolName.includes('search') || toolName.includes('grep') ? 'search' :
|
|
19
|
+
toolName.includes('mkdir') ? 'mkdir' :
|
|
20
|
+
toolName.includes('fetch') ? 'fetch' : 'command';
|
|
21
|
+
}
|
|
12
22
|
// ─── Dangerous tool detection ────────────────────────────────────────────────
|
|
13
23
|
const DANGEROUS_TOOLS = ['write', 'edit', 'delete', 'command', 'execute', 'shell', 'rm', 'mv'];
|
|
14
24
|
export function isDangerousTool(toolName, parameters) {
|
|
@@ -154,14 +164,7 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
154
164
|
const target = tool.parameters.path ||
|
|
155
165
|
tool.parameters.command ||
|
|
156
166
|
tool.parameters.pattern || '';
|
|
157
|
-
const actionType = toolName
|
|
158
|
-
toolName.includes('edit') ? 'edit' :
|
|
159
|
-
toolName.includes('read') ? 'read' :
|
|
160
|
-
toolName.includes('delete') ? 'delete' :
|
|
161
|
-
toolName.includes('list') ? 'list' :
|
|
162
|
-
toolName.includes('search') || toolName.includes('grep') ? 'search' :
|
|
163
|
-
toolName.includes('mkdir') ? 'mkdir' :
|
|
164
|
-
toolName.includes('fetch') ? 'fetch' : 'command';
|
|
167
|
+
const actionType = getActionType(toolName);
|
|
165
168
|
const shortTarget = target.length > 50 ? '...' + target.slice(-47) : target;
|
|
166
169
|
app.setAgentThinking(`${actionType}: ${shortTarget}`);
|
|
167
170
|
if (actionType === 'write' && tool.parameters.content) {
|
|
@@ -247,14 +250,7 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
247
250
|
onToolResult: (result, toolCall) => {
|
|
248
251
|
const toolName = toolCall.tool.toLowerCase();
|
|
249
252
|
const target = toolCall.parameters.path || toolCall.parameters.command || '';
|
|
250
|
-
const actionType = toolName
|
|
251
|
-
toolName.includes('edit') ? 'edit' :
|
|
252
|
-
toolName.includes('read') ? 'read' :
|
|
253
|
-
toolName.includes('delete') ? 'delete' :
|
|
254
|
-
toolName.includes('list') ? 'list' :
|
|
255
|
-
toolName.includes('search') || toolName.includes('grep') ? 'search' :
|
|
256
|
-
toolName.includes('mkdir') ? 'mkdir' :
|
|
257
|
-
toolName.includes('fetch') ? 'fetch' : 'command';
|
|
253
|
+
const actionType = getActionType(toolName);
|
|
258
254
|
app.updateAgentProgress(0, {
|
|
259
255
|
type: actionType,
|
|
260
256
|
target,
|
|
@@ -324,11 +320,13 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
324
320
|
app.addMessage({ role: 'assistant', content: 'Agent stopped by user.' });
|
|
325
321
|
}
|
|
326
322
|
else {
|
|
327
|
-
|
|
328
|
-
if (result.finalResponse)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
323
|
+
// Show the agent's summary if available, with error details below
|
|
324
|
+
if (result.finalResponse) {
|
|
325
|
+
app.addMessage({ role: 'assistant', content: result.finalResponse });
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
app.addMessage({ role: 'assistant', content: `Agent could not complete the task: ${result.error || 'Unknown error'}` });
|
|
329
|
+
}
|
|
332
330
|
}
|
|
333
331
|
autoSaveSession(app.getMessages(), ctx.projectPath);
|
|
334
332
|
}
|
package/dist/utils/agent.js
CHANGED
|
@@ -16,7 +16,7 @@ export { loadProjectRules, formatChatHistoryForAgent };
|
|
|
16
16
|
* Calculate dynamic timeout based on task complexity
|
|
17
17
|
* Complex tasks (creating pages, multiple files) need more time
|
|
18
18
|
*/
|
|
19
|
-
function calculateDynamicTimeout(
|
|
19
|
+
function calculateDynamicTimeout(iteration, baseTimeout) {
|
|
20
20
|
// Simple approach: just use base timeout with small multiplier for later iterations
|
|
21
21
|
// Complex calculations were causing more problems than they solved
|
|
22
22
|
let multiplier = 1.0;
|
|
@@ -224,11 +224,11 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
224
224
|
if (compressed !== messages) {
|
|
225
225
|
messages.length = 0;
|
|
226
226
|
messages.push(...compressed);
|
|
227
|
-
opts.onIteration?.(iteration, `Context compressed
|
|
227
|
+
opts.onIteration?.(iteration, `Context compressed to save memory — continuing with last ${compressed.length} messages`);
|
|
228
228
|
}
|
|
229
229
|
debug(`Starting iteration ${iteration}/${opts.maxIterations}, actions: ${actions.length}`);
|
|
230
230
|
// Calculate dynamic timeout based on task complexity
|
|
231
|
-
const dynamicTimeout = calculateDynamicTimeout(
|
|
231
|
+
const dynamicTimeout = calculateDynamicTimeout(iteration, baseTimeout);
|
|
232
232
|
debug(`Using timeout: ${dynamicTimeout}ms (base: ${baseTimeout}ms)`);
|
|
233
233
|
// Get AI response with retry logic for timeouts
|
|
234
234
|
let chatResponse = null;
|
|
@@ -283,23 +283,37 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
283
283
|
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
|
|
284
284
|
continue;
|
|
285
285
|
}
|
|
286
|
-
//
|
|
286
|
+
// All non-abort errors are retryable — retry with backoff
|
|
287
|
+
retryCount++;
|
|
287
288
|
const isRateLimit = err.message.includes('429');
|
|
288
289
|
const isServerError = err.message.includes('500') || err.message.includes('502') || err.message.includes('503') || err.message.includes('529');
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (
|
|
297
|
-
|
|
290
|
+
const code = isRateLimit ? '429' : isServerError ? '5xx' : 'error';
|
|
291
|
+
const waitSec = Math.min(5 * retryCount, 30);
|
|
292
|
+
debug(`${code} (retry ${retryCount}/${maxTimeoutRetries}): ${err.message}`);
|
|
293
|
+
opts.onIteration?.(iteration, `API ${code}, retrying in ${waitSec}s... (${retryCount}/${maxTimeoutRetries})`);
|
|
294
|
+
if (retryCount >= maxTimeoutRetries) {
|
|
295
|
+
// Don't throw — skip this iteration like timeouts do
|
|
296
|
+
consecutiveTimeouts++;
|
|
297
|
+
if (consecutiveTimeouts >= maxConsecutiveTimeouts) {
|
|
298
|
+
result = {
|
|
299
|
+
success: false,
|
|
300
|
+
iterations: iteration,
|
|
301
|
+
actions,
|
|
302
|
+
finalResponse: actions.length > 0
|
|
303
|
+
? `Agent made progress (${actions.length} actions) but API errors prevented completion. You can continue by running the agent again.`
|
|
304
|
+
: 'Agent could not complete the task due to repeated API errors. Check your API key and network connection.',
|
|
305
|
+
error: `API failed after ${maxTimeoutRetries} retries: ${err.message}`,
|
|
306
|
+
};
|
|
307
|
+
return result;
|
|
298
308
|
}
|
|
299
|
-
|
|
300
|
-
|
|
309
|
+
messages.push({
|
|
310
|
+
role: 'user',
|
|
311
|
+
content: 'The previous request failed. Please continue with the task.'
|
|
312
|
+
});
|
|
313
|
+
break; // Break retry loop, continue main loop
|
|
301
314
|
}
|
|
302
|
-
|
|
315
|
+
await new Promise(resolve => setTimeout(resolve, waitSec * 1000));
|
|
316
|
+
continue;
|
|
303
317
|
}
|
|
304
318
|
}
|
|
305
319
|
// If we broke out due to max retries without a response, continue to next iteration
|
|
@@ -326,7 +340,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
326
340
|
.replace(/<arg_key>[\s\S]*?<\/arg_value>/gi, '')
|
|
327
341
|
.replace(/Tool parameters:[\s\S]*?(?=\n\n|$)/gi, '')
|
|
328
342
|
.replace(/\{'path'[\s\S]*?\}/g, '')
|
|
329
|
-
.replace(/```[\s\S]
|
|
343
|
+
.replace(/```(?:json|tool_call)?\s*\{[\s\S]*?\}\s*```/g, '') // Only strip tool-call-like code blocks
|
|
330
344
|
.trim();
|
|
331
345
|
// Check if model indicates it wants to continue (incomplete response)
|
|
332
346
|
const continueIndicators = [
|
|
@@ -426,11 +440,14 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
426
440
|
else {
|
|
427
441
|
toolResults.push(`Tool ${toolCall.tool} failed:\n${toolResult.error || 'Unknown error'}`);
|
|
428
442
|
}
|
|
429
|
-
// Invalidate read cache when
|
|
443
|
+
// Invalidate read cache when files may have changed
|
|
430
444
|
if ((toolCall.tool === 'write_file' || toolCall.tool === 'edit_file') && toolResult.success) {
|
|
431
445
|
const filePath = toolCall.parameters.path || '';
|
|
432
446
|
readCache.delete(filePath);
|
|
433
447
|
}
|
|
448
|
+
else if (toolCall.tool === 'execute_command' && toolResult.success) {
|
|
449
|
+
readCache.clear(); // Commands can modify arbitrary files
|
|
450
|
+
}
|
|
434
451
|
}
|
|
435
452
|
// Add tool results to messages
|
|
436
453
|
messages.push({
|
|
@@ -543,7 +560,8 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
543
560
|
const actionLog = createActionLog(toolCall, toolResult);
|
|
544
561
|
actions.push(actionLog);
|
|
545
562
|
if (toolResult.success) {
|
|
546
|
-
|
|
563
|
+
const truncated = truncateToolResult(toolResult.output, toolCall.tool);
|
|
564
|
+
fixResults.push(`Tool ${toolCall.tool} succeeded:\n${truncated}`);
|
|
547
565
|
}
|
|
548
566
|
else {
|
|
549
567
|
fixResults.push(`Tool ${toolCall.tool} failed:\n${toolResult.error || 'Unknown error'}`);
|
package/dist/utils/agentChat.js
CHANGED
|
@@ -154,7 +154,7 @@ export async function agentChat(messages, systemPrompt, onChunk, abortSignal, dy
|
|
|
154
154
|
let isTimeout = false;
|
|
155
155
|
const timeout = setTimeout(() => { isTimeout = true; controller.abort(); }, timeoutMs);
|
|
156
156
|
if (abortSignal) {
|
|
157
|
-
abortSignal.addEventListener('abort', () => { isTimeout = false; controller.abort(); });
|
|
157
|
+
abortSignal.addEventListener('abort', () => { isTimeout = false; controller.abort(); }, { once: true });
|
|
158
158
|
}
|
|
159
159
|
const headers = { 'Content-Type': 'application/json' };
|
|
160
160
|
if (authHeader === 'Bearer') {
|
|
@@ -268,7 +268,7 @@ export async function agentChatFallback(messages, systemPrompt, onChunk, abortSi
|
|
|
268
268
|
let isTimeout = false;
|
|
269
269
|
const timeout = setTimeout(() => { isTimeout = true; controller.abort(); }, timeoutMs);
|
|
270
270
|
if (abortSignal) {
|
|
271
|
-
abortSignal.addEventListener('abort', () => { isTimeout = false; controller.abort(); });
|
|
271
|
+
abortSignal.addEventListener('abort', () => { isTimeout = false; controller.abort(); }, { once: true });
|
|
272
272
|
}
|
|
273
273
|
const headers = { 'Content-Type': 'application/json' };
|
|
274
274
|
if (authHeader === 'Bearer') {
|
package/dist/utils/shell.js
CHANGED
|
@@ -112,8 +112,8 @@ export function validateCommand(command, args, options) {
|
|
|
112
112
|
}
|
|
113
113
|
// Special validation for rm command
|
|
114
114
|
if (command === 'rm') {
|
|
115
|
-
const hasRecursive = args.some(a => a.includes('r'));
|
|
116
|
-
const hasForce = args.some(a => a.includes('f'));
|
|
115
|
+
const hasRecursive = args.some(a => a.startsWith('-') && a.includes('r'));
|
|
116
|
+
const hasForce = args.some(a => a.startsWith('-') && a.includes('f'));
|
|
117
117
|
if (hasRecursive && hasForce) {
|
|
118
118
|
// rm -rf requires extra validation
|
|
119
119
|
const paths = args.filter(a => !a.startsWith('-'));
|
|
@@ -176,8 +176,8 @@ export async function executeTool(toolCall, projectRoot) {
|
|
|
176
176
|
return { success: false, output: '', error: 'Missing required parameter: path', tool, parameters };
|
|
177
177
|
}
|
|
178
178
|
if (content === undefined || content === null) {
|
|
179
|
-
debug('write_file: content was undefined
|
|
180
|
-
|
|
179
|
+
debug('write_file failed: content was undefined');
|
|
180
|
+
return { success: false, output: '', error: 'File content was empty or truncated by the API. Try writing a smaller file or splitting into multiple writes.', tool, parameters };
|
|
181
181
|
}
|
|
182
182
|
const validation = validatePath(path, projectRoot);
|
|
183
183
|
if (!validation.valid)
|
package/dist/utils/verify.js
CHANGED
|
@@ -390,25 +390,26 @@ export async function runLintVerification(projectRoot, timeout = 60000) {
|
|
|
390
390
|
export async function runAllVerifications(projectRoot, options = {}) {
|
|
391
391
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
392
392
|
const results = [];
|
|
393
|
-
// Run typecheck
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
393
|
+
// Run typecheck and lint in parallel (independent checks)
|
|
394
|
+
const parallel = [];
|
|
395
|
+
if (opts.runTypecheck)
|
|
396
|
+
parallel.push(runTypecheckVerification(projectRoot, opts.timeout));
|
|
397
|
+
if (opts.runLint)
|
|
398
|
+
parallel.push(runLintVerification(projectRoot, opts.timeout));
|
|
399
|
+
if (parallel.length > 0) {
|
|
400
|
+
const parallelResults = await Promise.all(parallel);
|
|
401
|
+
for (const r of parallelResults) {
|
|
402
|
+
if (r)
|
|
403
|
+
results.push(r);
|
|
404
|
+
}
|
|
398
405
|
}
|
|
399
|
-
// Run build
|
|
406
|
+
// Run build after typecheck/lint (may depend on them)
|
|
400
407
|
if (opts.runBuild) {
|
|
401
408
|
const result = await runBuildVerification(projectRoot, opts.timeout);
|
|
402
409
|
if (result)
|
|
403
410
|
results.push(result);
|
|
404
411
|
}
|
|
405
|
-
// Run
|
|
406
|
-
if (opts.runLint) {
|
|
407
|
-
const result = await runLintVerification(projectRoot, opts.timeout);
|
|
408
|
-
if (result)
|
|
409
|
-
results.push(result);
|
|
410
|
-
}
|
|
411
|
-
// Run tests last (slowest)
|
|
412
|
+
// Run tests last (slowest, depends on build)
|
|
412
413
|
if (opts.runTest) {
|
|
413
414
|
const result = await runTestVerification(projectRoot, opts.timeout);
|
|
414
415
|
if (result)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.70",
|
|
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",
|