@probelabs/probe 0.6.0-rc154 → 0.6.0-rc161
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/README.md +80 -1
- package/build/agent/FallbackManager.d.ts +176 -0
- package/build/agent/FallbackManager.js +545 -0
- package/build/agent/ProbeAgent.d.ts +9 -1
- package/build/agent/ProbeAgent.js +218 -10
- package/build/agent/RetryManager.d.ts +157 -0
- package/build/agent/RetryManager.js +334 -0
- package/build/agent/acp/server.js +1 -0
- package/build/agent/acp/tools.js +6 -2
- package/build/agent/index.js +1814 -355
- package/build/agent/probeTool.js +20 -2
- package/build/agent/tools.js +16 -0
- package/build/delegate.js +326 -201
- package/build/downloader.js +46 -17
- package/build/extractor.js +12 -12
- package/build/index.js +13 -0
- package/build/tools/common.js +5 -3
- package/build/tools/edit.js +409 -0
- package/build/tools/index.js +11 -0
- package/build/tools/vercel.js +55 -14
- package/build/utils.js +18 -9
- package/cjs/agent/ProbeAgent.cjs +2268 -699
- package/cjs/index.cjs +75902 -74348
- package/package.json +2 -2
- package/src/agent/FallbackManager.d.ts +176 -0
- package/src/agent/FallbackManager.js +545 -0
- package/src/agent/ProbeAgent.d.ts +9 -1
- package/src/agent/ProbeAgent.js +218 -10
- package/src/agent/RetryManager.d.ts +157 -0
- package/src/agent/RetryManager.js +334 -0
- package/src/agent/acp/server.js +1 -0
- package/src/agent/acp/tools.js +6 -2
- package/src/agent/index.js +8 -0
- package/src/agent/probeTool.js +20 -2
- package/src/agent/tools.js +16 -0
- package/src/delegate.js +326 -201
- package/src/downloader.js +46 -17
- package/src/extractor.js +12 -12
- package/src/index.js +13 -0
- package/src/tools/common.js +5 -3
- package/src/tools/edit.js +409 -0
- package/src/tools/index.js +11 -0
- package/src/tools/vercel.js +55 -14
- package/src/utils.js +18 -9
- package/bin/binaries/probe-v0.6.0-rc154-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc154-aarch64-unknown-linux-gnu.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc154-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc154-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc154-x86_64-unknown-linux-gnu.tar.gz +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RetryManager - Handles retry logic with exponential backoff
|
|
3
|
+
*
|
|
4
|
+
* Provides configurable retry behavior for AI API calls with:
|
|
5
|
+
* - Exponential backoff
|
|
6
|
+
* - Configurable retry limits
|
|
7
|
+
* - Error type filtering
|
|
8
|
+
* - Detailed error context
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Default retryable error patterns
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_RETRYABLE_ERRORS = [
|
|
15
|
+
'Overloaded',
|
|
16
|
+
'overloaded',
|
|
17
|
+
'rate_limit',
|
|
18
|
+
'rate limit',
|
|
19
|
+
'429',
|
|
20
|
+
'500',
|
|
21
|
+
'502',
|
|
22
|
+
'503',
|
|
23
|
+
'504',
|
|
24
|
+
'timeout',
|
|
25
|
+
'ECONNRESET',
|
|
26
|
+
'ETIMEDOUT',
|
|
27
|
+
'ENOTFOUND',
|
|
28
|
+
'api_error'
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if an error is retryable based on error patterns
|
|
33
|
+
* @param {Error} error - The error to check
|
|
34
|
+
* @param {Array<string>} retryableErrors - List of retryable error patterns
|
|
35
|
+
* @returns {boolean} - True if error should be retried
|
|
36
|
+
*/
|
|
37
|
+
function isRetryableError(error, retryableErrors = DEFAULT_RETRYABLE_ERRORS) {
|
|
38
|
+
if (!error) return false;
|
|
39
|
+
|
|
40
|
+
const errorString = error.toString().toLowerCase();
|
|
41
|
+
const errorMessage = (error.message || '').toLowerCase();
|
|
42
|
+
const errorCode = (error.code || '').toLowerCase();
|
|
43
|
+
const errorType = (error.type || '').toLowerCase();
|
|
44
|
+
const statusCode = error.statusCode || error.status;
|
|
45
|
+
|
|
46
|
+
// Check if error matches any retryable pattern
|
|
47
|
+
for (const pattern of retryableErrors) {
|
|
48
|
+
const lowerPattern = pattern.toLowerCase();
|
|
49
|
+
|
|
50
|
+
if (errorString.includes(lowerPattern) ||
|
|
51
|
+
errorMessage.includes(lowerPattern) ||
|
|
52
|
+
errorCode.includes(lowerPattern) ||
|
|
53
|
+
errorType.includes(lowerPattern) ||
|
|
54
|
+
statusCode?.toString() === pattern) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Extract meaningful error information for logging
|
|
64
|
+
* @param {Error} error - The error to extract info from
|
|
65
|
+
* @returns {Object} - Error information object
|
|
66
|
+
*/
|
|
67
|
+
function extractErrorInfo(error) {
|
|
68
|
+
return {
|
|
69
|
+
message: error.message || error.toString(),
|
|
70
|
+
type: error.type || error.constructor.name,
|
|
71
|
+
code: error.code,
|
|
72
|
+
statusCode: error.statusCode || error.status,
|
|
73
|
+
provider: error.provider,
|
|
74
|
+
isRetryable: isRetryableError(error)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Sleep for a specified duration
|
|
80
|
+
* @param {number} ms - Milliseconds to sleep
|
|
81
|
+
* @returns {Promise<void>}
|
|
82
|
+
*/
|
|
83
|
+
function sleep(ms) {
|
|
84
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* RetryManager class for handling retry logic with exponential backoff
|
|
89
|
+
*/
|
|
90
|
+
export class RetryManager {
|
|
91
|
+
/**
|
|
92
|
+
* Create a new RetryManager
|
|
93
|
+
* @param {Object} options - Configuration options
|
|
94
|
+
* @param {number} [options.maxRetries=3] - Maximum retry attempts
|
|
95
|
+
* @param {number} [options.initialDelay=1000] - Initial delay in ms (1 second)
|
|
96
|
+
* @param {number} [options.maxDelay=30000] - Maximum delay in ms (30 seconds)
|
|
97
|
+
* @param {number} [options.backoffFactor=2] - Exponential backoff multiplier
|
|
98
|
+
* @param {Array<string>} [options.retryableErrors] - List of retryable error patterns
|
|
99
|
+
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
100
|
+
*/
|
|
101
|
+
constructor(options = {}) {
|
|
102
|
+
// Validate and set configuration with defaults
|
|
103
|
+
this.maxRetries = this._validateNumber(options.maxRetries, 3, 'maxRetries', 0, 100);
|
|
104
|
+
this.initialDelay = this._validateNumber(options.initialDelay, 1000, 'initialDelay', 0, 60000);
|
|
105
|
+
this.maxDelay = this._validateNumber(options.maxDelay, 30000, 'maxDelay', 0, 300000);
|
|
106
|
+
this.backoffFactor = this._validateNumber(options.backoffFactor, 2, 'backoffFactor', 1, 10);
|
|
107
|
+
this.retryableErrors = options.retryableErrors || DEFAULT_RETRYABLE_ERRORS;
|
|
108
|
+
this.debug = options.debug ?? false;
|
|
109
|
+
this.jitter = options.jitter ?? true; // Add random jitter by default
|
|
110
|
+
|
|
111
|
+
// Validate that maxDelay >= initialDelay
|
|
112
|
+
if (this.maxDelay < this.initialDelay) {
|
|
113
|
+
throw new Error('maxDelay must be greater than or equal to initialDelay');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Statistics
|
|
117
|
+
this.stats = {
|
|
118
|
+
totalAttempts: 0,
|
|
119
|
+
totalRetries: 0,
|
|
120
|
+
successfulRetries: 0,
|
|
121
|
+
failedRetries: 0
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Validate a numeric parameter
|
|
127
|
+
* @param {*} value - Value to validate
|
|
128
|
+
* @param {number} defaultValue - Default if undefined
|
|
129
|
+
* @param {string} name - Parameter name for error messages
|
|
130
|
+
* @param {number} min - Minimum allowed value
|
|
131
|
+
* @param {number} max - Maximum allowed value
|
|
132
|
+
* @returns {number} - Validated number
|
|
133
|
+
* @private
|
|
134
|
+
*/
|
|
135
|
+
_validateNumber(value, defaultValue, name, min, max) {
|
|
136
|
+
if (value === undefined || value === null) {
|
|
137
|
+
return defaultValue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const num = Number(value);
|
|
141
|
+
|
|
142
|
+
if (isNaN(num)) {
|
|
143
|
+
throw new Error(`${name} must be a number, got: ${value}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (num < min || num > max) {
|
|
147
|
+
throw new Error(`${name} must be between ${min} and ${max}, got: ${num}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return num;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Execute a function with retry logic
|
|
155
|
+
* @param {Function} fn - Async function to execute
|
|
156
|
+
* @param {Object} [context={}] - Context information for logging
|
|
157
|
+
* @param {AbortSignal} [context.signal] - Optional abort signal for cancellation
|
|
158
|
+
* @returns {Promise<*>} - Result from the function
|
|
159
|
+
* @throws {Error} - If all retries are exhausted or operation is aborted
|
|
160
|
+
*/
|
|
161
|
+
async executeWithRetry(fn, context = {}) {
|
|
162
|
+
let lastError = null;
|
|
163
|
+
let currentDelay = this.initialDelay;
|
|
164
|
+
|
|
165
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
166
|
+
// Check for abort signal
|
|
167
|
+
if (context.signal?.aborted) {
|
|
168
|
+
const abortError = new Error('Operation aborted');
|
|
169
|
+
abortError.name = 'AbortError';
|
|
170
|
+
throw abortError;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
this.stats.totalAttempts++;
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
if (this.debug && attempt > 0) {
|
|
177
|
+
console.log(`[RetryManager] Retry attempt ${attempt}/${this.maxRetries}`, context);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const result = await fn();
|
|
181
|
+
|
|
182
|
+
// Success!
|
|
183
|
+
if (attempt > 0) {
|
|
184
|
+
this.stats.successfulRetries++;
|
|
185
|
+
if (this.debug) {
|
|
186
|
+
console.log(`[RetryManager] ✅ Retry successful on attempt ${attempt + 1}`, context);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return result;
|
|
191
|
+
|
|
192
|
+
} catch (error) {
|
|
193
|
+
lastError = error;
|
|
194
|
+
const errorInfo = extractErrorInfo(error);
|
|
195
|
+
|
|
196
|
+
// Check if we should retry
|
|
197
|
+
const shouldRetry = isRetryableError(error, this.retryableErrors);
|
|
198
|
+
const hasRetriesLeft = attempt < this.maxRetries;
|
|
199
|
+
|
|
200
|
+
if (this.debug) {
|
|
201
|
+
console.log(`[RetryManager] ❌ Attempt ${attempt + 1}/${this.maxRetries + 1} failed:`, {
|
|
202
|
+
...context,
|
|
203
|
+
error: errorInfo,
|
|
204
|
+
shouldRetry,
|
|
205
|
+
hasRetriesLeft
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// If this is not a retryable error or no retries left, throw
|
|
210
|
+
if (!shouldRetry) {
|
|
211
|
+
if (this.debug) {
|
|
212
|
+
console.log(`[RetryManager] Error is not retryable, failing immediately`, errorInfo);
|
|
213
|
+
}
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!hasRetriesLeft) {
|
|
218
|
+
this.stats.failedRetries++;
|
|
219
|
+
if (this.debug) {
|
|
220
|
+
console.log(`[RetryManager] Max retries (${this.maxRetries}) exhausted`, context);
|
|
221
|
+
}
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Wait before retrying with exponential backoff
|
|
226
|
+
this.stats.totalRetries++;
|
|
227
|
+
|
|
228
|
+
// Add jitter to prevent thundering herd
|
|
229
|
+
let delayWithJitter = currentDelay;
|
|
230
|
+
if (this.jitter) {
|
|
231
|
+
// Add ±25% jitter
|
|
232
|
+
const jitterAmount = currentDelay * 0.25;
|
|
233
|
+
delayWithJitter = currentDelay + (Math.random() * jitterAmount * 2 - jitterAmount);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (this.debug) {
|
|
237
|
+
console.log(`[RetryManager] Waiting ${Math.round(delayWithJitter)}ms before retry...`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
await sleep(delayWithJitter);
|
|
241
|
+
|
|
242
|
+
// Calculate next delay with exponential backoff
|
|
243
|
+
currentDelay = Math.min(currentDelay * this.backoffFactor, this.maxDelay);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// This should never be reached, but just in case
|
|
248
|
+
throw lastError;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Check if an error is retryable
|
|
253
|
+
* @param {Error} error - The error to check
|
|
254
|
+
* @returns {boolean} - True if error should be retried
|
|
255
|
+
*/
|
|
256
|
+
isRetryable(error) {
|
|
257
|
+
return isRetryableError(error, this.retryableErrors);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get retry statistics
|
|
262
|
+
* @returns {Object} - Statistics object
|
|
263
|
+
*/
|
|
264
|
+
getStats() {
|
|
265
|
+
return { ...this.stats };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Reset statistics
|
|
270
|
+
*/
|
|
271
|
+
resetStats() {
|
|
272
|
+
this.stats = {
|
|
273
|
+
totalAttempts: 0,
|
|
274
|
+
totalRetries: 0,
|
|
275
|
+
successfulRetries: 0,
|
|
276
|
+
failedRetries: 0
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Create a RetryManager from environment variables
|
|
283
|
+
* @param {boolean} [debug=false] - Enable debug logging
|
|
284
|
+
* @returns {RetryManager} - Configured RetryManager instance
|
|
285
|
+
*/
|
|
286
|
+
export function createRetryManagerFromEnv(debug = false) {
|
|
287
|
+
const options = { debug };
|
|
288
|
+
|
|
289
|
+
// Parse and validate environment variables
|
|
290
|
+
if (process.env.MAX_RETRIES) {
|
|
291
|
+
const parsed = parseInt(process.env.MAX_RETRIES, 10);
|
|
292
|
+
if (!isNaN(parsed) && parsed >= 0 && parsed <= 50) {
|
|
293
|
+
options.maxRetries = parsed;
|
|
294
|
+
} else {
|
|
295
|
+
console.warn(`[RetryManager] MAX_RETRIES must be between 0 and 50, using default`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (process.env.RETRY_INITIAL_DELAY) {
|
|
300
|
+
const parsed = parseInt(process.env.RETRY_INITIAL_DELAY, 10);
|
|
301
|
+
if (!isNaN(parsed) && parsed >= 0 && parsed <= 60000) {
|
|
302
|
+
options.initialDelay = parsed;
|
|
303
|
+
} else {
|
|
304
|
+
console.warn(`[RetryManager] RETRY_INITIAL_DELAY must be between 0 and 60000ms, using default`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (process.env.RETRY_MAX_DELAY) {
|
|
309
|
+
const parsed = parseInt(process.env.RETRY_MAX_DELAY, 10);
|
|
310
|
+
if (!isNaN(parsed) && parsed >= 0 && parsed <= 300000) {
|
|
311
|
+
options.maxDelay = parsed;
|
|
312
|
+
} else {
|
|
313
|
+
console.warn(`[RetryManager] RETRY_MAX_DELAY must be between 0 and 300000ms, using default`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (process.env.RETRY_BACKOFF_FACTOR) {
|
|
318
|
+
const parsed = parseFloat(process.env.RETRY_BACKOFF_FACTOR);
|
|
319
|
+
if (!isNaN(parsed) && parsed >= 1 && parsed <= 10) {
|
|
320
|
+
options.backoffFactor = parsed;
|
|
321
|
+
} else {
|
|
322
|
+
console.warn(`[RetryManager] RETRY_BACKOFF_FACTOR must be between 1 and 10, using default`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (process.env.RETRY_JITTER === '0' || process.env.RETRY_JITTER === 'false') {
|
|
327
|
+
options.jitter = false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return new RetryManager(options);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Export utility functions
|
|
334
|
+
export { isRetryableError, extractErrorInfo, DEFAULT_RETRYABLE_ERRORS };
|
|
@@ -329,6 +329,7 @@ export class ACPServer {
|
|
|
329
329
|
provider: this.options.provider,
|
|
330
330
|
model: this.options.model,
|
|
331
331
|
allowEdit: this.options.allowEdit,
|
|
332
|
+
enableDelegate: this.options.enableDelegate,
|
|
332
333
|
debug: this.options.debug,
|
|
333
334
|
enableMcp: this.options.enableMcp,
|
|
334
335
|
mcpConfig: this.options.mcpConfig,
|
package/build/agent/acp/tools.js
CHANGED
|
@@ -286,7 +286,7 @@ export class ACPToolManager {
|
|
|
286
286
|
},
|
|
287
287
|
allow_tests: {
|
|
288
288
|
type: 'boolean',
|
|
289
|
-
description: 'Include test files in results (default:
|
|
289
|
+
description: 'Include test files in results (default: true)'
|
|
290
290
|
}
|
|
291
291
|
},
|
|
292
292
|
required: ['query']
|
|
@@ -314,6 +314,10 @@ export class ACPToolManager {
|
|
|
314
314
|
max_results: {
|
|
315
315
|
type: 'number',
|
|
316
316
|
description: 'Maximum number of results to return (default: 10)'
|
|
317
|
+
},
|
|
318
|
+
allow_tests: {
|
|
319
|
+
type: 'boolean',
|
|
320
|
+
description: 'Allow test files in search results (default: true)'
|
|
317
321
|
}
|
|
318
322
|
},
|
|
319
323
|
required: ['pattern']
|
|
@@ -337,7 +341,7 @@ export class ACPToolManager {
|
|
|
337
341
|
},
|
|
338
342
|
allow_tests: {
|
|
339
343
|
type: 'boolean',
|
|
340
|
-
description: 'Allow test files in results (default:
|
|
344
|
+
description: 'Allow test files in results (default: true)'
|
|
341
345
|
},
|
|
342
346
|
format: {
|
|
343
347
|
type: 'string',
|