@probelabs/probe 0.6.0-rc159 → 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 (36) 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 +7 -1
  5. package/build/agent/ProbeAgent.js +199 -7
  6. package/build/agent/RetryManager.d.ts +157 -0
  7. package/build/agent/RetryManager.js +334 -0
  8. package/build/agent/acp/tools.js +6 -2
  9. package/build/agent/index.js +1379 -168
  10. package/build/agent/probeTool.js +20 -2
  11. package/build/agent/tools.js +16 -0
  12. package/build/index.js +13 -0
  13. package/build/tools/common.js +5 -3
  14. package/build/tools/edit.js +409 -0
  15. package/build/tools/index.js +11 -0
  16. package/cjs/agent/ProbeAgent.cjs +1822 -497
  17. package/cjs/index.cjs +1784 -454
  18. package/package.json +2 -2
  19. package/src/agent/FallbackManager.d.ts +176 -0
  20. package/src/agent/FallbackManager.js +545 -0
  21. package/src/agent/ProbeAgent.d.ts +7 -1
  22. package/src/agent/ProbeAgent.js +199 -7
  23. package/src/agent/RetryManager.d.ts +157 -0
  24. package/src/agent/RetryManager.js +334 -0
  25. package/src/agent/acp/tools.js +6 -2
  26. package/src/agent/probeTool.js +20 -2
  27. package/src/agent/tools.js +16 -0
  28. package/src/index.js +13 -0
  29. package/src/tools/common.js +5 -3
  30. package/src/tools/edit.js +409 -0
  31. package/src/tools/index.js +11 -0
  32. package/bin/binaries/probe-v0.6.0-rc159-aarch64-apple-darwin.tar.gz +0 -0
  33. package/bin/binaries/probe-v0.6.0-rc159-aarch64-unknown-linux-musl.tar.gz +0 -0
  34. package/bin/binaries/probe-v0.6.0-rc159-x86_64-apple-darwin.tar.gz +0 -0
  35. package/bin/binaries/probe-v0.6.0-rc159-x86_64-pc-windows-msvc.zip +0 -0
  36. package/bin/binaries/probe-v0.6.0-rc159-x86_64-unknown-linux-musl.tar.gz +0 -0
@@ -2007,7 +2007,7 @@ async function waitForFileLock(lockPath, binaryPath) {
2007
2007
  }
2008
2008
  } catch {
2009
2009
  }
2010
- await new Promise((resolve5) => setTimeout(resolve5, LOCK_POLL_INTERVAL_MS));
2010
+ await new Promise((resolve6) => setTimeout(resolve6, LOCK_POLL_INTERVAL_MS));
2011
2011
  }
2012
2012
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2013
2013
  console.log(`Timeout waiting for file lock`);
@@ -2978,7 +2978,7 @@ Command: ${command}`;
2978
2978
  }
2979
2979
  }
2980
2980
  function extractWithStdin(binaryPath, cliArgs, content, options) {
2981
- return new Promise((resolve5, reject2) => {
2981
+ return new Promise((resolve6, reject2) => {
2982
2982
  const childProcess = spawn(binaryPath, ["extract", ...cliArgs], {
2983
2983
  stdio: ["pipe", "pipe", "pipe"]
2984
2984
  });
@@ -3000,7 +3000,7 @@ function extractWithStdin(binaryPath, cliArgs, content, options) {
3000
3000
  }
3001
3001
  try {
3002
3002
  const result = processExtractOutput(stdout, options);
3003
- resolve5(result);
3003
+ resolve6(result);
3004
3004
  } catch (error) {
3005
3005
  reject2(error);
3006
3006
  }
@@ -7560,7 +7560,7 @@ function parseTargets(targets) {
7560
7560
  }
7561
7561
  return targets.split(/\s+/).filter((f) => f.length > 0);
7562
7562
  }
7563
- var searchSchema, querySchema, extractSchema, delegateSchema, bashSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, 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;
7564
7564
  var init_common = __esm({
7565
7565
  "src/tools/common.js"() {
7566
7566
  "use strict";
@@ -7573,11 +7573,12 @@ var init_common = __esm({
7573
7573
  pattern: external_exports.string().describe("AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc."),
7574
7574
  path: external_exports.string().optional().default(".").describe("Path to search in"),
7575
7575
  language: external_exports.string().optional().default("rust").describe("Programming language to use for parsing"),
7576
- 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")
7577
7577
  });
7578
7578
  extractSchema = external_exports.object({
7579
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.'),
7580
- 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")
7581
7582
  });
7582
7583
  delegateSchema = external_exports.object({
7583
7584
  task: external_exports.string().describe("The task to delegate to a subagent. Be specific about what needs to be accomplished.")
@@ -7704,7 +7705,7 @@ Parameters:
7704
7705
  - pattern: (required) AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc.
7705
7706
  - path: (optional, default: '.') Path to search in.
7706
7707
  - language: (optional, default: 'rust') Programming language to use for parsing.
7707
- - 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).
7708
7709
  Usage Example:
7709
7710
 
7710
7711
  <examples>
@@ -7729,6 +7730,7 @@ Full file extraction should be the LAST RESORT! Always prefer search.
7729
7730
  Parameters:
7730
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.
7731
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.
7732
7734
 
7733
7735
  Usage Example:
7734
7736
 
@@ -7795,6 +7797,61 @@ Usage Example:
7795
7797
  <attempt_completion>
7796
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.
7797
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>
7798
7855
  `;
7799
7856
  searchDescription = "Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.";
7800
7857
  queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
@@ -8894,14 +8951,14 @@ async function executeBashCommand(command, options = {}) {
8894
8951
  console.log(`[BashExecutor] Working directory: "${cwd}"`);
8895
8952
  console.log(`[BashExecutor] Timeout: ${timeout}ms`);
8896
8953
  }
