@compilr-dev/agents 0.3.1 → 0.3.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/agent.d.ts +107 -2
- package/dist/agent.js +151 -22
- package/dist/context/manager.d.ts +8 -0
- package/dist/context/manager.js +25 -2
- package/dist/errors.d.ts +20 -1
- package/dist/errors.js +44 -2
- package/dist/index.d.ts +16 -1
- package/dist/index.js +13 -1
- package/dist/messages/index.d.ts +12 -5
- package/dist/messages/index.js +53 -15
- package/dist/providers/claude.js +7 -0
- package/dist/providers/fireworks.d.ts +86 -0
- package/dist/providers/fireworks.js +123 -0
- package/dist/providers/gemini-native.d.ts +86 -0
- package/dist/providers/gemini-native.js +374 -0
- package/dist/providers/groq.d.ts +86 -0
- package/dist/providers/groq.js +123 -0
- package/dist/providers/index.d.ts +17 -2
- package/dist/providers/index.js +13 -2
- package/dist/providers/openai-compatible.js +12 -1
- package/dist/providers/openrouter.d.ts +95 -0
- package/dist/providers/openrouter.js +138 -0
- package/dist/providers/perplexity.d.ts +86 -0
- package/dist/providers/perplexity.js +123 -0
- package/dist/providers/together.d.ts +86 -0
- package/dist/providers/together.js +123 -0
- package/dist/providers/types.d.ts +19 -0
- package/dist/state/agent-state.d.ts +1 -0
- package/dist/state/agent-state.js +2 -0
- package/dist/state/serializer.js +20 -2
- package/dist/state/types.d.ts +5 -0
- package/dist/tools/builtin/ask-user-simple.js +1 -0
- package/dist/tools/builtin/ask-user.js +1 -0
- package/dist/tools/builtin/bash.js +123 -2
- package/dist/tools/builtin/shell-manager.d.ts +15 -0
- package/dist/tools/builtin/shell-manager.js +51 -0
- package/dist/tools/builtin/suggest.js +1 -0
- package/dist/tools/builtin/todo.js +2 -0
- package/dist/tools/define.d.ts +6 -0
- package/dist/tools/define.js +1 -0
- package/dist/tools/types.d.ts +19 -0
- package/dist/utils/index.d.ts +119 -4
- package/dist/utils/index.js +164 -13
- package/package.json +7 -1
|
@@ -22,6 +22,7 @@ export function createEmptyState(sessionId, systemPrompt) {
|
|
|
22
22
|
systemPrompt,
|
|
23
23
|
todos: [],
|
|
24
24
|
currentIteration: 0,
|
|
25
|
+
turnCount: 0,
|
|
25
26
|
totalTokensUsed: 0,
|
|
26
27
|
createdAt: now,
|
|
27
28
|
updatedAt: now,
|
|
@@ -40,6 +41,7 @@ export function createAgentState(options) {
|
|
|
40
41
|
model: options.model,
|
|
41
42
|
todos: serializeTodos(options.todos),
|
|
42
43
|
currentIteration: options.currentIteration,
|
|
44
|
+
turnCount: options.turnCount,
|
|
43
45
|
totalTokensUsed: options.totalTokensUsed,
|
|
44
46
|
createdAt: options.createdAt || now,
|
|
45
47
|
updatedAt: now,
|
package/dist/state/serializer.js
CHANGED
|
@@ -34,7 +34,11 @@ export class JsonSerializer {
|
|
|
34
34
|
throw StateError.deserialization('Invalid JSON format', error instanceof Error ? error : undefined);
|
|
35
35
|
}
|
|
36
36
|
this.validateParsed(parsed);
|
|
37
|
-
|
|
37
|
+
// Ensure turnCount has a default value for backward compatibility
|
|
38
|
+
// with saved states from before turnCount was added (pre-v2 states)
|
|
39
|
+
const partialState = parsed;
|
|
40
|
+
const turnCount = partialState.turnCount ?? partialState.messages.filter((m) => m.role === 'user').length;
|
|
41
|
+
return { ...partialState, turnCount };
|
|
38
42
|
}
|
|
39
43
|
/**
|
|
40
44
|
* Validate state before serialization (public interface method).
|
|
@@ -77,6 +81,11 @@ export class JsonSerializer {
|
|
|
77
81
|
if (typeof state.version !== 'number') {
|
|
78
82
|
throw StateError.invalidState('version must be a number');
|
|
79
83
|
}
|
|
84
|
+
// turnCount is optional for backward compatibility
|
|
85
|
+
// (old saved states may not have it)
|
|
86
|
+
if (state.turnCount !== undefined && typeof state.turnCount !== 'number') {
|
|
87
|
+
throw StateError.invalidState('turnCount must be a number if present');
|
|
88
|
+
}
|
|
80
89
|
// Version check - safe to cast since we validated it's a number
|
|
81
90
|
const version = state.version;
|
|
82
91
|
if (version > CURRENT_STATE_VERSION) {
|
|
@@ -119,7 +128,11 @@ export class CompactJsonSerializer {
|
|
|
119
128
|
throw StateError.deserialization('Invalid JSON format', error instanceof Error ? error : undefined);
|
|
120
129
|
}
|
|
121
130
|
this.validateParsed(parsed);
|
|
122
|
-
|
|
131
|
+
// Ensure turnCount has a default value for backward compatibility
|
|
132
|
+
// with saved states from before turnCount was added (pre-v2 states)
|
|
133
|
+
const partialState = parsed;
|
|
134
|
+
const turnCount = partialState.turnCount ?? partialState.messages.filter((m) => m.role === 'user').length;
|
|
135
|
+
return { ...partialState, turnCount };
|
|
123
136
|
}
|
|
124
137
|
/**
|
|
125
138
|
* Validate state before serialization (public interface method).
|
|
@@ -159,6 +172,11 @@ export class CompactJsonSerializer {
|
|
|
159
172
|
if (typeof state.version !== 'number') {
|
|
160
173
|
throw StateError.invalidState('version must be a number');
|
|
161
174
|
}
|
|
175
|
+
// turnCount is optional for backward compatibility
|
|
176
|
+
// (old saved states may not have it)
|
|
177
|
+
if (state.turnCount !== undefined && typeof state.turnCount !== 'number') {
|
|
178
|
+
throw StateError.invalidState('turnCount must be a number if present');
|
|
179
|
+
}
|
|
162
180
|
// Version check - safe to cast since we validated it's a number
|
|
163
181
|
const version = state.version;
|
|
164
182
|
if (version > CURRENT_STATE_VERSION) {
|
package/dist/state/types.d.ts
CHANGED
|
@@ -35,6 +35,11 @@ export interface AgentState {
|
|
|
35
35
|
* Current iteration count
|
|
36
36
|
*/
|
|
37
37
|
currentIteration: number;
|
|
38
|
+
/**
|
|
39
|
+
* Number of conversation turns (user + assistant exchanges)
|
|
40
|
+
* Used by context manager to track recent vs old messages for compaction.
|
|
41
|
+
*/
|
|
42
|
+
turnCount: number;
|
|
38
43
|
/**
|
|
39
44
|
* Total tokens used in the session
|
|
40
45
|
*/
|
|
@@ -132,6 +132,8 @@ async function executeBash(input, context) {
|
|
|
132
132
|
env,
|
|
133
133
|
onOutput: context.onOutput,
|
|
134
134
|
fifoCheck,
|
|
135
|
+
abortSignal: context.abortSignal,
|
|
136
|
+
onBackground: context.onBackground,
|
|
135
137
|
});
|
|
136
138
|
}
|
|
137
139
|
// Non-streaming execution (original behavior)
|
|
@@ -179,7 +181,7 @@ async function executeBash(input, context) {
|
|
|
179
181
|
* Execute command with streaming output to onOutput callback
|
|
180
182
|
*/
|
|
181
183
|
async function executeWithStreaming(command, options) {
|
|
182
|
-
const { cwd, timeout = DEFAULT_TIMEOUT, env, onOutput, fifoCheck } = options;
|
|
184
|
+
const { cwd, timeout = DEFAULT_TIMEOUT, env, onOutput, fifoCheck, abortSignal, onBackground, } = options;
|
|
183
185
|
return new Promise((resolve) => {
|
|
184
186
|
const child = spawn(command, [], {
|
|
185
187
|
cwd,
|
|
@@ -189,6 +191,7 @@ async function executeWithStreaming(command, options) {
|
|
|
189
191
|
let stdoutBuffer = '';
|
|
190
192
|
let stderrBuffer = '';
|
|
191
193
|
let timedOut = false;
|
|
194
|
+
let backgrounded = false;
|
|
192
195
|
// Set up timeout
|
|
193
196
|
const timeoutId = timeout > 0
|
|
194
197
|
? setTimeout(() => {
|
|
@@ -198,8 +201,62 @@ async function executeWithStreaming(command, options) {
|
|
|
198
201
|
setTimeout(() => child.kill('SIGKILL'), 5000);
|
|
199
202
|
}, timeout)
|
|
200
203
|
: undefined;
|
|
204
|
+
// Handle abort signal for backgrounding
|
|
205
|
+
if (abortSignal) {
|
|
206
|
+
const handleAbort = () => {
|
|
207
|
+
// Check if this is a "background" abort (not a cancel)
|
|
208
|
+
const reason = abortSignal.reason;
|
|
209
|
+
if (reason === 'background') {
|
|
210
|
+
backgrounded = true;
|
|
211
|
+
if (timeoutId)
|
|
212
|
+
clearTimeout(timeoutId);
|
|
213
|
+
// Move process to ShellManager
|
|
214
|
+
const manager = getDefaultShellManager();
|
|
215
|
+
const shellId = manager.adoptProcess(child, {
|
|
216
|
+
command,
|
|
217
|
+
cwd,
|
|
218
|
+
initialStdout: stdoutBuffer,
|
|
219
|
+
initialStderr: stderrBuffer,
|
|
220
|
+
});
|
|
221
|
+
// Notify via callback
|
|
222
|
+
if (onBackground) {
|
|
223
|
+
onBackground(shellId, stdoutBuffer + stderrBuffer);
|
|
224
|
+
}
|
|
225
|
+
// Resolve with backgrounded result
|
|
226
|
+
resolve(createSuccessResult({
|
|
227
|
+
backgrounded: true,
|
|
228
|
+
shell_id: shellId,
|
|
229
|
+
partial_stdout: truncateOutput(stdoutBuffer, DEFAULT_MAX_OUTPUT_SIZE).content,
|
|
230
|
+
partial_stderr: truncateOutput(stderrBuffer, DEFAULT_MAX_OUTPUT_SIZE).content,
|
|
231
|
+
message: `Command moved to background. Use bash_output with shell_id '${shellId}' to check status.`,
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// Regular cancellation - kill the process
|
|
236
|
+
if (timeoutId)
|
|
237
|
+
clearTimeout(timeoutId);
|
|
238
|
+
child.kill('SIGTERM');
|
|
239
|
+
setTimeout(() => {
|
|
240
|
+
try {
|
|
241
|
+
child.kill('SIGKILL');
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
/* ignore */
|
|
245
|
+
}
|
|
246
|
+
}, 5000);
|
|
247
|
+
resolve(createErrorResult('Command cancelled by user'));
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
if (abortSignal.aborted) {
|
|
251
|
+
handleAbort();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
abortSignal.addEventListener('abort', handleAbort, { once: true });
|
|
255
|
+
}
|
|
201
256
|
// Stream stdout
|
|
202
257
|
child.stdout.on('data', (data) => {
|
|
258
|
+
if (backgrounded)
|
|
259
|
+
return; // Stop collecting if backgrounded
|
|
203
260
|
const text = data.toString();
|
|
204
261
|
stdoutBuffer += text;
|
|
205
262
|
// Emit each line separately for better UI updates
|
|
@@ -212,6 +269,8 @@ async function executeWithStreaming(command, options) {
|
|
|
212
269
|
});
|
|
213
270
|
// Stream stderr
|
|
214
271
|
child.stderr.on('data', (data) => {
|
|
272
|
+
if (backgrounded)
|
|
273
|
+
return; // Stop collecting if backgrounded
|
|
215
274
|
const text = data.toString();
|
|
216
275
|
stderrBuffer += text;
|
|
217
276
|
// Emit each line separately
|
|
@@ -224,6 +283,8 @@ async function executeWithStreaming(command, options) {
|
|
|
224
283
|
});
|
|
225
284
|
// Handle completion
|
|
226
285
|
child.on('close', (code) => {
|
|
286
|
+
if (backgrounded)
|
|
287
|
+
return; // Already resolved
|
|
227
288
|
if (timeoutId)
|
|
228
289
|
clearTimeout(timeoutId);
|
|
229
290
|
if (timedOut) {
|
|
@@ -243,6 +304,8 @@ async function executeWithStreaming(command, options) {
|
|
|
243
304
|
});
|
|
244
305
|
// Handle spawn errors
|
|
245
306
|
child.on('error', (error) => {
|
|
307
|
+
if (backgrounded)
|
|
308
|
+
return; // Already resolved
|
|
246
309
|
if (timeoutId)
|
|
247
310
|
clearTimeout(timeoutId);
|
|
248
311
|
resolve(createErrorResult(error.message));
|
|
@@ -253,7 +316,7 @@ async function executeWithStreaming(command, options) {
|
|
|
253
316
|
* Execute command with streaming output (custom options version for createBashTool)
|
|
254
317
|
*/
|
|
255
318
|
async function executeWithStreamingCustom(command, options) {
|
|
256
|
-
const { cwd, timeout = DEFAULT_TIMEOUT, env, onOutput, fifoCheck, maxOutputSize, shell, } = options;
|
|
319
|
+
const { cwd, timeout = DEFAULT_TIMEOUT, env, onOutput, fifoCheck, maxOutputSize, shell, abortSignal, onBackground, } = options;
|
|
257
320
|
return new Promise((resolve) => {
|
|
258
321
|
const child = spawn(command, [], {
|
|
259
322
|
cwd,
|
|
@@ -263,6 +326,7 @@ async function executeWithStreamingCustom(command, options) {
|
|
|
263
326
|
let stdoutBuffer = '';
|
|
264
327
|
let stderrBuffer = '';
|
|
265
328
|
let timedOut = false;
|
|
329
|
+
let backgrounded = false;
|
|
266
330
|
// Set up timeout
|
|
267
331
|
const timeoutId = timeout > 0
|
|
268
332
|
? setTimeout(() => {
|
|
@@ -272,8 +336,57 @@ async function executeWithStreamingCustom(command, options) {
|
|
|
272
336
|
setTimeout(() => child.kill('SIGKILL'), 5000);
|
|
273
337
|
}, timeout)
|
|
274
338
|
: undefined;
|
|
339
|
+
// Handle abort signal for backgrounding
|
|
340
|
+
if (abortSignal) {
|
|
341
|
+
const handleAbort = () => {
|
|
342
|
+
const reason = abortSignal.reason;
|
|
343
|
+
if (reason === 'background') {
|
|
344
|
+
backgrounded = true;
|
|
345
|
+
if (timeoutId)
|
|
346
|
+
clearTimeout(timeoutId);
|
|
347
|
+
const manager = getDefaultShellManager();
|
|
348
|
+
const shellId = manager.adoptProcess(child, {
|
|
349
|
+
command,
|
|
350
|
+
cwd,
|
|
351
|
+
initialStdout: stdoutBuffer,
|
|
352
|
+
initialStderr: stderrBuffer,
|
|
353
|
+
});
|
|
354
|
+
if (onBackground) {
|
|
355
|
+
onBackground(shellId, stdoutBuffer + stderrBuffer);
|
|
356
|
+
}
|
|
357
|
+
resolve(createSuccessResult({
|
|
358
|
+
backgrounded: true,
|
|
359
|
+
shell_id: shellId,
|
|
360
|
+
partial_stdout: truncateOutput(stdoutBuffer, maxOutputSize).content,
|
|
361
|
+
partial_stderr: truncateOutput(stderrBuffer, maxOutputSize).content,
|
|
362
|
+
message: `Command moved to background. Use bash_output with shell_id '${shellId}' to check status.`,
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
if (timeoutId)
|
|
367
|
+
clearTimeout(timeoutId);
|
|
368
|
+
child.kill('SIGTERM');
|
|
369
|
+
setTimeout(() => {
|
|
370
|
+
try {
|
|
371
|
+
child.kill('SIGKILL');
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
/* ignore */
|
|
375
|
+
}
|
|
376
|
+
}, 5000);
|
|
377
|
+
resolve(createErrorResult('Command cancelled by user'));
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
if (abortSignal.aborted) {
|
|
381
|
+
handleAbort();
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
abortSignal.addEventListener('abort', handleAbort, { once: true });
|
|
385
|
+
}
|
|
275
386
|
// Stream stdout
|
|
276
387
|
child.stdout.on('data', (data) => {
|
|
388
|
+
if (backgrounded)
|
|
389
|
+
return;
|
|
277
390
|
const text = data.toString();
|
|
278
391
|
stdoutBuffer += text;
|
|
279
392
|
// Emit each line separately for better UI updates
|
|
@@ -286,6 +399,8 @@ async function executeWithStreamingCustom(command, options) {
|
|
|
286
399
|
});
|
|
287
400
|
// Stream stderr
|
|
288
401
|
child.stderr.on('data', (data) => {
|
|
402
|
+
if (backgrounded)
|
|
403
|
+
return;
|
|
289
404
|
const text = data.toString();
|
|
290
405
|
stderrBuffer += text;
|
|
291
406
|
// Emit each line separately
|
|
@@ -298,6 +413,8 @@ async function executeWithStreamingCustom(command, options) {
|
|
|
298
413
|
});
|
|
299
414
|
// Handle completion
|
|
300
415
|
child.on('close', (code) => {
|
|
416
|
+
if (backgrounded)
|
|
417
|
+
return;
|
|
301
418
|
if (timeoutId)
|
|
302
419
|
clearTimeout(timeoutId);
|
|
303
420
|
if (timedOut) {
|
|
@@ -317,6 +434,8 @@ async function executeWithStreamingCustom(command, options) {
|
|
|
317
434
|
});
|
|
318
435
|
// Handle spawn errors
|
|
319
436
|
child.on('error', (error) => {
|
|
437
|
+
if (backgrounded)
|
|
438
|
+
return;
|
|
320
439
|
if (timeoutId)
|
|
321
440
|
clearTimeout(timeoutId);
|
|
322
441
|
resolve(createErrorResult(error.message));
|
|
@@ -444,6 +563,8 @@ export function createBashTool(options) {
|
|
|
444
563
|
fifoCheck,
|
|
445
564
|
maxOutputSize,
|
|
446
565
|
shell,
|
|
566
|
+
abortSignal: context.abortSignal,
|
|
567
|
+
onBackground: context.onBackground,
|
|
447
568
|
});
|
|
448
569
|
}
|
|
449
570
|
// Execute with merged options (non-streaming)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shell Manager - Track and manage background shell processes
|
|
3
3
|
*/
|
|
4
|
+
import { type ChildProcess } from 'node:child_process';
|
|
4
5
|
/**
|
|
5
6
|
* Status of a background shell
|
|
6
7
|
*/
|
|
@@ -153,6 +154,20 @@ export declare class ShellManager {
|
|
|
153
154
|
* Kill all running shells
|
|
154
155
|
*/
|
|
155
156
|
killAll(): number;
|
|
157
|
+
/**
|
|
158
|
+
* Adopt an existing running process into the shell manager.
|
|
159
|
+
* Used when moving a foreground bash command to background (Ctrl+B).
|
|
160
|
+
*
|
|
161
|
+
* @param process - The ChildProcess to adopt
|
|
162
|
+
* @param options - Options including command and initial buffers
|
|
163
|
+
* @returns The shell ID for tracking
|
|
164
|
+
*/
|
|
165
|
+
adoptProcess(process: ChildProcess, options: {
|
|
166
|
+
command: string;
|
|
167
|
+
cwd?: string;
|
|
168
|
+
initialStdout?: string;
|
|
169
|
+
initialStderr?: string;
|
|
170
|
+
}): string;
|
|
156
171
|
/**
|
|
157
172
|
* Cleanup - kill all shells and clear state
|
|
158
173
|
*/
|
|
@@ -277,6 +277,57 @@ export class ShellManager {
|
|
|
277
277
|
}
|
|
278
278
|
return count;
|
|
279
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Adopt an existing running process into the shell manager.
|
|
282
|
+
* Used when moving a foreground bash command to background (Ctrl+B).
|
|
283
|
+
*
|
|
284
|
+
* @param process - The ChildProcess to adopt
|
|
285
|
+
* @param options - Options including command and initial buffers
|
|
286
|
+
* @returns The shell ID for tracking
|
|
287
|
+
*/
|
|
288
|
+
adoptProcess(process, options) {
|
|
289
|
+
const id = randomUUID().slice(0, 8);
|
|
290
|
+
const state = {
|
|
291
|
+
info: {
|
|
292
|
+
id,
|
|
293
|
+
command: options.command,
|
|
294
|
+
status: 'running',
|
|
295
|
+
startTime: new Date(),
|
|
296
|
+
cwd: options.cwd,
|
|
297
|
+
},
|
|
298
|
+
process,
|
|
299
|
+
stdoutBuffer: options.initialStdout ? [options.initialStdout] : [],
|
|
300
|
+
stderrBuffer: options.initialStderr ? [options.initialStderr] : [],
|
|
301
|
+
readIndex: { stdout: 0, stderr: 0 },
|
|
302
|
+
};
|
|
303
|
+
// Attach listeners for ongoing output (stdout may be null if already closed)
|
|
304
|
+
if (process.stdout) {
|
|
305
|
+
process.stdout.on('data', (chunk) => {
|
|
306
|
+
this.appendToBuffer(state.stdoutBuffer, chunk.toString());
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
// Attach stderr listener
|
|
310
|
+
if (process.stderr) {
|
|
311
|
+
process.stderr.on('data', (chunk) => {
|
|
312
|
+
this.appendToBuffer(state.stderrBuffer, chunk.toString());
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
// Handle completion
|
|
316
|
+
process.on('close', (code) => {
|
|
317
|
+
state.info.status = code === 0 ? 'completed' : 'failed';
|
|
318
|
+
state.info.exitCode = code ?? undefined;
|
|
319
|
+
state.info.endTime = new Date();
|
|
320
|
+
this.scheduleCleanup(id);
|
|
321
|
+
});
|
|
322
|
+
process.on('error', (error) => {
|
|
323
|
+
state.info.status = 'failed';
|
|
324
|
+
state.info.endTime = new Date();
|
|
325
|
+
state.stderrBuffer.push(`Process error: ${error.message}`);
|
|
326
|
+
this.scheduleCleanup(id);
|
|
327
|
+
});
|
|
328
|
+
this.shells.set(id, state);
|
|
329
|
+
return id;
|
|
330
|
+
}
|
|
280
331
|
/**
|
|
281
332
|
* Cleanup - kill all shells and clear state
|
|
282
333
|
*/
|
|
@@ -271,6 +271,7 @@ export function createTodoTools(store) {
|
|
|
271
271
|
total: input.todos.length,
|
|
272
272
|
}));
|
|
273
273
|
},
|
|
274
|
+
silent: true,
|
|
274
275
|
});
|
|
275
276
|
const todoRead = defineTool({
|
|
276
277
|
name: 'todo_read',
|
|
@@ -308,6 +309,7 @@ export function createTodoTools(store) {
|
|
|
308
309
|
total: todos.length,
|
|
309
310
|
}));
|
|
310
311
|
},
|
|
312
|
+
silent: true,
|
|
311
313
|
});
|
|
312
314
|
return { todoWrite, todoRead, store: todoStore };
|
|
313
315
|
}
|
package/dist/tools/define.d.ts
CHANGED
|
@@ -29,6 +29,12 @@ export interface DefineToolOptions<T extends object> {
|
|
|
29
29
|
* Default: false (sequential execution)
|
|
30
30
|
*/
|
|
31
31
|
parallel?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* If true, this tool runs silently without spinner updates or result output.
|
|
34
|
+
* Used for internal housekeeping tools like todo_read, suggest, etc.
|
|
35
|
+
* Default: false (normal visibility)
|
|
36
|
+
*/
|
|
37
|
+
silent?: boolean;
|
|
32
38
|
}
|
|
33
39
|
/**
|
|
34
40
|
* Define a tool with type-safe input handling
|
package/dist/tools/define.js
CHANGED
package/dist/tools/types.d.ts
CHANGED
|
@@ -39,6 +39,19 @@ export interface ToolExecutionContext {
|
|
|
39
39
|
* Tool use ID for correlation with events
|
|
40
40
|
*/
|
|
41
41
|
toolUseId?: string;
|
|
42
|
+
/**
|
|
43
|
+
* AbortSignal for cancelling/backgrounding the tool execution.
|
|
44
|
+
* When aborted with reason 'background', the bash tool should move the
|
|
45
|
+
* process to ShellManager instead of terminating.
|
|
46
|
+
*/
|
|
47
|
+
abortSignal?: AbortSignal;
|
|
48
|
+
/**
|
|
49
|
+
* Callback to notify when a process has been moved to background.
|
|
50
|
+
* Called by bash tool when user presses Ctrl+B (abortSignal with 'background' reason).
|
|
51
|
+
* @param shellId - The shell ID in ShellManager for later retrieval
|
|
52
|
+
* @param partialOutput - Output collected so far (stdout + stderr)
|
|
53
|
+
*/
|
|
54
|
+
onBackground?: (shellId: string, partialOutput: string) => void;
|
|
42
55
|
}
|
|
43
56
|
/**
|
|
44
57
|
* Tool handler function type
|
|
@@ -59,6 +72,12 @@ export interface Tool<T = object> {
|
|
|
59
72
|
* Default: false (sequential execution)
|
|
60
73
|
*/
|
|
61
74
|
parallel?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* If true, this tool runs silently without spinner updates or result output.
|
|
77
|
+
* Used for internal housekeeping tools like todo_read, suggest, etc.
|
|
78
|
+
* Default: false (normal visibility)
|
|
79
|
+
*/
|
|
80
|
+
silent?: boolean;
|
|
62
81
|
}
|
|
63
82
|
/**
|
|
64
83
|
* Tool registry for managing available tools
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,16 +1,131 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Utility functions
|
|
2
|
+
* Utility functions for @compilr-dev/agents
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
5
|
* Generate a unique ID for tool uses
|
|
6
6
|
*/
|
|
7
7
|
export declare function generateId(): string;
|
|
8
8
|
/**
|
|
9
|
-
* Sleep for a specified duration
|
|
9
|
+
* Sleep for a specified duration, respecting AbortSignal
|
|
10
|
+
*
|
|
11
|
+
* @param ms - Duration in milliseconds
|
|
12
|
+
* @param signal - Optional AbortSignal to cancel sleep
|
|
13
|
+
* @throws Error if signal is aborted
|
|
10
14
|
*/
|
|
11
|
-
export declare function sleep(ms: number): Promise<void>;
|
|
15
|
+
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
12
16
|
/**
|
|
13
|
-
*
|
|
17
|
+
* Configuration for LLM retry behavior
|
|
18
|
+
*/
|
|
19
|
+
export interface RetryConfig {
|
|
20
|
+
/**
|
|
21
|
+
* Enable/disable automatic retry.
|
|
22
|
+
* @default true
|
|
23
|
+
*/
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Maximum number of retry attempts (not including initial attempt).
|
|
27
|
+
* Total attempts = maxAttempts + 1
|
|
28
|
+
* @default 10
|
|
29
|
+
*/
|
|
30
|
+
maxAttempts?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Base delay in milliseconds for exponential backoff.
|
|
33
|
+
* Actual delay = min(baseDelayMs * 2^attempt, maxDelayMs) + jitter
|
|
34
|
+
* @default 1000
|
|
35
|
+
*/
|
|
36
|
+
baseDelayMs?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Maximum delay in milliseconds (cap for exponential growth).
|
|
39
|
+
* @default 30000
|
|
40
|
+
*/
|
|
41
|
+
maxDelayMs?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Default retry configuration
|
|
45
|
+
*/
|
|
46
|
+
export declare const DEFAULT_RETRY_CONFIG: Required<RetryConfig>;
|
|
47
|
+
/**
|
|
48
|
+
* Options for the withRetry function
|
|
49
|
+
*/
|
|
50
|
+
export interface WithRetryOptions<E extends Error> {
|
|
51
|
+
/**
|
|
52
|
+
* Maximum retry attempts (default: 10)
|
|
53
|
+
*/
|
|
54
|
+
maxAttempts?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Base delay in milliseconds (default: 1000)
|
|
57
|
+
*/
|
|
58
|
+
baseDelayMs?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Maximum delay cap in milliseconds (default: 30000)
|
|
61
|
+
*/
|
|
62
|
+
maxDelayMs?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Function to determine if an error is retryable
|
|
65
|
+
*/
|
|
66
|
+
isRetryable?: (error: E) => boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Callback invoked before each retry attempt
|
|
69
|
+
*/
|
|
70
|
+
onRetry?: (attempt: number, maxAttempts: number, error: E, delayMs: number) => void;
|
|
71
|
+
/**
|
|
72
|
+
* Callback invoked when all retries are exhausted
|
|
73
|
+
*/
|
|
74
|
+
onExhausted?: (attempts: number, error: E) => void;
|
|
75
|
+
/**
|
|
76
|
+
* AbortSignal to cancel retries
|
|
77
|
+
*/
|
|
78
|
+
signal?: AbortSignal;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Calculate delay with exponential backoff and jitter
|
|
82
|
+
*
|
|
83
|
+
* @param attempt - Current attempt number (0-indexed)
|
|
84
|
+
* @param baseDelayMs - Base delay in milliseconds
|
|
85
|
+
* @param maxDelayMs - Maximum delay cap in milliseconds
|
|
86
|
+
* @returns Delay in milliseconds
|
|
87
|
+
*/
|
|
88
|
+
export declare function calculateBackoffDelay(attempt: number, baseDelayMs?: number, maxDelayMs?: number): number;
|
|
89
|
+
/**
|
|
90
|
+
* Execute an async function with automatic retry on failure
|
|
91
|
+
*
|
|
92
|
+
* @param fn - Async function to execute
|
|
93
|
+
* @param options - Retry options
|
|
94
|
+
* @returns Result of the function
|
|
95
|
+
* @throws Last error if all retries exhausted
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const result = await withRetry(
|
|
100
|
+
* async () => {
|
|
101
|
+
* const response = await fetch(url);
|
|
102
|
+
* if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
103
|
+
* return response.json();
|
|
104
|
+
* },
|
|
105
|
+
* {
|
|
106
|
+
* maxAttempts: 5,
|
|
107
|
+
* isRetryable: (error) => error.message.includes('HTTP 5'),
|
|
108
|
+
* onRetry: (attempt, max, error, delay) => {
|
|
109
|
+
* console.log(`Retry ${attempt}/${max} in ${delay}ms: ${error.message}`);
|
|
110
|
+
* },
|
|
111
|
+
* }
|
|
112
|
+
* );
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export declare function withRetry<T, E extends Error = Error>(fn: () => Promise<T>, options?: WithRetryOptions<E>): Promise<T>;
|
|
116
|
+
/**
|
|
117
|
+
* Create a retryable async generator for streaming responses
|
|
118
|
+
*
|
|
119
|
+
* This is specifically designed for streaming LLM responses where we need to
|
|
120
|
+
* retry the entire stream if it fails partway through.
|
|
121
|
+
*
|
|
122
|
+
* @param fn - Function that returns an async iterable
|
|
123
|
+
* @param options - Retry options
|
|
124
|
+
* @returns Async generator that retries on failure
|
|
125
|
+
*/
|
|
126
|
+
export declare function withRetryGenerator<T, E extends Error = Error>(fn: () => AsyncIterable<T>, options?: WithRetryOptions<E>): AsyncGenerator<T, void, undefined>;
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated Use withRetry instead. This function is kept for backward compatibility.
|
|
14
129
|
*/
|
|
15
130
|
export declare function retry<T>(fn: () => Promise<T>, options?: {
|
|
16
131
|
maxRetries?: number;
|