@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.
Files changed (49) hide show
  1. package/README.md +80 -1
  2. package/build/agent/FallbackManager.d.ts +176 -0
  3. package/build/agent/FallbackManager.js +545 -0
  4. package/build/agent/ProbeAgent.d.ts +9 -1
  5. package/build/agent/ProbeAgent.js +218 -10
  6. package/build/agent/RetryManager.d.ts +157 -0
  7. package/build/agent/RetryManager.js +334 -0
  8. package/build/agent/acp/server.js +1 -0
  9. package/build/agent/acp/tools.js +6 -2
  10. package/build/agent/index.js +1814 -355
  11. package/build/agent/probeTool.js +20 -2
  12. package/build/agent/tools.js +16 -0
  13. package/build/delegate.js +326 -201
  14. package/build/downloader.js +46 -17
  15. package/build/extractor.js +12 -12
  16. package/build/index.js +13 -0
  17. package/build/tools/common.js +5 -3
  18. package/build/tools/edit.js +409 -0
  19. package/build/tools/index.js +11 -0
  20. package/build/tools/vercel.js +55 -14
  21. package/build/utils.js +18 -9
  22. package/cjs/agent/ProbeAgent.cjs +2268 -699
  23. package/cjs/index.cjs +75902 -74348
  24. package/package.json +2 -2
  25. package/src/agent/FallbackManager.d.ts +176 -0
  26. package/src/agent/FallbackManager.js +545 -0
  27. package/src/agent/ProbeAgent.d.ts +9 -1
  28. package/src/agent/ProbeAgent.js +218 -10
  29. package/src/agent/RetryManager.d.ts +157 -0
  30. package/src/agent/RetryManager.js +334 -0
  31. package/src/agent/acp/server.js +1 -0
  32. package/src/agent/acp/tools.js +6 -2
  33. package/src/agent/index.js +8 -0
  34. package/src/agent/probeTool.js +20 -2
  35. package/src/agent/tools.js +16 -0
  36. package/src/delegate.js +326 -201
  37. package/src/downloader.js +46 -17
  38. package/src/extractor.js +12 -12
  39. package/src/index.js +13 -0
  40. package/src/tools/common.js +5 -3
  41. package/src/tools/edit.js +409 -0
  42. package/src/tools/index.js +11 -0
  43. package/src/tools/vercel.js +55 -14
  44. package/src/utils.js +18 -9
  45. package/bin/binaries/probe-v0.6.0-rc154-aarch64-apple-darwin.tar.gz +0 -0
  46. package/bin/binaries/probe-v0.6.0-rc154-aarch64-unknown-linux-gnu.tar.gz +0 -0
  47. package/bin/binaries/probe-v0.6.0-rc154-x86_64-apple-darwin.tar.gz +0 -0
  48. package/bin/binaries/probe-v0.6.0-rc154-x86_64-pc-windows-msvc.zip +0 -0
  49. 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,
@@ -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: false)'
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: false)'
344
+ description: 'Allow test files in results (default: true)'
341
345
  },
