@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
@@ -1893,6 +1893,28 @@ import { exec as execCallback } from "child_process";
1893
1893
  import tar from "tar";
1894
1894
  import os2 from "os";
1895
1895
  import { fileURLToPath as fileURLToPath2 } from "url";
1896
+ function sanitizeError(err) {
1897
+ try {
1898
+ const status = err?.response?.status;
1899
+ const statusText = err?.response?.statusText;
1900
+ const url = err?.config?.url || err?.response?.config?.url;
1901
+ const code = err?.code;
1902
+ const serverMsg = typeof err?.response?.data === "string" ? err.response.data.slice(0, 500) : typeof err?.response?.data?.message === "string" ? err.response.data.message : void 0;
1903
+ const base = err?.message || String(err);
1904
+ const parts = [base];
1905
+ if (status || statusText) parts.push(`[${status ?? ""} ${statusText ?? ""}]`.trim());
1906
+ if (code) parts.push(`code=${code}`);
1907
+ if (url) parts.push(`url=${url}`);
1908
+ if (serverMsg) parts.push(`server="${String(serverMsg).replace(/\s+/g, " ").trim()}"`);
1909
+ const e = new Error(parts.filter(Boolean).join(" "));
1910
+ if (status) e.status = status;
1911
+ if (code) e.code = code;
1912
+ if (url) e.url = url;
1913
+ return e;
1914
+ } catch (_) {
1915
+ return new Error(err?.message || String(err));
1916
+ }
1917
+ }
1896
1918
  async function acquireFileLock(lockPath, version) {
1897
1919
  const lockData = {
1898
1920
  version,
@@ -1985,7 +2007,7 @@ async function waitForFileLock(lockPath, binaryPath) {
1985
2007
  }
1986
2008
  } catch {
1987
2009
  }
1988
- await new Promise((resolve5) => setTimeout(resolve5, LOCK_POLL_INTERVAL_MS));
2010
+ await new Promise((resolve6) => setTimeout(resolve6, LOCK_POLL_INTERVAL_MS));
1989
2011
  }
1990
2012
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
1991
2013
  console.log(`Timeout waiting for file lock`);
@@ -2041,7 +2063,7 @@ function detectOsArch() {
2041
2063
  case "linux":
2042
2064
  osInfo = {
2043
2065
  type: "linux",
2044
- keywords: ["linux", "Linux", "gnu"]
2066
+ keywords: ["linux", "Linux", "musl", "gnu"]
2045
2067
  };
2046
2068
  break;
2047
2069
  case "darwin":
@@ -2085,7 +2107,7 @@ function constructAssetInfo(version, osInfo, archInfo) {
2085
2107
  let extension;
2086
2108
  switch (osInfo.type) {
2087
2109
  case "linux":
2088
- platform = `${archInfo.type}-unknown-linux-gnu`;
2110
+ platform = `${archInfo.type}-unknown-linux-musl`;
2089
2111
  extension = "tar.gz";
2090
2112
  break;
2091
2113
  case "darwin":
@@ -2202,7 +2224,7 @@ async function getLatestRelease(version) {
2202
2224
  }
2203
2225
  return { tag, assets };
2204
2226
  }
2205
- throw error;
2227
+ throw sanitizeError(error);
2206
2228
  }
2207
2229
  }
2208
2230
  function findBestAsset(assets, osInfo, archInfo) {
@@ -2416,7 +2438,7 @@ async function extractBinary(assetPath, outputDir) {
2416
2438
  return binaryPath;
2417
2439
  } catch (error) {
2418
2440
  console.error(`Error extracting binary: ${error instanceof Error ? error.message : String(error)}`);
2419
- throw error;
2441
+ throw sanitizeError(error);
2420
2442
  }
2421
2443
  }
2422
2444
  async function getVersionInfo(binDir) {
@@ -2581,8 +2603,8 @@ async function downloadProbeBinary(version) {
2581
2603
  }
2582
2604
  return await withDownloadLock(version, () => doDownload(version));
2583
2605
  } catch (error) {
2584
- console.error("Error downloading probe binary:", error);
2585
- throw error;
2606
+ console.error("Error downloading probe binary:", error?.message || String(error));
2607
+ throw sanitizeError(error);
2586
2608
  }
2587
2609
  }
2588
2610
  var exec, REPO_OWNER, REPO_NAME, BINARY_NAME, __filename2, __dirname2, downloadLocks, LOCK_TIMEOUT_MS, LOCK_POLL_INTERVAL_MS, MAX_LOCK_WAIT_MS;
@@ -2619,9 +2641,15 @@ async function getBinaryPath(options = {}) {
2619
2641
  probeBinaryPath = await downloadProbeBinary(version);
2620
2642
  return probeBinaryPath;
2621
2643
  }
2622
- const binDir = await getPackageBinDir();
2623
2644
  const isWindows = process.platform === "win32";
2624
2645
  const binaryName = isWindows ? "probe.exe" : "probe-binary";
2646
+ const localPackageBin = path3.resolve(__dirname3, "..", "bin");
2647
+ const localBinaryPath = path3.join(localPackageBin, binaryName);
2648
+ if (fs3.existsSync(localBinaryPath) && !forceDownload) {
2649
+ probeBinaryPath = localBinaryPath;
2650
+ return probeBinaryPath;
2651
+ }
2652
+ const binDir = await getPackageBinDir();
2625
2653
  const binaryPath = path3.join(binDir, binaryName);
2626
2654
  if (fs3.existsSync(binaryPath) && !forceDownload) {
2627
2655
  probeBinaryPath = binaryPath;
@@ -2950,7 +2978,7 @@ Command: ${command}`;
2950
2978
  }
2951
2979
  }
2952
2980
  function extractWithStdin(binaryPath, cliArgs, content, options) {
2953
- return new Promise((resolve5, reject2) => {
2981
+ return new Promise((resolve6, reject2) => {
2954
2982
  const childProcess = spawn(binaryPath, ["extract", ...cliArgs], {
2955
2983
  stdio: ["pipe", "pipe", "pipe"]
2956
2984
  });
@@ -2972,7 +3000,7 @@ function extractWithStdin(binaryPath, cliArgs, content, options) {
2972
3000
  }
2973
3001
  try {
2974
3002
  const result = processExtractOutput(stdout, options);
2975
- resolve5(result);
3003
+ resolve6(result);
2976
3004
  } catch (error) {
2977
3005
  reject2(error);
2978
3006
  }
@@ -3063,6 +3091,262 @@ var init_grep = __esm({
3063
3091
  }
3064
3092
  });
3065
3093
 
3094
+ // src/delegate.js
3095
+ import { randomUUID } from "crypto";
3096
+ async function delegate({
3097
+ task,
3098
+ timeout = 300,
3099
+ debug = false,
3100
+ currentIteration = 0,
3101
+ maxIterations = 30,
3102
+ tracer = null,
3103
+ parentSessionId = null,
3104
+ path: path7 = null,
3105
+ provider = null,
3106
+ model = null
3107
+ }) {
3108
+ if (!task || typeof task !== "string") {
3109
+ throw new Error("Task parameter is required and must be a string");
3110
+ }
3111
+ const sessionId = randomUUID();
3112
+ const startTime = Date.now();
3113
+ const remainingIterations = Math.max(1, maxIterations - currentIteration);
3114
+ const delegationSpan = tracer ? tracer.createDelegationSpan(sessionId, task) : null;
3115
+ let timeoutId = null;
3116
+ let acquired = false;
3117
+ try {
3118
+ delegationManager.tryAcquire(parentSessionId);
3119
+ acquired = true;
3120
+ if (debug) {
3121
+ const stats = delegationManager.getStats();
3122
+ console.error(`[DELEGATE] Starting delegation session ${sessionId}`);
3123
+ console.error(`[DELEGATE] Parent session: ${parentSessionId || "none"}`);
3124
+ console.error(`[DELEGATE] Task: ${task}`);
3125
+ console.error(`[DELEGATE] Current iteration: ${currentIteration}/${maxIterations}`);
3126
+ console.error(`[DELEGATE] Remaining iterations for subagent: ${remainingIterations}`);
3127
+ console.error(`[DELEGATE] Timeout configured: ${timeout} seconds`);
3128
+ console.error(`[DELEGATE] Global active delegations: ${stats.globalActive}/${stats.maxConcurrent}`);
3129
+ console.error(`[DELEGATE] Using ProbeAgent SDK with code-researcher prompt`);
3130
+ }
3131
+ const subagent = new ProbeAgent({
3132
+ sessionId,
3133
+ promptType: "code-researcher",
3134
+ // Clean prompt, not inherited from parent
3135
+ enableDelegate: false,
3136
+ // Explicitly disable delegation to prevent recursion
3137
+ disableMermaidValidation: true,
3138
+ // Faster processing
3139
+ disableJsonValidation: true,
3140
+ // Simpler responses
3141
+ maxIterations: remainingIterations,
3142
+ debug,
3143
+ tracer,
3144
+ path: path7,
3145
+ // Inherit from parent
3146
+ provider,
3147
+ // Inherit from parent
3148
+ model
3149
+ // Inherit from parent
3150
+ });
3151
+ if (debug) {
3152
+ console.error(`[DELEGATE] Created subagent with session ${sessionId}`);
3153
+ console.error(`[DELEGATE] Subagent config: promptType=code-researcher, enableDelegate=false, maxIterations=${remainingIterations}`);
3154
+ }
3155
+ const timeoutPromise = new Promise((_, reject2) => {
3156
+ timeoutId = setTimeout(() => {
3157
+ reject2(new Error(`Delegation timed out after ${timeout} seconds`));
3158
+ }, timeout * 1e3);
3159
+ });
3160
+ const answerPromise = subagent.answer(task);
3161
+ const response = await Promise.race([answerPromise, timeoutPromise]);
3162
+ if (timeoutId !== null) {
3163
+ clearTimeout(timeoutId);
3164
+ timeoutId = null;
3165
+ }
3166
+ const duration = Date.now() - startTime;
3167
+ if (typeof response !== "string") {
3168
+ throw new Error("Delegate agent returned invalid response (not a string)");
3169
+ }
3170
+ const trimmedResponse = response.trim();
3171
+ if (trimmedResponse.length === 0) {
3172
+ throw new Error("Delegate agent returned empty or whitespace-only response");
3173
+ }
3174
+ if (trimmedResponse.includes("\0")) {
3175
+ throw new Error("Delegate agent returned response containing null bytes");
3176
+ }
3177
+ if (debug) {
3178
+ console.error(`[DELEGATE] Task completed successfully for session ${sessionId}`);
3179
+ console.error(`[DELEGATE] Duration: ${(duration / 1e3).toFixed(2)}s`);
3180
+ console.error(`[DELEGATE] Response length: ${response.length} chars`);
3181
+ }
3182
+ if (tracer) {
3183
+ tracer.recordDelegationEvent("completed", {
3184
+ "delegation.session_id": sessionId,
3185
+ "delegation.parent_session_id": parentSessionId,
3186
+ "delegation.duration_ms": duration,
3187
+ "delegation.response_length": response.length,
3188
+ "delegation.success": true
3189
+ });
3190
+ if (delegationSpan) {
3191
+ delegationSpan.setAttributes({
3192
+ "delegation.result.success": true,
3193
+ "delegation.result.response_length": response.length,
3194
+ "delegation.result.duration_ms": duration
3195
+ });
3196
+ delegationSpan.setStatus({ code: 1 });
3197
+ delegationSpan.end();
3198
+ }
3199
+ }
3200
+ if (acquired) {
3201
+ delegationManager.release(parentSessionId, debug);
3202
+ }
3203
+ return response;
3204
+ } catch (error) {
3205
+ if (timeoutId !== null) {
3206
+ clearTimeout(timeoutId);
3207
+ timeoutId = null;
3208
+ }
3209
+ const duration = Date.now() - startTime;
3210
+ if (acquired) {
3211
+ delegationManager.release(parentSessionId, debug);
3212
+ }
3213
+ if (debug) {
3214
+ console.error(`[DELEGATE] Task failed for session ${sessionId} after ${duration}ms`);
3215
+ console.error(`[DELEGATE] Error: ${error.message}`);
3216
+ console.error(`[DELEGATE] Stack: ${error.stack}`);
3217
+ }
3218
+ if (tracer) {
3219
+ tracer.recordDelegationEvent("failed", {
3220
+ "delegation.session_id": sessionId,
3221
+ "delegation.parent_session_id": parentSessionId,
3222
+ "delegation.duration_ms": duration,
3223
+ "delegation.error_message": error.message,
3224
+ "delegation.success": false
3225
+ });
3226
+ if (delegationSpan) {
3227
+ delegationSpan.setAttributes({
3228
+ "delegation.result.success": false,
3229
+ "delegation.result.error": error.message,
3230
+ "delegation.result.duration_ms": duration
3231
+ });
3232
+ delegationSpan.setStatus({ code: 2, message: error.message });
3233
+ delegationSpan.end();
3234
+ }
3235
+ }
3236
+ throw new Error(`Delegation failed: ${error.message}`);
3237
+ }
3238
+ }
3239
+ var DelegationManager, delegationManager;
3240
+ var init_delegate = __esm({
3241
+ "src/delegate.js"() {
3242
+ "use strict";
3243
+ init_ProbeAgent();
3244
+ DelegationManager = class {
3245
+ constructor() {
3246
+ this.maxConcurrent = parseInt(process.env.MAX_CONCURRENT_DELEGATIONS || "3", 10);
3247
+ this.maxPerSession = parseInt(process.env.MAX_DELEGATIONS_PER_SESSION || "10", 10);
3248
+ this.sessionDelegations = /* @__PURE__ */ new Map();
3249
+ this.globalActive = 0;
3250
+ this.cleanupInterval = setInterval(() => {
3251
+ try {
3252
+ this.cleanupStaleSessions();
3253
+ } catch (error) {
3254
+ console.error("[DelegationManager] Error during cleanup:", error);
3255
+ }
3256
+ }, 5 * 60 * 1e3);
3257
+ if (this.cleanupInterval.unref) {
3258
+ this.cleanupInterval.unref();
3259
+ }
3260
+ }
3261
+ /**
3262
+ * Check limits and increment counters (synchronous, atomic in Node.js event loop)
3263
+ * @param {string|null|undefined} parentSessionId - Parent session ID for tracking
3264
+ */
3265
+ tryAcquire(parentSessionId) {
3266
+ if (parentSessionId !== null && parentSessionId !== void 0 && typeof parentSessionId !== "string") {
3267
+ throw new TypeError("parentSessionId must be a string, null, or undefined");
3268
+ }
3269
+ if (this.globalActive >= this.maxConcurrent) {
3270
+ throw new Error(`Maximum concurrent delegations (${this.maxConcurrent}) reached. Please wait for some delegations to complete.`);
3271
+ }
3272
+ if (parentSessionId) {
3273
+ const sessionData = this.sessionDelegations.get(parentSessionId);
3274
+ const sessionCount = sessionData?.count || 0;
3275
+ if (sessionCount >= this.maxPerSession) {
3276
+ throw new Error(`Maximum delegations per session (${this.maxPerSession}) reached for session ${parentSessionId}`);
3277
+ }
3278
+ }
3279
+ this.globalActive++;
3280
+ if (parentSessionId) {
3281
+ const sessionData = this.sessionDelegations.get(parentSessionId);
3282
+ if (sessionData) {
3283
+ sessionData.count++;
3284
+ sessionData.lastUpdated = Date.now();
3285
+ } else {
3286
+ this.sessionDelegations.set(parentSessionId, {
3287
+ count: 1,
3288
+ lastUpdated: Date.now()
3289
+ });
3290
+ }
3291
+ }
3292
+ return true;
3293
+ }
3294
+ /**
3295
+ * Decrement counters (synchronous, atomic in Node.js event loop)
3296
+ */
3297
+ release(parentSessionId, debug = false) {
3298
+ this.globalActive = Math.max(0, this.globalActive - 1);
3299
+ if (parentSessionId) {
3300
+ const sessionData = this.sessionDelegations.get(parentSessionId);
3301
+ if (sessionData) {
3302
+ sessionData.count = Math.max(0, sessionData.count - 1);
3303
+ if (sessionData.count === 0) {
3304
+ this.sessionDelegations.delete(parentSessionId);
3305
+ }
3306
+ }
3307
+ }
3308
+ if (debug) {
3309
+ console.error(`[DELEGATE] Released. Global active: ${this.globalActive}`);
3310
+ }
3311
+ }
3312
+ /**
3313
+ * Get current stats for monitoring
3314
+ */
3315
+ getStats() {
3316
+ return {
3317
+ globalActive: this.globalActive,
3318
+ maxConcurrent: this.maxConcurrent,
3319
+ maxPerSession: this.maxPerSession,
3320
+ sessionCount: this.sessionDelegations.size
3321
+ };
3322
+ }
3323
+ /**
3324
+ * Clean up stale sessions (sessions with count=0 that haven't been updated in 1 hour)
3325
+ */
3326
+ cleanupStaleSessions() {
3327
+ const oneHourAgo = Date.now() - 60 * 60 * 1e3;
3328
+ for (const [sessionId, data] of this.sessionDelegations.entries()) {
3329
+ if (data.count === 0 && data.lastUpdated < oneHourAgo) {
3330
+ this.sessionDelegations.delete(sessionId);
3331
+ }
3332
+ }
3333
+ }
3334
+ /**
3335
+ * Cleanup all resources (for testing or shutdown)
3336
+ */
3337
+ cleanup() {
3338
+ if (this.cleanupInterval) {
3339
+ clearInterval(this.cleanupInterval);
3340
+ this.cleanupInterval = null;
3341
+ }
3342
+ this.sessionDelegations.clear();
3343
+ this.globalActive = 0;
3344
+ }
3345
+ };
3346
+ delegationManager = new DelegationManager();
3347
+ }
3348
+ });
3349
+
3066
3350
  // node_modules/zod/v3/helpers/util.js
3067
3351
  var util, objectUtil, ZodParsedType, getParsedType;
3068
3352
  var init_util2 = __esm({
@@ -7276,7 +7560,7 @@ function parseTargets(targets) {
7276
7560
  }
7277
7561
  return targets.split(/\s+/).filter((f) => f.length > 0);
7278
7562
  }
7279
- var searchSchema, querySchema, extractSchema, delegateSchema, bashSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, attemptCompletionToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, DEFAULT_VALID_TOOLS;
7563
+ var searchSchema, querySchema, extractSchema, delegateSchema, bashSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, bashToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, DEFAULT_VALID_TOOLS;
7280
7564
  var init_common = __esm({
7281
7565
  "src/tools/common.js"() {
7282
7566
  "use strict";
@@ -7289,11 +7573,12 @@ var init_common = __esm({
7289
7573
  pattern: external_exports.string().describe("AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc."),
7290
7574
  path: external_exports.string().optional().default(".").describe("Path to search in"),
7291
7575
  language: external_exports.string().optional().default("rust").describe("Programming language to use for parsing"),
7292
- allow_tests: external_exports.boolean().optional().default(false).describe("Allow test files in search results")
7576
+ allow_tests: external_exports.boolean().optional().default(true).describe("Allow test files in search results")
7293
7577
  });
7294
7578
  extractSchema = external_exports.object({
7295
7579
  targets: external_exports.string().optional().describe('File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (symbol). Multiple targets separated by spaces.'),
7296
- input_content: external_exports.string().optional().describe("Text content to extract file paths from (alternative to targets)")
7580
+ input_content: external_exports.string().optional().describe("Text content to extract file paths from (alternative to targets)"),
7581
+ allow_tests: external_exports.boolean().optional().default(true).describe("Include test files in extraction results")
7297
7582
  });
7298
7583
  delegateSchema = external_exports.object({
7299
7584
  task: external_exports.string().describe("The task to delegate to a subagent. Be specific about what needs to be accomplished.")
@@ -7420,7 +7705,7 @@ Parameters:
7420
7705
  - pattern: (required) AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc.
7421
7706
  - path: (optional, default: '.') Path to search in.
7422
7707
  - language: (optional, default: 'rust') Programming language to use for parsing.
7423
- - allow_tests: (optional, default: false) Allow test files in search results (true/false).
7708
+ - allow_tests: (optional, default: true) Allow test files in search results (true/false).
7424
7709
  Usage Example:
7425
7710
 
7426
7711
  <examples>
@@ -7445,6 +7730,7 @@ Full file extraction should be the LAST RESORT! Always prefer search.
7445
7730
  Parameters:
7446
7731
  - targets: (required) File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (code block at line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (specific symbol). Multiple targets separated by spaces.
7447
7732
  - input_content: (optional) Text content to extract file paths from (alternative to targets for processing diffs/logs).
7733
+ - allow_tests: (optional, default: true) Include test files in extraction results.
7448
7734
 
7449
7735
  Usage Example:
7450
7736
 
@@ -7481,6 +7767,26 @@ User: Read file inside the dependency
7481
7767
  </extract>
7482
7768
 
7483
7769
  </examples>
7770
+ `;
7771
+ delegateToolDefinition = `
7772
+ ## delegate
7773
+ Description: Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Use this when you recognize that a user's request involves multiple large, distinct components that would benefit from parallel processing or specialized focus. The AI agent should automatically identify opportunities for task separation and use delegation without explicit user instruction.
7774
+
7775
+ Parameters:
7776
+ - task: (required) A complete, self-contained task that can be executed independently by a subagent. Should be specific and focused on one area of expertise.
7777
+
7778
+ Usage Pattern:
7779
+ When the AI agent encounters complex multi-part requests, it should automatically break them down and delegate:
7780
+
7781
+ <delegate>
7782
+ <task>Analyze all authentication and authorization code in the codebase for security vulnerabilities and provide specific remediation recommendations</task>
7783
+ </delegate>
7784
+
7785
+ <delegate>
7786
+ <task>Review database queries and API endpoints for performance bottlenecks and suggest optimization strategies</task>
7787
+ </delegate>
7788
+
7789
+ The agent uses this tool automatically when it identifies that work can be separated into distinct, parallel tasks for more efficient processing.
7484
7790
  `;
7485
7791
  attemptCompletionToolDefinition = `
7486
7792
  ## attempt_completion
@@ -7491,6 +7797,61 @@ Usage Example:
7491
7797
  <attempt_completion>
7492
7798
  I have refactored the search module according to the requirements and verified the tests pass. The module now uses the new BM25 ranking algorithm and has improved error handling.
7493
7799
  </attempt_completion>
7800
+ `;
7801
+ bashToolDefinition = `
7802
+ ## bash
7803
+ Description: Execute bash commands for system exploration and development tasks. This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
7804
+
7805
+ Parameters:
7806
+ - command: (required) The bash command to execute
7807
+ - workingDirectory: (optional) Directory to execute the command in
7808
+ - timeout: (optional) Command timeout in milliseconds
7809
+ - env: (optional) Additional environment variables as an object
7810
+
7811
+ Security: Commands are filtered through allow/deny lists for safety:
7812
+ - Allowed by default: ls, cat, git status, npm list, find, grep, etc.
7813
+ - Denied by default: rm -rf, sudo, npm install, dangerous system commands
7814
+
7815
+ Usage Examples:
7816
+
7817
+ <examples>
7818
+
7819
+ User: What files are in the src directory?
7820
+ <bash>
7821
+ <command>ls -la src/</command>
7822
+ </bash>
7823
+
7824
+ User: Show me the git status
7825
+ <bash>
7826
+ <command>git status</command>
7827
+ </bash>
7828
+
7829
+ User: Find all TypeScript files
7830
+ <bash>
7831
+ <command>find . -name "*.ts" -type f</command>
7832
+ </bash>
7833
+
7834
+ User: Check installed npm packages
7835
+ <bash>
7836
+ <command>npm list --depth=0</command>
7837
+ </bash>
7838
+
7839
+ User: Search for TODO comments in code
7840
+ <bash>
7841
+ <command>grep -r "TODO" src/</command>
7842
+ </bash>
7843
+
7844
+ User: Show recent git commits
7845
+ <bash>
7846
+ <command>git log --oneline -10</command>
7847
+ </bash>
7848
+
7849
+ User: Check system info
7850
+ <bash>
7851
+ <command>uname -a</command>
7852
+ </bash>
7853
+
7854
+ </examples>
7494
7855
  `;
7495
7856
  searchDescription = "Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.";
7496
7857
  queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
@@ -7509,193 +7870,6 @@ I have refactored the search module according to the requirements and verified t
7509
7870
  }
