@compilr-dev/agents 0.3.0 → 0.3.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/dist/agent.d.ts +82 -1
- package/dist/agent.js +162 -23
- 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 +6 -1
- package/dist/index.js +7 -1
- package/dist/messages/index.d.ts +12 -5
- package/dist/messages/index.js +53 -15
- package/dist/providers/gemini-native.d.ts +86 -0
- package/dist/providers/gemini-native.js +339 -0
- package/dist/providers/index.d.ts +7 -2
- package/dist/providers/index.js +7 -2
- package/dist/providers/ollama.d.ts +2 -2
- package/dist/providers/ollama.js +3 -3
- package/dist/providers/types.d.ts +11 -0
- package/dist/skills/index.js +474 -56
- 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/utils/index.d.ts +119 -4
- package/dist/utils/index.js +164 -13
- package/package.json +4 -1
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
|
*/
|
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;
|
package/dist/utils/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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
|
|
@@ -8,30 +8,181 @@ export function generateId() {
|
|
|
8
8
|
return `toolu_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* Sleep for a specified duration
|
|
11
|
+
* Sleep for a specified duration, respecting AbortSignal
|
|
12
|
+
*
|
|
13
|
+
* @param ms - Duration in milliseconds
|
|
14
|
+
* @param signal - Optional AbortSignal to cancel sleep
|
|
15
|
+
* @throws Error if signal is aborted
|
|
12
16
|
*/
|
|
13
|
-
export function sleep(ms) {
|
|
14
|
-
return new Promise((resolve) =>
|
|
17
|
+
export function sleep(ms, signal) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
if (signal?.aborted) {
|
|
20
|
+
reject(new Error('Aborted'));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const timeout = setTimeout(resolve, ms);
|
|
24
|
+
if (signal) {
|
|
25
|
+
const abortHandler = () => {
|
|
26
|
+
clearTimeout(timeout);
|
|
27
|
+
reject(new Error('Aborted'));
|
|
28
|
+
};
|
|
29
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
30
|
+
// Clean up abort listener when timer completes
|
|
31
|
+
const cleanup = () => {
|
|
32
|
+
signal.removeEventListener('abort', abortHandler);
|
|
33
|
+
resolve();
|
|
34
|
+
};
|
|
35
|
+
clearTimeout(timeout);
|
|
36
|
+
setTimeout(cleanup, ms);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
15
39
|
}
|
|
16
40
|
/**
|
|
17
|
-
*
|
|
41
|
+
* Default retry configuration
|
|
18
42
|
*/
|
|
19
|
-
export
|
|
20
|
-
|
|
43
|
+
export const DEFAULT_RETRY_CONFIG = {
|
|
44
|
+
enabled: true,
|
|
45
|
+
maxAttempts: 10,
|
|
46
|
+
baseDelayMs: 1000,
|
|
47
|
+
maxDelayMs: 30000,
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Calculate delay with exponential backoff and jitter
|
|
51
|
+
*
|
|
52
|
+
* @param attempt - Current attempt number (0-indexed)
|
|
53
|
+
* @param baseDelayMs - Base delay in milliseconds
|
|
54
|
+
* @param maxDelayMs - Maximum delay cap in milliseconds
|
|
55
|
+
* @returns Delay in milliseconds
|
|
56
|
+
*/
|
|
57
|
+
export function calculateBackoffDelay(attempt, baseDelayMs = DEFAULT_RETRY_CONFIG.baseDelayMs, maxDelayMs = DEFAULT_RETRY_CONFIG.maxDelayMs) {
|
|
58
|
+
// Exponential backoff: baseDelay * 2^attempt
|
|
59
|
+
const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
|
|
60
|
+
// Cap at maxDelay
|
|
61
|
+
const cappedDelay = Math.min(exponentialDelay, maxDelayMs);
|
|
62
|
+
// Add jitter (0 to 50% of base delay)
|
|
63
|
+
const jitter = Math.random() * baseDelayMs * 0.5;
|
|
64
|
+
return Math.floor(cappedDelay + jitter);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Execute an async function with automatic retry on failure
|
|
68
|
+
*
|
|
69
|
+
* @param fn - Async function to execute
|
|
70
|
+
* @param options - Retry options
|
|
71
|
+
* @returns Result of the function
|
|
72
|
+
* @throws Last error if all retries exhausted
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const result = await withRetry(
|
|
77
|
+
* async () => {
|
|
78
|
+
* const response = await fetch(url);
|
|
79
|
+
* if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
80
|
+
* return response.json();
|
|
81
|
+
* },
|
|
82
|
+
* {
|
|
83
|
+
* maxAttempts: 5,
|
|
84
|
+
* isRetryable: (error) => error.message.includes('HTTP 5'),
|
|
85
|
+
* onRetry: (attempt, max, error, delay) => {
|
|
86
|
+
* console.log(`Retry ${attempt}/${max} in ${delay}ms: ${error.message}`);
|
|
87
|
+
* },
|
|
88
|
+
* }
|
|
89
|
+
* );
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export async function withRetry(fn, options = {}) {
|
|
93
|
+
const { maxAttempts = DEFAULT_RETRY_CONFIG.maxAttempts, baseDelayMs = DEFAULT_RETRY_CONFIG.baseDelayMs, maxDelayMs = DEFAULT_RETRY_CONFIG.maxDelayMs, isRetryable = () => true, onRetry, onExhausted, signal, } = options;
|
|
21
94
|
let lastError;
|
|
22
|
-
|
|
95
|
+
let attempt = 0;
|
|
96
|
+
while (attempt <= maxAttempts) {
|
|
97
|
+
// Check for abort before each attempt
|
|
98
|
+
if (signal?.aborted) {
|
|
99
|
+
throw new Error('Aborted');
|
|
100
|
+
}
|
|
23
101
|
try {
|
|
24
102
|
return await fn();
|
|
25
103
|
}
|
|
26
104
|
catch (error) {
|
|
27
|
-
lastError = error
|
|
28
|
-
if
|
|
29
|
-
|
|
30
|
-
|
|
105
|
+
lastError = error;
|
|
106
|
+
// Check if error is retryable
|
|
107
|
+
if (!isRetryable(lastError)) {
|
|
108
|
+
throw lastError;
|
|
109
|
+
}
|
|
110
|
+
// Check if we have attempts remaining
|
|
111
|
+
if (attempt >= maxAttempts) {
|
|
112
|
+
onExhausted?.(attempt + 1, lastError);
|
|
113
|
+
throw lastError;
|
|
114
|
+
}
|
|
115
|
+
// Calculate delay and notify
|
|
116
|
+
const delayMs = calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs);
|
|
117
|
+
onRetry?.(attempt + 1, maxAttempts, lastError, delayMs);
|
|
118
|
+
// Wait before next attempt
|
|
119
|
+
await sleep(delayMs, signal);
|
|
120
|
+
attempt++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// This should never be reached, but TypeScript needs it
|
|
124
|
+
throw lastError ?? new Error('Retry failed with no error');
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a retryable async generator for streaming responses
|
|
128
|
+
*
|
|
129
|
+
* This is specifically designed for streaming LLM responses where we need to
|
|
130
|
+
* retry the entire stream if it fails partway through.
|
|
131
|
+
*
|
|
132
|
+
* @param fn - Function that returns an async iterable
|
|
133
|
+
* @param options - Retry options
|
|
134
|
+
* @returns Async generator that retries on failure
|
|
135
|
+
*/
|
|
136
|
+
export async function* withRetryGenerator(fn, options = {}) {
|
|
137
|
+
const { maxAttempts = DEFAULT_RETRY_CONFIG.maxAttempts, baseDelayMs = DEFAULT_RETRY_CONFIG.baseDelayMs, maxDelayMs = DEFAULT_RETRY_CONFIG.maxDelayMs, isRetryable = () => true, onRetry, onExhausted, signal, } = options;
|
|
138
|
+
let attempt = 0;
|
|
139
|
+
while (attempt <= maxAttempts) {
|
|
140
|
+
// Check for abort before each attempt
|
|
141
|
+
if (signal?.aborted) {
|
|
142
|
+
throw new Error('Aborted');
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
// Yield all items from the generator
|
|
146
|
+
for await (const item of fn()) {
|
|
147
|
+
// Check for abort during iteration
|
|
148
|
+
if (signal?.aborted) {
|
|
149
|
+
throw new Error('Aborted');
|
|
150
|
+
}
|
|
151
|
+
yield item;
|
|
152
|
+
}
|
|
153
|
+
// Successfully completed, return
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const typedError = error;
|
|
158
|
+
// Check if error is retryable
|
|
159
|
+
if (!isRetryable(typedError)) {
|
|
160
|
+
throw typedError;
|
|
161
|
+
}
|
|
162
|
+
// Check if we have attempts remaining
|
|
163
|
+
if (attempt >= maxAttempts) {
|
|
164
|
+
onExhausted?.(attempt + 1, typedError);
|
|
165
|
+
throw typedError;
|
|
31
166
|
}
|
|
167
|
+
// Calculate delay and notify
|
|
168
|
+
const delayMs = calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs);
|
|
169
|
+
onRetry?.(attempt + 1, maxAttempts, typedError, delayMs);
|
|
170
|
+
// Wait before next attempt
|
|
171
|
+
await sleep(delayMs, signal);
|
|
172
|
+
attempt++;
|
|
32
173
|
}
|
|
33
174
|
}
|
|
34
|
-
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* @deprecated Use withRetry instead. This function is kept for backward compatibility.
|
|
178
|
+
*/
|
|
179
|
+
export async function retry(fn, options = {}) {
|
|
180
|
+
const { maxRetries = 3, baseDelay = 1000, maxDelay = 10000 } = options;
|
|
181
|
+
return withRetry(fn, {
|
|
182
|
+
maxAttempts: maxRetries,
|
|
183
|
+
baseDelayMs: baseDelay,
|
|
184
|
+
maxDelayMs: maxDelay,
|
|
185
|
+
});
|
|
35
186
|
}
|
|
36
187
|
/**
|
|
37
188
|
* Truncate a string to a maximum length
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@compilr-dev/agents",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Lightweight multi-LLM agent library for building CLI AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -75,5 +75,8 @@
|
|
|
75
75
|
"typescript": "^5.3.0",
|
|
76
76
|
"typescript-eslint": "^8.48.0",
|
|
77
77
|
"vitest": "^3.2.4"
|
|
78
|
+
},
|
|
79
|
+
"dependencies": {
|
|
80
|
+
"@google/genai": "^1.38.0"
|
|
78
81
|
}
|
|
79
82
|
}
|