342
346
  format: {
343
347
  type: 'string',
@@ -123,6 +123,7 @@ function parseArgs() {
123
123
  provider: null,
124
124
  model: null,
125
125
  allowEdit: false,
126
+ enableDelegate: false,
126
127
  verbose: false,
127
128
  help: false,
128
129
  maxIterations: null,
@@ -156,6 +157,10 @@ function parseArgs() {
156
157
  config.verbose = true;
157
158
  } else if (arg === '--allow-edit') {
158
159
  config.allowEdit = true;
160
+ } else if (arg === '--enable-delegate') {
161
+ config.enableDelegate = true;
162
+ } else if (arg === '--no-delegate') {
163
+ config.enableDelegate = false; // Explicitly disable delegation (used by subagents)
159
164
  } else if (arg === '--path' && i + 1 < args.length) {
160
165
  config.path = args[++i];
161
166
  } else if (arg === '--allowed-folders' && i + 1 < args.length) {
@@ -237,6 +242,7 @@ Options:
237
242
  --provider <name> Force AI provider: anthropic, openai, google
238
243
  --model <name> Override model name
239
244
  --allow-edit Enable code modification capabilities
245
+ --enable-delegate Enable delegate tool for task distribution to subagents
240
246
  --verbose Enable verbose output
241
247
  --outline Use outline-xml format for code search results
242
248
  --mcp Run as MCP server
@@ -565,6 +571,7 @@ async function main() {
565
571
  model: config.model,
566
572
  path: config.path,
567
573
  allowEdit: config.allowEdit,
574
+ enableDelegate: config.enableDelegate,
568
575
  debug: config.verbose
569
576
  });
570
577
  await server.start();
@@ -713,6 +720,7 @@ async function main() {
713
720
  promptType: config.prompt,
714
721
  customPrompt: systemPrompt,
715
722
  allowEdit: config.allowEdit,
723
+ enableDelegate: config.enableDelegate,
716
724
  debug: config.verbose,
717
725
  tracer: appTracer,
718
726
  outline: config.outline,
@@ -204,12 +204,30 @@ export function createWrappedTools(baseTools) {
204
204
  // Wrap bash tool
205
205
  if (baseTools.bashTool) {
206
206
  wrappedTools.bashToolInstance = wrapToolWithEmitter(
207
- baseTools.bashTool,
208
- 'bash',
207
+ baseTools.bashTool,
208
+ 'bash',
209
209
  baseTools.bashTool.execute
210
210
  );
211
211
  }
212
212
 
213
+ // Wrap edit tool
214
+ if (baseTools.editTool) {
215
+ wrappedTools.editToolInstance = wrapToolWithEmitter(
216
+ baseTools.editTool,
217
+ 'edit',
218
+ baseTools.editTool.execute
219
+ );
220
+ }
221
+
222
+ // Wrap create tool
223
+ if (baseTools.createTool) {
224
+ wrappedTools.createToolInstance = wrapToolWithEmitter(
225
+ baseTools.createTool,
226
+ 'create',
227
+ baseTools.createTool.execute
228
+ );
229
+ }
230
+
213
231
  return wrappedTools;
214
232
  }
215
233
 
@@ -5,6 +5,8 @@ import {
5
5
  extractTool,
6
6
  delegateTool,
7
7
  bashTool,
8
+ editTool,
9
+ createTool,
8
10
  DEFAULT_SYSTEM_MESSAGE,
9
11
  attemptCompletionSchema,
10
12
  attemptCompletionToolDefinition,
@@ -13,11 +15,15 @@ import {
13
15
  extractSchema,
14
16
  delegateSchema,
15
17
  bashSchema,
18
+ editSchema,
19
+ createSchema,
16
20
  searchToolDefinition,
17
21
  queryToolDefinition,
18
22
  extractToolDefinition,
19
23
  delegateToolDefinition,
20
24
  bashToolDefinition,
25
+ editToolDefinition,
26
+ createToolDefinition,
21
27
  parseXmlToolCall
22
28
  } from '../index.js';
23
29
  import { randomUUID } from 'crypto';
@@ -37,6 +43,12 @@ export function createTools(configOptions) {
37
43
  tools.bashTool = bashTool(configOptions);
38
44
  }
39
45
 
46
+ // Add edit and create tools if enabled
47
+ if (configOptions.allowEdit) {
48
+ tools.editTool = editTool(configOptions);
49
+ tools.createTool = createTool(configOptions);
50
+ }
51
+
40
52
  return tools;
41
53
  }
42
54
 
@@ -48,12 +60,16 @@ export {
48
60
  extractSchema,
49
61
  delegateSchema,
50
62
  bashSchema,
63
+ editSchema,
64
+ createSchema,
51
65
  attemptCompletionSchema,
52
66
  searchToolDefinition,
53
67
  queryToolDefinition,
54
68
  extractToolDefinition,
55
69
  delegateToolDefinition,
56
70
  bashToolDefinition,
71
+ editToolDefinition,
72
+ createToolDefinition,
57
73
  attemptCompletionToolDefinition,
58
74
  parseXmlToolCall
59
75
  };