7510
7871
  });
7511
7872
 
7512
- // src/delegate.js
7513
- import { spawn as spawn2 } from "child_process";
7514
- import { randomUUID } from "crypto";
7515
- async function delegate({ task, timeout = 300, debug = false, currentIteration = 0, maxIterations = 30, tracer = null }) {
7516
- if (!task || typeof task !== "string") {
7517
- throw new Error("Task parameter is required and must be a string");
7518
- }
7519
- const sessionId = randomUUID();
7520
- const startTime = Date.now();
7521
- const remainingIterations = Math.max(1, maxIterations - currentIteration);
7522
- if (debug) {
7523
- console.error(`[DELEGATE] Starting delegation session ${sessionId}`);
7524
- console.error(`[DELEGATE] Task: ${task}`);
7525
- console.error(`[DELEGATE] Current iteration: ${currentIteration}/${maxIterations}`);
7526
- console.error(`[DELEGATE] Remaining iterations for subagent: ${remainingIterations}`);
7527
- console.error(`[DELEGATE] Timeout configured: ${timeout} seconds`);
7528
- console.error(`[DELEGATE] Using clean agent environment with code-researcher prompt`);
7529
- }
7530
- try {
7531
- const binaryPath = await getBinaryPath();
7532
- const args = [
7533
- "agent",
7534
- "--task",
7535
- task,
7536
- "--session-id",
7537
- sessionId,
7538
- "--prompt-type",
7539
- "code-researcher",
7540
- // Automatically use default code researcher prompt
7541
- "--no-schema-validation",
7542
- // Automatically disable schema validation
7543
- "--no-mermaid-validation",
7544
- // Automatically disable mermaid validation
7545
- "--max-iterations",
7546
- remainingIterations.toString()
7547
- // Automatically limit to remaining iterations
7548
- ];
7549
- if (debug) {
7550
- args.push("--debug");
7551
- console.error(`[DELEGATE] Using binary at: ${binaryPath}`);
7552
- console.error(`[DELEGATE] Command args: ${args.join(" ")}`);
7553
- }
7554
- return new Promise((resolve5, reject2) => {
7555
- const delegationSpan = tracer ? tracer.createDelegationSpan(sessionId, task) : null;
7556
- const process2 = spawn2(binaryPath, args, {
7557
- stdio: ["pipe", "pipe", "pipe"],
7558
- timeout: timeout * 1e3
7559
- });
7560
- let stdout = "";
7561
- let stderr = "";
7562
- let isResolved = false;
7563
- process2.stdout.on("data", (data) => {
7564
- const chunk = data.toString();
7565
- stdout += chunk;
7566
- if (debug) {
7567
- const preview = createMessagePreview(chunk);
7568
- console.error(`[DELEGATE] stdout chunk received (${chunk.length} chars): ${preview}`);
7569
- }
7570
- });
7571
- process2.stderr.on("data", (data) => {
7572
- const chunk = data.toString();
7573
- stderr += chunk;
7574
- if (debug) {
7575
- const preview = createMessagePreview(chunk);
7576
- console.error(`[DELEGATE] stderr chunk received (${chunk.length} chars): ${preview}`);
7577
- }
7578
- });
7579
- process2.on("close", (code) => {
7580
- if (isResolved) return;
7581
- isResolved = true;
7582
- const duration = Date.now() - startTime;
7583
- if (debug) {
7584
- console.error(`[DELEGATE] Process completed with code ${code} in ${duration}ms`);
7585
- console.error(`[DELEGATE] Duration: ${(duration / 1e3).toFixed(2)}s`);
7586
- console.error(`[DELEGATE] Total stdout: ${stdout.length} chars`);
7587
- console.error(`[DELEGATE] Total stderr: ${stderr.length} chars`);
7588
- }
7589
- if (code === 0) {
7590
- const response = stdout.trim();
7591
- if (!response) {
7592
- if (debug) {
7593
- console.error(`[DELEGATE] Task completed but returned empty response for session ${sessionId}`);
7594
- }
7595
- reject2(new Error("Delegate agent returned empty response"));
7596
- return;
7597
- }
7598
- if (debug) {
7599
- console.error(`[DELEGATE] Task completed successfully for session ${sessionId}`);
7600
- console.error(`[DELEGATE] Response length: ${response.length} chars`);
7601
- }
7602
- if (tracer) {
7603
- tracer.recordDelegationEvent("completed", {
7604
- "delegation.session_id": sessionId,
7605
- "delegation.duration_ms": duration,
7606
- "delegation.response_length": response.length,
7607
- "delegation.success": true
7608
- });
7609
- if (delegationSpan) {
7610
- delegationSpan.setAttributes({
7611
- "delegation.result.success": true,
7612
- "delegation.result.response_length": response.length,
7613
- "delegation.result.duration_ms": duration
7614
- });
7615
- delegationSpan.setStatus({ code: 1 });
7616
- delegationSpan.end();
7617
- }
7618
- }
7619
- resolve5(response);
7620
- } else {
7621
- const errorMessage = stderr.trim() || `Delegate process failed with exit code ${code}`;
7622
- if (debug) {
7623
- console.error(`[DELEGATE] Task failed for session ${sessionId} with code ${code}`);
7624
- console.error(`[DELEGATE] Error message: ${errorMessage}`);
7625
- }
7626
- if (tracer) {
7627
- tracer.recordDelegationEvent("failed", {
7628
- "delegation.session_id": sessionId,
7629
- "delegation.duration_ms": duration,
7630
- "delegation.exit_code": code,
7631
- "delegation.error_message": errorMessage,
7632
- "delegation.success": false
7633
- });
7634
- if (delegationSpan) {
7635
- delegationSpan.setAttributes({
7636
- "delegation.result.success": false,
7637
- "delegation.result.exit_code": code,
7638
- "delegation.result.error": errorMessage,
7639
- "delegation.result.duration_ms": duration
7640
- });
7641
- delegationSpan.setStatus({ code: 2, message: errorMessage });
7642
- delegationSpan.end();
7643
- }
7644
- }
7645
- reject2(new Error(`Delegation failed: ${errorMessage}`));
7646
- }
7647
- });
7648
- process2.on("error", (error) => {
7649
- if (isResolved) return;
7650
- isResolved = true;
7651
- const duration = Date.now() - startTime;
7652
- if (debug) {
7653
- console.error(`[DELEGATE] Process spawn error after ${duration}ms:`, error);
7654
- console.error(`[DELEGATE] Session ${sessionId} failed during process creation`);
7655
- console.error(`[DELEGATE] Error type: ${error.code || "unknown"}`);
7656
- }
7657
- reject2(new Error(`Failed to start delegate process: ${error.message}`));
7658
- });
7659
- setTimeout(() => {
7660
- if (isResolved) return;
7661
- isResolved = true;
7662
- const duration = Date.now() - startTime;
7663
- if (debug) {
7664
- console.error(`[DELEGATE] Process timeout after ${(duration / 1e3).toFixed(2)}s (limit: ${timeout}s)`);
7665
- console.error(`[DELEGATE] Terminating session ${sessionId} due to timeout`);
7666
- console.error(`[DELEGATE] Partial stdout: ${stdout.substring(0, 500)}${stdout.length > 500 ? "..." : ""}`);
7667
- console.error(`[DELEGATE] Partial stderr: ${stderr.substring(0, 500)}${stderr.length > 500 ? "..." : ""}`);
7668
- }
7669
- process2.kill("SIGTERM");
7670
- setTimeout(() => {
7671
- if (!process2.killed) {
7672
- if (debug) {
7673
- console.error(`[DELEGATE] Force killing process ${sessionId} after graceful timeout`);
7674
- }
7675
- process2.kill("SIGKILL");
7676
- }
7677
- }, 5e3);
7678
- reject2(new Error(`Delegation timed out after ${timeout} seconds`));
7679
- }, timeout * 1e3);
7680
- });
7681
- } catch (error) {
7682
- const duration = Date.now() - startTime;
7683
- if (debug) {
7684
- console.error(`[DELEGATE] Error in delegate function after ${duration}ms:`, error);
7685
- console.error(`[DELEGATE] Session ${sessionId} failed during setup`);
7686
- console.error(`[DELEGATE] Error stack: ${error.stack}`);
7687
- }
7688
- throw new Error(`Delegation setup failed: ${error.message}`);
7689
- }
7690
- }
7691
- var init_delegate = __esm({
7692
- "src/delegate.js"() {
7693
- "use strict";
7694
- init_utils();
7695
- init_common();
7696
- }
7697
- });
7698
-
7699
7873
  // src/tools/vercel.js
7700
7874
  import { tool } from "ai";
7701
7875
  var searchTool, queryTool, extractTool, delegateTool;
@@ -7868,21 +8042,50 @@ var init_vercel = __esm({
7868
8042
  name: "delegate",
7869
8043
  description: delegateDescription,
7870
8044
  inputSchema: delegateSchema,
7871
- execute: async ({ task }) => {
7872
- try {
7873
- if (debug) {
7874
- console.error(`Executing delegate with task: "${task}"`);
7875
- }
7876
- const result = await delegate({
7877
- task,
7878
- timeout,
7879
- debug
7880
- });
7881
- return result;
7882
- } catch (error) {
7883
- console.error("Error executing delegate command:", error);
7884
- return `Error executing delegate command: ${error.message}`;
8045
+ execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path7, provider, model, tracer }) => {
8046
+ if (!task || typeof task !== "string") {
8047
+ throw new Error("Task parameter is required and must be a non-empty string");
8048
+ }
8049
+ if (task.trim().length === 0) {
8050
+ throw new Error("Task parameter cannot be empty or whitespace only");
8051
+ }
8052
+ if (currentIteration !== void 0 && (typeof currentIteration !== "number" || currentIteration < 0)) {
8053
+ throw new Error("currentIteration must be a non-negative number");
8054
+ }
8055
+ if (maxIterations !== void 0 && (typeof maxIterations !== "number" || maxIterations < 1)) {
8056
+ throw new Error("maxIterations must be a positive number");
8057
+ }
8058
+ if (parentSessionId !== void 0 && parentSessionId !== null && typeof parentSessionId !== "string") {
8059
+ throw new TypeError("parentSessionId must be a string, null, or undefined");
7885
8060
  }
8061
+ if (path7 !== void 0 && path7 !== null && typeof path7 !== "string") {
8062
+ throw new TypeError("path must be a string, null, or undefined");
8063
+ }
8064
+ if (provider !== void 0 && provider !== null && typeof provider !== "string") {
8065
+ throw new TypeError("provider must be a string, null, or undefined");
8066
+ }
8067
+ if (model !== void 0 && model !== null && typeof model !== "string") {
8068
+ throw new TypeError("model must be a string, null, or undefined");
8069
+ }
8070
+ if (debug) {
8071
+ console.error(`Executing delegate with task: "${task.substring(0, 100)}${task.length > 100 ? "..." : ""}"`);
8072
+ if (parentSessionId) {
8073
+ console.error(`Parent session: ${parentSessionId}`);
8074
+ }
8075
+ }
8076
+ const result = await delegate({
8077
+ task,
8078
+ timeout,
8079
+ debug,
8080
+ currentIteration: currentIteration || 0,
8081
+ maxIterations: maxIterations || 30,
8082
+ parentSessionId,
8083
+ path: path7,
8084
+ provider,
8085
+ model,
8086
+ tracer
8087
+ });
8088
+ return result;
7886
8089
  }
7887
8090
  });
7888
8091
  };
@@ -8711,7 +8914,7 @@ var init_bashPermissions = __esm({
8711
8914
  });
8712
8915
 
8713
8916
  // src/agent/bashExecutor.js
8714
- import { spawn as spawn3 } from "child_process";
8917
+ import { spawn as spawn2 } from "child_process";
8715
8918
  import { resolve, join } from "path";
8716
8919
  import { existsSync } from "fs";