8897
- return new Promise((resolve5, reject2) => {
8954
+ return new Promise((resolve6, reject2) => {
8898
8955
  const processEnv = {
8899
8956
  ...process.env,
8900
8957
  ...env
8901
8958
  };
8902
8959
  const args = parseCommandForExecution(command);
8903
8960
  if (!args || args.length === 0) {
8904
- resolve5({
8961
+ resolve6({
8905
8962
  success: false,
8906
8963
  error: "Failed to parse command",
8907
8964
  stdout: "",
@@ -8984,7 +9041,7 @@ async function executeBashCommand(command, options = {}) {
8984
9041
  success = false;
8985
9042
  error = `Command exited with code ${code}`;
8986
9043
  }
8987
- resolve5({
9044
+ resolve6({
8988
9045
  success,
8989
9046
  error,
8990
9047
  stdout: stdout.trim(),
@@ -9004,7 +9061,7 @@ async function executeBashCommand(command, options = {}) {
9004
9061
  if (debug) {
9005
9062
  console.log(`[BashExecutor] Spawn error:`, error);
9006
9063
  }
9007
- resolve5({
9064
+ resolve6({
9008
9065
  success: false,
9009
9066
  error: `Failed to execute command: ${error.message}`,
9010
9067
  stdout: "",
@@ -9276,6 +9333,283 @@ Command failed with exit code ${result.exitCode}`;
9276
9333
  }
9277
9334
  });
9278
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
+
9279
9613
  // src/tools/langchain.js
9280
9614
  var init_langchain = __esm({
9281
9615
  "src/tools/langchain.js"() {
@@ -9425,8 +9759,10 @@ var init_tools = __esm({
9425
9759
  "use strict";
9426
9760
  init_vercel();
9427
9761
  init_bash();
9762
+ init_edit();
9428
9763
  init_langchain();
9429
9764
  init_common();
9765
+ init_edit();
9430
9766
  init_system_message();
9431
9767
  init_vercel();
9432
9768
  init_bash();
@@ -9443,7 +9779,7 @@ var init_tools = __esm({
9443
9779
  });
9444
9780
 
9445
9781
  // src/utils/file-lister.js
9446
- import fs4 from "fs";
9782
+ import fs5 from "fs";
9447
9783
  import path4 from "path";
9448
9784
  import { promisify as promisify6 } from "util";
9449
9785
  import { exec as exec4 } from "child_process";
@@ -9453,10 +9789,10 @@ async function listFilesByLevel(options) {
9453
9789
  maxFiles = 100,
9454
9790
  respectGitignore = true
9455
9791
  } = options;
9456
- if (!fs4.existsSync(directory)) {
9792
+ if (!fs5.existsSync(directory)) {
9457
9793
  throw new Error(`Directory does not exist: ${directory}`);
9458
9794
  }
9459
- const gitDirExists = fs4.existsSync(path4.join(directory, ".git"));
9795
+ const gitDirExists = fs5.existsSync(path4.join(directory, ".git"));
9460
9796
  if (gitDirExists && respectGitignore) {
9461
9797
  try {
9462
9798
  return await listFilesUsingGit(directory, maxFiles);
@@ -9487,7 +9823,7 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
9487
9823
  while (queue.length > 0 && result.length < maxFiles) {
9488
9824
  const { dir, level } = queue.shift();
9489
9825
  try {
9490
- const entries = fs4.readdirSync(dir, { withFileTypes: true });
9826
+ const entries = fs5.readdirSync(dir, { withFileTypes: true });
9491
9827
  const files = entries.filter((entry) => entry.isFile());
9492
9828
  for (const file of files) {
9493
9829
  if (result.length >= maxFiles) break;
@@ -9512,11 +9848,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
9512
9848
  }
9513
9849
  function loadGitignorePatterns(directory) {
9514
9850
  const gitignorePath = path4.join(directory, ".gitignore");
9515
- if (!fs4.existsSync(gitignorePath)) {
9851
+ if (!fs5.existsSync(gitignorePath)) {
9516
9852
  return [];
9517
9853
  }
9518
9854
  try {
9519
- const content = fs4.readFileSync(gitignorePath, "utf8");
9855
+ const content = fs5.readFileSync(gitignorePath, "utf8");
9520
9856
  return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
9521
9857
  } catch (error) {
9522
9858
  console.error(`Warning: Could not read .gitignore: ${error.message}`);
@@ -9543,8 +9879,8 @@ var init_file_lister = __esm({
9543
9879
  });
9544
9880
 
9545
9881
  // src/agent/simpleTelemetry.js
9546
- import { existsSync as existsSync2, mkdirSync, createWriteStream } from "fs";
9547
- import { dirname } from "path";
9882
+ import { existsSync as existsSync3, mkdirSync, createWriteStream } from "fs";
9883
+ import { dirname as dirname2 } from "path";
9548
9884
  function initializeSimpleTelemetryFromOptions(options) {
9549
9885
  const telemetry = new SimpleTelemetry({
9550
9886
  serviceName: "probe-agent",
@@ -9571,8 +9907,8 @@ var init_simpleTelemetry = __esm({
9571
9907
  }
9572
9908
  initializeFileExporter() {
9573
9909
  try {
9574
- const dir = dirname(this.filePath);
9575
- if (!existsSync2(dir)) {
9910
+ const dir = dirname2(this.filePath);
9911
+ if (!existsSync3(dir)) {
9576
9912
  mkdirSync(dir, { recursive: true });
9577
9913
  }
9578
9914
  this.stream = createWriteStream(this.filePath, { flags: "a" });
@@ -9636,20 +9972,20 @@ var init_simpleTelemetry = __esm({
9636
9972
  }
9637
9973
  async flush() {
9638
9974
  if (this.stream) {
9639
- return new Promise((resolve5) => {
9640
- this.stream.once("drain", resolve5);
9975
+ return new Promise((resolve6) => {
9976
+ this.stream.once("drain", resolve6);
9641
9977
  if (!this.stream.writableNeedDrain) {
9642
- resolve5();
9978
+ resolve6();
9643
9979
  }
9644
9980
  });
9645
9981
  }
9646
9982
  }
9647
9983
  async shutdown() {
9648
9984
  if (this.stream) {
9649
- return new Promise((resolve5) => {
9985
+ return new Promise((resolve6) => {
9650
9986
  this.stream.end(() => {
9651
9987
  console.log(`[SimpleTelemetry] File stream closed: ${this.filePath}`);
9652
- resolve5();
9988
+ resolve6();
9653
9989
  });
9654
9990
  });
9655
9991
  }
@@ -10612,7 +10948,7 @@ var init_escape = __esm({
10612
10948
  });
10613
10949
 
10614
10950
  // node_modules/minimatch/dist/esm/index.js
10615
- 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;
10616
10952
  var init_esm = __esm({
10617
10953
  "node_modules/minimatch/dist/esm/index.js"() {
10618
10954
  import_brace_expansion = __toESM(require_brace_expansion(), 1);
@@ -10685,8 +11021,8 @@ var init_esm = __esm({
10685
11021
  win32: { sep: "\\" },
10686
11022
  posix: { sep: "/" }
10687
11023
  };
10688
- sep = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
10689
- minimatch.sep = sep;
11024
+ sep2 = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
11025
+ minimatch.sep = sep2;
10690
11026
  GLOBSTAR = Symbol("globstar **");
10691
11027
  minimatch.GLOBSTAR = GLOBSTAR;
10692
11028
  qmark2 = "[^/]";
@@ -13448,10 +13784,10 @@ var init_esm3 = __esm({
13448
13784
  * Return a void Promise that resolves once the stream ends.
13449
13785
  */
13450
13786
  async promise() {
13451
- return new Promise((resolve5, reject2) => {
13787
+ return new Promise((resolve6, reject2) => {
13452
13788
  this.on(DESTROYED, () => reject2(new Error("stream destroyed")));
13453
13789
  this.on("error", (er) => reject2(er));
13454
- this.on("end", () => resolve5());
13790
+ this.on("end", () => resolve6());
13455
13791
  });
13456
13792
  }
13457
13793
  /**
@@ -13475,7 +13811,7 @@ var init_esm3 = __esm({
13475
13811
  return Promise.resolve({ done: false, value: res });
13476
13812
  if (this[EOF])
13477
13813
  return stop();
13478
- let resolve5;
13814
+ let resolve6;
13479
13815
  let reject2;
13480
13816
  const onerr = (er) => {
13481
13817
  this.off("data", ondata);
@@ -13489,19 +13825,19 @@ var init_esm3 = __esm({
13489
13825
  this.off("end", onend);
13490
13826
  this.off(DESTROYED, ondestroy);
13491
13827
  this.pause();
13492
- resolve5({ value, done: !!this[EOF] });
13828
+ resolve6({ value, done: !!this[EOF] });
13493
13829
  };
13494
13830
  const onend = () => {
13495
13831
  this.off("error", onerr);
13496
13832
  this.off("data", ondata);
13497
13833
  this.off(DESTROYED, ondestroy);
13498
13834
  stop();
13499
- resolve5({ done: true, value: void 0 });
13835
+ resolve6({ done: true, value: void 0 });
13500
13836
  };
13501
13837
  const ondestroy = () => onerr(new Error("stream destroyed"));
13502
13838
  return new Promise((res2, rej) => {
13503
13839
  reject2 = rej;
13504
- resolve5 = res2;
13840
+ resolve6 = res2;
13505
13841
  this.once(DESTROYED, ondestroy);
13506
13842
  this.once("error", onerr);
13507
13843
  this.once("end", onend);
@@ -14481,9 +14817,9 @@ var init_esm4 = __esm({
14481
14817
  if (this.#asyncReaddirInFlight) {
14482
14818
  await this.#asyncReaddirInFlight;
14483
14819
  } else {
14484
- let resolve5 = () => {
14820
+ let resolve6 = () => {
14485
14821
  };
14486
- this.#asyncReaddirInFlight = new Promise((res) => resolve5 = res);
14822
+ this.#asyncReaddirInFlight = new Promise((res) => resolve6 = res);
14487
14823
  try {
14488
14824
  for (const e of await this.#fs.promises.readdir(fullpath, {
14489
14825
  withFileTypes: true
@@ -14496,7 +14832,7 @@ var init_esm4 = __esm({
14496
14832
  children.provisional = 0;
14497
14833
  }
14498
14834
  this.#asyncReaddirInFlight = void 0;
14499
- resolve5();
14835
+ resolve6();
14500
14836
  }
14501
14837
  return children.slice(0, children.provisional);
14502
14838
  }
@@ -14726,8 +15062,8 @@ var init_esm4 = __esm({
14726
15062
  *
14727
15063
  * @internal
14728
15064
  */
14729
- constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs6 = defaultFS } = {}) {
14730
- this.#fs = fsFromOption(fs6);
15065
+ constructor(cwd = process.cwd(), pathImpl, sep3, { nocase, childrenCacheSize = 16 * 1024, fs: fs7 = defaultFS } = {}) {
15066
+ this.#fs = fsFromOption(fs7);
14731
15067
  if (cwd instanceof URL || cwd.startsWith("file://")) {
14732
15068
  cwd = fileURLToPath4(cwd);
14733
15069
  }
@@ -14737,7 +15073,7 @@ var init_esm4 = __esm({
14737
15073
  this.#resolveCache = new ResolveCache();
14738
15074
  this.#resolvePosixCache = new ResolveCache();
14739
15075
  this.#children = new ChildrenCache(childrenCacheSize);
14740
- const split = cwdPath.substring(this.rootPath.length).split(sep2);
15076
+ const split = cwdPath.substring(this.rootPath.length).split(sep3);
14741
15077
  if (split.length === 1 && !split[0]) {
14742
15078
  split.pop();
14743
15079
  }
@@ -15285,8 +15621,8 @@ var init_esm4 = __esm({
15285
15621
  /**
15286
15622
  * @internal
15287
15623
  */
15288
- newRoot(fs6) {
15289
- 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 });
15290
15626
  }
15291
15627
  /**
15292
15628
  * Return true if the provided path string is an absolute path
@@ -15314,8 +15650,8 @@ var init_esm4 = __esm({
15314
15650
  /**
15315
15651
  * @internal
15316
15652
  */
15317
- newRoot(fs6) {
15318
- 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 });
15319
15655
  }
15320
15656
  /**
15321
15657
  * Return true if the provided path string is an absolute path
@@ -16455,7 +16791,7 @@ import { exec as exec5 } from "child_process";
16455
16791
  import { promisify as promisify7 } from "util";
16456
16792
  import { randomUUID as randomUUID2 } from "crypto";
16457
16793
  import { EventEmitter as EventEmitter2 } from "events";
16458
- import fs5 from "fs";
16794
+ import fs6 from "fs";
16459
16795
  import { promises as fsPromises } from "fs";
16460
16796
  import path6 from "path";
16461
16797
  function isSessionCancelled(sessionId) {
@@ -16515,6 +16851,20 @@ function createWrappedTools(baseTools) {
16515
16851
  baseTools.bashTool.execute
16516
16852
  );
16517
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
+ }
16518
16868
  return wrappedTools;
16519
16869
  }
16520
16870
  var toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
@@ -16525,9 +16875,9 @@ var init_probeTool = __esm({
16525
16875
  init_esm5();
16526
16876
  toolCallEmitter = new EventEmitter2();
16527
16877
  activeToolExecutions = /* @__PURE__ */ new Map();
16528
- wrapToolWithEmitter = (tool3, toolName, baseExecute) => {
16878
+ wrapToolWithEmitter = (tool4, toolName, baseExecute) => {
16529
16879
  return {
16530
- ...tool3,
16880
+ ...tool4,
16531
16881
  // Spread schema, description etc.
16532
16882
  execute: async (params) => {
16533
16883
  const debug = process.env.DEBUG === "1";
@@ -16762,8 +17112,10 @@ var init_index = __esm({
16762
17112
  init_file_lister();
16763
17113
  init_system_message();
16764
17114
  init_common();
17115
+ init_edit();
16765
17116
  init_vercel();
16766
17117
  init_bash();
17118
+ init_edit();
16767
17119
  init_ProbeAgent();
16768
17120
  init_simpleTelemetry();
16769
17121
  init_probeTool();
@@ -16845,8 +17197,8 @@ function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
16845
17197
  function hasOtherToolTags(xmlString, validTools = []) {
16846
17198
  const defaultTools = ["search", "query", "extract", "listFiles", "searchFiles", "implement", "attempt_completion"];
16847
17199
  const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
16848
- for (const tool3 of toolsToCheck) {
16849
- if (tool3 !== "attempt_completion" && xmlString.includes(`<${tool3}`)) {
17200
+ for (const tool4 of toolsToCheck) {
17201
+ if (tool4 !== "attempt_completion" && xmlString.includes(`<${tool4}`)) {
16850
17202
  return true;
16851
17203
  }
16852
17204
  }
@@ -16884,6 +17236,10 @@ function createTools(configOptions) {
16884
17236
  if (configOptions.enableBash) {
16885
17237
  tools2.bashTool = bashTool(configOptions);
16886
17238
  }
17239
+ if (configOptions.allowEdit) {
17240
+ tools2.editTool = editTool(configOptions);
17241
+ tools2.createTool = createTool(configOptions);
17242
+ }
16887
17243
  return tools2;
16888
17244
  }
16889
17245
  function parseXmlToolCallWithThinking(xmlString, validTools) {
@@ -16984,7 +17340,7 @@ function createMockProvider() {
16984
17340
  provider: "mock",
16985
17341
  // Mock the doGenerate method used by Vercel AI SDK
16986
17342
  doGenerate: async ({ messages, tools: tools2 }) => {
16987
- await new Promise((resolve5) => setTimeout(resolve5, 10));
17343
+ await new Promise((resolve6) => setTimeout(resolve6, 10));
16988
17344
  return {
16989
17345
  text: "This is a mock response for testing",
16990
17346
  toolCalls: [],
@@ -28971,16 +29327,6 @@ var init_parser2 = __esm({
28971
29327
  this.subgraph = this.RULE("subgraph", () => {
28972
29328
  this.CONSUME(SubgraphKeyword);
28973
29329
  this.OR([
28974
- {
28975
- ALT: () => {
28976
- this.CONSUME(Identifier, { LABEL: "subgraphId" });
28977
- this.OPTION(() => {
28978
- this.CONSUME1(SquareOpen);
28979
- this.SUBRULE(this.nodeContent);
28980
- this.CONSUME1(SquareClose);
28981
- });
28982
- }
28983
- },
28984
29330
  {
28985
29331
  ALT: () => {
28986
29332
  this.CONSUME(QuotedString, { LABEL: "subgraphTitleQ" });
@@ -28992,6 +29338,33 @@ var init_parser2 = __esm({
28992
29338
  this.SUBRULE2(this.nodeContent);
28993
29339
  this.CONSUME2(SquareClose);
28994
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
+ }
28995
29368
  }
28996
29369
  ]);
28997
29370
  this.CONSUME(Newline);
@@ -29595,7 +29968,6 @@ var init_semantics = __esm({
29595
29968
  const last2 = allChildren[allChildren.length - 1];
29596
29969
  const isSlash = (t) => t.image === "/" || t.image === "\\";
29597
29970
  if (isSlash(first2) && isSlash(last2)) {
29598
- continue;
29599
29971
  }
29600
29972
  }
29601
29973
  const opens = ch.RoundOpen || [];
@@ -30389,16 +30761,19 @@ function mapFlowchartParserError(err, text) {
30389
30761
  const subgraphIdx = lineStr.indexOf("subgraph");
30390
30762
  if (subgraphIdx !== -1) {
30391
30763
  const afterSubgraph = lineStr.slice(subgraphIdx + 8).trim();
30392
- if (afterSubgraph && !afterSubgraph.startsWith('"') && !afterSubgraph.startsWith("'") && afterSubgraph.includes(" ")) {
30393
- return {
30394
- line,
30395
- column,
30396
- severity: "error",
30397
- code: "FL-SUBGRAPH-UNQUOTED-TITLE",
30398
- message: "Subgraph titles with spaces must be quoted.",
30399
- hint: 'Example: subgraph "Existing Logic Path" or use underscores: subgraph Existing_Logic_Path',
30400
- length: afterSubgraph.length
30401
- };
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
+ }
30402
30777
  }
30403
30778
  }
30404
30779
  }
@@ -31096,11 +31471,11 @@ function validateFlowchart(text, options = {}) {
31096
31471
  const byLine = /* @__PURE__ */ new Map();
31097
31472
  const collect = (arr) => {
31098
31473
  for (const e of arr || []) {
31099
- if (e && e.code === "FL-LABEL-PARENS-UNQUOTED") {
31474
+ if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED")) {
31100
31475
  const ln = e.line ?? 0;
31101
31476
  const col = e.column ?? 1;
31102
31477
  const list = byLine.get(ln) || [];
31103
- list.push(col);
31478
+ list.push({ start: col, end: col });
31104
31479
  byLine.set(ln, list);
31105
31480
  }
31106
31481
  }
@@ -31109,33 +31484,91 @@ function validateFlowchart(text, options = {}) {
31109
31484
  collect(errs);
31110
31485
  const lines2 = text2.split(/\r?\n/);
31111
31486
  for (let ii = 0; ii < lines2.length; ii++) {
31112
- const raw2 = lines2[ii] || "";
31113
- if (!raw2.includes("[") || !raw2.includes("]"))
31487
+ const raw = lines2[ii] || "";
31488
+ if (!raw.includes("[") || !raw.includes("]"))
31114
31489
  continue;
31115
- let search2 = 0;
31116
- while (true) {
31117
- const open2 = raw2.indexOf("[", search2);
31118
- if (open2 === -1)
31119
- break;
31120
- const close2 = raw2.indexOf("]", open2 + 1);
31121
- if (close2 === -1)
31122
- break;
31123
- const seg2 = raw2.slice(open2 + 1, close2);
31124
- const trimmed2 = seg2.trim();
31125
- const ln2 = ii + 1;
31126
- const lsp = trimmed2.slice(0, 1);
31127
- const rsp = trimmed2.slice(-1);
31128
- const isSlashPair = (lsp === "/" || lsp === "\\") && (rsp === "/" || rsp === "\\");
31129
- const isParenWrapped = lsp === "(" && rsp === ")";
31130
- const segStartCol = open2 + 2;
31131
- const segEndCol = close2 + 1;
31132
- const existing = byLine.get(ln2) || [];
31133
- const covered = existing.some((c) => c >= segStartCol && c <= segEndCol);
31134
- if (!covered && !/^".*"$/.test(trimmed2) && (seg2.includes("(") || seg2.includes(")")) && !isSlashPair && !isParenWrapped) {
31135
- errs.push({ line: ln2, column: segStartCol, 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;.' });
31136
- byLine.set(ln2, existing.concat([segStartCol]));
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
+ }
31137
31570
  }
31138
- search2 = close2 + 1;
31571
+ i++;
31139
31572
  }
31140
31573
  }
31141
31574
  }
@@ -42621,7 +43054,7 @@ var require_bk = __commonJS({
42621
43054
  return xs;
42622
43055
  }
42623
43056
  function buildBlockGraph(g, layering, root2, reverseSep) {
42624
- 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);
42625
43058
  _.forEach(layering, function(layer) {
42626
43059
  var u;
42627
43060
  _.forEach(layer, function(v) {
@@ -42711,7 +43144,7 @@ var require_bk = __commonJS({
42711
43144
  alignCoordinates(xss, smallestWidth);
42712
43145
  return balance(xss, g.graph().align);
42713
43146
  }
42714
- function sep2(nodeSep, edgeSep, reverseSep) {
43147
+ function sep3(nodeSep, edgeSep, reverseSep) {
42715
43148
  return function(g, v, w) {
42716
43149
  var vLabel = g.node(v);
42717
43150
  var wLabel = g.node(w);
@@ -50005,7 +50438,7 @@ var require_compile = __commonJS({
50005
50438
  const schOrFunc = root2.refs[ref];
50006
50439
  if (schOrFunc)
50007
50440
  return schOrFunc;
50008
- let _sch = resolve5.call(this, root2, ref);
50441
+ let _sch = resolve6.call(this, root2, ref);
50009
50442
  if (_sch === void 0) {
50010
50443
  const schema = (_a = root2.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
50011
50444
  const { schemaId } = this.opts;
@@ -50032,7 +50465,7 @@ var require_compile = __commonJS({
50032
50465
  function sameSchemaEnv(s1, s2) {
50033
50466
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
50034
50467
  }
50035
- function resolve5(root2, ref) {
50468
+ function resolve6(root2, ref) {
50036
50469
  let sch;
50037
50470
  while (typeof (sch = this.refs[ref]) == "string")
50038
50471
  ref = sch;
@@ -50607,7 +51040,7 @@ var require_fast_uri = __commonJS({
50607
51040
  }
50608
51041
  return uri;
50609
51042
  }
50610
- function resolve5(baseURI, relativeURI, options) {
51043
+ function resolve6(baseURI, relativeURI, options) {
50611
51044
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
50612
51045
  const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
50613
51046
  schemelessOptions.skipEscape = true;
@@ -50834,7 +51267,7 @@ var require_fast_uri = __commonJS({
50834
51267
  var fastUri = {
50835
51268
  SCHEMES,
50836
51269
  normalize: normalize2,
50837
- resolve: resolve5,
51270
+ resolve: resolve6,
50838
51271
  resolveComponent,
50839
51272
  equal,
50840
51273
  serialize,
@@ -54852,15 +55285,15 @@ Provide only the corrected Mermaid diagram within a mermaid code block. Do not a
54852
55285
  });
54853
55286
 
54854
55287
  // src/agent/mcp/config.js
54855
- import { readFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync } from "fs";
54856
- 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";
54857
55290
  import { homedir } from "os";
54858
55291
  import { fileURLToPath as fileURLToPath6 } from "url";
54859
55292
  function loadMCPConfigurationFromPath(configPath) {
54860
55293
  if (!configPath) {
54861
55294
  throw new Error("Config path is required");
54862
55295
  }
54863
- if (!existsSync3(configPath)) {
55296
+ if (!existsSync4(configPath)) {
54864
55297
  throw new Error(`MCP configuration file not found: ${configPath}`);
54865
55298
  }
54866
55299
  try {
@@ -54889,7 +55322,7 @@ function loadMCPConfiguration() {
54889
55322
  ].filter(Boolean);
54890
55323
  let config = null;
54891
55324
  for (const configPath of configPaths) {
54892
- if (existsSync3(configPath)) {
55325
+ if (existsSync4(configPath)) {
54893
55326
  try {
54894
55327
  const content = readFileSync(configPath, "utf8");
54895
55328
  config = JSON.parse(content);
@@ -54992,7 +55425,7 @@ var init_config = __esm({
54992
55425
  "src/agent/mcp/config.js"() {
54993
55426
  "use strict";
54994
55427
  __filename4 = fileURLToPath6(import.meta.url);
54995
- __dirname4 = dirname2(__filename4);
55428
+ __dirname4 = dirname3(__filename4);
54996
55429
  DEFAULT_CONFIG = {
54997
55430
  mcpServers: {
54998
55431
  // Example probe server configuration
@@ -55181,12 +55614,12 @@ var init_client = __esm({
55181
55614
  const toolsResponse = await client.listTools();
55182
55615
  const toolCount = toolsResponse?.tools?.length || 0;
55183
55616
  if (toolsResponse && toolsResponse.tools) {
55184
- for (const tool3 of toolsResponse.tools) {
55185
- const qualifiedName = `${name}_${tool3.name}`;
55617
+ for (const tool4 of toolsResponse.tools) {
55618
+ const qualifiedName = `${name}_${tool4.name}`;
55186
55619
  this.tools.set(qualifiedName, {
55187
- ...tool3,
55620
+ ...tool4,
55188
55621
  serverName: name,
55189
- originalName: tool3.name
55622
+ originalName: tool4.name
55190
55623
  });
55191
55624
  if (this.debug) {
55192
55625
  console.error(`[MCP DEBUG] Registered tool: ${qualifiedName}`);
@@ -55209,13 +55642,13 @@ var init_client = __esm({
55209
55642
  * @param {Object} args - Tool arguments
55210
55643
  */
55211
55644
  async callTool(toolName, args) {
55212
- const tool3 = this.tools.get(toolName);
55213
- if (!tool3) {
55645
+ const tool4 = this.tools.get(toolName);
55646
+ if (!tool4) {
55214
55647
  throw new Error(`Unknown tool: ${toolName}`);
55215
55648
  }
55216
- const clientInfo = this.clients.get(tool3.serverName);
55649
+ const clientInfo = this.clients.get(tool4.serverName);
55217
55650
  if (!clientInfo) {
55218
- throw new Error(`Server ${tool3.serverName} not connected`);
55651
+ throw new Error(`Server ${tool4.serverName} not connected`);
55219
55652
  }
55220
55653
  try {
55221
55654
  if (this.debug) {
@@ -55229,7 +55662,7 @@ var init_client = __esm({
55229
55662
  });
55230
55663
  const result = await Promise.race([
55231
55664
  clientInfo.client.callTool({
55232
- name: tool3.originalName,
55665
+ name: tool4.originalName,
55233
55666
  arguments: args
55234
55667
  }),
55235
55668
  timeoutPromise
@@ -55252,11 +55685,11 @@ var init_client = __esm({
55252
55685
  */
55253
55686
  getTools() {
55254
55687
  const tools2 = {};
55255
- for (const [name, tool3] of this.tools.entries()) {
55688
+ for (const [name, tool4] of this.tools.entries()) {
55256
55689
  tools2[name] = {
55257
- description: tool3.description,
55258
- inputSchema: tool3.inputSchema,
55259
- serverName: tool3.serverName
55690
+ description: tool4.description,
55691
+ inputSchema: tool4.inputSchema,
55692
+ serverName: tool4.serverName
55260
55693
  };
55261
55694
  }
55262
55695
  return tools2;
@@ -55267,10 +55700,10 @@ var init_client = __esm({
55267
55700
  */
55268
55701
  getVercelTools() {
55269
55702
  const tools2 = {};
55270
- for (const [name, tool3] of this.tools.entries()) {
55703
+ for (const [name, tool4] of this.tools.entries()) {
55271
55704
  tools2[name] = {
55272
- description: tool3.description,
55273
- inputSchema: tool3.inputSchema,
55705
+ description: tool4.description,
55706
+ inputSchema: tool4.inputSchema,
55274
55707
  execute: async (args) => {
55275
55708
  const result = await this.callTool(name, args);
55276
55709
  if (result.content && result.content[0]) {
@@ -55319,9 +55752,9 @@ var init_client = __esm({
55319
55752
  });
55320
55753
 
55321
55754
  // src/agent/mcp/xmlBridge.js
55322
- function mcpToolToXmlDefinition(name, tool3) {
55323
- const description = tool3.description || "MCP tool";
55324
- 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 || {};
55325
55758
  let paramDocs = "";
55326
55759
  if (inputSchema.properties) {
55327
55760
  paramDocs = "\n\nParameters (provide as JSON object):";
@@ -55500,8 +55933,8 @@ var init_xmlBridge = __esm({
55500
55933
  const vercelTools = this.mcpManager.getVercelTools();
55501
55934
  this.mcpTools = vercelTools;
55502
55935
  const toolCount = Object.keys(vercelTools).length;
55503
- for (const [name, tool3] of Object.entries(vercelTools)) {
55504
- this.xmlDefinitions[name] = mcpToolToXmlDefinition(name, tool3);
55936
+ for (const [name, tool4] of Object.entries(vercelTools)) {
55937
+ this.xmlDefinitions[name] = mcpToolToXmlDefinition(name, tool4);
55505
55938
  }
55506
55939
  if (toolCount === 0) {
55507
55940
  console.error("[MCP INFO] MCP initialization complete: 0 tools loaded");
@@ -55548,14 +55981,14 @@ var init_xmlBridge = __esm({
55548
55981
  console.error(`[MCP DEBUG] Executing MCP tool: ${toolName}`);
55549
55982
  console.error(`[MCP DEBUG] Parameters:`, JSON.stringify(params, null, 2));
55550
55983
  }
55551
- const tool3 = this.mcpTools[toolName];
55552
- if (!tool3) {
55984
+ const tool4 = this.mcpTools[toolName];
55985
+ if (!tool4) {
55553
55986
  console.error(`[MCP ERROR] Unknown MCP tool: ${toolName}`);
55554
55987
  console.error(`[MCP ERROR] Available tools: ${this.getToolNames().join(", ")}`);
55555
55988
  throw new Error(`Unknown MCP tool: ${toolName}`);
55556
55989
  }
55557
55990
  try {
55558
- const result = await tool3.execute(params);
55991
+ const result = await tool4.execute(params);
55559
55992
  if (this.debug) {
55560
55993
  console.error(`[MCP DEBUG] Tool ${toolName} executed successfully`);
55561
55994
  }
@@ -55609,22 +56042,636 @@ var init_mcp = __esm({
55609
56042
  }
55610
56043
  });
55611
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
+
55612
56659
  // src/agent/ProbeAgent.js
55613
56660
  var ProbeAgent_exports = {};
55614
56661
  __export(ProbeAgent_exports, {
55615
56662
  ProbeAgent: () => ProbeAgent
55616
56663
  });
55617
56664
  import dotenv2 from "dotenv";
55618
- import { createAnthropic } from "@ai-sdk/anthropic";
55619
- import { createOpenAI } from "@ai-sdk/openai";
55620
- import { createGoogleGenerativeAI } from "@ai-sdk/google";
55621
- 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";
55622
56669
  import { streamText } from "ai";
55623
56670
  import { randomUUID as randomUUID4 } from "crypto";
55624
56671
  import { EventEmitter as EventEmitter3 } from "events";
55625
- import { existsSync as existsSync4 } from "fs";
56672
+ import { existsSync as existsSync5 } from "fs";
55626
56673
  import { readFile, stat } from "fs/promises";
55627
- import { resolve as resolve3, isAbsolute, dirname as dirname3 } from "path";
56674
+ import { resolve as resolve4, isAbsolute as isAbsolute2, dirname as dirname4 } from "path";
55628
56675
  var MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
55629
56676
  var init_ProbeAgent = __esm({
55630
56677
  "src/agent/ProbeAgent.js"() {
@@ -55641,8 +56688,17 @@ var init_ProbeAgent = __esm({
55641
56688
  init_schemaUtils();
55642
56689
  init_xmlParsingUtils();
55643
56690
  init_mcp();
56691
+ init_RetryManager();
56692
+ init_FallbackManager();
55644
56693
  dotenv2.config();
55645
- 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
+ })();
55646
56702
  MAX_HISTORY_MESSAGES = 100;
55647
56703
  MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024;
55648
56704
  ProbeAgent = class _ProbeAgent {
@@ -55669,6 +56725,18 @@ var init_ProbeAgent = __esm({
55669
56725
  * @param {Array} [options.mcpServers] - Deprecated, use mcpConfig instead
55670
56726
  * @param {Object} [options.storageAdapter] - Custom storage adapter for history management
55671
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
55672
56740
  */
55673
56741
  constructor(options = {}) {
55674
56742
  this.sessionId = options.sessionId || randomUUID4();
@@ -55680,7 +56748,13 @@ var init_ProbeAgent = __esm({
55680
56748
  this.cancelled = false;
55681
56749
  this.tracer = options.tracer || null;
55682
56750
  this.outline = !!options.outline;
55683
- 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
+ })();
55684
56758
  this.maxIterations = options.maxIterations || null;
55685
56759
  this.disableMermaidValidation = !!options.disableMermaidValidation;
55686
56760
  this.disableJsonValidation = !!options.disableJsonValidation;
@@ -55721,6 +56795,10 @@ var init_ProbeAgent = __esm({
55721
56795
  this.mcpServers = options.mcpServers || null;
55722
56796
  this.mcpBridge = null;
55723
56797
  this._mcpInitialized = false;
56798
+ this.retryConfig = options.retry || {};
56799
+ this.retryManager = null;
56800
+ this.fallbackConfig = options.fallback || null;
56801
+ this.fallbackManager = null;
55724
56802
  this.initializeModel();
55725
56803
  }
55726
56804
  /**
@@ -55805,6 +56883,14 @@ var init_ProbeAgent = __esm({
55805
56883
  if (this.enableBash && wrappedTools.bashToolInstance) {
55806
56884
  this.toolImplementations.bash = wrappedTools.bashToolInstance;
55807
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
+ }
55808
56894
  this.wrappedTools = wrappedTools;
55809
56895
  if (this.debug) {
55810
56896
  console.error("\n[DEBUG] ========================================");
@@ -55855,36 +56941,147 @@ var init_ProbeAgent = __esm({
55855
56941
  if (forceProvider) {
55856
56942
  if (forceProvider === "anthropic" && anthropicApiKey) {
55857
56943
  this.initializeAnthropicModel(anthropicApiKey, anthropicApiUrl, modelName);
56944
+ this.initializeFallbackManager(forceProvider, modelName);
55858
56945
  return;
55859
56946
  } else if (forceProvider === "openai" && openaiApiKey) {
55860
56947
  this.initializeOpenAIModel(openaiApiKey, openaiApiUrl, modelName);
56948
+ this.initializeFallbackManager(forceProvider, modelName);
55861
56949
  return;
55862
56950
  } else if (forceProvider === "google" && googleApiKey) {
55863
56951
  this.initializeGoogleModel(googleApiKey, googleApiUrl, modelName);
56952
+ this.initializeFallbackManager(forceProvider, modelName);
55864
56953
  return;
55865
56954
  } else if (forceProvider === "bedrock" && (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey)) {
55866
56955
  this.initializeBedrockModel(awsAccessKeyId, awsSecretAccessKey, awsRegion, awsSessionToken, awsApiKey, awsBedrockBaseUrl, modelName);
56956
+ this.initializeFallbackManager(forceProvider, modelName);
55867
56957
  return;
55868
56958
  }
55869
56959
  console.warn(`WARNING: Forced provider "${forceProvider}" selected but required API key is missing or invalid! Falling back to auto-detection.`);
55870
56960
  }
55871
56961
  if (anthropicApiKey) {
55872
56962
  this.initializeAnthropicModel(anthropicApiKey, anthropicApiUrl, modelName);
56963
+ this.initializeFallbackManager("anthropic", modelName);
55873
56964
  } else if (openaiApiKey) {
55874
56965
  this.initializeOpenAIModel(openaiApiKey, openaiApiUrl, modelName);
56966
+ this.initializeFallbackManager("openai", modelName);
55875
56967
  } else if (googleApiKey) {
55876
56968
  this.initializeGoogleModel(googleApiKey, googleApiUrl, modelName);
56969
+ this.initializeFallbackManager("google", modelName);
55877
56970
  } else if (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey) {
55878
56971
  this.initializeBedrockModel(awsAccessKeyId, awsSecretAccessKey, awsRegion, awsSessionToken, awsApiKey, awsBedrockBaseUrl, modelName);
56972
+ this.initializeFallbackManager("bedrock", modelName);
55879
56973
  } else {
55880
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.");
55881
56975
  }
55882
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
+ }
55883
57080
  /**
55884
57081
  * Initialize Anthropic model
55885
57082
  */
55886
57083
  initializeAnthropicModel(apiKey, apiUrl, modelName) {
55887
- this.provider = createAnthropic({
57084
+ this.provider = createAnthropic2({
55888
57085
  apiKey,
55889
57086
  ...apiUrl && { baseURL: apiUrl }
55890
57087
  });
@@ -55898,7 +57095,7 @@ var init_ProbeAgent = __esm({
55898
57095
  * Initialize OpenAI model
55899
57096
  */
55900
57097
  initializeOpenAIModel(apiKey, apiUrl, modelName) {
55901
- this.provider = createOpenAI({
57098
+ this.provider = createOpenAI2({
55902
57099
  compatibility: "strict",
55903
57100
  apiKey,
55904
57101
  ...apiUrl && { baseURL: apiUrl }
@@ -55913,7 +57110,7 @@ var init_ProbeAgent = __esm({
55913
57110
  * Initialize Google model
55914
57111
  */
55915
57112
  initializeGoogleModel(apiKey, apiUrl, modelName) {
55916
- this.provider = createGoogleGenerativeAI({
57113
+ this.provider = createGoogleGenerativeAI2({
55917
57114
  apiKey,
55918
57115
  ...apiUrl && { baseURL: apiUrl }
55919
57116
  });
@@ -55943,7 +57140,7 @@ var init_ProbeAgent = __esm({
55943
57140
  if (baseURL) {
55944
57141
  config.baseURL = baseURL;
55945
57142
  }
55946
- this.provider = createAmazonBedrock(config);
57143
+ this.provider = createAmazonBedrock2(config);
55947
57144
  this.model = modelName || "anthropic.claude-sonnet-4-20250514-v1:0";
55948
57145
  this.apiType = "bedrock";
55949
57146
  if (this.debug) {
@@ -55988,7 +57185,7 @@ var init_ProbeAgent = __esm({
55988
57185
  let resolvedPath = imagePath;
55989
57186
  if (!imagePath.includes("/") && !imagePath.includes("\\")) {
55990
57187
  for (const dir of listFilesDirectories) {
55991
- const potentialPath = resolve3(dir, imagePath);
57188
+ const potentialPath = resolve4(dir, imagePath);
55992
57189
  const loaded = await this.loadImageIfValid(potentialPath);
55993
57190
  if (loaded) {
55994
57191
  if (this.debug) {
@@ -56013,7 +57210,7 @@ var init_ProbeAgent = __esm({
56013
57210
  let match2;
56014
57211
  while ((match2 = fileHeaderPattern.exec(content)) !== null) {
56015
57212
  const filePath = match2[1].trim();
56016
- const dir = dirname3(filePath);
57213
+ const dir = dirname4(filePath);
56017
57214
  if (dir && dir !== ".") {
56018
57215
  directories.push(dir);
56019
57216
  if (this.debug) {
@@ -56057,21 +57254,21 @@ var init_ProbeAgent = __esm({
56057
57254
  }
56058
57255
  const allowedDirs = this.allowedFolders && this.allowedFolders.length > 0 ? this.allowedFolders : [process.cwd()];
56059
57256
  let absolutePath;
56060
- let isPathAllowed = false;
56061
- if (isAbsolute(imagePath)) {
57257
+ let isPathAllowed2 = false;
57258
+ if (isAbsolute2(imagePath)) {
56062
57259
  absolutePath = imagePath;
56063
- isPathAllowed = allowedDirs.some((dir) => absolutePath.startsWith(resolve3(dir)));
57260
+ isPathAllowed2 = allowedDirs.some((dir) => absolutePath.startsWith(resolve4(dir)));
56064
57261
  } else {
56065
57262
  for (const dir of allowedDirs) {
56066
- const resolvedPath = resolve3(dir, imagePath);
56067
- if (resolvedPath.startsWith(resolve3(dir))) {
57263
+ const resolvedPath = resolve4(dir, imagePath);
57264
+ if (resolvedPath.startsWith(resolve4(dir))) {
56068
57265
  absolutePath = resolvedPath;
56069
- isPathAllowed = true;
57266
+ isPathAllowed2 = true;
56070
57267
  break;
56071
57268
  }
56072
57269
  }
56073
57270
  }
56074
- if (!isPathAllowed) {
57271
+ if (!isPathAllowed2) {
56075
57272
  if (this.debug) {
56076
57273
  console.log(`[DEBUG] Image path outside allowed directories: ${imagePath}`);
56077
57274
  }
@@ -56270,6 +57467,14 @@ ${attemptCompletionToolDefinition}
56270
57467
  `;
56271
57468
  if (this.allowEdit) {
56272
57469
  toolDefinitions += `${implementToolDefinition}
57470
+ `;
57471
+ toolDefinitions += `${editToolDefinition}
57472
+ `;
57473
+ toolDefinitions += `${createToolDefinition}
57474
+ `;
57475
+ }
57476
+ if (this.enableBash) {
57477
+ toolDefinitions += `${bashToolDefinition}
56273
57478
  `;
56274
57479
  }
56275
57480
  if (this.enableDelegate) {
@@ -56337,7 +57542,7 @@ Available Tools:
56337
57542
  - extract: Extract specific code blocks or lines from files.
56338
57543
  - listFiles: List files and directories in a specified location.
56339
57544
  - searchFiles: Find files matching a glob pattern with recursive search capability.
56340
- ${this.allowEdit ? "- implement: Implement a feature or fix a bug using aider.\n" : ""}${this.enableDelegate ? "- delegate: Delegate big distinct tasks to specialized probe subagents.\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" : ""}
56341
57546
  - attempt_completion: Finalize the task and provide the result to the user.
56342
57547
  - attempt_complete: Quick completion using previous response (shorthand).
56343
57548
  `;
@@ -56351,7 +57556,10 @@ Follow these instructions carefully:
56351
57556
  6. You MUST respond with exactly ONE tool call per message, using the specified XML format, until the task is complete.
56352
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.
56353
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.
56354
- 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` : ""}
56355
57563
  </instructions>
56356
57564
  `;
56357
57565
  const predefinedPrompts = {
@@ -56603,7 +57811,7 @@ You are working with a repository located at: ${searchDirectory}
56603
57811
  try {
56604
57812
  const executeAIRequest = async () => {
56605
57813
  const messagesForAI = this.prepareMessagesWithImages(currentMessages);
56606
- const result = await streamText({
57814
+ const result = await this.streamTextWithRetryAndFallback({
56607
57815
  model: this.provider(this.model),
56608
57816
  messages: messagesForAI,
56609
57817
  maxTokens: maxResponseTokens,
@@ -56655,7 +57863,10 @@ You are working with a repository located at: ${searchDirectory}
56655
57863
  "attempt_completion"
56656
57864
  ];
56657
57865
  if (this.allowEdit) {
56658
- validTools.push("implement");
57866
+ validTools.push("implement", "edit", "create");
57867
+ }
57868
+ if (this.enableBash) {
57869
+ validTools.push("bash");
56659
57870
  }
56660
57871
  if (this.enableDelegate) {
56661
57872
  validTools.push("delegate");
@@ -57569,8 +58780,8 @@ import {
57569
58780
  ListToolsRequestSchema,
57570
58781
  McpError
57571
58782
  } from "@modelcontextprotocol/sdk/types.js";
57572
- import { readFileSync as readFileSync2, existsSync as existsSync5 } from "fs";
57573
- import { resolve as resolve4 } from "path";
58783
+ import { readFileSync as readFileSync2, existsSync as existsSync6 } from "fs";
58784
+ import { resolve as resolve5 } from "path";
57574
58785
 
57575
58786
  // src/agent/acp/server.js
57576
58787
  import { randomUUID as randomUUID5 } from "crypto";
@@ -57829,8 +59040,8 @@ var ACPConnection = class extends EventEmitter4 {
57829
59040
  if (params !== null) {
57830
59041
  message.params = params;
57831
59042
  }
57832
- return new Promise((resolve5, reject2) => {
57833
- this.pendingRequests.set(id, { resolve: resolve5, reject: reject2 });
59043
+ return new Promise((resolve6, reject2) => {
59044
+ this.pendingRequests.set(id, { resolve: resolve6, reject: reject2 });
57834
59045
  this.sendMessage(message);
57835
59046
  setTimeout(() => {
57836
59047
  if (this.pendingRequests.has(id)) {
@@ -58244,8 +59455,8 @@ dotenv3.config();
58244
59455
  function readInputContent(input) {
58245
59456
  if (!input) return null;
58246
59457
  try {
58247
- const resolvedPath = resolve4(input);
58248
- if (existsSync5(resolvedPath)) {
59458
+ const resolvedPath = resolve5(input);
59459
+ if (existsSync6(resolvedPath)) {
58249
59460
  return readFileSync2(resolvedPath, "utf-8").trim();
58250
59461
  }
58251
59462
  } catch (error) {
@@ -58253,7 +59464,7 @@ function readInputContent(input) {
58253
59464
  return input;
58254
59465
  }
58255
59466
  function readFromStdin() {
58256
- return new Promise((resolve5, reject2) => {
59467
+ return new Promise((resolve6, reject2) => {
58257
59468
  let data = "";
58258
59469
  let hasReceivedData = false;
58259
59470
  let dataChunks = [];
@@ -58278,7 +59489,7 @@ function readFromStdin() {
58278
59489
  if (!trimmed && dataChunks.length === 0) {
58279
59490
  reject2(new Error("No input received from stdin"));
58280
59491
  } else {
58281
- resolve5(trimmed);
59492
+ resolve6(trimmed);
58282
59493
  }
58283
59494
  });
58284
59495
  process.stdin.on("error", (error) => {
@@ -58804,7 +60015,7 @@ async function main() {
58804
60015
  bashConfig.timeout = timeout;
58805
60016
  }
58806
60017
  if (config.bashWorkingDir) {
58807
- if (!existsSync5(config.bashWorkingDir)) {
60018
+ if (!existsSync6(config.bashWorkingDir)) {
58808
60019
  console.error(`Error: Bash working directory does not exist: ${config.bashWorkingDir}`);
58809
60020
  process.exit(1);
58810
60021
  }