codemini-cli 0.4.0 → 0.4.2
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/OPERATIONS.md +4 -2
- package/README.md +89 -11
- package/deployment.md +14 -7
- package/package.json +1 -2
- package/src/cli.js +1 -1
- package/src/commands/skill.js +145 -53
- package/src/core/agent-loop.js +18 -311
- package/src/core/chat-runtime.js +389 -53
- package/src/core/command-loader.js +12 -5
- package/src/core/config-store.js +2 -0
- package/src/core/context-compact.js +34 -9
- package/src/core/default-system-prompt.js +5 -5
- package/src/core/dream-audit.js +12 -0
- package/src/core/dream-consolidate.js +131 -59
- package/src/core/dream-evaluator.js +86 -0
- package/src/core/fff-adapter.js +1 -1
- package/src/core/memory-store.js +145 -10
- package/src/core/provider/openai-compatible.js +40 -5
- package/src/core/reflect-skill.js +178 -0
- package/src/core/shell-profile.js +8 -8
- package/src/core/tool-args.js +181 -0
- package/src/core/tool-result-store.js +206 -0
- package/src/core/tools.js +144 -190
- package/src/tui/chat-app.js +270 -28
- package/src/tui/tool-activity/presenters/misc.js +14 -0
- package/src/core/provider/anthropic.sdk-backup.js +0 -439
- package/src/core/provider/openai-compatible.sdk-backup.js +0 -412
package/src/core/agent-loop.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
1
|
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs/promises';
|
|
4
|
-
import { BoundedCache } from './bounded-cache.js';
|
|
5
2
|
import { trimInline as _trimInline, normalizePath } from './string-utils.js';
|
|
6
3
|
import { captureToInbox, listInbox } from './memory-store.js';
|
|
7
4
|
import { requiresApprovalEvaluation } from './command-risk.js';
|
|
8
5
|
import { getToolOutputSanitizeOptions, sanitizeTextForModel } from './tool-output.js';
|
|
6
|
+
import { normalizeToolArguments } from './tool-args.js';
|
|
7
|
+
import { storeResultIfNeeded, summarizeToolResult } from './tool-result-store.js';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* 安全解析 JSON 字符串。
|
|
@@ -25,20 +24,6 @@ function safeJsonParse(raw) {
|
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
function parseInlineRangePath(value) {
|
|
29
|
-
const text = String(value || '').trim();
|
|
30
|
-
if (!text) return null;
|
|
31
|
-
const match = text.match(/^(.*?):(\d+)(?:-(\d+))?$/);
|
|
32
|
-
if (!match) return null;
|
|
33
|
-
const [, maybePath, startRaw, endRaw] = match;
|
|
34
|
-
if (!maybePath || /^(?:[A-Za-z])$/.test(maybePath)) return null;
|
|
35
|
-
const start = Number(startRaw);
|
|
36
|
-
const end = Number(endRaw || startRaw);
|
|
37
|
-
if (!Number.isFinite(start) || start <= 0) return null;
|
|
38
|
-
if (!Number.isFinite(end) || end < start) return null;
|
|
39
|
-
return { path: maybePath, start_line: start, end_line: end };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
27
|
function buildDeleteApprovalDetails(source, rawPath) {
|
|
43
28
|
const existing =
|
|
44
29
|
source?.approval && typeof source.approval === 'object' && !Array.isArray(source.approval)
|
|
@@ -74,90 +59,6 @@ function buildDeleteCancellationResult(args) {
|
|
|
74
59
|
};
|
|
75
60
|
}
|
|
76
61
|
|
|
77
|
-
function normalizeToolArguments(toolName, args, rawArguments) {
|
|
78
|
-
const rawText = typeof rawArguments === 'string' ? rawArguments.trim() : '';
|
|
79
|
-
const primitive =
|
|
80
|
-
args == null || Array.isArray(args) || typeof args !== 'object'
|
|
81
|
-
? args
|
|
82
|
-
: null;
|
|
83
|
-
const source =
|
|
84
|
-
args && typeof args === 'object' && !Array.isArray(args)
|
|
85
|
-
? { ...args }
|
|
86
|
-
: {};
|
|
87
|
-
|
|
88
|
-
if (primitive != null && typeof primitive !== 'object') {
|
|
89
|
-
source._raw = rawText || String(primitive);
|
|
90
|
-
} else if (!source._raw && rawText && source._invalid_json) {
|
|
91
|
-
source._raw = rawText;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const stringValue =
|
|
95
|
-
typeof primitive === 'string'
|
|
96
|
-
? primitive.trim()
|
|
97
|
-
: String(source._raw || '').trim();
|
|
98
|
-
|
|
99
|
-
if (toolName === 'read') {
|
|
100
|
-
const value = String(source.path || source.file_path || source.file || stringValue || '').trim();
|
|
101
|
-
if (value) source.path = value;
|
|
102
|
-
if (source.offset != null && source.start_line == null) source.start_line = source.offset;
|
|
103
|
-
if (source.limit != null && source.end_line == null && Number(source.start_line) > 0) {
|
|
104
|
-
source.end_line = Number(source.start_line) + Number(source.limit) - 1;
|
|
105
|
-
}
|
|
106
|
-
const range = parseInlineRangePath(source.path);
|
|
107
|
-
if (range) {
|
|
108
|
-
source.path = range.path;
|
|
109
|
-
if (source.start_line == null) source.start_line = range.start_line;
|
|
110
|
-
if (source.end_line == null) source.end_line = range.end_line;
|
|
111
|
-
}
|
|
112
|
-
return source;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (toolName === 'list') {
|
|
116
|
-
const value = String(source.path || source.dir || source.directory || stringValue || '.').trim();
|
|
117
|
-
return { ...source, path: value || '.' };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (toolName === 'glob') {
|
|
121
|
-
const pattern = String(source.pattern || source.glob || source.query || stringValue || '').trim();
|
|
122
|
-
if (pattern) source.pattern = pattern;
|
|
123
|
-
if (!source.path && source.directory) source.path = source.directory;
|
|
124
|
-
return source;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (toolName === 'grep') {
|
|
128
|
-
const pattern = String(source.pattern || source.query || source.symbol || source.q || stringValue || '').trim();
|
|
129
|
-
if (pattern) source.pattern = pattern;
|
|
130
|
-
if (!source.path && (source.directory || source.dir || source.cwd)) {
|
|
131
|
-
source.path = source.directory || source.dir || source.cwd;
|
|
132
|
-
}
|
|
133
|
-
return source;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (toolName === 'write') {
|
|
137
|
-
const value = String(source.path || source.file_path || source.file || stringValue || '').trim();
|
|
138
|
-
if (value) source.path = value;
|
|
139
|
-
if (source.content == null && source.text != null) source.content = source.text;
|
|
140
|
-
if (source.content == null && source.new_content != null) source.content = source.new_content;
|
|
141
|
-
return source;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (toolName === 'edit') {
|
|
145
|
-
const value = String(source.path || source.file || source.file_path || '').trim();
|
|
146
|
-
if (value && !source.path) source.path = value;
|
|
147
|
-
return source;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (toolName === 'delete') {
|
|
151
|
-
const value = String(source.path || source.file_path || source.file || source.target || source.directory || source.dir || stringValue || '').trim();
|
|
152
|
-
if (value) source.path = value;
|
|
153
|
-
const approval = buildDeleteApprovalDetails(source, source.path);
|
|
154
|
-
if (approval) source.approval = approval;
|
|
155
|
-
return source;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return source;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
62
|
function emptyToolResultMarker(toolName) {
|
|
162
63
|
const name = String(toolName || 'tool').trim() || 'tool';
|
|
163
64
|
return `(${name} completed with no output)`;
|
|
@@ -259,107 +160,6 @@ function compactToolResult(result, toolName, args, maxChars = 12000) {
|
|
|
259
160
|
return clipToolResult(obj, Math.min(maxChars, 4000));
|
|
260
161
|
}
|
|
261
162
|
|
|
262
|
-
// ─── P0: Large result disk store ─────────────────────────────────────
|
|
263
|
-
|
|
264
|
-
const TOOL_RESULT_DISK_THRESHOLD = 6000;
|
|
265
|
-
const PREVIEW_SIZE_BYTES = 2000;
|
|
266
|
-
const TOOL_RESULTS_SUBDIR = 'tool-results';
|
|
267
|
-
|
|
268
|
-
let currentResultDir = null;
|
|
269
|
-
let resultDirReady = false;
|
|
270
|
-
const storedResults = new BoundedCache({
|
|
271
|
-
maxSize: 64,
|
|
272
|
-
ttlMs: 30 * 60 * 1000,
|
|
273
|
-
onEvict(key, value) {
|
|
274
|
-
if (value?.filePath) {
|
|
275
|
-
fs.unlink(value.filePath).catch(() => {});
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}); // callId -> { filePath, summary }
|
|
279
|
-
const readCache = new BoundedCache({ maxSize: 128, ttlMs: 10 * 60 * 1000 }); // "path:startLine:endLine:mtimeMs" -> true
|
|
280
|
-
|
|
281
|
-
function generatePreview(content) {
|
|
282
|
-
if (content.length <= PREVIEW_SIZE_BYTES) {
|
|
283
|
-
return { preview: content, hasMore: false };
|
|
284
|
-
}
|
|
285
|
-
const truncated = content.slice(0, PREVIEW_SIZE_BYTES);
|
|
286
|
-
const lastNewline = truncated.lastIndexOf('\n');
|
|
287
|
-
const cutPoint = lastNewline > PREVIEW_SIZE_BYTES * 0.5 ? lastNewline : PREVIEW_SIZE_BYTES;
|
|
288
|
-
return { preview: content.slice(0, cutPoint), hasMore: true };
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function formatFileSize(chars) {
|
|
292
|
-
if (chars < 1024) return `${chars} B`;
|
|
293
|
-
return `${(chars / 1024).toFixed(1)} KB`;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export function setResultDir(dir) {
|
|
297
|
-
currentResultDir = dir ? path.join(dir, TOOL_RESULTS_SUBDIR) : null;
|
|
298
|
-
resultDirReady = false;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async function ensureResultDir() {
|
|
302
|
-
if (!currentResultDir) return false;
|
|
303
|
-
if (!resultDirReady) {
|
|
304
|
-
await fs.mkdir(currentResultDir, { recursive: true });
|
|
305
|
-
resultDirReady = true;
|
|
306
|
-
}
|
|
307
|
-
return true;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
async function storeResultIfNeeded(callId, formattedContent, rawResult) {
|
|
311
|
-
if (formattedContent.length <= TOOL_RESULT_DISK_THRESHOLD) {
|
|
312
|
-
return formattedContent;
|
|
313
|
-
}
|
|
314
|
-
try {
|
|
315
|
-
const ready = await ensureResultDir();
|
|
316
|
-
const dir = ready ? currentResultDir : path.join(os.tmpdir(), 'codemini-results');
|
|
317
|
-
if (!resultDirReady && dir === currentResultDir) {
|
|
318
|
-
await fs.mkdir(dir, { recursive: true });
|
|
319
|
-
} else if (!resultDirReady) {
|
|
320
|
-
await fs.mkdir(dir, { recursive: true });
|
|
321
|
-
}
|
|
322
|
-
const filePath = path.join(dir, `${callId}.txt`);
|
|
323
|
-
const payload = typeof rawResult === 'string' ? rawResult : JSON.stringify(rawResult, null, 2);
|
|
324
|
-
await fs.writeFile(filePath, payload, 'utf-8');
|
|
325
|
-
const summary = summarizeToolResult(rawResult);
|
|
326
|
-
const { preview, hasMore } = generatePreview(payload);
|
|
327
|
-
storedResults.set(callId, { filePath, summary });
|
|
328
|
-
|
|
329
|
-
return `<persisted-output>
|
|
330
|
-
Output too large (${formatFileSize(payload.length)}). Full output saved to: ${filePath}
|
|
331
|
-
|
|
332
|
-
Preview (first ${formatFileSize(PREVIEW_SIZE_BYTES)}):
|
|
333
|
-
${preview}${hasMore ? '\n...' : ''}
|
|
334
|
-
|
|
335
|
-
Summary: ${summary}
|
|
336
|
-
</persisted-output>`;
|
|
337
|
-
} catch {
|
|
338
|
-
return formattedContent;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
export function clearResultStore() {
|
|
343
|
-
const files = [];
|
|
344
|
-
for (const [, val] of storedResults.entries()) {
|
|
345
|
-
files.push(val.filePath);
|
|
346
|
-
}
|
|
347
|
-
storedResults.clear();
|
|
348
|
-
readCache.clear();
|
|
349
|
-
return Promise.allSettled(files.map((f) => fs.unlink(f).catch(() => {})));
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// ─── Read deduplication ─────────────────────────────────────────────
|
|
353
|
-
|
|
354
|
-
export function checkReadDedup(filePath, startLine, endLine, mtimeMs) {
|
|
355
|
-
const key = `${filePath}:${startLine || 0}:${endLine || 0}:${mtimeMs}`;
|
|
356
|
-
if (readCache.has(key)) {
|
|
357
|
-
return true;
|
|
358
|
-
}
|
|
359
|
-
readCache.set(key, true);
|
|
360
|
-
return false;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
163
|
// ─── P1a: Read-only tool classification ──────────────────────────────
|
|
364
164
|
|
|
365
165
|
const READ_ONLY_TOOLS = new Set([
|
|
@@ -408,18 +208,19 @@ function shouldAutoCaptureError(toolName, message) {
|
|
|
408
208
|
return true;
|
|
409
209
|
}
|
|
410
210
|
|
|
411
|
-
function
|
|
211
|
+
async function captureToolFailure(toolName, message, args, config = {}) {
|
|
212
|
+
if (config?.memory?.enabled === false || config?.memory?.auto_capture === false) return;
|
|
412
213
|
const summary = `[${toolName}] ${String(message).slice(0, 120)}`;
|
|
413
214
|
const details = args
|
|
414
215
|
? `Tool: ${toolName}\nError: ${message}\nArgs: ${JSON.stringify(args).slice(0, 300)}`
|
|
415
216
|
: `Tool: ${toolName}\nError: ${message}`;
|
|
416
|
-
captureToInbox({
|
|
417
|
-
scope: '
|
|
217
|
+
await captureToInbox({
|
|
218
|
+
scope: 'repo',
|
|
418
219
|
type: 'failure',
|
|
419
220
|
summary,
|
|
420
221
|
details,
|
|
421
222
|
source: 'auto-capture'
|
|
422
|
-
})
|
|
223
|
+
});
|
|
423
224
|
}
|
|
424
225
|
|
|
425
226
|
async function checkAutoDreamThreshold(config) {
|
|
@@ -462,108 +263,6 @@ function extractFileChange(toolName, result) {
|
|
|
462
263
|
return null;
|
|
463
264
|
}
|
|
464
265
|
|
|
465
|
-
export function summarizeToolResult(result) {
|
|
466
|
-
if (result === null || result === undefined) return 'no output';
|
|
467
|
-
if (typeof result === 'string') {
|
|
468
|
-
const oneLine = result.replace(/\s+/g, ' ').trim();
|
|
469
|
-
return oneLine.length > 90 ? `${oneLine.slice(0, 87)}...` : oneLine || 'empty string';
|
|
470
|
-
}
|
|
471
|
-
if (typeof result === 'object') {
|
|
472
|
-
const obj = result;
|
|
473
|
-
if (Array.isArray(obj)) return `array(${obj.length})`;
|
|
474
|
-
if ('deleted' in obj && 'path' in obj) {
|
|
475
|
-
const kind = trimInline(obj.type || 'item', 16);
|
|
476
|
-
const target = trimInline(obj.path || '', 96);
|
|
477
|
-
if (obj.deleted) return target ? `deleted ${kind} ${target}` : `deleted ${kind}`;
|
|
478
|
-
if (obj.cancelled) return target ? `cancelled delete ${target}` : 'cancelled delete';
|
|
479
|
-
}
|
|
480
|
-
if ('path' in obj && 'action' in obj) {
|
|
481
|
-
const p = String(obj.path || '');
|
|
482
|
-
const action = String(obj.action || 'write');
|
|
483
|
-
const line = Number(obj.changed_line || 1);
|
|
484
|
-
const suffix =
|
|
485
|
-
action === 'delete'
|
|
486
|
-
? 'deleted'
|
|
487
|
-
: action === 'create'
|
|
488
|
-
? 'created'
|
|
489
|
-
: action === 'patch'
|
|
490
|
-
? 'patched'
|
|
491
|
-
: action === 'replace_block' || action === 'replace_text'
|
|
492
|
-
? 'edited'
|
|
493
|
-
: action === 'append'
|
|
494
|
-
? 'appended'
|
|
495
|
-
: 'updated';
|
|
496
|
-
return p ? `${suffix} ${p}${line > 0 ? ` @L${line}` : ''}` : suffix;
|
|
497
|
-
}
|
|
498
|
-
if ('path' in obj && 'phase' in obj) {
|
|
499
|
-
const phase = String(obj.phase || '');
|
|
500
|
-
const p = String(obj.path || '');
|
|
501
|
-
const total = Number(obj.total_lines);
|
|
502
|
-
const start =
|
|
503
|
-
Number(obj.suggested_start_line || obj.start_line) > 0
|
|
504
|
-
? Number(obj.suggested_start_line || obj.start_line)
|
|
505
|
-
: 1;
|
|
506
|
-
const end =
|
|
507
|
-
Number(obj.suggested_end_line || obj.end_line) >= start
|
|
508
|
-
? Number(obj.suggested_end_line || obj.end_line)
|
|
509
|
-
: start;
|
|
510
|
-
const rangeText = start > 0 && end >= start ? ` lines ${start}-${end}` : '';
|
|
511
|
-
const totalText = total > 0 ? ` of ${total}` : '';
|
|
512
|
-
const enclosingText = obj.enclosing_symbol ? ` in ${obj.enclosing_symbol}` : '';
|
|
513
|
-
const errorText = obj.error ? ` (${trimInline(obj.error, 64)})` : '';
|
|
514
|
-
const truncatedText = obj.truncated ? ' [truncated]' : '';
|
|
515
|
-
return phase === 'metadata'
|
|
516
|
-
? `metadata for ${p}${rangeText}${totalText}${errorText}`
|
|
517
|
-
: `content from ${p}${rangeText}${totalText}${enclosingText}${truncatedText}`;
|
|
518
|
-
}
|
|
519
|
-
if ('stdout' in obj || 'stderr' in obj || 'code' in obj) {
|
|
520
|
-
const stdout = trimInline(obj.stdout || '', 96);
|
|
521
|
-
const stderr = trimInline(obj.stderr || '', 96);
|
|
522
|
-
const command = trimInline(obj.command || '', 72);
|
|
523
|
-
const lead = command ? `${command} -> ` : '';
|
|
524
|
-
if (stdout) return `${lead}exit ${obj.code ?? 0}\nstdout: ${stdout}`;
|
|
525
|
-
if (stderr) return `${lead}exit ${obj.code ?? 0}\nstderr: ${stderr}`;
|
|
526
|
-
return `${lead}exit ${obj.code ?? 0}`;
|
|
527
|
-
}
|
|
528
|
-
if ('task_id' in obj && 'startup_confirmed' in obj) {
|
|
529
|
-
const status = trimInline(obj.status || 'unknown', 32);
|
|
530
|
-
const taskId = trimInline(obj.task_id || '', 24);
|
|
531
|
-
const source = trimInline(obj.startup_source || '', 24);
|
|
532
|
-
const outputFile = trimInline(obj.output_file || '', 72);
|
|
533
|
-
const output = Array.isArray(obj.recent_output) ? trimInline(obj.recent_output.slice(-1)[0] || '', 96) : '';
|
|
534
|
-
return `${taskId || 'task'} ${status}${source ? ` (${source})` : ''}${outputFile ? ` -> ${outputFile}` : ''}${output ? `\n${output}` : ''}`;
|
|
535
|
-
}
|
|
536
|
-
if ('tasks' in obj && Array.isArray(obj.tasks)) {
|
|
537
|
-
const count = obj.tasks.length;
|
|
538
|
-
const first = obj.tasks[0];
|
|
539
|
-
const lead = first?.task_id ? `${trimInline(first.task_id, 24)} ${trimInline(first.status || 'unknown', 24)}` : '';
|
|
540
|
-
return `tasks(${count})${lead ? `\n${lead}` : ''}`;
|
|
541
|
-
}
|
|
542
|
-
if ('files' in obj && Array.isArray(obj.files)) {
|
|
543
|
-
return `patched ${obj.files.length} file(s)`;
|
|
544
|
-
}
|
|
545
|
-
if ('diff' in obj && 'new_hash' in obj && 'path' in obj) {
|
|
546
|
-
const p = String(obj.path || '');
|
|
547
|
-
return p ? `diff preview for ${p}` : 'diff preview';
|
|
548
|
-
}
|
|
549
|
-
if ('created' in obj && Array.isArray(obj.created)) {
|
|
550
|
-
return `created ${obj.created.length} task(s)`;
|
|
551
|
-
}
|
|
552
|
-
if ('tasks' in obj && Array.isArray(obj.tasks)) {
|
|
553
|
-
return `${obj.tasks.length} task(s)`;
|
|
554
|
-
}
|
|
555
|
-
if ('newTodos' in obj && Array.isArray(obj.newTodos)) {
|
|
556
|
-
return obj.newTodos.length > 0 ? `updated ${obj.newTodos.length} todo item(s)` : 'cleared todo list';
|
|
557
|
-
}
|
|
558
|
-
if ('newPlan' in obj) {
|
|
559
|
-
return obj.newPlan ? `updated plan state (${String(obj.newPlan.status || 'draft')})` : 'cleared plan state';
|
|
560
|
-
}
|
|
561
|
-
const keys = Object.keys(obj);
|
|
562
|
-
return keys.length > 0 ? `keys: ${keys.slice(0, 5).join(',')}` : 'object';
|
|
563
|
-
}
|
|
564
|
-
return String(result);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
266
|
export const trimInline = _trimInline;
|
|
568
267
|
|
|
569
268
|
function normalizeAssistantText(value) {
|
|
@@ -750,6 +449,14 @@ function formatToolDisplayName(name, args) {
|
|
|
750
449
|
const command = trimInline(args?.command || '', 96);
|
|
751
450
|
return command ? `run(${command})` : name;
|
|
752
451
|
}
|
|
452
|
+
if (name === 'web_fetch') {
|
|
453
|
+
const url = trimInline(args?.url || args?.href || '', 96);
|
|
454
|
+
return url ? `web_fetch(${url})` : name;
|
|
455
|
+
}
|
|
456
|
+
if (name === 'web_search') {
|
|
457
|
+
const query = trimInline(args?.query || args?.q || '', 96);
|
|
458
|
+
return query ? `web_search(${query})` : name;
|
|
459
|
+
}
|
|
753
460
|
if (name === 'edit') {
|
|
754
461
|
const target = trimInline(args?.path || args?.file || '.', 96) || '.';
|
|
755
462
|
return `edit(${target})`;
|
|
@@ -1099,7 +806,7 @@ export async function runAgentLoop({
|
|
|
1099
806
|
onEvent({ type: 'tool:error', name: displayName, id: call.id, arguments: effectiveArgs, durationMs, summary: trimInline(message, 120) });
|
|
1100
807
|
}
|
|
1101
808
|
if (shouldAutoCaptureError(toolName, message)) {
|
|
1102
|
-
|
|
809
|
+
await captureToolFailure(toolName, message, effectiveArgs, config).catch(() => {});
|
|
1103
810
|
}
|
|
1104
811
|
return {
|
|
1105
812
|
callId: call.id,
|
|
@@ -1122,13 +829,13 @@ export async function runAgentLoop({
|
|
|
1122
829
|
if (typeof exitCode === 'number' && exitCode !== 0 && stderr) {
|
|
1123
830
|
const failMsg = `exit ${exitCode}: ${stderr.slice(0, 120)}`;
|
|
1124
831
|
if (shouldAutoCaptureError(toolName, failMsg)) {
|
|
1125
|
-
|
|
832
|
+
await captureToolFailure(toolName, failMsg, effectiveArgs, config).catch(() => {});
|
|
1126
833
|
}
|
|
1127
834
|
}
|
|
1128
835
|
if (toolResult.error) {
|
|
1129
836
|
const errMsg = String(toolResult.error).slice(0, 120);
|
|
1130
837
|
if (shouldAutoCaptureError(toolName, errMsg)) {
|
|
1131
|
-
|
|
838
|
+
await captureToolFailure(toolName, errMsg, effectiveArgs, config).catch(() => {});
|
|
1132
839
|
}
|
|
1133
840
|
}
|
|
1134
841
|
}
|