8717
8920
  async function executeBashCommand(command, options = {}) {
@@ -8748,14 +8951,14 @@ async function executeBashCommand(command, options = {}) {
8748
8951
  console.log(`[BashExecutor] Working directory: "${cwd}"`);
8749
8952
  console.log(`[BashExecutor] Timeout: ${timeout}ms`);
8750
8953
  }
8751
- return new Promise((resolve5, reject2) => {
8954
+ return new Promise((resolve6, reject2) => {
8752
8955
  const processEnv = {
8753
8956
  ...process.env,
8754
8957
  ...env
8755
8958
  };
8756
8959
  const args = parseCommandForExecution(command);
8757
8960
  if (!args || args.length === 0) {
8758
- resolve5({
8961
+ resolve6({
8759
8962
  success: false,
8760
8963
  error: "Failed to parse command",
8761
8964
  stdout: "",
@@ -8768,7 +8971,7 @@ async function executeBashCommand(command, options = {}) {
8768
8971
  return;
8769
8972
  }
8770
8973
  const [cmd, ...cmdArgs] = args;
8771
- const child = spawn3(cmd, cmdArgs, {
8974
+ const child = spawn2(cmd, cmdArgs, {
8772
8975
  cwd,
8773
8976
  env: processEnv,
8774
8977
  stdio: ["ignore", "pipe", "pipe"],
@@ -8838,7 +9041,7 @@ async function executeBashCommand(command, options = {}) {
8838
9041
  success = false;
8839
9042
  error = `Command exited with code ${code}`;
8840
9043
  }
8841
- resolve5({
9044
+ resolve6({
8842
9045
  success,
8843
9046
  error,
8844
9047
  stdout: stdout.trim(),
@@ -8858,7 +9061,7 @@ async function executeBashCommand(command, options = {}) {
8858
9061
  if (debug) {
8859
9062
  console.log(`[BashExecutor] Spawn error:`, error);
8860
9063
  }
8861
- resolve5({
9064
+ resolve6({
8862
9065
  success: false,
8863
9066
  error: `Failed to execute command: ${error.message}`,
8864
9067
  stdout: "",
@@ -9130,6 +9333,283 @@ Command failed with exit code ${result.exitCode}`;
9130
9333
  }
9131
9334
  });
9132
9335
 
9336
+ // src/tools/edit.js
9337
+ import { tool as tool3 } from "ai";
9338
+ import { promises as fs4 } from "fs";
9339
+ import { dirname, resolve as resolve3, isAbsolute, sep } from "path";
9340
+ import { existsSync as existsSync2 } from "fs";
9341
+ function isPathAllowed(filePath, allowedFolders) {
9342
+ if (!allowedFolders || allowedFolders.length === 0) {
9343
+ const resolvedPath2 = resolve3(filePath);
9344
+ const cwd = resolve3(process.cwd());
9345
+ return resolvedPath2 === cwd || resolvedPath2.startsWith(cwd + sep);
9346
+ }
9347
+ const resolvedPath = resolve3(filePath);
9348
+ return allowedFolders.some((folder) => {
9349
+ const allowedPath = resolve3(folder);
9350
+ return resolvedPath === allowedPath || resolvedPath.startsWith(allowedPath + sep);
9351
+ });
9352
+ }
9353
+ function parseFileToolOptions(options = {}) {
9354
+ return {
9355
+ debug: options.debug || false,
9356
+ allowedFolders: options.allowedFolders || [],
9357
+ defaultPath: options.defaultPath
9358
+ };
9359
+ }
9360
+ var editTool, createTool, editDescription, createDescription, editToolDefinition, createToolDefinition;
9361
+ var init_edit = __esm({
9362
+ "src/tools/edit.js"() {
9363
+ "use strict";
9364
+ editTool = (options = {}) => {
9365
+ const { debug, allowedFolders, defaultPath } = parseFileToolOptions(options);
9366
+ return tool3({
9367
+ name: "edit",
9368
+ description: `Edit files using exact string replacement (Claude Code style).
9369
+
9370
+ This tool performs exact string replacements in files. It requires the old_string to match exactly what's in the file, including all whitespace and indentation.
9371
+
9372
+ Parameters:
9373
+ - file_path: Path to the file to edit (absolute or relative)
9374
+ - old_string: Exact text to find and replace (must be unique in the file unless replace_all is true)
9375
+ - new_string: Text to replace with
9376
+ - replace_all: (optional) Replace all occurrences instead of requiring uniqueness
9377
+
9378
+ Important:
9379
+ - The old_string must match EXACTLY including whitespace
9380
+ - If old_string appears multiple times and replace_all is false, the edit will fail
9381
+ - Use larger context around the string to ensure uniqueness when needed`,
9382
+ inputSchema: {
9383
+ type: "object",
9384
+ properties: {
9385
+ file_path: {
9386
+ type: "string",
9387
+ description: "Path to the file to edit"
9388
+ },
9389
+ old_string: {
9390
+ type: "string",
9391
+ description: "Exact text to find and replace"
9392
+ },
9393
+ new_string: {
9394
+ type: "string",
9395
+ description: "Text to replace with"
9396
+ },
9397
+ replace_all: {
9398
+ type: "boolean",
9399
+ description: "Replace all occurrences (default: false)",
9400
+ default: false
9401
+ }
9402
+ },
9403
+ required: ["file_path", "old_string", "new_string"]
9404
+ },
9405
+ execute: async ({ file_path, old_string, new_string, replace_all = false }) => {
9406
+ try {
9407
+ if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
9408
+ return `Error editing file: Invalid file_path - must be a non-empty string`;
9409
+ }
9410
+ if (old_string === void 0 || old_string === null || typeof old_string !== "string") {
9411
+ return `Error editing file: Invalid old_string - must be a string`;
9412
+ }
9413
+ if (new_string === void 0 || new_string === null || typeof new_string !== "string") {
9414
+ return `Error editing file: Invalid new_string - must be a string`;
9415
+ }
9416
+ const resolvedPath = isAbsolute(file_path) ? file_path : resolve3(defaultPath || process.cwd(), file_path);
9417
+ if (debug) {
9418
+ console.error(`[Edit] Attempting to edit file: ${resolvedPath}`);
9419
+ }
9420
+ if (!isPathAllowed(resolvedPath, allowedFolders)) {
9421
+ return `Error editing file: Permission denied - ${file_path} is outside allowed directories`;
9422
+ }
9423
+ if (!existsSync2(resolvedPath)) {
9424
+ return `Error editing file: File not found - ${file_path}`;
9425
+ }
9426
+ const content = await fs4.readFile(resolvedPath, "utf-8");
9427
+ if (!content.includes(old_string)) {
9428
+ return `Error editing file: String not found - the specified old_string was not found in ${file_path}`;
9429
+ }
9430
+ const occurrences = content.split(old_string).length - 1;
9431
+ if (!replace_all && occurrences > 1) {
9432
+ return `Error editing file: Multiple occurrences found - the old_string appears ${occurrences} times. Use replace_all: true to replace all occurrences, or provide more context to make the string unique.`;
9433
+ }
9434
+ let newContent;
9435
+ if (replace_all) {
9436
+ newContent = content.replaceAll(old_string, new_string);
9437
+ } else {
9438
+ newContent = content.replace(old_string, new_string);
9439
+ }
9440
+ if (newContent === content) {
9441
+ return `Error editing file: No changes made - old_string and new_string might be the same`;
9442
+ }
9443
+ await fs4.writeFile(resolvedPath, newContent, "utf-8");
9444
+ const replacedCount = replace_all ? occurrences : 1;
9445
+ if (debug) {
9446
+ console.error(`[Edit] Successfully edited ${resolvedPath}, replaced ${replacedCount} occurrence(s)`);
9447
+ }
9448
+ return `Successfully edited ${file_path} (${replacedCount} replacement${replacedCount !== 1 ? "s" : ""})`;
9449
+ } catch (error) {
9450
+ console.error("[Edit] Error:", error);
9451
+ return `Error editing file: ${error.message}`;
9452
+ }
9453
+ }
9454
+ });
9455
+ };
9456
+ createTool = (options = {}) => {
9457
+ const { debug, allowedFolders, defaultPath } = parseFileToolOptions(options);
9458
+ return tool3({
9459
+ name: "create",
9460
+ description: `Create new files with specified content.
9461
+
9462
+ This tool creates new files in the filesystem. It will create parent directories if they don't exist.
9463
+
9464
+ Parameters:
9465
+ - file_path: Path where the file should be created (absolute or relative)
9466
+ - content: Content to write to the file
9467
+ - overwrite: (optional) Whether to overwrite if file exists (default: false)
9468
+
9469
+ Important:
9470
+ - By default, will fail if the file already exists
9471
+ - Set overwrite: true to replace existing files
9472
+ - Parent directories will be created automatically if needed`,
9473
+ inputSchema: {
9474
+ type: "object",
9475
+ properties: {
9476
+ file_path: {
9477
+ type: "string",
9478
+ description: "Path where the file should be created"
9479
+ },
9480
+ content: {
9481
+ type: "string",
9482
+ description: "Content to write to the file"
9483
+ },
9484
+ overwrite: {
9485
+ type: "boolean",
9486
+ description: "Overwrite if file exists (default: false)",
9487
+ default: false
9488
+ }
9489
+ },
9490
+ required: ["file_path", "content"]
9491
+ },
9492
+ execute: async ({ file_path, content, overwrite = false }) => {
9493
+ try {
9494
+ if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
9495
+ return `Error creating file: Invalid file_path - must be a non-empty string`;
9496
+ }
9497
+ if (content === void 0 || content === null || typeof content !== "string") {
9498
+ return `Error creating file: Invalid content - must be a string`;
9499
+ }
9500
+ const resolvedPath = isAbsolute(file_path) ? file_path : resolve3(defaultPath || process.cwd(), file_path);
9501
+ if (debug) {
9502
+ console.error(`[Create] Attempting to create file: ${resolvedPath}`);
9503
+ }
9504
+ if (!isPathAllowed(resolvedPath, allowedFolders)) {
9505
+ return `Error creating file: Permission denied - ${file_path} is outside allowed directories`;
9506
+ }
9507
+ if (existsSync2(resolvedPath) && !overwrite) {
9508
+ return `Error creating file: File already exists - ${file_path}. Use overwrite: true to replace it.`;
9509
+ }
9510
+ const dir = dirname(resolvedPath);
9511
+ await fs4.mkdir(dir, { recursive: true });
9512
+ await fs4.writeFile(resolvedPath, content, "utf-8");
9513
+ const action = existsSync2(resolvedPath) && overwrite ? "overwrote" : "created";
9514
+ const bytes = Buffer.byteLength(content, "utf-8");
9515
+ if (debug) {
9516
+ console.error(`[Create] Successfully ${action} ${resolvedPath}`);
9517
+ }
9518
+ return `Successfully ${action} ${file_path} (${bytes} bytes)`;
9519
+ } catch (error) {
9520
+ console.error("[Create] Error:", error);
9521
+ return `Error creating file: ${error.message}`;
9522
+ }
9523
+ }
9524
+ });
9525
+ };
9526
+ editDescription = "Edit files using exact string replacement. Requires exact match including whitespace.";
9527
+ createDescription = "Create new files with specified content. Will create parent directories if needed.";
9528
+ editToolDefinition = `
9529
+ ## edit
9530
+ Description: ${editDescription}
9531
+
9532
+ When to use:
9533
+ - For precise, surgical edits to existing files
9534
+ - When you need to change specific lines or blocks of code
9535
+ - For renaming functions, variables, or updating configuration values
9536
+ - When the exact text to replace is known and unique (or use replace_all for multiple occurrences)
9537
+
9538
+ When NOT to use:
9539
+ - For creating new files (use 'create' tool instead)
9540
+ - When you cannot determine the exact text to replace
9541
+ - When changes span multiple locations that would be better handled together
9542
+
9543
+ Parameters:
9544
+ - file_path: (required) Path to the file to edit
9545
+ - old_string: (required) Exact text to find and replace (must match including whitespace, newlines, and indentation)
9546
+ - new_string: (required) Text to replace with
9547
+ - replace_all: (optional, default: false) Replace all occurrences if the string appears multiple times
9548
+
9549
+ Important notes:
9550
+ - The old_string MUST match EXACTLY, including all whitespace, indentation, and line breaks
9551
+ - If old_string appears multiple times and replace_all is false, the tool will fail
9552
+ - Always verify the exact formatting of the text you want to replace
9553
+
9554
+ Examples:
9555
+ <edit>
9556
+ <file_path>src/main.js</file_path>
9557
+ <old_string>function oldName() {
9558
+ return 42;
9559
+ }</old_string>
9560
+ <new_string>function newName() {
9561
+ return 42;
9562
+ }</new_string>
9563
+ </edit>
9564
+
9565
+ <edit>
9566
+ <file_path>config.json</file_path>
9567
+ <old_string>"debug": false</old_string>
9568
+ <new_string>"debug": true</new_string>
9569
+ <replace_all>true</replace_all>
9570
+ </edit>`;
9571
+ createToolDefinition = `
9572
+ ## create
9573
+ Description: ${createDescription}
9574
+
9575
+ When to use:
9576
+ - For creating brand new files from scratch
9577
+ - When you need to add configuration files, documentation, or new modules
9578
+ - For generating boilerplate code or templates
9579
+ - When you have the complete content ready to write
9580
+
9581
+ When NOT to use:
9582
+ - For editing existing files (use 'edit' tool instead)
9583
+ - When a file already exists unless you explicitly want to overwrite it
9584
+
9585
+ Parameters:
9586
+ - file_path: (required) Path where the file should be created
9587
+ - content: (required) Complete content to write to the file
9588
+ - overwrite: (optional, default: false) Whether to overwrite if file already exists
9589
+
9590
+ Important notes:
9591
+ - Parent directories will be created automatically if they don't exist
9592
+ - The tool will fail if the file already exists and overwrite is false
9593
+ - Be careful with the overwrite option as it completely replaces existing files
9594
+
9595
+ Examples:
9596
+ <create>
9597
+ <file_path>src/newFile.js</file_path>
9598
+ <content>export function hello() {
9599
+ return "Hello, world!";
9600
+ }</content>
9601
+ </create>
9602
+
9603
+ <create>
9604
+ <file_path>README.md</file_path>
9605
+ <content># My Project
9606
+
9607
+ This is a new project.</content>
9608
+ <overwrite>true</overwrite>
9609
+ </create>`;
9610
+ }
9611
+ });
9612
+
9133
9613
  // src/tools/langchain.js
9134
9614
  var init_langchain = __esm({
9135
9615
  "src/tools/langchain.js"() {
@@ -9279,8 +9759,10 @@ var init_tools = __esm({
9279
9759
  "use strict";
9280
9760
  init_vercel();
9281
9761
  init_bash();
9762
+ init_edit();
9282
9763
  init_langchain();
9283
9764
  init_common();
9765
+ init_edit();
9284
9766
  init_system_message();
9285
9767
  init_vercel();
9286
9768
  init_bash();
@@ -9297,7 +9779,7 @@ var init_tools = __esm({
9297
9779
  });
9298
9780
 
9299
9781
  // src/utils/file-lister.js
9300
- import fs4 from "fs";
9782
+ import fs5 from "fs";
9301
9783
  import path4 from "path";
9302
9784
  import { promisify as promisify6 } from "util";
9303
9785
  import { exec as exec4 } from "child_process";
@@ -9307,10 +9789,10 @@ async function listFilesByLevel(options) {
9307
9789
  maxFiles = 100,
9308
9790
  respectGitignore = true
9309
9791
  } = options;
9310
- if (!fs4.existsSync(directory)) {
9792
+ if (!fs5.existsSync(directory)) {
9311
9793
  throw new Error(`Directory does not exist: ${directory}`);
9312
9794
  }
9313
- const gitDirExists = fs4.existsSync(path4.join(directory, ".git"));
9795
+ const gitDirExists = fs5.existsSync(path4.join(directory, ".git"));
9314
9796
  if (gitDirExists && respectGitignore) {
9315
9797
  try {
9316
9798
  return await listFilesUsingGit(directory, maxFiles);
@@ -9341,7 +9823,7 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
9341
9823
  while (queue.length > 0 && result.length < maxFiles) {
9342
9824
  const { dir, level } = queue.shift();
9343
9825
  try {
9344
- const entries = fs4.readdirSync(dir, { withFileTypes: true });
9826
+ const entries = fs5.readdirSync(dir, { withFileTypes: true });
9345
9827
  const files = entries.filter((entry) => entry.isFile());
9346
9828
  for (const file of files) {
9347
9829
  if (result.length >= maxFiles) break;
@@ -9366,11 +9848,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
9366
9848
  }
9367
9849
  function loadGitignorePatterns(directory) {
9368
9850
  const gitignorePath = path4.join(directory, ".gitignore");
9369
- if (!fs4.existsSync(gitignorePath)) {
9851
+ if (!fs5.existsSync(gitignorePath)) {
9370
9852
  return [];
9371
9853
  }
9372
9854
  try {
9373
- const content = fs4.readFileSync(gitignorePath, "utf8");
9855
+ const content = fs5.readFileSync(gitignorePath, "utf8");
9374
9856
  return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
9375
9857
  } catch (error) {
9376
9858
  console.error(`Warning: Could not read .gitignore: ${error.message}`);
@@ -9397,8 +9879,8 @@ var init_file_lister = __esm({
9397
9879
  });
9398
9880
 
9399
9881
  // src/agent/simpleTelemetry.js
9400
- import { existsSync as existsSync2, mkdirSync, createWriteStream } from "fs";
9401
- import { dirname } from "path";
9882
+ import { existsSync as existsSync3, mkdirSync, createWriteStream } from "fs";
9883
+ import { dirname as dirname2 } from "path";
9402
9884
  function initializeSimpleTelemetryFromOptions(options) {
9403
9885
  const telemetry = new SimpleTelemetry({
9404
9886
  serviceName: "probe-agent",
@@ -9425,8 +9907,8 @@ var init_simpleTelemetry = __esm({
9425
9907
  }
9426
9908
  initializeFileExporter() {
9427
9909
  try {
9428
- const dir = dirname(this.filePath);
9429
- if (!existsSync2(dir)) {
9910
+ const dir = dirname2(this.filePath);
9911
+ if (!existsSync3(dir)) {
9430
9912
  mkdirSync(dir, { recursive: true });
9431
9913
  }
9432
9914
  this.stream = createWriteStream(this.filePath, { flags: "a" });
@@ -9490,20 +9972,20 @@ var init_simpleTelemetry = __esm({
9490
9972
  }
9491
9973
  async flush() {
9492
9974
  if (this.stream) {
9493
- return new Promise((resolve5) => {
9494
- this.stream.once("drain", resolve5);
9975
+ return new Promise((resolve6) => {
9976
+ this.stream.once("drain", resolve6);
9495
9977
  if (!this.stream.writableNeedDrain) {
9496
- resolve5();
9978
+ resolve6();
9497
9979
  }
9498
9980
  });
9499
9981
  }
9500
9982
  }
9501
9983
  async shutdown() {
9502
9984
  if (this.stream) {
9503
- return new Promise((resolve5) => {
9985
+ return new Promise((resolve6) => {
9504
9986
  this.stream.end(() => {
9505
9987
  console.log(`[SimpleTelemetry] File stream closed: ${this.filePath}`);
9506
- resolve5();
9988
+ resolve6();
9507
9989
  });
9508
9990
  });
9509
9991
  }
@@ -10466,7 +10948,7 @@ var init_escape = __esm({
10466
10948
  });
10467
10949
 
10468
10950
  // node_modules/minimatch/dist/esm/index.js
10469
- var import_brace_expansion, minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path5, sep, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
10951
+ var import_brace_expansion, minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path5, sep2, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
10470
10952
  var init_esm = __esm({
10471
10953
  "node_modules/minimatch/dist/esm/index.js"() {
10472
10954
  import_brace_expansion = __toESM(require_brace_expansion(), 1);
@@ -10539,8 +11021,8 @@ var init_esm = __esm({
10539
11021
  win32: { sep: "\\" },
10540
11022
  posix: { sep: "/" }
10541
11023
  };
10542
- sep = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
10543
- minimatch.sep = sep;
11024
+ sep2 = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
11025
+ minimatch.sep = sep2;
10544
11026
  GLOBSTAR = Symbol("globstar **");
10545
11027
  minimatch.GLOBSTAR = GLOBSTAR;
10546
11028
  qmark2 = "[^/]";
@@ -13302,10 +13784,10 @@ var init_esm3 = __esm({
13302
13784
  * Return a void Promise that resolves once the stream ends.
13303
13785
  */
13304
13786
  async promise() {
13305
- return new Promise((resolve5, reject2) => {
13787
+ return new Promise((resolve6, reject2) => {
13306
13788
  this.on(DESTROYED, () => reject2(new Error("stream destroyed")));
13307
13789
  this.on("error", (er) => reject2(er));
13308
- this.on("end", () => resolve5());
13790
+ this.on("end", () => resolve6());
13309
13791
  });
13310
13792
  }
13311
13793
  /**
@@ -13329,7 +13811,7 @@ var init_esm3 = __esm({
13329
13811
  return Promise.resolve({ done: false, value: res });
13330
13812
  if (this[EOF])
13331
13813
  return stop();
13332
- let resolve5;
13814
+ let resolve6;
13333
13815
  let reject2;
13334
13816
  const onerr = (er) => {
13335
13817
  this.off("data", ondata);
@@ -13343,19 +13825,19 @@ var init_esm3 = __esm({
13343
13825
  this.off("end", onend);
13344
13826
  this.off(DESTROYED, ondestroy);
13345
13827
  this.pause();
13346
- resolve5({ value, done: !!this[EOF] });
13828
+ resolve6({ value, done: !!this[EOF] });
13347
13829
  };
13348
13830
  const onend = () => {
13349
13831
  this.off("error", onerr);
13350
13832
  this.off("data", ondata);
13351
13833
  this.off(DESTROYED, ondestroy);
13352
13834
  stop();
13353
- resolve5({ done: true, value: void 0 });
13835
+ resolve6({ done: true, value: void 0 });
13354
13836
  };
13355
13837
  const ondestroy = () => onerr(new Error("stream destroyed"));
13356
13838
  return new Promise((res2, rej) => {
13357
13839
  reject2 = rej;
13358
- resolve5 = res2;
13840
+ resolve6 = res2;
13359
13841
  this.once(DESTROYED, ondestroy);
13360
13842
  this.once("error", onerr);
13361
13843
  this.once("end", onend);
@@ -14335,9 +14817,9 @@ var init_esm4 = __esm({
14335
14817
  if (this.#asyncReaddirInFlight) {
14336
14818
  await this.#asyncReaddirInFlight;
14337
14819
  } else {
14338
- let resolve5 = () => {
14820
+ let resolve6 = () => {
14339
14821
  };
14340
- this.#asyncReaddirInFlight = new Promise((res) => resolve5 = res);
14822
+ this.#asyncReaddirInFlight = new Promise((res) => resolve6 = res);
14341
14823
  try {
14342
14824
  for (const e of await this.#fs.promises.readdir(fullpath, {
14343
14825
  withFileTypes: true
@@ -14350,7 +14832,7 @@ var init_esm4 = __esm({
14350
14832
  children.provisional = 0;
14351
14833
  }
14352
14834
  this.#asyncReaddirInFlight = void 0;
14353
- resolve5();
14835
+ resolve6();
14354
14836
  }
14355
14837
  return children.slice(0, children.provisional);
14356
14838
  }
@@ -14580,8 +15062,8 @@ var init_esm4 = __esm({
14580
15062
  *
14581
15063
  * @internal
14582
15064
  */
14583
- constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs6 = defaultFS } = {}) {
14584
- this.#fs = fsFromOption(fs6);
15065
+ constructor(cwd = process.cwd(), pathImpl, sep3, { nocase, childrenCacheSize = 16 * 1024, fs: fs7 = defaultFS } = {}) {
15066
+ this.#fs = fsFromOption(fs7);
14585
15067
  if (cwd instanceof URL || cwd.startsWith("file://")) {
14586
15068
  cwd = fileURLToPath4(cwd);
14587
15069
  }
@@ -14591,7 +15073,7 @@ var init_esm4 = __esm({
14591
15073
  this.#resolveCache = new ResolveCache();
14592
15074
  this.#resolvePosixCache = new ResolveCache();
14593
15075
  this.#children = new ChildrenCache(childrenCacheSize);
14594
- const split = cwdPath.substring(this.rootPath.length).split(sep2);
15076
+ const split = cwdPath.substring(this.rootPath.length).split(sep3);
14595
15077
  if (split.length === 1 && !split[0]) {
14596
15078
  split.pop();
14597
15079
  }
@@ -15139,8 +15621,8 @@ var init_esm4 = __esm({
15139
15621
  /**
15140
15622
  * @internal
15141
15623
  */
15142
- newRoot(fs6) {
15143
- return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs6 });
15624
+ newRoot(fs7) {
15625
+ return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs7 });
15144
15626
  }
15145
15627
  /**
15146
15628
  * Return true if the provided path string is an absolute path
@@ -15168,8 +15650,8 @@ var init_esm4 = __esm({
15168
15650
  /**
15169
15651
  * @internal
15170
15652
  */
15171
- newRoot(fs6) {
15172
- return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs6 });
15653
+ newRoot(fs7) {
15654
+ return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs7 });
15173
15655
  }
15174
15656
  /**
15175
15657
  * Return true if the provided path string is an absolute path
@@ -16309,7 +16791,7 @@ import { exec as exec5 } from "child_process";
16309
16791
  import { promisify as promisify7 } from "util";
16310
16792
  import { randomUUID as randomUUID2 } from "crypto";
16311
16793
  import { EventEmitter as EventEmitter2 } from "events";
16312
- import fs5 from "fs";
16794
+ import fs6 from "fs";
16313
16795
  import { promises as fsPromises } from "fs";
16314
16796
  import path6 from "path";
16315
16797
  function isSessionCancelled(sessionId) {
@@ -16369,6 +16851,20 @@ function createWrappedTools(baseTools) {
16369
16851
  baseTools.bashTool.execute
16370
16852
  );
16371
16853
  }
16854
+ if (baseTools.editTool) {
16855
+ wrappedTools.editToolInstance = wrapToolWithEmitter(
16856
+ baseTools.editTool,
16857
+ "edit",
16858
+ baseTools.editTool.execute
16859
+ );
16860
+ }
16861
+ if (baseTools.createTool) {
16862
+ wrappedTools.createToolInstance = wrapToolWithEmitter(
16863
+ baseTools.createTool,
16864
+ "create",
16865
+ baseTools.createTool.execute
16866
+ );
16867
+ }
16372
16868
  return wrappedTools;
16373
16869
  }
16374
16870
  var toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
@@ -16379,9 +16875,9 @@ var init_probeTool = __esm({
16379
16875
  init_esm5();
16380
16876
  toolCallEmitter = new EventEmitter2();
16381
16877
  activeToolExecutions = /* @__PURE__ */ new Map();
16382
- wrapToolWithEmitter = (tool3, toolName, baseExecute) => {
16878
+ wrapToolWithEmitter = (tool4, toolName, baseExecute) => {
16383
16879
  return {
16384
- ...tool3,
16880
+ ...tool4,
16385
16881
  // Spread schema, description etc.
16386
16882
  execute: async (params) => {
16387
16883
  const debug = process.env.DEBUG === "1";
@@ -16616,8 +17112,10 @@ var init_index = __esm({
16616
17112
  init_file_lister();
16617
17113
  init_system_message();
16618
17114
  init_common();
17115
+ init_edit();
16619
17116
  init_vercel();
16620
17117
  init_bash();
17118
+ init_edit();
16621
17119
  init_ProbeAgent();
16622
17120
  init_simpleTelemetry();
16623
17121
  init_probeTool();
@@ -16699,8 +17197,8 @@ function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
16699
17197
  function hasOtherToolTags(xmlString, validTools = []) {
16700
17198
  const defaultTools = ["search", "query", "extract", "listFiles", "searchFiles", "implement", "attempt_completion"];
16701
17199
  const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
16702
- for (const tool3 of toolsToCheck) {
16703
- if (tool3 !== "attempt_completion" && xmlString.includes(`<${tool3}`)) {
17200
+ for (const tool4 of toolsToCheck) {
17201
+ if (tool4 !== "attempt_completion" && xmlString.includes(`<${tool4}`)) {
16704
17202
  return true;
16705
17203
  }
16706
17204
  }
@@ -16738,6 +17236,10 @@ function createTools(configOptions) {
16738
17236
  if (configOptions.enableBash) {
16739
17237
  tools2.bashTool = bashTool(configOptions);
16740
17238
  }
17239
+ if (configOptions.allowEdit) {
17240
+ tools2.editTool = editTool(configOptions);
17241
+ tools2.createTool = createTool(configOptions);
17242
+ }
16741
17243
  return tools2;
16742
17244
  }
16743
17245
  function parseXmlToolCallWithThinking(xmlString, validTools) {
@@ -16838,7 +17340,7 @@ function createMockProvider() {
16838
17340
  provider: "mock",
16839
17341
  // Mock the doGenerate method used by Vercel AI SDK
16840
17342
  doGenerate: async ({ messages, tools: tools2 }) => {
16841
- await new Promise((resolve5) => setTimeout(resolve5, 10));
17343
+ await new Promise((resolve6) => setTimeout(resolve6, 10));
16842
17344
  return {
16843
17345
  text: "This is a mock response for testing",
16844
17346
  toolCalls: [],
@@ -28825,16 +29327,6 @@ var init_parser2 = __esm({
28825
29327
  this.subgraph = this.RULE("subgraph", () => {
28826
29328
  this.CONSUME(SubgraphKeyword);
28827
29329
  this.OR([
28828
- {
28829
- ALT: () => {
28830
- this.CONSUME(Identifier, { LABEL: "subgraphId" });
28831
- this.OPTION(() => {
28832
- this.CONSUME1(SquareOpen);
28833
- this.SUBRULE(this.nodeContent);
28834
- this.CONSUME1(SquareClose);
28835
- });
28836
- }
28837
- },
28838
29330
  {
28839
29331
  ALT: () => {
28840
29332
  this.CONSUME(QuotedString, { LABEL: "subgraphTitleQ" });
@@ -28846,6 +29338,33 @@ var init_parser2 = __esm({
28846
29338
  this.SUBRULE2(this.nodeContent);
28847
29339
  this.CONSUME2(SquareClose);
28848
29340
  }
29341
+ },
29342
+ {
29343
+ ALT: () => {
29344
+ this.CONSUME1(Identifier, { LABEL: "subgraphIdOrFirstWord" });
29345
+ this.OPTION(() => {
29346
+ this.OR1([
29347
+ {
29348
+ ALT: () => {
29349
+ this.CONSUME1(SquareOpen);
29350
+ this.SUBRULE(this.nodeContent);
29351
+ this.CONSUME1(SquareClose);
29352
+ }
29353
+ },
29354
+ {
29355
+ ALT: () => {
29356
+ this.AT_LEAST_ONE(() => {
29357
+ this.OR2([
29358
+ { ALT: () => this.CONSUME2(Identifier) },
29359
+ { ALT: () => this.CONSUME(Text) },
29360
+ { ALT: () => this.CONSUME(NumberLiteral) }
29361
+ ]);
29362
+ });
29363
+ }
29364
+ }
29365
+ ]);
29366
+ });
29367
+ }
28849
29368
  }
28850
29369
  ]);
28851
29370
  this.CONSUME(Newline);
@@ -29449,7 +29968,6 @@ var init_semantics = __esm({
29449
29968
  const last2 = allChildren[allChildren.length - 1];
29450
29969
  const isSlash = (t) => t.image === "/" || t.image === "\\";
29451
29970
  if (isSlash(first2) && isSlash(last2)) {
29452
- continue;
29453
29971
  }
29454
29972
  }
29455
29973
  const opens = ch.RoundOpen || [];
@@ -29929,13 +30447,24 @@ function mapFlowchartParserError(err, text) {
29929
30447
  length: len
29930
30448
  };
29931
30449
  }
29932
- if (tokType === "QuotedString") {
30450
+ if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
29933
30451
  const context = err?.context;
29934
30452
  const inLinkRule = context?.ruleStack?.includes("linkTextInline") || context?.ruleStack?.includes("link") || false;
29935
30453
  const lineContent = allLines[Math.max(0, line - 1)] || "";
29936
30454
  const beforeQuote = lineContent.slice(0, Math.max(0, column - 1));
29937
30455
  const hasLinkBefore = beforeQuote.match(/--\s*$|==\s*$|-\.\s*$|-\.-\s*$|\[\s*$/);
29938
30456
  if (inLinkRule || hasLinkBefore) {
30457
+ if (tokType === "SquareOpen" || tokType === "SquareClose") {
30458
+ return {
30459
+ line,
30460
+ column,
30461
+ severity: "error",
30462
+ code: "FL-EDGE-LABEL-BRACKET",
30463
+ message: "Square brackets [ ] are not supported inside inline edge labels.",
30464
+ hint: "Use HTML entities &#91; and &#93; inside |...|, e.g., --|run: &#91;aggregate&#93;|-->",
30465
+ length: len
30466
+ };
30467
+ }
29939
30468
  const quotedText = found.startsWith('"') ? found.slice(1, -1) : found;
29940
30469
  return {
29941
30470
  line,
@@ -30029,7 +30558,7 @@ function mapFlowchartParserError(err, text) {
30029
30558
  }
30030
30559
  }
30031
30560
  }
30032
- if (tokType === "QuotedString") {
30561
+ if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
30033
30562
  return {
30034
30563
  line,
30035
30564
  column,
@@ -30050,7 +30579,7 @@ function mapFlowchartParserError(err, text) {
30050
30579
  return { line, column, severity: "error", code: "FL-NODE-UNCLOSED-BRACKET", message: "Unclosed '['. Add a matching ']' before the arrow or newline.", hint: "Example: A[Label] --> B", length: 1 };
30051
30580
  }
30052
30581
  if (expecting(err, "RoundClose")) {
30053
- if (tokType === "QuotedString") {
30582
+ if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
30054
30583
  return {
30055
30584
  line,
30056
30585
  column,
@@ -30089,7 +30618,7 @@ function mapFlowchartParserError(err, text) {
30089
30618
  return { line, column, severity: "error", code: "FL-NODE-UNCLOSED-BRACKET", message: "Unclosed '('. Add a matching ')'.", hint: "Example: B(Label)", length: 1 };
30090
30619
  }
30091
30620
  if (expecting(err, "DiamondClose")) {
30092
- if (tokType === "QuotedString") {
30621
+ if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
30093
30622
  return {
30094
30623
  line,
30095
30624
  column,
@@ -30232,16 +30761,19 @@ function mapFlowchartParserError(err, text) {
30232
30761
  const subgraphIdx = lineStr.indexOf("subgraph");
30233
30762
  if (subgraphIdx !== -1) {
30234
30763
  const afterSubgraph = lineStr.slice(subgraphIdx + 8).trim();
30235
- if (afterSubgraph && !afterSubgraph.startsWith('"') && !afterSubgraph.startsWith("'") && afterSubgraph.includes(" ")) {
30236
- return {
30237
- line,
30238
- column,
30239
- severity: "error",
30240
- code: "FL-SUBGRAPH-UNQUOTED-TITLE",
30241
- message: "Subgraph titles with spaces must be quoted.",
30242
- hint: 'Example: subgraph "Existing Logic Path" or use underscores: subgraph Existing_Logic_Path',
30243
- length: afterSubgraph.length
30244
- };
30764
+ if (afterSubgraph && !afterSubgraph.startsWith('"') && !afterSubgraph.startsWith("'")) {
30765
+ const hasHazard = /[\[\](){}/:|"'\\]/.test(afterSubgraph);
30766
+ if (hasHazard) {
30767
+ return {
30768
+ line,
30769
+ column,
30770
+ severity: "error",
30771
+ code: "FL-SUBGRAPH-UNQUOTED-TITLE",
30772
+ message: "Subgraph title contains special characters; wrap it in quotes.",
30773
+ hint: 'Example: subgraph "Streams (inside Gateway)"',
30774
+ length: afterSubgraph.length
30775
+ };
30776
+ }
30245
30777
  }
30246
30778
  }
30247
30779
  }
@@ -30935,6 +31467,111 @@ function validateFlowchart(text, options = {}) {
30935
31467
  }
30936
31468
  }
30937
31469
  }
31470
+ {
31471
+ const byLine = /* @__PURE__ */ new Map();
31472
+ const collect = (arr) => {
31473
+ for (const e of arr || []) {
31474
+ if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED")) {
31475
+ const ln = e.line ?? 0;
31476
+ const col = e.column ?? 1;
31477
+ const list = byLine.get(ln) || [];
31478
+ list.push({ start: col, end: col });
31479
+ byLine.set(ln, list);
31480
+ }
31481
+ }
31482
+ };
31483
+ collect(prevErrors);
31484
+ collect(errs);
31485
+ const lines2 = text2.split(/\r?\n/);
31486
+ for (let ii = 0; ii < lines2.length; ii++) {
31487
+ const raw = lines2[ii] || "";
31488
+ if (!raw.includes("[") || !raw.includes("]"))
31489
+ continue;
31490
+ let i = 0;
31491
+ const n = raw.length;
31492
+ let inQuote = false;
31493
+ let esc = false;
31494
+ while (i < n) {
31495
+ const ch = raw[i];
31496
+ if (inQuote) {
31497
+ if (esc) {
31498
+ esc = false;
31499
+ } else if (ch === "\\") {
31500
+ esc = true;
31501
+ } else if (ch === '"') {
31502
+ inQuote = false;
31503
+ }
31504
+ i++;
31505
+ continue;
31506
+ }
31507
+ if (ch === '"') {
31508
+ inQuote = true;
31509
+ i++;
31510
+ continue;
31511
+ }
31512
+ if (ch === "[") {
31513
+ let j = i + 1;
31514
+ let inQ = false;
31515
+ let esc2 = false;
31516
+ let depth = 1;
31517
+ while (j < n && depth > 0) {
31518
+ const cj = raw[j];
31519
+ if (inQ) {
31520
+ if (esc2) {
31521
+ esc2 = false;
31522
+ } else if (cj === "\\") {
31523
+ esc2 = true;
31524
+ } else if (cj === '"') {
31525
+ inQ = false;
31526
+ }
31527
+ j++;
31528
+ continue;
31529
+ }
31530
+ if (cj === '"') {
31531
+ inQ = true;
31532
+ j++;
31533
+ continue;
31534
+ }
31535
+ if (cj === "[")
31536
+ depth++;
31537
+ else if (cj === "]")
31538
+ depth--;
31539
+ j++;
31540
+ }
31541
+ if (depth === 0) {
31542
+ const startCol = i + 2;
31543
+ const endCol = j;
31544
+ const seg = raw.slice(i + 1, j - 1);
31545
+ const trimmed = seg.trim();
31546
+ const ln = ii + 1;
31547
+ const lsp = trimmed.slice(0, 1), rsp = trimmed.slice(-1);
31548
+ const isSlashPair = (lsp === "/" || lsp === "\\") && (rsp === "/" || rsp === "\\");
31549
+ const isParenWrapped = lsp === "(" && rsp === ")";
31550
+ const isQuoted = /^"[\s\S]*"$/.test(trimmed);
31551
+ const existing = byLine.get(ln) || [];
31552
+ const covered = existing.some((r) => !(endCol < r.start || startCol > r.end));
31553
+ const hasParens = seg.includes("(") || seg.includes(")");
31554
+ const hasAt = seg.includes("@");
31555
+ if (!covered && !isQuoted && !isParenWrapped && hasParens) {
31556
+ errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-PARENS-UNQUOTED", message: "Parentheses inside an unquoted label are not supported by Mermaid.", hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: &#40; and &#41;.' });
31557
+ existing.push({ start: startCol, end: endCol });
31558
+ byLine.set(ln, existing);
31559
+ }
31560
+ if (!covered && !isQuoted && !isSlashPair && hasAt) {
31561
+ errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-AT-IN-UNQUOTED", message: "'@' inside an unquoted label can be misparsed by Mermaid.", hint: 'Wrap the label in quotes, e.g., B["@probelabs/probe v0.6.0-rc149"]' });
31562
+ existing.push({ start: startCol, end: endCol });
31563
+ byLine.set(ln, existing);
31564
+ }
31565
+ i = j;
31566
+ continue;
31567
+ } else {
31568
+ break;
31569
+ }
31570
+ }
31571
+ i++;
31572
+ }
31573
+ }
31574
+ }
30938
31575
  const dblEsc = (text2.match(/\\\"/g) || []).length;
30939
31576
  const dq = (text2.match(/\"/g) || []).length - dblEsc;
30940
31577
  const sq = (text2.match(/'/g) || []).length;
@@ -33279,6 +33916,21 @@ function computeFixes(text, errors, level = "safe") {
33279
33916
  }
33280
33917
  continue;
33281
33918
  }
33919
+ if (is("FL-EDGE-LABEL-BRACKET", e)) {
33920
+ const lineText = lineTextAt(text, e.line);
33921
+ const firstBar = lineText.indexOf("|");
33922
+ const secondBar = firstBar >= 0 ? lineText.indexOf("|", firstBar + 1) : -1;
33923
+ if (firstBar >= 0 && secondBar > firstBar) {
33924
+ const before = lineText.slice(0, firstBar + 1);
33925
+ const label = lineText.slice(firstBar + 1, secondBar);
33926
+ const after = lineText.slice(secondBar);
33927
+ const fixedLabel = label.replace(/\[/g, "&#91;").replace(/\]/g, "&#93;");
33928
+ const fixedLine = before + fixedLabel + after;
33929
+ const finalLine = fixedLine.replace(/\[([^\]]*)\]/g, (m, seg) => "[" + String(seg).replace(/`/g, "") + "]");
33930
+ edits.push({ start: { line: e.line, column: 1 }, end: { line: e.line, column: lineText.length + 1 }, newText: finalLine });
33931
+ }
33932
+ continue;
33933
+ }
33282
33934
  if (is("FL-EDGE-LABEL-BACKTICK", e)) {
33283
33935
  const lineText = lineTextAt(text, e.line);
33284
33936
  const re = /^(.*?--)\s*([^|>]+?)\s*(-->|==>|\.->|->)(.*)$/;
@@ -42402,7 +43054,7 @@ var require_bk = __commonJS({
42402
43054
  return xs;
42403
43055
  }
42404
43056
  function buildBlockGraph(g, layering, root2, reverseSep) {
42405
- var blockGraph = new Graph(), graphLabel = g.graph(), sepFn = sep2(graphLabel.nodesep, graphLabel.edgesep, reverseSep);
43057
+ var blockGraph = new Graph(), graphLabel = g.graph(), sepFn = sep3(graphLabel.nodesep, graphLabel.edgesep, reverseSep);
42406
43058
  _.forEach(layering, function(layer) {
42407
43059
  var u;
42408
43060
  _.forEach(layer, function(v) {
@@ -42492,7 +43144,7 @@ var require_bk = __commonJS({
42492
43144
  alignCoordinates(xss, smallestWidth);
42493
43145
  return balance(xss, g.graph().align);
42494
43146
  }
42495
- function sep2(nodeSep, edgeSep, reverseSep) {
43147
+ function sep3(nodeSep, edgeSep, reverseSep) {
42496
43148
  return function(g, v, w) {
42497
43149
  var vLabel = g.node(v);
42498
43150
  var wLabel = g.node(w);
@@ -49786,7 +50438,7 @@ var require_compile = __commonJS({
49786
50438
  const schOrFunc = root2.refs[ref];
49787
50439
  if (schOrFunc)
49788
50440
  return schOrFunc;
49789
- let _sch = resolve5.call(this, root2, ref);
50441
+ let _sch = resolve6.call(this, root2, ref);
49790
50442
  if (_sch === void 0) {
49791
50443
  const schema = (_a = root2.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
49792
50444
  const { schemaId } = this.opts;
@@ -49813,7 +50465,7 @@ var require_compile = __commonJS({
49813
50465
  function sameSchemaEnv(s1, s2) {
49814
50466
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
49815
50467
  }
49816
- function resolve5(root2, ref) {
50468
+ function resolve6(root2, ref) {
49817
50469
  let sch;
49818
50470
  while (typeof (sch = this.refs[ref]) == "string")
49819
50471
  ref = sch;
@@ -50388,7 +51040,7 @@ var require_fast_uri = __commonJS({
50388
51040
  }
50389
51041
  return uri;
50390
51042
  }
50391
- function resolve5(baseURI, relativeURI, options) {
51043
+ function resolve6(baseURI, relativeURI, options) {
50392
51044
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
50393
51045
  const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
50394
51046
  schemelessOptions.skipEscape = true;
@@ -50615,7 +51267,7 @@ var require_fast_uri = __commonJS({
50615
51267
  var fastUri = {
50616
51268
  SCHEMES,
50617
51269
  normalize: normalize2,
50618
- resolve: resolve5,
51270
+ resolve: resolve6,
50619
51271
  resolveComponent,
50620
51272
  equal,
50621
51273
  serialize,
@@ -54633,15 +55285,15 @@ Provide only the corrected Mermaid diagram within a mermaid code block. Do not a
54633
55285
  });
54634
55286
 
54635
55287
  // src/agent/mcp/config.js
54636
- import { readFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync } from "fs";
54637
- import { join as join2, dirname as dirname2 } from "path";
55288
+ import { readFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync } from "fs";
55289
+ import { join as join2, dirname as dirname3 } from "path";
54638
55290
  import { homedir } from "os";
54639
55291
  import { fileURLToPath as fileURLToPath6 } from "url";
54640
55292
  function loadMCPConfigurationFromPath(configPath) {
54641
55293
  if (!configPath) {
54642
55294
  throw new Error("Config path is required");
54643
55295
  }
54644
- if (!existsSync3(configPath)) {
55296
+ if (!existsSync4(configPath)) {
54645
55297
  throw new Error(`MCP configuration file not found: ${configPath}`);
54646
55298
  }
54647
55299
  try {
@@ -54670,7 +55322,7 @@ function loadMCPConfiguration() {
54670
55322
  ].filter(Boolean);
54671
55323
  let config = null;
54672
55324
  for (const configPath of configPaths) {
54673
- if (existsSync3(configPath)) {
55325
+ if (existsSync4(configPath)) {
54674
55326
  try {
54675
55327
  const content = readFileSync(configPath, "utf8");
54676
55328
  config = JSON.parse(content);
@@ -54773,7 +55425,7 @@ var init_config = __esm({
54773
55425
  "src/agent/mcp/config.js"() {
54774
55426
  "use strict";
54775
55427
  __filename4 = fileURLToPath6(import.meta.url);
54776
- __dirname4 = dirname2(__filename4);
55428
+ __dirname4 = dirname3(__filename4);
54777
55429
  DEFAULT_CONFIG = {
54778
55430
  mcpServers: {
54779
55431
  // Example probe server configuration
@@ -54962,12 +55614,12 @@ var init_client = __esm({
54962
55614
  const toolsResponse = await client.listTools();
54963
55615
  const toolCount = toolsResponse?.tools?.length || 0;
54964
55616
  if (toolsResponse && toolsResponse.tools) {
54965
- for (const tool3 of toolsResponse.tools) {
54966
- const qualifiedName = `${name}_${tool3.name}`;
55617
+ for (const tool4 of toolsResponse.tools) {
55618
+ const qualifiedName = `${name}_${tool4.name}`;
54967
55619
  this.tools.set(qualifiedName, {
54968
- ...tool3,
55620
+ ...tool4,
54969
55621
  serverName: name,
54970
- originalName: tool3.name
55622
+ originalName: tool4.name
54971
55623
  });
54972
55624
  if (this.debug) {
54973
55625
  console.error(`[MCP DEBUG] Registered tool: ${qualifiedName}`);
@@ -54990,13 +55642,13 @@ var init_client = __esm({
54990
55642
  * @param {Object} args - Tool arguments
54991
55643
  */
54992
55644
  async callTool(toolName, args) {
54993
- const tool3 = this.tools.get(toolName);
54994
- if (!tool3) {
55645
+ const tool4 = this.tools.get(toolName);
55646
+ if (!tool4) {
54995
55647
  throw new Error(`Unknown tool: ${toolName}`);
54996
55648
  }
54997
- const clientInfo = this.clients.get(tool3.serverName);
55649
+ const clientInfo = this.clients.get(tool4.serverName);
54998
55650
  if (!clientInfo) {
54999
- throw new Error(`Server ${tool3.serverName} not connected`);
55651
+ throw new Error(`Server ${tool4.serverName} not connected`);
55000
55652
  }
55001
55653
  try {
55002
55654
  if (this.debug) {
@@ -55010,7 +55662,7 @@ var init_client = __esm({
55010
55662
  });
55011
55663
  const result = await Promise.race([
55012
55664
  clientInfo.client.callTool({
55013
- name: tool3.originalName,
55665
+ name: tool4.originalName,
55014
55666
  arguments: args
55015
55667
  }),
55016
55668
  timeoutPromise
@@ -55033,11 +55685,11 @@ var init_client = __esm({
55033
55685
  */
55034
55686
  getTools() {
55035
55687
  const tools2 = {};
55036
- for (const [name, tool3] of this.tools.entries()) {
55688
+ for (const [name, tool4] of this.tools.entries()) {
55037
55689
  tools2[name] = {
55038
- description: tool3.description,
55039
- inputSchema: tool3.inputSchema,
55040
- serverName: tool3.serverName
55690
+ description: tool4.description,
55691
+ inputSchema: tool4.inputSchema,
55692
+ serverName: tool4.serverName
55041
55693
  };
55042
55694
  }
55043
55695
  return tools2;
@@ -55048,10 +55700,10 @@ var init_client = __esm({
55048
55700
  */
55049
55701
  getVercelTools() {
55050
55702
  const tools2 = {};
55051
- for (const [name, tool3] of this.tools.entries()) {
55703
+ for (const [name, tool4] of this.tools.entries()) {
55052
55704
  tools2[name] = {
55053
- description: tool3.description,
55054
- inputSchema: tool3.inputSchema,
55705
+ description: tool4.description,
55706
+ inputSchema: tool4.inputSchema,
55055
55707
  execute: async (args) => {
55056
55708
  const result = await this.callTool(name, args);
55057
55709
  if (result.content && result.content[0]) {
@@ -55100,9 +55752,9 @@ var init_client = __esm({
55100
55752
  });
55101
55753
 
55102
55754
  // src/agent/mcp/xmlBridge.js
55103
- function mcpToolToXmlDefinition(name, tool3) {
55104
- const description = tool3.description || "MCP tool";
55105
- const inputSchema = tool3.inputSchema || tool3.parameters || {};
55755
+ function mcpToolToXmlDefinition(name, tool4) {
55756
+ const description = tool4.description || "MCP tool";
55757
+ const inputSchema = tool4.inputSchema || tool4.parameters || {};
55106
55758
  let paramDocs = "";
55107
55759
  if (inputSchema.properties) {
55108
55760
  paramDocs = "\n\nParameters (provide as JSON object):";
@@ -55281,8 +55933,8 @@ var init_xmlBridge = __esm({
55281
55933
  const vercelTools = this.mcpManager.getVercelTools();
55282
55934
  this.mcpTools = vercelTools;
55283
55935
  const toolCount = Object.keys(vercelTools).length;
55284
- for (const [name, tool3] of Object.entries(vercelTools)) {
55285
- this.xmlDefinitions[name] = mcpToolToXmlDefinition(name, tool3);
55936
+ for (const [name, tool4] of Object.entries(vercelTools)) {
55937
+ this.xmlDefinitions[name] = mcpToolToXmlDefinition(name, tool4);
55286
55938
  }
55287
55939
  if (toolCount === 0) {
55288
55940
  console.error("[MCP INFO] MCP initialization complete: 0 tools loaded");
@@ -55329,14 +55981,14 @@ var init_xmlBridge = __esm({
55329
55981
  console.error(`[MCP DEBUG] Executing MCP tool: ${toolName}`);
55330
55982
  console.error(`[MCP DEBUG] Parameters:`, JSON.stringify(params, null, 2));
55331
55983
  }
55332
- const tool3 = this.mcpTools[toolName];
55333
- if (!tool3) {
55984
+ const tool4 = this.mcpTools[toolName];
55985
+ if (!tool4) {
55334
55986
  console.error(`[MCP ERROR] Unknown MCP tool: ${toolName}`);
55335
55987
  console.error(`[MCP ERROR] Available tools: ${this.getToolNames().join(", ")}`);
55336
55988
  throw new Error(`Unknown MCP tool: ${toolName}`);
55337
55989
  }
55338
55990
  try {
55339
- const result = await tool3.execute(params);
55991
+ const result = await tool4.execute(params);
55340
55992
  if (this.debug) {
55341
55993
  console.error(`[MCP DEBUG] Tool ${toolName} executed successfully`);
55342
55994
  }
@@ -55390,22 +56042,636 @@ var init_mcp = __esm({
55390
56042
  }
55391
56043
  });
55392
56044
 
56045
+ // src/agent/RetryManager.js
56046
+ function isRetryableError(error, retryableErrors = DEFAULT_RETRYABLE_ERRORS) {
56047
+ if (!error) return false;
56048
+ const errorString = error.toString().toLowerCase();
56049
+ const errorMessage = (error.message || "").toLowerCase();
56050
+ const errorCode = (error.code || "").toLowerCase();
56051
+ const errorType = (error.type || "").toLowerCase();
56052
+ const statusCode = error.statusCode || error.status;
56053
+ for (const pattern of retryableErrors) {
56054
+ const lowerPattern = pattern.toLowerCase();
56055
+ if (errorString.includes(lowerPattern) || errorMessage.includes(lowerPattern) || errorCode.includes(lowerPattern) || errorType.includes(lowerPattern) || statusCode?.toString() === pattern) {
56056
+ return true;
56057
+ }
56058
+ }
56059
+ return false;
56060
+ }
56061
+ function extractErrorInfo(error) {
56062
+ return {
56063
+ message: error.message || error.toString(),
56064
+ type: error.type || error.constructor.name,
56065
+ code: error.code,
56066
+ statusCode: error.statusCode || error.status,
56067
+ provider: error.provider,
56068
+ isRetryable: isRetryableError(error)
56069
+ };
56070
+ }
56071
+ function sleep(ms) {
56072
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
56073
+ }
56074
+ var DEFAULT_RETRYABLE_ERRORS, RetryManager;
56075
+ var init_RetryManager = __esm({
56076
+ "src/agent/RetryManager.js"() {
56077
+ "use strict";
56078
+ DEFAULT_RETRYABLE_ERRORS = [
56079
+ "Overloaded",
56080
+ "overloaded",
56081
+ "rate_limit",
56082
+ "rate limit",
56083
+ "429",
56084
+ "500",
56085
+ "502",
56086
+ "503",
56087
+ "504",
56088
+ "timeout",
56089
+ "ECONNRESET",
56090
+ "ETIMEDOUT",
56091
+ "ENOTFOUND",
56092
+ "api_error"
56093
+ ];
56094
+ RetryManager = class {
56095
+ /**
56096
+ * Create a new RetryManager
56097
+ * @param {Object} options - Configuration options
56098
+ * @param {number} [options.maxRetries=3] - Maximum retry attempts
56099
+ * @param {number} [options.initialDelay=1000] - Initial delay in ms (1 second)
56100
+ * @param {number} [options.maxDelay=30000] - Maximum delay in ms (30 seconds)
56101
+ * @param {number} [options.backoffFactor=2] - Exponential backoff multiplier
56102
+ * @param {Array<string>} [options.retryableErrors] - List of retryable error patterns
56103
+ * @param {boolean} [options.debug=false] - Enable debug logging
56104
+ */
56105
+ constructor(options = {}) {
56106
+ this.maxRetries = this._validateNumber(options.maxRetries, 3, "maxRetries", 0, 100);
56107
+ this.initialDelay = this._validateNumber(options.initialDelay, 1e3, "initialDelay", 0, 6e4);
56108
+ this.maxDelay = this._validateNumber(options.maxDelay, 3e4, "maxDelay", 0, 3e5);
56109
+ this.backoffFactor = this._validateNumber(options.backoffFactor, 2, "backoffFactor", 1, 10);
56110
+ this.retryableErrors = options.retryableErrors || DEFAULT_RETRYABLE_ERRORS;
56111
+ this.debug = options.debug ?? false;
56112
+ this.jitter = options.jitter ?? true;
56113
+ if (this.maxDelay < this.initialDelay) {
56114
+ throw new Error("maxDelay must be greater than or equal to initialDelay");
56115
+ }
56116
+ this.stats = {
56117
+ totalAttempts: 0,
56118
+ totalRetries: 0,
56119
+ successfulRetries: 0,
56120
+ failedRetries: 0
56121
+ };
56122
+ }
56123
+ /**
56124
+ * Validate a numeric parameter
56125
+ * @param {*} value - Value to validate
56126
+ * @param {number} defaultValue - Default if undefined
56127
+ * @param {string} name - Parameter name for error messages
56128
+ * @param {number} min - Minimum allowed value
56129
+ * @param {number} max - Maximum allowed value
56130
+ * @returns {number} - Validated number
56131
+ * @private
56132
+ */
56133
+ _validateNumber(value, defaultValue, name, min, max) {
56134
+ if (value === void 0 || value === null) {
56135
+ return defaultValue;
56136
+ }
56137
+ const num = Number(value);
56138
+ if (isNaN(num)) {
56139
+ throw new Error(`${name} must be a number, got: ${value}`);
56140
+ }
56141
+ if (num < min || num > max) {
56142
+ throw new Error(`${name} must be between ${min} and ${max}, got: ${num}`);
56143
+ }
56144
+ return num;
56145
+ }
56146
+ /**
56147
+ * Execute a function with retry logic
56148
+ * @param {Function} fn - Async function to execute
56149
+ * @param {Object} [context={}] - Context information for logging
56150
+ * @param {AbortSignal} [context.signal] - Optional abort signal for cancellation
56151
+ * @returns {Promise<*>} - Result from the function
56152
+ * @throws {Error} - If all retries are exhausted or operation is aborted
56153
+ */
56154
+ async executeWithRetry(fn, context = {}) {
56155
+ let lastError = null;
56156
+ let currentDelay = this.initialDelay;
56157
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
56158
+ if (context.signal?.aborted) {
56159
+ const abortError = new Error("Operation aborted");
56160
+ abortError.name = "AbortError";
56161
+ throw abortError;
56162
+ }
56163
+ this.stats.totalAttempts++;
56164
+ try {
56165
+ if (this.debug && attempt > 0) {
56166
+ console.log(`[RetryManager] Retry attempt ${attempt}/${this.maxRetries}`, context);
56167
+ }
56168
+ const result = await fn();
56169
+ if (attempt > 0) {
56170
+ this.stats.successfulRetries++;
56171
+ if (this.debug) {
56172
+ console.log(`[RetryManager] \u2705 Retry successful on attempt ${attempt + 1}`, context);
56173
+ }
56174
+ }
56175
+ return result;
56176
+ } catch (error) {
56177
+ lastError = error;
56178
+ const errorInfo = extractErrorInfo(error);
56179
+ const shouldRetry = isRetryableError(error, this.retryableErrors);
56180
+ const hasRetriesLeft = attempt < this.maxRetries;
56181
+ if (this.debug) {
56182
+ console.log(`[RetryManager] \u274C Attempt ${attempt + 1}/${this.maxRetries + 1} failed:`, {
56183
+ ...context,
56184
+ error: errorInfo,
56185
+ shouldRetry,
56186
+ hasRetriesLeft
56187
+ });
56188
+ }
56189
+ if (!shouldRetry) {
56190
+ if (this.debug) {
56191
+ console.log(`[RetryManager] Error is not retryable, failing immediately`, errorInfo);
56192
+ }
56193
+ throw error;
56194
+ }
56195
+ if (!hasRetriesLeft) {
56196
+ this.stats.failedRetries++;
56197
+ if (this.debug) {
56198
+ console.log(`[RetryManager] Max retries (${this.maxRetries}) exhausted`, context);
56199
+ }
56200
+ throw error;
56201
+ }
56202
+ this.stats.totalRetries++;
56203
+ let delayWithJitter = currentDelay;
56204
+ if (this.jitter) {
56205
+ const jitterAmount = currentDelay * 0.25;
56206
+ delayWithJitter = currentDelay + (Math.random() * jitterAmount * 2 - jitterAmount);
56207
+ }
56208
+ if (this.debug) {
56209
+ console.log(`[RetryManager] Waiting ${Math.round(delayWithJitter)}ms before retry...`);
56210
+ }
56211
+ await sleep(delayWithJitter);
56212
+ currentDelay = Math.min(currentDelay * this.backoffFactor, this.maxDelay);
56213
+ }
56214
+ }
56215
+ throw lastError;
56216
+ }
56217
+ /**
56218
+ * Check if an error is retryable
56219
+ * @param {Error} error - The error to check
56220
+ * @returns {boolean} - True if error should be retried
56221
+ */
56222
+ isRetryable(error) {
56223
+ return isRetryableError(error, this.retryableErrors);
56224
+ }
56225
+ /**
56226
+ * Get retry statistics
56227
+ * @returns {Object} - Statistics object
56228
+ */
56229
+ getStats() {
56230
+ return { ...this.stats };
56231
+ }
56232
+ /**
56233
+ * Reset statistics
56234
+ */
56235
+ resetStats() {
56236
+ this.stats = {
56237
+ totalAttempts: 0,
56238
+ totalRetries: 0,
56239
+ successfulRetries: 0,
56240
+ failedRetries: 0
56241
+ };
56242
+ }
56243
+ };
56244
+ }
56245
+ });
56246
+
56247
+ // src/agent/FallbackManager.js
56248
+ import { createAnthropic } from "@ai-sdk/anthropic";
56249
+ import { createOpenAI } from "@ai-sdk/openai";
56250
+ import { createGoogleGenerativeAI } from "@ai-sdk/google";
56251
+ import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock";
56252
+ function createFallbackManagerFromEnv(debug = false) {
56253
+ const fallbackProvidersEnv = process.env.FALLBACK_PROVIDERS;
56254
+ const fallbackModelsEnv = process.env.FALLBACK_MODELS;
56255
+ if (!fallbackProvidersEnv && !fallbackModelsEnv) {
56256
+ return null;
56257
+ }
56258
+ let providers = [];
56259
+ let models = [];
56260
+ let strategy = FALLBACK_STRATEGIES.ANY;
56261
+ if (fallbackProvidersEnv) {
56262
+ try {
56263
+ if (typeof fallbackProvidersEnv !== "string" || fallbackProvidersEnv.length > 1e4) {
56264
+ console.error("[FallbackManager] FALLBACK_PROVIDERS must be a valid JSON string under 10KB");
56265
+ return null;
56266
+ }
56267
+ const parsed = JSON.parse(fallbackProvidersEnv);
56268
+ if (!Array.isArray(parsed)) {
56269
+ console.error("[FallbackManager] FALLBACK_PROVIDERS must be a JSON array");
56270
+ return null;
56271
+ }
56272
+ providers = parsed;
56273
+ strategy = FALLBACK_STRATEGIES.CUSTOM;
56274
+ } catch (error) {
56275
+ console.error("[FallbackManager] Failed to parse FALLBACK_PROVIDERS:", error.message);
56276
+ return null;
56277
+ }
56278
+ }
56279
+ if (fallbackModelsEnv) {
56280
+ try {
56281
+ if (typeof fallbackModelsEnv !== "string" || fallbackModelsEnv.length > 1e4) {
56282
+ console.error("[FallbackManager] FALLBACK_MODELS must be a valid JSON string under 10KB");
56283
+ return null;
56284
+ }
56285
+ const parsed = JSON.parse(fallbackModelsEnv);
56286
+ if (!Array.isArray(parsed)) {
56287
+ console.error("[FallbackManager] FALLBACK_MODELS must be a JSON array");
56288
+ return null;
56289
+ }
56290
+ models = parsed;
56291
+ strategy = FALLBACK_STRATEGIES.SAME_PROVIDER;
56292
+ } catch (error) {
56293
+ console.error("[FallbackManager] Failed to parse FALLBACK_MODELS:", error.message);
56294
+ return null;
56295
+ }
56296
+ }
56297
+ const maxTotalAttempts = process.env.FALLBACK_MAX_TOTAL_ATTEMPTS ? (() => {
56298
+ const val = parseInt(process.env.FALLBACK_MAX_TOTAL_ATTEMPTS, 10);
56299
+ if (isNaN(val) || val < 1 || val > 100) {
56300
+ console.warn("[FallbackManager] FALLBACK_MAX_TOTAL_ATTEMPTS must be between 1 and 100, using default: 10");
56301
+ return 10;
56302
+ }
56303
+ return val;
56304
+ })() : 10;
56305
+ return new FallbackManager({
56306
+ strategy,
56307
+ providers,
56308
+ models,
56309
+ maxTotalAttempts,
56310
+ debug
56311
+ });
56312
+ }
56313
+ function buildFallbackProvidersFromEnv(options = {}) {
56314
+ const providers = [];
56315
+ const anthropicApiKey = process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN;
56316
+ const openaiApiKey = process.env.OPENAI_API_KEY;
56317
+ const googleApiKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY;
56318
+ const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
56319
+ const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
56320
+ const awsRegion = process.env.AWS_REGION;
56321
+ const awsApiKey = process.env.AWS_BEDROCK_API_KEY;
56322
+ const llmBaseUrl = process.env.LLM_BASE_URL;
56323
+ const anthropicApiUrl = process.env.ANTHROPIC_API_URL || process.env.ANTHROPIC_BASE_URL || llmBaseUrl;
56324
+ const openaiApiUrl = process.env.OPENAI_API_URL || llmBaseUrl;
56325
+ const googleApiUrl = process.env.GOOGLE_API_URL || llmBaseUrl;
56326
+ const awsBedrockBaseUrl = process.env.AWS_BEDROCK_BASE_URL || llmBaseUrl;
56327
+ const primaryProvider = options.primaryProvider?.toLowerCase();
56328
+ const primaryModel = options.primaryModel;
56329
+ if (primaryProvider === "anthropic" && anthropicApiKey) {
56330
+ providers.push({
56331
+ provider: "anthropic",
56332
+ apiKey: anthropicApiKey,
56333
+ ...anthropicApiUrl && { baseURL: anthropicApiUrl },
56334
+ ...primaryModel && { model: primaryModel }
56335
+ });
56336
+ } else if (primaryProvider === "openai" && openaiApiKey) {
56337
+ providers.push({
56338
+ provider: "openai",
56339
+ apiKey: openaiApiKey,
56340
+ ...openaiApiUrl && { baseURL: openaiApiUrl },
56341
+ ...primaryModel && { model: primaryModel }
56342
+ });
56343
+ } else if (primaryProvider === "google" && googleApiKey) {
56344
+ providers.push({
56345
+ provider: "google",
56346
+ apiKey: googleApiKey,
56347
+ ...googleApiUrl && { baseURL: googleApiUrl },
56348
+ ...primaryModel && { model: primaryModel }
56349
+ });
56350
+ } else if (primaryProvider === "bedrock" && (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey)) {
56351
+ const config = { provider: "bedrock" };
56352
+ if (awsApiKey) {
56353
+ config.apiKey = awsApiKey;
56354
+ } else {
56355
+ config.accessKeyId = awsAccessKeyId;
56356
+ config.secretAccessKey = awsSecretAccessKey;
56357
+ config.region = awsRegion;
56358
+ if (process.env.AWS_SESSION_TOKEN) {
56359
+ config.sessionToken = process.env.AWS_SESSION_TOKEN;
56360
+ }
56361
+ }
56362
+ if (awsBedrockBaseUrl) config.baseURL = awsBedrockBaseUrl;
56363
+ if (primaryModel) config.model = primaryModel;
56364
+ providers.push(config);
56365
+ }
56366
+ if (anthropicApiKey && primaryProvider !== "anthropic") {
56367
+ providers.push({
56368
+ provider: "anthropic",
56369
+ apiKey: anthropicApiKey,
56370
+ ...anthropicApiUrl && { baseURL: anthropicApiUrl }
56371
+ });
56372
+ }
56373
+ if (openaiApiKey && primaryProvider !== "openai") {
56374
+ providers.push({
56375
+ provider: "openai",
56376
+ apiKey: openaiApiKey,
56377
+ ...openaiApiUrl && { baseURL: openaiApiUrl }
56378
+ });
56379
+ }
56380
+ if (googleApiKey && primaryProvider !== "google") {
56381
+ providers.push({
56382
+ provider: "google",
56383
+ apiKey: googleApiKey,
56384
+ ...googleApiUrl && { baseURL: googleApiUrl }
56385
+ });
56386
+ }
56387
+ if ((awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey) && primaryProvider !== "bedrock") {
56388
+ const config = { provider: "bedrock" };
56389
+ if (awsApiKey) {
56390
+ config.apiKey = awsApiKey;
56391
+ } else {
56392
+ config.accessKeyId = awsAccessKeyId;
56393
+ config.secretAccessKey = awsSecretAccessKey;
56394
+ config.region = awsRegion;
56395
+ if (process.env.AWS_SESSION_TOKEN) {
56396
+ config.sessionToken = process.env.AWS_SESSION_TOKEN;
56397
+ }
56398
+ }
56399
+ if (awsBedrockBaseUrl) config.baseURL = awsBedrockBaseUrl;
56400
+ providers.push(config);
56401
+ }
56402
+ return providers;
56403
+ }
56404
+ var FALLBACK_STRATEGIES, DEFAULT_MODELS, FallbackManager;
56405
+ var init_FallbackManager = __esm({
56406
+ "src/agent/FallbackManager.js"() {
56407
+ "use strict";
56408
+ FALLBACK_STRATEGIES = {
56409
+ SAME_MODEL: "same-model",
56410
+ // Try same model on different providers
56411
+ SAME_PROVIDER: "same-provider",
56412
+ // Try different models on same provider
56413
+ ANY: "any",
56414
+ // Try any available provider/model
56415
+ CUSTOM: "custom"
56416
+ // Use custom provider list
56417
+ };
56418
+ DEFAULT_MODELS = {
56419
+ anthropic: "claude-sonnet-4-5-20250929",
56420
+ openai: "gpt-4o",
56421
+ google: "gemini-2.0-flash-exp",
56422
+ bedrock: "anthropic.claude-sonnet-4-20250514-v1:0"
56423
+ };
56424
+ FallbackManager = class {
56425
+ /**
56426
+ * Create a new FallbackManager
56427
+ * @param {Object} options - Configuration options
56428
+ * @param {string} [options.strategy='any'] - Fallback strategy
56429
+ * @param {Array<string>} [options.models] - List of models for same-provider fallback
56430
+ * @param {Array<ProviderConfig>} [options.providers] - List of provider configurations
56431
+ * @param {boolean} [options.stopOnSuccess=true] - Stop on first success
56432
+ * @param {boolean} [options.continueOnNonRetryableError=false] - Continue to fallback on non-retryable errors
56433
+ * @param {number} [options.maxTotalAttempts=10] - Maximum total attempts across all providers
56434
+ * @param {boolean} [options.debug=false] - Enable debug logging
56435
+ */
56436
+ constructor(options = {}) {
56437
+ this.strategy = options.strategy || FALLBACK_STRATEGIES.ANY;
56438
+ this.models = Array.isArray(options.models) ? options.models : [];
56439
+ this.providers = Array.isArray(options.providers) ? options.providers : [];
56440
+ this.stopOnSuccess = options.stopOnSuccess ?? true;
56441
+ this.continueOnNonRetryableError = options.continueOnNonRetryableError ?? false;
56442
+ this.debug = options.debug ?? false;
56443
+ const maxAttempts = options.maxTotalAttempts ?? 10;
56444
+ if (typeof maxAttempts !== "number" || isNaN(maxAttempts) || maxAttempts < 1 || maxAttempts > 100) {
56445
+ throw new Error(`FallbackManager: maxTotalAttempts must be a number between 1 and 100, got: ${maxAttempts}`);
56446
+ }
56447
+ this.maxTotalAttempts = maxAttempts;
56448
+ this.stats = {
56449
+ totalAttempts: 0,
56450
+ providerAttempts: {},
56451
+ successfulProvider: null,
56452
+ failedProviders: []
56453
+ };
56454
+ this._validateConfiguration();
56455
+ }
56456
+ /**
56457
+ * Validate the fallback configuration
56458
+ * @private
56459
+ */
56460
+ _validateConfiguration() {
56461
+ if (this.strategy === FALLBACK_STRATEGIES.SAME_PROVIDER && this.models.length === 0) {
56462
+ throw new Error('FallbackManager: strategy "same-provider" requires models list');
56463
+ }
56464
+ if (this.strategy === FALLBACK_STRATEGIES.CUSTOM && this.providers.length === 0) {
56465
+ throw new Error('FallbackManager: strategy "custom" requires providers list');
56466
+ }
56467
+ for (const config of this.providers) {
56468
+ if (!config.provider) {
56469
+ throw new Error('FallbackManager: Each provider config must have a "provider" field');
56470
+ }
56471
+ if (!["anthropic", "openai", "google", "bedrock"].includes(config.provider)) {
56472
+ throw new Error(`FallbackManager: Invalid provider "${config.provider}". Must be: anthropic, openai, google, or bedrock`);
56473
+ }
56474
+ if (config.provider === "bedrock") {
56475
+ const hasCredentials = config.accessKeyId && config.secretAccessKey && config.region;
56476
+ const hasApiKey = config.apiKey;
56477
+ if (!hasCredentials && !hasApiKey) {
56478
+ throw new Error("FallbackManager: Bedrock provider requires either (accessKeyId, secretAccessKey, region) or apiKey");
56479
+ }
56480
+ } else {
56481
+ if (!config.apiKey) {
56482
+ throw new Error(`FallbackManager: Provider "${config.provider}" requires apiKey`);
56483
+ }
56484
+ }
56485
+ }
56486
+ }
56487
+ /**
56488
+ * Create a provider instance from configuration
56489
+ * @param {ProviderConfig} config - Provider configuration
56490
+ * @returns {Object} - Provider instance
56491
+ * @throws {Error} - If provider creation fails
56492
+ * @private
56493
+ */
56494
+ _createProviderInstance(config) {
56495
+ try {
56496
+ switch (config.provider) {
56497
+ case "anthropic":
56498
+ return createAnthropic({
56499
+ apiKey: config.apiKey,
56500
+ ...config.baseURL && { baseURL: config.baseURL }
56501
+ });
56502
+ case "openai":
56503
+ return createOpenAI({
56504
+ compatibility: "strict",
56505
+ apiKey: config.apiKey,
56506
+ ...config.baseURL && { baseURL: config.baseURL }
56507
+ });
56508
+ case "google":
56509
+ return createGoogleGenerativeAI({
56510
+ apiKey: config.apiKey,
56511
+ ...config.baseURL && { baseURL: config.baseURL }
56512
+ });
56513
+ case "bedrock": {
56514
+ const bedrockConfig = {};
56515
+ if (config.apiKey) {
56516
+ bedrockConfig.apiKey = config.apiKey;
56517
+ } else if (config.accessKeyId && config.secretAccessKey) {
56518
+ bedrockConfig.accessKeyId = config.accessKeyId;
56519
+ bedrockConfig.secretAccessKey = config.secretAccessKey;
56520
+ if (config.sessionToken) {
56521
+ bedrockConfig.sessionToken = config.sessionToken;
56522
+ }
56523
+ }
56524
+ if (config.region) {
56525
+ bedrockConfig.region = config.region;
56526
+ }
56527
+ if (config.baseURL) {
56528
+ bedrockConfig.baseURL = config.baseURL;
56529
+ }
56530
+ return createAmazonBedrock(bedrockConfig);
56531
+ }
56532
+ default:
56533
+ throw new Error(`FallbackManager: Unknown provider "${config.provider}"`);
56534
+ }
56535
+ } catch (error) {
56536
+ const providerName = this._getProviderDisplayName(config);
56537
+ throw new Error(`Failed to create provider instance for ${providerName}: ${error.message}`);
56538
+ }
56539
+ }
56540
+ /**
56541
+ * Get the model name for a provider configuration
56542
+ * @param {ProviderConfig} config - Provider configuration
56543
+ * @returns {string} - Model name
56544
+ * @private
56545
+ */
56546
+ _getModelName(config) {
56547
+ return config.model || DEFAULT_MODELS[config.provider];
56548
+ }
56549
+ /**
56550
+ * Get provider display name for logging
56551
+ * @param {ProviderConfig} config - Provider configuration
56552
+ * @returns {string} - Display name
56553
+ * @private
56554
+ */
56555
+ _getProviderDisplayName(config) {
56556
+ const model = this._getModelName(config);
56557
+ const provider = config.provider;
56558
+ const url = config.baseURL ? ` (${config.baseURL})` : "";
56559
+ return `${provider}/${model}${url}`;
56560
+ }
56561
+ /**
56562
+ * Execute a function with fallback support
56563
+ * @param {Function} fn - Function that takes (provider, model, config) and returns a Promise
56564
+ * @returns {Promise<*>} - Result from the function
56565
+ * @throws {Error} - If all fallbacks are exhausted
56566
+ */
56567
+ async executeWithFallback(fn) {
56568
+ if (this.providers.length === 0) {
56569
+ throw new Error("FallbackManager: No providers configured for fallback");
56570
+ }
56571
+ let lastError = null;
56572
+ let totalAttempts = 0;
56573
+ for (const config of this.providers) {
56574
+ if (totalAttempts >= this.maxTotalAttempts) {
56575
+ if (this.debug) {
56576
+ console.log(`[FallbackManager] \u26A0\uFE0F Max total attempts (${this.maxTotalAttempts}) reached`);
56577
+ }
56578
+ break;
56579
+ }
56580
+ totalAttempts++;
56581
+ this.stats.totalAttempts++;
56582
+ const providerName = this._getProviderDisplayName(config);
56583
+ this.stats.providerAttempts[providerName] = (this.stats.providerAttempts[providerName] || 0) + 1;
56584
+ try {
56585
+ if (this.debug) {
56586
+ console.log(`[FallbackManager] Attempting provider: ${providerName} (attempt ${totalAttempts}/${this.maxTotalAttempts})`);
56587
+ }
56588
+ const provider = this._createProviderInstance(config);
56589
+ const model = this._getModelName(config);
56590
+ const result = await fn(provider, model, config);
56591
+ this.stats.successfulProvider = providerName;
56592
+ if (this.debug) {
56593
+ console.log(`[FallbackManager] \u2705 Success with provider: ${providerName}`);
56594
+ }
56595
+ return result;
56596
+ } catch (error) {
56597
+ lastError = error;
56598
+ const errorInfo = {
56599
+ message: error.message || error.toString(),
56600
+ type: error.type || error.constructor.name,
56601
+ statusCode: error.statusCode || error.status
56602
+ };
56603
+ this.stats.failedProviders.push({
56604
+ provider: providerName,
56605
+ error: errorInfo
56606
+ });
56607
+ if (this.debug) {
56608
+ console.log(`[FallbackManager] \u274C Failed with provider: ${providerName}`, errorInfo);
56609
+ }
56610
+ if (!this.continueOnNonRetryableError && error.nonRetryable) {
56611
+ if (this.debug) {
56612
+ console.log(`[FallbackManager] Non-retryable error, stopping fallback chain`);
56613
+ }
56614
+ throw error;
56615
+ }
56616
+ if (this.debug) {
56617
+ const remaining = this.providers.length - (this.providers.indexOf(config) + 1);
56618
+ console.log(`[FallbackManager] Trying next provider (${remaining} remaining)...`);
56619
+ }
56620
+ }
56621
+ }
56622
+ if (this.debug) {
56623
+ console.log(`[FallbackManager] \u274C All providers exhausted. Total attempts: ${totalAttempts}`);
56624
+ }
56625
+ const fallbackError = new Error(
56626
+ `All provider fallbacks exhausted after ${totalAttempts} attempts. Last error: ${lastError?.message || "Unknown error"}`
56627
+ );
56628
+ fallbackError.cause = lastError;
56629
+ fallbackError.stats = this.getStats();
56630
+ fallbackError.allProvidersFailed = true;
56631
+ throw fallbackError;
56632
+ }
56633
+ /**
56634
+ * Get fallback statistics
56635
+ * @returns {Object} - Statistics object
56636
+ */
56637
+ getStats() {
56638
+ return {
56639
+ ...this.stats,
56640
+ providerAttempts: { ...this.stats.providerAttempts },
56641
+ failedProviders: [...this.stats.failedProviders]
56642
+ };
56643
+ }
56644
+ /**
56645
+ * Reset statistics
56646
+ */
56647
+ resetStats() {
56648
+ this.stats = {
56649
+ totalAttempts: 0,
56650
+ providerAttempts: {},
56651
+ successfulProvider: null,
56652
+ failedProviders: []
56653
+ };
56654
+ }
56655
+ };
56656
+ }
56657
+ });
56658
+
55393
56659
  // src/agent/ProbeAgent.js
55394
56660
  var ProbeAgent_exports = {};
55395
56661
  __export(ProbeAgent_exports, {
55396
56662
  ProbeAgent: () => ProbeAgent
55397
56663
  });
55398
56664
  import dotenv2 from "dotenv";
55399
- import { createAnthropic } from "@ai-sdk/anthropic";
55400
- import { createOpenAI } from "@ai-sdk/openai";
55401
- import { createGoogleGenerativeAI } from "@ai-sdk/google";
55402
- import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock";
56665
+ import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
56666
+ import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
56667
+ import { createGoogleGenerativeAI as createGoogleGenerativeAI2 } from "@ai-sdk/google";
56668
+ import { createAmazonBedrock as createAmazonBedrock2 } from "@ai-sdk/amazon-bedrock";
55403
56669
  import { streamText } from "ai";
55404
56670
  import { randomUUID as randomUUID4 } from "crypto";
55405
56671
  import { EventEmitter as EventEmitter3 } from "events";
55406
- import { existsSync as existsSync4 } from "fs";
56672
+ import { existsSync as existsSync5 } from "fs";
55407
56673
  import { readFile, stat } from "fs/promises";
55408
- import { resolve as resolve3, isAbsolute, dirname as dirname3 } from "path";
56674
+ import { resolve as resolve4, isAbsolute as isAbsolute2, dirname as dirname4 } from "path";
55409
56675
  var MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
55410
56676
  var init_ProbeAgent = __esm({
55411
56677
  "src/agent/ProbeAgent.js"() {
@@ -55422,8 +56688,17 @@ var init_ProbeAgent = __esm({
55422
56688
  init_schemaUtils();
55423
56689
  init_xmlParsingUtils();
55424
56690
  init_mcp();
56691
+ init_RetryManager();
56692
+ init_FallbackManager();
55425
56693
  dotenv2.config();
55426
- MAX_TOOL_ITERATIONS = parseInt(process.env.MAX_TOOL_ITERATIONS || "30", 10);
56694
+ MAX_TOOL_ITERATIONS = (() => {
56695
+ const val = parseInt(process.env.MAX_TOOL_ITERATIONS || "30", 10);
56696
+ if (isNaN(val) || val < 1 || val > 200) {
56697
+ console.warn("[ProbeAgent] MAX_TOOL_ITERATIONS must be between 1 and 200, using default: 30");
56698
+ return 30;
56699
+ }
56700
+ return val;
56701
+ })();
55427
56702
  MAX_HISTORY_MESSAGES = 100;
55428
56703
  MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024;
55429
56704
  ProbeAgent = class _ProbeAgent {
@@ -55434,6 +56709,7 @@ var init_ProbeAgent = __esm({
55434
56709
  * @param {string} [options.customPrompt] - Custom prompt to replace the default system message
55435
56710
  * @param {string} [options.promptType] - Predefined prompt type (architect, code-review, support)
55436
56711
  * @param {boolean} [options.allowEdit=false] - Allow the use of the 'implement' tool
56712
+ * @param {boolean} [options.enableDelegate=false] - Enable the delegate tool for task distribution to subagents
55437
56713
  * @param {string} [options.path] - Search directory path
55438
56714
  * @param {string} [options.provider] - Force specific AI provider
55439
56715
  * @param {string} [options.model] - Override model name
@@ -55449,17 +56725,36 @@ var init_ProbeAgent = __esm({
55449
56725
  * @param {Array} [options.mcpServers] - Deprecated, use mcpConfig instead
55450
56726
  * @param {Object} [options.storageAdapter] - Custom storage adapter for history management
55451
56727
  * @param {Object} [options.hooks] - Hook callbacks for events (e.g., {'tool:start': callback})
56728
+ * @param {Object} [options.retry] - Retry configuration
56729
+ * @param {number} [options.retry.maxRetries=3] - Maximum retry attempts per provider
56730
+ * @param {number} [options.retry.initialDelay=1000] - Initial delay in ms
56731
+ * @param {number} [options.retry.maxDelay=30000] - Maximum delay in ms
56732
+ * @param {number} [options.retry.backoffFactor=2] - Exponential backoff multiplier
56733
+ * @param {Array<string>} [options.retry.retryableErrors] - List of retryable error patterns
56734
+ * @param {Object} [options.fallback] - Fallback configuration
56735
+ * @param {string} [options.fallback.strategy] - Fallback strategy: 'same-model', 'same-provider', 'any', 'custom'
56736
+ * @param {Array<string>} [options.fallback.models] - List of models for same-provider fallback
56737
+ * @param {Array<Object>} [options.fallback.providers] - List of provider configurations for custom fallback
56738
+ * @param {boolean} [options.fallback.stopOnSuccess=true] - Stop on first success
56739
+ * @param {number} [options.fallback.maxTotalAttempts=10] - Maximum total attempts across all providers
55452
56740
  */
55453
56741
  constructor(options = {}) {
55454
56742
  this.sessionId = options.sessionId || randomUUID4();
55455
56743
  this.customPrompt = options.customPrompt || null;
55456
56744
  this.promptType = options.promptType || "code-explorer";
55457
56745
  this.allowEdit = !!options.allowEdit;
56746
+ this.enableDelegate = !!options.enableDelegate;
55458
56747
  this.debug = options.debug || process.env.DEBUG === "1";
55459
56748
  this.cancelled = false;
55460
56749
  this.tracer = options.tracer || null;
55461
56750
  this.outline = !!options.outline;
55462
- this.maxResponseTokens = options.maxResponseTokens || parseInt(process.env.MAX_RESPONSE_TOKENS || "0", 10) || null;
56751
+ this.maxResponseTokens = options.maxResponseTokens || (() => {
56752
+ const val = parseInt(process.env.MAX_RESPONSE_TOKENS || "0", 10);
56753
+ if (isNaN(val) || val < 0 || val > 2e5) {
56754
+ return null;
56755
+ }
56756
+ return val || null;
56757
+ })();
55463
56758
  this.maxIterations = options.maxIterations || null;
55464
56759
  this.disableMermaidValidation = !!options.disableMermaidValidation;
55465
56760
  this.disableJsonValidation = !!options.disableJsonValidation;
@@ -55500,6 +56795,10 @@ var init_ProbeAgent = __esm({
55500
56795
  this.mcpServers = options.mcpServers || null;
55501
56796
  this.mcpBridge = null;
55502
56797
  this._mcpInitialized = false;
56798
+ this.retryConfig = options.retry || {};
56799
+ this.retryManager = null;
56800
+ this.fallbackConfig = options.fallback || null;
56801
+ this.fallbackManager = null;
55503
56802
  this.initializeModel();
55504
56803
  }
55505
56804
  /**
@@ -55584,6 +56883,14 @@ var init_ProbeAgent = __esm({
55584
56883
  if (this.enableBash && wrappedTools.bashToolInstance) {
55585
56884
  this.toolImplementations.bash = wrappedTools.bashToolInstance;
55586
56885
  }
56886
+ if (this.allowEdit) {
56887
+ if (wrappedTools.editToolInstance) {
56888
+ this.toolImplementations.edit = wrappedTools.editToolInstance;
56889
+ }
56890
+ if (wrappedTools.createToolInstance) {
56891
+ this.toolImplementations.create = wrappedTools.createToolInstance;
56892
+ }
56893
+ }
55587
56894
  this.wrappedTools = wrappedTools;
55588
56895
  if (this.debug) {
55589
56896
  console.error("\n[DEBUG] ========================================");
@@ -55634,36 +56941,147 @@ var init_ProbeAgent = __esm({
55634
56941
  if (forceProvider) {
55635
56942
  if (forceProvider === "anthropic" && anthropicApiKey) {
55636
56943
  this.initializeAnthropicModel(anthropicApiKey, anthropicApiUrl, modelName);
56944
+ this.initializeFallbackManager(forceProvider, modelName);
55637
56945
  return;
55638
56946
  } else if (forceProvider === "openai" && openaiApiKey) {
55639
56947
  this.initializeOpenAIModel(openaiApiKey, openaiApiUrl, modelName);
56948
+ this.initializeFallbackManager(forceProvider, modelName);
55640
56949
  return;
55641
56950
  } else if (forceProvider === "google" && googleApiKey) {
55642
56951
  this.initializeGoogleModel(googleApiKey, googleApiUrl, modelName);
56952
+ this.initializeFallbackManager(forceProvider, modelName);
55643
56953
  return;
55644
56954
  } else if (forceProvider === "bedrock" && (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey)) {
55645
56955
  this.initializeBedrockModel(awsAccessKeyId, awsSecretAccessKey, awsRegion, awsSessionToken, awsApiKey, awsBedrockBaseUrl, modelName);
56956
+ this.initializeFallbackManager(forceProvider, modelName);
55646
56957
  return;
55647
56958
  }
55648
56959
  console.warn(`WARNING: Forced provider "${forceProvider}" selected but required API key is missing or invalid! Falling back to auto-detection.`);
55649
56960
  }
55650
56961
  if (anthropicApiKey) {
55651
56962
  this.initializeAnthropicModel(anthropicApiKey, anthropicApiUrl, modelName);
56963
+ this.initializeFallbackManager("anthropic", modelName);
55652
56964
  } else if (openaiApiKey) {
55653
56965
  this.initializeOpenAIModel(openaiApiKey, openaiApiUrl, modelName);
56966
+ this.initializeFallbackManager("openai", modelName);
55654
56967
  } else if (googleApiKey) {
55655
56968
  this.initializeGoogleModel(googleApiKey, googleApiUrl, modelName);
56969
+ this.initializeFallbackManager("google", modelName);
55656
56970
  } else if (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey) {
55657
56971
  this.initializeBedrockModel(awsAccessKeyId, awsSecretAccessKey, awsRegion, awsSessionToken, awsApiKey, awsBedrockBaseUrl, modelName);
56972
+ this.initializeFallbackManager("bedrock", modelName);
55658
56973
  } else {
55659
56974
  throw new Error("No API key provided. Please set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN), OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY (or GOOGLE_API_KEY), AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION), or AWS_BEDROCK_API_KEY environment variables.");
55660
56975
  }
55661
56976
  }
56977
+ /**
56978
+ * Initialize fallback manager based on configuration
56979
+ * @param {string} primaryProvider - The primary provider being used
56980
+ * @param {string} primaryModel - The primary model being used
56981
+ * @private
56982
+ */
56983
+ initializeFallbackManager(primaryProvider, primaryModel) {
56984
+ if (this.fallbackConfig === false || process.env.DISABLE_FALLBACK === "1") {
56985
+ return;
56986
+ }
56987
+ if (this.fallbackConfig && this.fallbackConfig.providers) {
56988
+ try {
56989
+ this.fallbackManager = new FallbackManager({
56990
+ ...this.fallbackConfig,
56991
+ debug: this.debug
56992
+ });
56993
+ if (this.debug) {
56994
+ console.log(`[DEBUG] Fallback manager initialized with ${this.fallbackManager.providers.length} providers`);
56995
+ }
56996
+ } catch (error) {
56997
+ console.error("[WARNING] Failed to initialize fallback manager:", error.message);
56998
+ }
56999
+ return;
57000
+ }
57001
+ const envFallbackManager = createFallbackManagerFromEnv(this.debug);
57002
+ if (envFallbackManager) {
57003
+ this.fallbackManager = envFallbackManager;
57004
+ if (this.debug) {
57005
+ console.log(`[DEBUG] Fallback manager initialized from environment variables`);
57006
+ }
57007
+ return;
57008
+ }
57009
+ if (process.env.AUTO_FALLBACK === "1" || this.fallbackConfig?.auto) {
57010
+ const providers = buildFallbackProvidersFromEnv({
57011
+ primaryProvider,
57012
+ primaryModel
57013
+ });
57014
+ if (providers.length > 1) {
57015
+ try {
57016
+ this.fallbackManager = new FallbackManager({
57017
+ strategy: "custom",
57018
+ providers,
57019
+ debug: this.debug
57020
+ });
57021
+ if (this.debug) {
57022
+ console.log(`[DEBUG] Auto-fallback enabled with ${providers.length} providers`);
57023
+ }
57024
+ } catch (error) {
57025
+ console.error("[WARNING] Failed to initialize auto-fallback:", error.message);
57026
+ }
57027
+ }
57028
+ }
57029
+ }
57030
+ /**
57031
+ * Execute streamText with retry and fallback support
57032
+ * @param {Object} options - streamText options
57033
+ * @returns {Promise<Object>} - streamText result
57034
+ * @private
57035
+ */
57036
+ async streamTextWithRetryAndFallback(options) {
57037
+ if (!this.retryManager) {
57038
+ this.retryManager = new RetryManager({
57039
+ maxRetries: this.retryConfig.maxRetries ?? 3,
57040
+ initialDelay: this.retryConfig.initialDelay ?? 1e3,
57041
+ maxDelay: this.retryConfig.maxDelay ?? 3e4,
57042
+ backoffFactor: this.retryConfig.backoffFactor ?? 2,
57043
+ retryableErrors: this.retryConfig.retryableErrors,
57044
+ debug: this.debug
57045
+ });
57046
+ }
57047
+ if (!this.fallbackManager) {
57048
+ return await this.retryManager.executeWithRetry(
57049
+ () => streamText(options),
57050
+ {
57051
+ provider: this.apiType,
57052
+ model: this.model
57053
+ }
57054
+ );
57055
+ }
57056
+ return await this.fallbackManager.executeWithFallback(
57057
+ async (provider, model, config) => {
57058
+ const fallbackOptions = {
57059
+ ...options,
57060
+ model: provider(model)
57061
+ };
57062
+ const providerRetryManager = new RetryManager({
57063
+ maxRetries: config.maxRetries ?? this.retryConfig.maxRetries ?? 3,
57064
+ initialDelay: this.retryConfig.initialDelay ?? 1e3,
57065
+ maxDelay: this.retryConfig.maxDelay ?? 3e4,
57066
+ backoffFactor: this.retryConfig.backoffFactor ?? 2,
57067
+ retryableErrors: this.retryConfig.retryableErrors,
57068
+ debug: this.debug
57069
+ });
57070
+ return await providerRetryManager.executeWithRetry(
57071
+ () => streamText(fallbackOptions),
57072
+ {
57073
+ provider: config.provider,
57074
+ model
57075
+ }
57076
+ );
57077
+ }
57078
+ );
57079
+ }
55662
57080
  /**
55663
57081
  * Initialize Anthropic model
55664
57082
  */
55665
57083
  initializeAnthropicModel(apiKey, apiUrl, modelName) {
55666
- this.provider = createAnthropic({
57084
+ this.provider = createAnthropic2({
55667
57085
  apiKey,
55668
57086
  ...apiUrl && { baseURL: apiUrl }
55669
57087
  });
@@ -55677,7 +57095,7 @@ var init_ProbeAgent = __esm({
55677
57095
  * Initialize OpenAI model
55678
57096
  */
55679
57097
  initializeOpenAIModel(apiKey, apiUrl, modelName) {
55680
- this.provider = createOpenAI({
57098
+ this.provider = createOpenAI2({
55681
57099
  compatibility: "strict",
55682
57100
  apiKey,
55683
57101
  ...apiUrl && { baseURL: apiUrl }
@@ -55692,7 +57110,7 @@ var init_ProbeAgent = __esm({
55692
57110
  * Initialize Google model
55693
57111
  */
55694
57112
  initializeGoogleModel(apiKey, apiUrl, modelName) {
55695
- this.provider = createGoogleGenerativeAI({
57113
+ this.provider = createGoogleGenerativeAI2({
55696
57114
  apiKey,
55697
57115
  ...apiUrl && { baseURL: apiUrl }
55698
57116
  });
@@ -55722,7 +57140,7 @@ var init_ProbeAgent = __esm({
55722
57140
  if (baseURL) {
55723
57141
  config.baseURL = baseURL;
55724
57142
  }
55725
- this.provider = createAmazonBedrock(config);
57143
+ this.provider = createAmazonBedrock2(config);
55726
57144
  this.model = modelName || "anthropic.claude-sonnet-4-20250514-v1:0";
55727
57145
  this.apiType = "bedrock";
55728
57146
  if (this.debug) {
@@ -55767,7 +57185,7 @@ var init_ProbeAgent = __esm({
55767
57185
  let resolvedPath = imagePath;
55768
57186
  if (!imagePath.includes("/") && !imagePath.includes("\\")) {
55769
57187
  for (const dir of listFilesDirectories) {
55770
- const potentialPath = resolve3(dir, imagePath);
57188
+ const potentialPath = resolve4(dir, imagePath);
55771
57189
  const loaded = await this.loadImageIfValid(potentialPath);
55772
57190
  if (loaded) {
55773
57191
  if (this.debug) {
@@ -55792,7 +57210,7 @@ var init_ProbeAgent = __esm({
55792
57210
  let match2;
55793
57211
  while ((match2 = fileHeaderPattern.exec(content)) !== null) {
55794
57212
  const filePath = match2[1].trim();
55795
- const dir = dirname3(filePath);
57213
+ const dir = dirname4(filePath);
55796
57214
  if (dir && dir !== ".") {
55797
57215
  directories.push(dir);
55798
57216
  if (this.debug) {
@@ -55836,21 +57254,21 @@ var init_ProbeAgent = __esm({
55836
57254
  }
55837
57255
  const allowedDirs = this.allowedFolders && this.allowedFolders.length > 0 ? this.allowedFolders : [process.cwd()];
55838
57256
  let absolutePath;
55839
- let isPathAllowed = false;
55840
- if (isAbsolute(imagePath)) {
57257
+ let isPathAllowed2 = false;
57258
+ if (isAbsolute2(imagePath)) {
55841
57259
  absolutePath = imagePath;
55842
- isPathAllowed = allowedDirs.some((dir) => absolutePath.startsWith(resolve3(dir)));
57260
+ isPathAllowed2 = allowedDirs.some((dir) => absolutePath.startsWith(resolve4(dir)));
55843
57261
  } else {
55844
57262
  for (const dir of allowedDirs) {
55845
- const resolvedPath = resolve3(dir, imagePath);
55846
- if (resolvedPath.startsWith(resolve3(dir))) {
57263
+ const resolvedPath = resolve4(dir, imagePath);
57264
+ if (resolvedPath.startsWith(resolve4(dir))) {
55847
57265
  absolutePath = resolvedPath;
55848
- isPathAllowed = true;
57266
+ isPathAllowed2 = true;
55849
57267
  break;
55850
57268
  }
55851
57269
  }
55852
57270
  }
55853
- if (!isPathAllowed) {
57271
+ if (!isPathAllowed2) {
55854
57272
  if (this.debug) {
55855
57273
  console.log(`[DEBUG] Image path outside allowed directories: ${imagePath}`);
55856
57274
  }
@@ -56049,6 +57467,18 @@ ${attemptCompletionToolDefinition}
56049
57467
  `;
56050
57468
  if (this.allowEdit) {
56051
57469
  toolDefinitions += `${implementToolDefinition}
57470
+ `;
57471
+ toolDefinitions += `${editToolDefinition}
57472
+ `;
57473
+ toolDefinitions += `${createToolDefinition}
57474
+ `;
57475
+ }
57476
+ if (this.enableBash) {
57477
+ toolDefinitions += `${bashToolDefinition}
57478
+ `;
57479
+ }
57480
+ if (this.enableDelegate) {
57481
+ toolDefinitions += `${delegateToolDefinition}
56052
57482
  `;
56053
57483
  }
56054
57484
  let xmlToolGuidelines = `
@@ -56112,7 +57542,7 @@ Available Tools:
56112
57542
  - extract: Extract specific code blocks or lines from files.
56113
57543
  - listFiles: List files and directories in a specified location.
56114
57544
  - searchFiles: Find files matching a glob pattern with recursive search capability.
56115
- ${this.allowEdit ? "- implement: Implement a feature or fix a bug using aider.\n" : ""}
57545
+ ${this.allowEdit ? "- implement: Implement a feature or fix a bug using aider.\n- edit: Edit files using exact string replacement.\n- create: Create new files with specified content.\n" : ""}${this.enableDelegate ? "- delegate: Delegate big distinct tasks to specialized probe subagents.\n" : ""}${this.enableBash ? "- bash: Execute bash commands for system operations.\n" : ""}
56116
57546
  - attempt_completion: Finalize the task and provide the result to the user.
56117
57547
  - attempt_complete: Quick completion using previous response (shorthand).
56118
57548
  `;
@@ -56126,7 +57556,10 @@ Follow these instructions carefully:
56126
57556
  6. You MUST respond with exactly ONE tool call per message, using the specified XML format, until the task is complete.
56127
57557
  7. Wait for the tool execution result (provided in the next user message in a <tool_result> block) before proceeding to the next step.
56128
57558
  8. Once the task is fully completed, use the '<attempt_completion>' tool to provide the final result. This is the ONLY way to signal completion.
56129
- 9. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results.
57559
+ 9. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results.${this.allowEdit ? `
57560
+ 10. When modifying files, choose the appropriate tool:
57561
+ - Use 'edit' for precise changes to existing files (requires exact string match)
57562
+ - Use 'create' for new files or complete file rewrites` : ""}
56130
57563
  </instructions>
56131
57564
  `;
56132
57565
  const predefinedPrompts = {
@@ -56378,7 +57811,7 @@ You are working with a repository located at: ${searchDirectory}
56378
57811
  try {
56379
57812
  const executeAIRequest = async () => {
56380
57813
  const messagesForAI = this.prepareMessagesWithImages(currentMessages);
56381
- const result = await streamText({
57814
+ const result = await this.streamTextWithRetryAndFallback({
56382
57815
  model: this.provider(this.model),
56383
57816
  messages: messagesForAI,
56384
57817
  maxTokens: maxResponseTokens,
@@ -56430,7 +57863,13 @@ You are working with a repository located at: ${searchDirectory}
56430
57863
  "attempt_completion"
56431
57864
  ];
56432
57865
  if (this.allowEdit) {
56433
- validTools.push("implement");
57866
+ validTools.push("implement", "edit", "create");
57867
+ }
57868
+ if (this.enableBash) {
57869
+ validTools.push("bash");
57870
+ }
57871
+ if (this.enableDelegate) {
57872
+ validTools.push("delegate");
56434
57873
  }
56435
57874
  const nativeTools = validTools;
56436
57875
  const parsedTool = this.mcpBridge ? parseHybridXmlToolCall(assistantResponseContent, nativeTools, this.mcpBridge) : parseXmlToolCallWithThinking(assistantResponseContent, validTools);
@@ -56545,11 +57984,21 @@ ${toolResultContent}
56545
57984
  ...toolParams,
56546
57985
  currentIteration,
56547
57986
  maxIterations,
57987
+ parentSessionId: this.sessionId,
57988
+ // Pass parent session ID for tracking
57989
+ path: this.searchPath,
57990
+ // Inherit search path
57991
+ provider: this.provider,
57992
+ // Inherit AI provider
57993
+ model: this.model,
57994
+ // Inherit model
56548
57995
  debug: this.debug,
56549
57996
  tracer: this.tracer
56550
57997
  };
56551
57998
  if (this.debug) {
56552
57999
  console.log(`[DEBUG] Executing delegate tool at iteration ${currentIteration}/${maxIterations}`);
58000
+ console.log(`[DEBUG] Parent session: ${this.sessionId}`);
58001
+ console.log(`[DEBUG] Inherited config: path=${this.searchPath}, provider=${this.provider}, model=${this.model}`);
56553
58002
  console.log(`[DEBUG] Delegate task: ${toolParams.task?.substring(0, 100)}...`);
56554
58003
  }
56555
58004
  if (this.tracer) {
@@ -57140,6 +58589,7 @@ Convert your previous response content into actual JSON data that follows this s
57140
58589
  customPrompt: this.customPrompt,
57141
58590
  promptType: this.promptType,
57142
58591
  allowEdit: this.allowEdit,
58592
+ enableDelegate: this.enableDelegate,
57143
58593
  path: this.allowedFolders[0],
57144
58594
  // Use first allowed folder as primary path
57145
58595
  allowedFolders: [...this.allowedFolders],
@@ -57330,8 +58780,8 @@ import {
57330
58780
  ListToolsRequestSchema,
57331
58781
  McpError
57332
58782
  } from "@modelcontextprotocol/sdk/types.js";
57333
- import { readFileSync as readFileSync2, existsSync as existsSync5 } from "fs";
57334
- import { resolve as resolve4 } from "path";
58783
+ import { readFileSync as readFileSync2, existsSync as existsSync6 } from "fs";
58784
+ import { resolve as resolve5 } from "path";
57335
58785
 
57336
58786
  // src/agent/acp/server.js
57337
58787
  import { randomUUID as randomUUID5 } from "crypto";
@@ -57590,8 +59040,8 @@ var ACPConnection = class extends EventEmitter4 {
57590
59040
  if (params !== null) {
57591
59041
  message.params = params;
57592
59042
  }
57593
- return new Promise((resolve5, reject2) => {
57594
- this.pendingRequests.set(id, { resolve: resolve5, reject: reject2 });
59043
+ return new Promise((resolve6, reject2) => {
59044
+ this.pendingRequests.set(id, { resolve: resolve6, reject: reject2 });
57595
59045
  this.sendMessage(message);
57596
59046
  setTimeout(() => {
57597
59047
  if (this.pendingRequests.has(id)) {
@@ -57896,6 +59346,7 @@ var ACPServer = class {
57896
59346
  provider: this.options.provider,
57897
59347
  model: this.options.model,
57898
59348
  allowEdit: this.options.allowEdit,
59349
+ enableDelegate: this.options.enableDelegate,
57899
59350
  debug: this.options.debug,
57900
59351
  enableMcp: this.options.enableMcp,
57901
59352
  mcpConfig: this.options.mcpConfig,
@@ -58004,8 +59455,8 @@ dotenv3.config();
58004
59455
  function readInputContent(input) {
58005
59456
  if (!input) return null;
58006
59457
  try {
58007
- const resolvedPath = resolve4(input);
58008
- if (existsSync5(resolvedPath)) {
59458
+ const resolvedPath = resolve5(input);
59459
+ if (existsSync6(resolvedPath)) {
58009
59460
  return readFileSync2(resolvedPath, "utf-8").trim();
58010
59461
  }
58011
59462
  } catch (error) {
@@ -58013,7 +59464,7 @@ function readInputContent(input) {
58013
59464
  return input;
58014
59465
  }
58015
59466
  function readFromStdin() {
58016
- return new Promise((resolve5, reject2) => {
59467
+ return new Promise((resolve6, reject2) => {
58017
59468
  let data = "";
58018
59469
  let hasReceivedData = false;
58019
59470
  let dataChunks = [];
@@ -58038,7 +59489,7 @@ function readFromStdin() {
58038
59489
  if (!trimmed && dataChunks.length === 0) {
58039
59490
  reject2(new Error("No input received from stdin"));
58040
59491
  } else {
58041
- resolve5(trimmed);
59492
+ resolve6(trimmed);
58042
59493
  }
58043
59494
  });
58044
59495
  process.stdin.on("error", (error) => {
@@ -58070,6 +59521,7 @@ function parseArgs() {
58070
59521
  provider: null,
58071
59522
  model: null,
58072
59523
  allowEdit: false,
59524
+ enableDelegate: false,
58073
59525
  verbose: false,
58074
59526
  help: false,
58075
59527
  maxIterations: null,
@@ -58104,6 +59556,10 @@ function parseArgs() {
58104
59556
  config.verbose = true;
58105
59557
  } else if (arg === "--allow-edit") {
58106
59558
  config.allowEdit = true;
59559
+ } else if (arg === "--enable-delegate") {
59560
+ config.enableDelegate = true;
59561
+ } else if (arg === "--no-delegate") {
59562
+ config.enableDelegate = false;
58107
59563
  } else if (arg === "--path" && i + 1 < args.length) {
58108
59564
  config.path = args[++i];
58109
59565
  } else if (arg === "--allowed-folders" && i + 1 < args.length) {
@@ -58175,6 +59631,7 @@ Options:
58175
59631
  --provider <name> Force AI provider: anthropic, openai, google
58176
59632
  --model <name> Override model name
58177
59633
  --allow-edit Enable code modification capabilities
59634
+ --enable-delegate Enable delegate tool for task distribution to subagents
58178
59635
  --verbose Enable verbose output
58179
59636
  --outline Use outline-xml format for code search results
58180
59637
  --mcp Run as MCP server
@@ -58455,6 +59912,7 @@ async function main() {
58455
59912
  model: config.model,
58456
59913
  path: config.path,
58457
59914
  allowEdit: config.allowEdit,
59915
+ enableDelegate: config.enableDelegate,
58458
59916
  debug: config.verbose
58459
59917
  });
58460
59918
  await server.start();
@@ -58557,7 +60015,7 @@ async function main() {
58557
60015
  bashConfig.timeout = timeout;
58558
60016
  }
58559
60017
  if (config.bashWorkingDir) {
58560
- if (!existsSync5(config.bashWorkingDir)) {
60018
+ if (!existsSync6(config.bashWorkingDir)) {
58561
60019
  console.error(`Error: Bash working directory does not exist: ${config.bashWorkingDir}`);
58562
60020
  process.exit(1);
58563
60021
  }
@@ -58573,6 +60031,7 @@ async function main() {
58573
60031
  promptType: config.prompt,
58574
60032
  customPrompt: systemPrompt,
58575
60033
  allowEdit: config.allowEdit,
60034
+ enableDelegate: config.enableDelegate,
58576
60035
  debug: config.verbose,
58577
60036
  tracer: appTracer,
58578
60037
  outline: config.outline,