@probelabs/probe 0.6.0-rc206 → 0.6.0-rc208

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 (30) hide show
  1. package/bin/binaries/{probe-v0.6.0-rc206-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc208-aarch64-apple-darwin.tar.gz} +0 -0
  2. package/bin/binaries/probe-v0.6.0-rc208-aarch64-unknown-linux-musl.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc208-x86_64-apple-darwin.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc208-x86_64-pc-windows-msvc.zip +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc208-x86_64-unknown-linux-musl.tar.gz +0 -0
  6. package/build/agent/ProbeAgent.js +144 -2
  7. package/build/agent/bashPermissions.js +88 -7
  8. package/build/agent/index.js +450 -18
  9. package/build/agent/mcp/client.js +234 -4
  10. package/build/agent/mcp/config.js +87 -0
  11. package/build/agent/mcp/xmlBridge.js +15 -5
  12. package/build/agent/simpleTelemetry.js +26 -0
  13. package/build/tools/bash.js +5 -3
  14. package/build/tools/common.js +31 -0
  15. package/cjs/agent/ProbeAgent.cjs +428 -18
  16. package/cjs/agent/simpleTelemetry.cjs +22 -0
  17. package/cjs/index.cjs +450 -18
  18. package/package.json +1 -1
  19. package/src/agent/ProbeAgent.js +144 -2
  20. package/src/agent/bashPermissions.js +88 -7
  21. package/src/agent/mcp/client.js +234 -4
  22. package/src/agent/mcp/config.js +87 -0
  23. package/src/agent/mcp/xmlBridge.js +15 -5
  24. package/src/agent/simpleTelemetry.js +26 -0
  25. package/src/tools/bash.js +5 -3
  26. package/src/tools/common.js +31 -0
  27. package/bin/binaries/probe-v0.6.0-rc206-aarch64-unknown-linux-musl.tar.gz +0 -0
  28. package/bin/binaries/probe-v0.6.0-rc206-x86_64-apple-darwin.tar.gz +0 -0
  29. package/bin/binaries/probe-v0.6.0-rc206-x86_64-pc-windows-msvc.zip +0 -0
  30. package/bin/binaries/probe-v0.6.0-rc206-x86_64-unknown-linux-musl.tar.gz +0 -0
package/cjs/index.cjs CHANGED
@@ -35787,6 +35787,22 @@ function detectUnrecognizedToolCall(xmlString, validTools) {
35787
35787
  return toolName;
35788
35788
  }
35789
35789
  }
35790
+ const allToolNames = [.../* @__PURE__ */ new Set([...knownToolNames, ...validTools])];
35791
+ for (const toolName of allToolNames) {
35792
+ const escapedToolName = toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
35793
+ const wrapperPatterns = [
35794
+ new RegExp(`<tool_name>\\s*${escapedToolName}\\s*</tool_name>`, "i"),
35795
+ new RegExp(`<function>\\s*${escapedToolName}\\s*</function>`, "i"),
35796
+ new RegExp(`<name>\\s*${escapedToolName}\\s*</name>`, "i"),
35797
+ // Also check for tool name immediately after api_call or call opening tag
35798
+ new RegExp(`<(?:api_call|call)[^>]*>[\\s\\S]*?<tool_name>\\s*${escapedToolName}`, "i")
35799
+ ];
35800
+ for (const pattern of wrapperPatterns) {
35801
+ if (pattern.test(xmlString)) {
35802
+ return `wrapped_tool:${toolName}`;
35803
+ }
35804
+ }
35805
+ }
35790
35806
  return null;
35791
35807
  }
35792
35808
  function parseTargets(targets) {
@@ -82284,6 +82300,47 @@ function validateTimeout(value) {
82284
82300
  if (!Number.isFinite(num) || num < 0) return void 0;
82285
82301
  return Math.min(num, MAX_TIMEOUT);
82286
82302
  }
82303
+ function validateMethodFilter(serverConfig, serverName = "unknown") {
82304
+ const result = { allowedMethods: null, blockedMethods: null };
82305
+ const debug = process.env.DEBUG === "1" || process.env.DEBUG_MCP === "1";
82306
+ if (serverConfig.allowedMethods && serverConfig.blockedMethods) {
82307
+ console.error(`[MCP WARN] Server '${serverName}' has both allowedMethods and blockedMethods - using allowedMethods only`);
82308
+ }
82309
+ if (serverConfig.allowedMethods) {
82310
+ if (!Array.isArray(serverConfig.allowedMethods)) {
82311
+ console.error(`[MCP WARN] Server '${serverName}' allowedMethods must be an array, ignoring`);
82312
+ } else {
82313
+ const validMethods = serverConfig.allowedMethods.filter((m4) => typeof m4 === "string" && m4.length > 0);
82314
+ if (validMethods.length !== serverConfig.allowedMethods.length) {
82315
+ console.error(`[MCP WARN] Server '${serverName}' allowedMethods contains non-string values, skipping those`);
82316
+ }
82317
+ if (validMethods.length > 0) {
82318
+ result.allowedMethods = validMethods;
82319
+ if (debug) {
82320
+ console.error(`[MCP DEBUG] Server '${serverName}' allowedMethods: ${validMethods.join(", ")}`);
82321
+ }
82322
+ }
82323
+ }
82324
+ return result;
82325
+ }
82326
+ if (serverConfig.blockedMethods) {
82327
+ if (!Array.isArray(serverConfig.blockedMethods)) {
82328
+ console.error(`[MCP WARN] Server '${serverName}' blockedMethods must be an array, ignoring`);
82329
+ } else {
82330
+ const validMethods = serverConfig.blockedMethods.filter((m4) => typeof m4 === "string" && m4.length > 0);
82331
+ if (validMethods.length !== serverConfig.blockedMethods.length) {
82332
+ console.error(`[MCP WARN] Server '${serverName}' blockedMethods contains non-string values, skipping those`);
82333
+ }
82334
+ if (validMethods.length > 0) {
82335
+ result.blockedMethods = validMethods;
82336
+ if (debug) {
82337
+ console.error(`[MCP DEBUG] Server '${serverName}' blockedMethods: ${validMethods.join(", ")}`);
82338
+ }
82339
+ }
82340
+ }
82341
+ }
82342
+ return result;
82343
+ }
82287
82344
  function loadMCPConfigurationFromPath(configPath) {
82288
82345
  if (!configPath) {
82289
82346
  throw new Error("Config path is required");
@@ -82377,6 +82434,12 @@ function mergeWithEnvironment(config) {
82377
82434
  console.error(`[MCP WARN] Invalid timeout value for ${normalizedName}: ${value}`);
82378
82435
  }
82379
82436
  break;
82437
+ case "ALLOWLIST":
82438
+ config.mcpServers[normalizedName].allowedMethods = value.split(",").map((m4) => m4.trim()).filter(Boolean);
82439
+ break;
82440
+ case "BLOCKLIST":
82441
+ config.mcpServers[normalizedName].blockedMethods = value.split(",").map((m4) => m4.trim()).filter(Boolean);
82442
+ break;
82380
82443
  }
82381
82444
  }
82382
82445
  }
@@ -82427,6 +82490,9 @@ function parseEnabledServers(config) {
82427
82490
  }
82428
82491
  server.timeout = validatedTimeout;
82429
82492
  }
82493
+ const methodFilter = validateMethodFilter(serverConfig, name14);
82494
+ server.allowedMethods = methodFilter.allowedMethods;
82495
+ server.blockedMethods = methodFilter.blockedMethods;
82430
82496
  servers.push(server);
82431
82497
  }
82432
82498
  return servers;
@@ -82464,6 +82530,22 @@ var init_config = __esm({
82464
82530
  });
82465
82531
 
82466
82532
  // src/agent/mcp/client.js
82533
+ function isMethodAllowed(methodName, allowedMethods, blockedMethods) {
82534
+ const matchesPattern2 = (name14, pattern) => {
82535
+ if (!pattern.includes("*")) {
82536
+ return name14 === pattern;
82537
+ }
82538
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
82539
+ return new RegExp(`^${regexPattern}$`).test(name14);
82540
+ };
82541
+ if (allowedMethods && allowedMethods.length > 0) {
82542
+ return allowedMethods.some((pattern) => matchesPattern2(methodName, pattern));
82543
+ }
82544
+ if (blockedMethods && blockedMethods.length > 0) {
82545
+ return !blockedMethods.some((pattern) => matchesPattern2(methodName, pattern));
82546
+ }
82547
+ return true;
82548
+ }
82467
82549
  function createTransport(serverConfig) {
82468
82550
  const { transport, command, args, url, env } = serverConfig;
82469
82551
  switch (transport) {
@@ -82548,6 +82630,17 @@ var init_client2 = __esm({
82548
82630
  this.tools = /* @__PURE__ */ new Map();
82549
82631
  this.debug = options.debug || process.env.DEBUG_MCP === "1";
82550
82632
  this.config = null;
82633
+ this.tracer = options.tracer || null;
82634
+ }
82635
+ /**
82636
+ * Record an MCP telemetry event if tracer is available
82637
+ * @param {string} eventType - Event type (e.g., 'server.connect', 'tool.discovered')
82638
+ * @param {Object} data - Event data
82639
+ */
82640
+ recordMcpEvent(eventType, data2 = {}) {
82641
+ if (this.tracer && typeof this.tracer.recordMcpEvent === "function") {
82642
+ this.tracer.recordMcpEvent(eventType, data2);
82643
+ }
82551
82644
  }
82552
82645
  /**
82553
82646
  * Initialize MCP clients from configuration
@@ -82556,10 +82649,20 @@ var init_client2 = __esm({
82556
82649
  async initialize(config = null) {
82557
82650
  this.config = config || loadMCPConfiguration();
82558
82651
  const servers = parseEnabledServers(this.config);
82652
+ this.recordMcpEvent("initialization.started", {
82653
+ serverCount: servers.length,
82654
+ serverNames: servers.map((s4) => s4.name)
82655
+ });
82559
82656
  console.error(`[MCP INFO] Found ${servers.length} enabled MCP server${servers.length !== 1 ? "s" : ""}`);
82560
82657
  if (servers.length === 0) {
82561
82658
  console.error("[MCP INFO] No MCP servers configured or enabled");
82562
82659
  console.error("[MCP INFO] 0 MCP tools available");
82660
+ this.recordMcpEvent("initialization.completed", {
82661
+ connected: 0,
82662
+ total: 0,
82663
+ toolCount: 0,
82664
+ tools: []
82665
+ });
82563
82666
  return {
82564
82667
  connected: 0,
82565
82668
  total: 0,
@@ -82596,10 +82699,17 @@ var init_client2 = __esm({
82596
82699
  console.error(`[MCP DEBUG] - ${toolName}`);
82597
82700
  });
82598
82701
  }
82702
+ const toolNames = Array.from(this.tools.keys());
82703
+ this.recordMcpEvent("initialization.completed", {
82704
+ connected: connectedCount,
82705
+ total: servers.length,
82706
+ toolCount: this.tools.size,
82707
+ tools: toolNames
82708
+ });
82599
82709
  return {
82600
82710
  connected: connectedCount,
82601
82711
  total: servers.length,
82602
- tools: Array.from(this.tools.keys())
82712
+ tools: toolNames
82603
82713
  };
82604
82714
  }
82605
82715
  /**
@@ -82608,6 +82718,12 @@ var init_client2 = __esm({
82608
82718
  */
82609
82719
  async connectToServer(serverConfig) {
82610
82720
  const { name: name14 } = serverConfig;
82721
+ this.recordMcpEvent("server.connecting", {
82722
+ serverName: name14,
82723
+ transport: serverConfig.transport,
82724
+ hasAllowedMethods: !!(serverConfig.allowedMethods && serverConfig.allowedMethods.length > 0),
82725
+ hasBlockedMethods: !!(serverConfig.blockedMethods && serverConfig.blockedMethods.length > 0)
82726
+ });
82611
82727
  try {
82612
82728
  if (this.debug) {
82613
82729
  console.error(`[MCP DEBUG] Connecting to ${name14} via ${serverConfig.transport}...`);
@@ -82629,27 +82745,92 @@ var init_client2 = __esm({
82629
82745
  config: serverConfig
82630
82746
  });
82631
82747
  const toolsResponse = await client.listTools();
82632
- const toolCount = toolsResponse?.tools?.length || 0;
82748
+ const totalToolCount = toolsResponse?.tools?.length || 0;
82749
+ let registeredCount = 0;
82750
+ let filteredCount = 0;
82751
+ const registeredTools = [];
82752
+ const filteredTools = [];
82633
82753
  if (toolsResponse && toolsResponse.tools) {
82754
+ const { allowedMethods, blockedMethods } = serverConfig;
82755
+ const allToolNames = toolsResponse.tools.map((t4) => t4.name);
82756
+ this.recordMcpEvent("tools.discovered", {
82757
+ serverName: name14,
82758
+ toolCount: totalToolCount,
82759
+ tools: allToolNames
82760
+ });
82634
82761
  for (const tool4 of toolsResponse.tools) {
82762
+ if (!isMethodAllowed(tool4.name, allowedMethods, blockedMethods)) {
82763
+ filteredCount++;
82764
+ filteredTools.push(tool4.name);
82765
+ if (this.debug) {
82766
+ console.error(`[MCP DEBUG] Filtered out tool: ${tool4.name} (not allowed by method filter)`);
82767
+ }
82768
+ continue;
82769
+ }
82635
82770
  const qualifiedName = `${name14}_${tool4.name}`;
82636
82771
  this.tools.set(qualifiedName, {
82637
82772
  ...tool4,
82638
82773
  serverName: name14,
82639
82774
  originalName: tool4.name
82640
82775
  });
82776
+ registeredCount++;
82777
+ registeredTools.push(qualifiedName);
82641
82778
  if (this.debug) {
82642
82779
  console.error(`[MCP DEBUG] Registered tool: ${qualifiedName}`);
82643
82780
  }
82644
82781
  }
82782
+ if (filteredCount > 0) {
82783
+ this.recordMcpEvent("tools.filtered", {
82784
+ serverName: name14,
82785
+ filteredCount,
82786
+ filteredTools,
82787
+ allowedMethods: allowedMethods || [],
82788
+ blockedMethods: blockedMethods || []
82789
+ });
82790
+ }
82791
+ if (allowedMethods && allowedMethods.length > 0) {
82792
+ const unmatchedPatterns = allowedMethods.filter((pattern) => {
82793
+ return !allToolNames.some((toolName) => isMethodAllowed(toolName, [pattern], null));
82794
+ });
82795
+ if (unmatchedPatterns.length > 0) {
82796
+ console.error(`[MCP WARN] Server '${name14}': The following allowedMethods patterns did not match any tools: ${unmatchedPatterns.join(", ")}`);
82797
+ console.error(`[MCP WARN] Available methods from '${name14}': ${allToolNames.join(", ")}`);
82798
+ }
82799
+ }
82800
+ if (blockedMethods && blockedMethods.length > 0) {
82801
+ const unmatchedPatterns = blockedMethods.filter((pattern) => {
82802
+ return !allToolNames.some((toolName) => !isMethodAllowed(toolName, null, [pattern]));
82803
+ });
82804
+ if (unmatchedPatterns.length > 0) {
82805
+ console.error(`[MCP WARN] Server '${name14}': The following blockedMethods patterns did not match any tools: ${unmatchedPatterns.join(", ")}`);
82806
+ console.error(`[MCP WARN] Available methods from '${name14}': ${allToolNames.join(", ")}`);
82807
+ }
82808
+ }
82645
82809
  }
82646
- console.error(`[MCP INFO] Connected to ${name14}: ${toolCount} tool${toolCount !== 1 ? "s" : ""} loaded`);
82810
+ if (filteredCount > 0) {
82811
+ console.error(`[MCP INFO] Connected to ${name14}: ${registeredCount} tool${registeredCount !== 1 ? "s" : ""} loaded (${filteredCount} filtered out)`);
82812
+ } else {
82813
+ console.error(`[MCP INFO] Connected to ${name14}: ${registeredCount} tool${registeredCount !== 1 ? "s" : ""} loaded`);
82814
+ }
82815
+ this.recordMcpEvent("server.connected", {
82816
+ serverName: name14,
82817
+ transport: serverConfig.transport,
82818
+ totalToolCount,
82819
+ registeredCount,
82820
+ filteredCount,
82821
+ registeredTools
82822
+ });
82647
82823
  return true;
82648
82824
  } catch (error2) {
82649
82825
  console.error(`[MCP ERROR] Error connecting to ${name14}:`, error2.message);
82650
82826
  if (this.debug) {
82651
82827
  console.error(`[MCP DEBUG] Full error details:`, error2);
82652
82828
  }
82829
+ this.recordMcpEvent("server.connection_failed", {
82830
+ serverName: name14,
82831
+ transport: serverConfig.transport,
82832
+ error: error2.message
82833
+ });
82653
82834
  return false;
82654
82835
  }
82655
82836
  }
@@ -82661,12 +82842,27 @@ var init_client2 = __esm({
82661
82842
  async callTool(toolName, args) {
82662
82843
  const tool4 = this.tools.get(toolName);
82663
82844
  if (!tool4) {
82845
+ this.recordMcpEvent("tool.call_failed", {
82846
+ toolName,
82847
+ error: "Unknown tool"
82848
+ });
82664
82849
  throw new Error(`Unknown tool: ${toolName}`);
82665
82850
  }
82666
82851
  const clientInfo = this.clients.get(tool4.serverName);
82667
82852
  if (!clientInfo) {
82853
+ this.recordMcpEvent("tool.call_failed", {
82854
+ toolName,
82855
+ serverName: tool4.serverName,
82856
+ error: "Server not connected"
82857
+ });
82668
82858
  throw new Error(`Server ${tool4.serverName} not connected`);
82669
82859
  }
82860
+ const startTime = Date.now();
82861
+ this.recordMcpEvent("tool.call_started", {
82862
+ toolName,
82863
+ serverName: tool4.serverName,
82864
+ originalToolName: tool4.originalName
82865
+ });
82670
82866
  try {
82671
82867
  if (this.debug) {
82672
82868
  console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
@@ -82686,15 +82882,31 @@ var init_client2 = __esm({
82686
82882
  }),
82687
82883
  timeoutPromise
82688
82884
  ]);
82885
+ const durationMs = Date.now() - startTime;
82689
82886
  if (this.debug) {
82690
82887
  console.error(`[MCP DEBUG] Tool ${toolName} executed successfully`);
82691
82888
  }
82889
+ this.recordMcpEvent("tool.call_completed", {
82890
+ toolName,
82891
+ serverName: tool4.serverName,
82892
+ originalToolName: tool4.originalName,
82893
+ durationMs
82894
+ });
82692
82895
  return result;
82693
82896
  } catch (error2) {
82897
+ const durationMs = Date.now() - startTime;
82694
82898
  console.error(`[MCP ERROR] Error calling tool ${toolName}:`, error2.message);
82695
82899
  if (this.debug) {
82696
82900
  console.error(`[MCP DEBUG] Full error details:`, error2);
82697
82901
  }
82902
+ this.recordMcpEvent("tool.call_failed", {
82903
+ toolName,
82904
+ serverName: tool4.serverName,
82905
+ originalToolName: tool4.originalName,
82906
+ error: error2.message,
82907
+ durationMs,
82908
+ isTimeout: error2.message.includes("timeout")
82909
+ });
82698
82910
  throw error2;
82699
82911
  }
82700
82912
  }
@@ -82739,12 +82951,17 @@ var init_client2 = __esm({
82739
82951
  */
82740
82952
  async disconnect() {
82741
82953
  const disconnectPromises = [];
82954
+ const serverNames = Array.from(this.clients.keys());
82742
82955
  if (this.clients.size === 0) {
82743
82956
  if (this.debug) {
82744
82957
  console.error("[MCP DEBUG] No MCP clients to disconnect");
82745
82958
  }
82746
82959
  return;
82747
82960
  }
82961
+ this.recordMcpEvent("disconnection.started", {
82962
+ serverCount: this.clients.size,
82963
+ serverNames
82964
+ });
82748
82965
  if (this.debug) {
82749
82966
  console.error(`[MCP DEBUG] Disconnecting from ${this.clients.size} MCP server${this.clients.size !== 1 ? "s" : ""}...`);
82750
82967
  }
@@ -82754,14 +82971,25 @@ var init_client2 = __esm({
82754
82971
  if (this.debug) {
82755
82972
  console.error(`[MCP DEBUG] Disconnected from ${name14}`);
82756
82973
  }
82974
+ this.recordMcpEvent("server.disconnected", {
82975
+ serverName: name14
82976
+ });
82757
82977
  }).catch((error2) => {
82758
82978
  console.error(`[MCP ERROR] Error disconnecting from ${name14}:`, error2.message);
82979
+ this.recordMcpEvent("server.disconnect_failed", {
82980
+ serverName: name14,
82981
+ error: error2.message
82982
+ });
82759
82983
  })
82760
82984
  );
82761
82985
  }
82762
82986
  await Promise.all(disconnectPromises);
82763
82987
  this.clients.clear();
82764
82988
  this.tools.clear();
82989
+ this.recordMcpEvent("disconnection.completed", {
82990
+ serverCount: serverNames.length,
82991
+ serverNames
82992
+ });
82765
82993
  if (this.debug) {
82766
82994
  console.error("[MCP DEBUG] All MCP connections closed");
82767
82995
  }
@@ -82905,6 +83133,7 @@ var init_xmlBridge = __esm({
82905
83133
  MCPXmlBridge = class {
82906
83134
  constructor(options = {}) {
82907
83135
  this.debug = options.debug || false;
83136
+ this.tracer = options.tracer || null;
82908
83137
  this.mcpTools = {};
82909
83138
  this.mcpManager = null;
82910
83139
  this.xmlDefinitions = {};
@@ -82947,7 +83176,7 @@ var init_xmlBridge = __esm({
82947
83176
  if (this.debug) {
82948
83177
  console.error("[MCP DEBUG] Initializing MCP client manager...");
82949
83178
  }
82950
- this.mcpManager = new MCPClientManager({ debug: this.debug });
83179
+ this.mcpManager = new MCPClientManager({ debug: this.debug, tracer: this.tracer });
82951
83180
  const result = await this.mcpManager.initialize(mcpConfigs);
82952
83181
  const vercelTools = this.mcpManager.getVercelTools();
82953
83182
  this.mcpTools = vercelTools;
@@ -82971,11 +83200,15 @@ var init_xmlBridge = __esm({
82971
83200
  }
82972
83201
  }
82973
83202
  /**
82974
- * Get all XML tool definitions for inclusion in system prompt
83203
+ * Get XML tool definitions for inclusion in system prompt
83204
+ * @param {Array<string>|null} filterToolNames - Optional list of tool names to include (if null, include all)
82975
83205
  * @returns {string} Combined XML tool definitions
82976
83206
  */
82977
- getXmlToolDefinitions() {
82978
- return Object.values(this.xmlDefinitions).join("\n\n");
83207
+ getXmlToolDefinitions(filterToolNames = null) {
83208
+ if (filterToolNames === null) {
83209
+ return Object.values(this.xmlDefinitions).join("\n\n");
83210
+ }
83211
+ return Object.entries(this.xmlDefinitions).filter(([name14]) => filterToolNames.includes(name14)).map(([, def]) => def).join("\n\n");
82979
83212
  }
82980
83213
  /**
82981
83214
  * Get list of MCP tool names
@@ -93061,6 +93294,27 @@ var ProbeAgent_exports = {};
93061
93294
  __export(ProbeAgent_exports, {
93062
93295
  ProbeAgent: () => ProbeAgent
93063
93296
  });
93297
+ function extractWrappedToolName(wrappedToolError) {
93298
+ if (!wrappedToolError || typeof wrappedToolError !== "string") {
93299
+ return "unknown";
93300
+ }
93301
+ const colonIndex = wrappedToolError.indexOf(":");
93302
+ return colonIndex !== -1 ? wrappedToolError.slice(colonIndex + 1) : "unknown";
93303
+ }
93304
+ function isWrappedToolError(error2) {
93305
+ return error2 && typeof error2 === "string" && error2.startsWith("wrapped_tool:");
93306
+ }
93307
+ function createWrappedToolErrorMessage(wrappedToolName) {
93308
+ return `Your response contained an incorrectly formatted tool call (${wrappedToolName} wrapped in XML tags). This cannot be used.
93309
+
93310
+ Please use the CORRECT format:
93311
+
93312
+ <${wrappedToolName}>
93313
+ Your content here
93314
+ </${wrappedToolName}>
93315
+
93316
+ Do NOT wrap in other tags like <api_call>, <tool_name>, <function>, etc.`;
93317
+ }
93064
93318
  var import_dotenv, import_anthropic2, import_openai2, import_google2, import_ai3, import_crypto8, import_events4, import_fs11, import_promises6, import_path13, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
93065
93319
  var init_ProbeAgent = __esm({
93066
93320
  "src/agent/ProbeAgent.js"() {
@@ -95071,6 +95325,9 @@ You are working with a repository located at: ${searchDirectory}
95071
95325
  console.log(`[DEBUG] Schema provided, using extended iteration limit: ${maxIterations} (base: ${baseMaxIterations})`);
95072
95326
  }
95073
95327
  }
95328
+ let lastFormatErrorType = null;
95329
+ let sameFormatErrorCount = 0;
95330
+ const MAX_REPEATED_FORMAT_ERRORS = 3;
95074
95331
  while (currentIteration < maxIterations && !completionAttempted) {
95075
95332
  currentIteration++;
95076
95333
  if (this.cancelled) throw new Error("Request was cancelled by the user");
@@ -95269,7 +95526,22 @@ You are working with a repository located at: ${searchDirectory}
95269
95526
  (msg) => msg.role === "assistant" && msg.content && !(this.mcpBridge ? parseHybridXmlToolCall(msg.content, validTools, this.mcpBridge) : parseXmlToolCallWithThinking(msg.content, validTools))
95270
95527
  );
95271
95528
  if (lastAssistantMessage) {
95272
- finalResult = lastAssistantMessage.content;
95529
+ const prevContent = lastAssistantMessage.content;
95530
+ const wrappedToolError = detectUnrecognizedToolCall(prevContent, validTools);
95531
+ if (isWrappedToolError(wrappedToolError)) {
95532
+ const wrappedToolName = extractWrappedToolName(wrappedToolError);
95533
+ if (this.debug) {
95534
+ console.log(`[DEBUG] Previous response contains wrapped tool '${wrappedToolName}' - rejecting for __PREVIOUS_RESPONSE__`);
95535
+ }
95536
+ currentMessages.push({ role: "assistant", content: assistantResponseContent });
95537
+ currentMessages.push({
95538
+ role: "user",
95539
+ content: createWrappedToolErrorMessage(wrappedToolName)
95540
+ });
95541
+ completionAttempted = false;
95542
+ continue;
95543
+ }
95544
+ finalResult = prevContent;
95273
95545
  if (this.debug) console.log(`[DEBUG] Using previous response as completion: ${finalResult.substring(0, 100)}...`);
95274
95546
  } else {
95275
95547
  finalResult = "Error: No previous response found to use as completion.";
@@ -95540,7 +95812,33 @@ ${errorXml}
95540
95812
  currentMessages.push({ role: "assistant", content: assistantResponseContent });
95541
95813
  const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
95542
95814
  let reminderContent;
95543
- if (unrecognizedTool) {
95815
+ if (isWrappedToolError(unrecognizedTool)) {
95816
+ const wrappedToolName = extractWrappedToolName(unrecognizedTool);
95817
+ if (this.debug) {
95818
+ console.log(`[DEBUG] Detected wrapped tool '${wrappedToolName}' in assistant response - wrong XML format.`);
95819
+ }
95820
+ const toolError = new ParameterError(
95821
+ `Tool '${wrappedToolName}' found but in WRONG FORMAT - do not wrap tools in other XML tags.`,
95822
+ {
95823
+ suggestion: `Use the tool tag DIRECTLY without any wrapper:
95824
+
95825
+ CORRECT FORMAT:
95826
+ <${wrappedToolName}>
95827
+ <param>value</param>
95828
+ </${wrappedToolName}>
95829
+
95830
+ WRONG (what you did - do not wrap in other tags):
95831
+ <api_call><tool_name>${wrappedToolName}</tool_name>...</api_call>
95832
+ <function>${wrappedToolName}</function>
95833
+ <call name="${wrappedToolName}">...</call>
95834
+
95835
+ Remove ALL wrapper tags and use <${wrappedToolName}> directly as the outermost tag.`
95836
+ }
95837
+ );
95838
+ reminderContent = `<tool_result>
95839
+ ${formatErrorForAI(toolError)}
95840
+ </tool_result>`;
95841
+ } else if (unrecognizedTool) {
95544
95842
  if (this.debug) {
95545
95843
  console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
95546
95844
  }
@@ -95551,6 +95849,20 @@ ${errorXml}
95551
95849
  ${formatErrorForAI(toolError)}
95552
95850
  </tool_result>`;
95553
95851
  } else {
95852
+ if (currentIteration >= maxIterations) {
95853
+ let cleanedResponse = assistantResponseContent;
95854
+ cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").trim();
95855
+ cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*$/gi, "").trim();
95856
+ const hasSubstantialContent = cleanedResponse.length > 50 && !cleanedResponse.includes("<api_call>") && !cleanedResponse.includes("<tool_name>") && !cleanedResponse.includes("<function>");
95857
+ if (hasSubstantialContent) {
95858
+ if (this.debug) {
95859
+ console.log(`[DEBUG] Max iterations reached - accepting AI response as final answer (${cleanedResponse.length} chars)`);
95860
+ }
95861
+ finalResult = cleanedResponse;
95862
+ completionAttempted = true;
95863
+ break;
95864
+ }
95865
+ }
95554
95866
  reminderContent = `Please use one of the available tools to help answer the question, or use attempt_completion if you have enough information to provide a final answer.
95555
95867
 
95556
95868
  Remember: Use proper XML format with BOTH opening and closing tags:
@@ -95580,6 +95892,25 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
95580
95892
  console.log(`[DEBUG] No tool call detected in assistant response. Prompting for tool use.`);
95581
95893
  }
95582
95894
  }
95895
+ if (unrecognizedTool) {
95896
+ const isWrapped = isWrappedToolError(unrecognizedTool);
95897
+ const errorCategory = isWrapped ? "wrapped_tool" : unrecognizedTool;
95898
+ if (errorCategory === lastFormatErrorType) {
95899
+ sameFormatErrorCount++;
95900
+ if (sameFormatErrorCount >= MAX_REPEATED_FORMAT_ERRORS) {
95901
+ const errorDesc = isWrapped ? "wrapped tool format" : unrecognizedTool;
95902
+ console.error(`[ERROR] Format error category '${errorCategory}' repeated ${sameFormatErrorCount} times. Breaking loop early to prevent infinite iteration.`);
95903
+ finalResult = `Error: Unable to complete request. The AI model repeatedly used incorrect tool call format (${errorDesc}). Please try rephrasing your question or using a different model.`;
95904
+ break;
95905
+ }
95906
+ } else {
95907
+ lastFormatErrorType = errorCategory;
95908
+ sameFormatErrorCount = 1;
95909
+ }
95910
+ } else {
95911
+ lastFormatErrorType = null;
95912
+ sameFormatErrorCount = 0;
95913
+ }
95583
95914
  }
95584
95915
  if (currentMessages.length > MAX_HISTORY_MESSAGES) {
95585
95916
  const messagesBefore = currentMessages.length;
@@ -97750,9 +98081,11 @@ var init_bashPermissions = __esm({
97750
98081
  * @param {boolean} [config.disableDefaultAllow] - Disable default allow list
97751
98082
  * @param {boolean} [config.disableDefaultDeny] - Disable default deny list
97752
98083
  * @param {boolean} [config.debug] - Enable debug logging
98084
+ * @param {Object} [config.tracer] - Optional tracer for telemetry
97753
98085
  */
97754
98086
  constructor(config = {}) {
97755
98087
  this.debug = config.debug || false;
98088
+ this.tracer = config.tracer || null;
97756
98089
  this.allowPatterns = [];
97757
98090
  if (!config.disableDefaultAllow) {
97758
98091
  this.allowPatterns.push(...DEFAULT_ALLOW_PATTERNS);
@@ -97782,6 +98115,24 @@ var init_bashPermissions = __esm({
97782
98115
  if (this.debug) {
97783
98116
  console.log(`[BashPermissions] Total patterns - Allow: ${this.allowPatterns.length}, Deny: ${this.denyPatterns.length}`);
97784
98117
  }
98118
+ this.recordBashEvent("permissions.initialized", {
98119
+ allowPatternCount: this.allowPatterns.length,
98120
+ denyPatternCount: this.denyPatterns.length,
98121
+ hasCustomAllowPatterns: !!(config.allow && config.allow.length > 0),
98122
+ hasCustomDenyPatterns: !!(config.deny && config.deny.length > 0),
98123
+ disableDefaultAllow: !!config.disableDefaultAllow,
98124
+ disableDefaultDeny: !!config.disableDefaultDeny
98125
+ });
98126
+ }
98127
+ /**
98128
+ * Record a bash telemetry event if tracer is available
98129
+ * @param {string} eventType - Event type (e.g., 'permission.checked', 'permission.denied')
98130
+ * @param {Object} data - Event data
98131
+ */
98132
+ recordBashEvent(eventType, data2 = {}) {
98133
+ if (this.tracer && typeof this.tracer.recordBashEvent === "function") {
98134
+ this.tracer.recordBashEvent(eventType, data2);
98135
+ }
97785
98136
  }
97786
98137
  /**
97787
98138
  * Check if a simple command is allowed (complex commands allowed if they match patterns)
@@ -97790,11 +98141,17 @@ var init_bashPermissions = __esm({
97790
98141
  */
97791
98142
  check(command) {
97792
98143
  if (!command || typeof command !== "string") {
97793
- return {
98144
+ const result2 = {
97794
98145
  allowed: false,
97795
98146
  reason: "Invalid or empty command",
97796
98147
  command
97797
98148
  };
98149
+ this.recordBashEvent("permission.denied", {
98150
+ command: String(command),
98151
+ reason: result2.reason,
98152
+ isComplex: false
98153
+ });
98154
+ return result2;
97798
98155
  }
97799
98156
  const commandIsComplex = isComplexCommand(command);
97800
98157
  if (commandIsComplex) {
@@ -97802,18 +98159,31 @@ var init_bashPermissions = __esm({
97802
98159
  }
97803
98160
  const parsed = parseCommand(command);
97804
98161
  if (parsed.error) {
97805
- return {
98162
+ const result2 = {
97806
98163
  allowed: false,
97807
98164
  reason: parsed.error,
97808
98165
  command
97809
98166
  };
98167
+ this.recordBashEvent("permission.denied", {
98168
+ command,
98169
+ reason: result2.reason,
98170
+ isComplex: false,
98171
+ parseError: true
98172
+ });
98173
+ return result2;
97810
98174
  }
97811
98175
  if (!parsed.command) {
97812
- return {
98176
+ const result2 = {
97813
98177
  allowed: false,
97814
98178
  reason: "No valid command found",
97815
98179
  command
97816
98180
  };
98181
+ this.recordBashEvent("permission.denied", {
98182
+ command,
98183
+ reason: result2.reason,
98184
+ isComplex: false
98185
+ });
98186
+ return result2;
97817
98187
  }
97818
98188
  if (this.debug) {
97819
98189
  console.log(`[BashPermissions] Checking simple command: "${command}"`);
@@ -97821,22 +98191,37 @@ var init_bashPermissions = __esm({
97821
98191
  }
97822
98192
  if (matchesAnyPattern(parsed, this.denyPatterns)) {
97823
98193
  const matchedPatterns = this.denyPatterns.filter((pattern) => matchesPattern(parsed, pattern));
97824
- return {
98194
+ const result2 = {
97825
98195
  allowed: false,
97826
98196
  reason: `Command matches deny pattern: ${matchedPatterns[0]}`,
97827
98197
  command,
97828
98198
  parsed,
97829
98199
  matchedPatterns
97830
98200
  };
98201
+ this.recordBashEvent("permission.denied", {
98202
+ command,
98203
+ parsedCommand: parsed.command,
98204
+ reason: "matches_deny_pattern",
98205
+ matchedPattern: matchedPatterns[0],
98206
+ isComplex: false
98207
+ });
98208
+ return result2;
97831
98209
  }
97832
98210
  if (this.allowPatterns.length > 0) {
97833
98211
  if (!matchesAnyPattern(parsed, this.allowPatterns)) {
97834
- return {
98212
+ const result2 = {
97835
98213
  allowed: false,
97836
98214
  reason: "Command not in allow list",
97837
98215
  command,
97838
98216
  parsed
97839
98217
  };
98218
+ this.recordBashEvent("permission.denied", {
98219
+ command,
98220
+ parsedCommand: parsed.command,
98221
+ reason: "not_in_allow_list",
98222
+ isComplex: false
98223
+ });
98224
+ return result2;
97840
98225
  }
97841
98226
  }
97842
98227
  const result = {
@@ -97848,6 +98233,11 @@ var init_bashPermissions = __esm({
97848
98233
  if (this.debug) {
97849
98234
  console.log(`[BashPermissions] ALLOWED - command passed all checks`);
97850
98235
  }
98236
+ this.recordBashEvent("permission.allowed", {
98237
+ command,
98238
+ parsedCommand: parsed.command,
98239
+ isComplex: false
98240
+ });
97851
98241
  return result;
97852
98242
  }
97853
98243
  /**
@@ -97871,13 +98261,20 @@ var init_bashPermissions = __esm({
97871
98261
  if (this.debug) {
97872
98262
  console.log(`[BashPermissions] DENIED - matches complex deny pattern: ${pattern}`);
97873
98263
  }
97874
- return {
98264
+ const result = {
97875
98265
  allowed: false,
97876
98266
  reason: `Command matches deny pattern: ${pattern}`,
97877
98267
  command,
97878
98268
  isComplex: true,
97879
98269
  matchedPatterns: [pattern]
97880
98270
  };
98271
+ this.recordBashEvent("permission.denied", {
98272
+ command,
98273
+ reason: "matches_deny_pattern",
98274
+ matchedPattern: pattern,
98275
+ isComplex: true
98276
+ });
98277
+ return result;
97881
98278
  }
97882
98279
  }
97883
98280
  for (const pattern of complexAllowPatterns) {
@@ -97885,17 +98282,28 @@ var init_bashPermissions = __esm({
97885
98282
  if (this.debug) {
97886
98283
  console.log(`[BashPermissions] ALLOWED - matches complex allow pattern: ${pattern}`);
97887
98284
  }
97888
- return {
98285
+ const result = {
97889
98286
  allowed: true,
97890
98287
  command,
97891
98288
  isComplex: true,
97892
98289
  matchedPattern: pattern
97893
98290
  };
98291
+ this.recordBashEvent("permission.allowed", {
98292
+ command,
98293
+ matchedPattern: pattern,
98294
+ isComplex: true
98295
+ });
98296
+ return result;
97894
98297
  }
97895
98298
  }
97896
98299
  if (this.debug) {
97897
98300
  console.log(`[BashPermissions] DENIED - no matching complex pattern found`);
97898
98301
  }
98302
+ this.recordBashEvent("permission.denied", {
98303
+ command,
98304
+ reason: "no_matching_complex_pattern",
98305
+ isComplex: true
98306
+ });
97899
98307
  return {
97900
98308
  allowed: false,
97901
98309
  reason: 'Complex shell commands require explicit allow patterns (e.g., "cd * && git *")',
@@ -98194,14 +98602,16 @@ var init_bash = __esm({
98194
98602
  bashConfig = {},
98195
98603
  debug = false,
98196
98604
  cwd,
98197
- allowedFolders = []
98605
+ allowedFolders = [],
98606
+ tracer = null
98198
98607
  } = options;
98199
98608
  const permissionChecker = new BashPermissionChecker({
98200
98609
  allow: bashConfig.allow,
98201
98610
  deny: bashConfig.deny,
98202
98611
  disableDefaultAllow: bashConfig.disableDefaultAllow,
98203
98612
  disableDefaultDeny: bashConfig.disableDefaultDeny,
98204
- debug
98613
+ debug,
98614
+ tracer
98205
98615
  });
98206
98616
  const getDefaultWorkingDirectory = () => {
98207
98617
  if (bashConfig.workingDirectory) {
@@ -98945,6 +99355,28 @@ var init_simpleTelemetry = __esm({
98945
99355
  ...data2
98946
99356
  });
98947
99357
  }
99358
+ /**
99359
+ * Record MCP (Model Context Protocol) events
99360
+ * Tracks server connections, tool discovery, method filtering, and tool execution
99361
+ */
99362
+ recordMcpEvent(eventType, data2 = {}) {
99363
+ if (!this.isEnabled()) return;
99364
+ this.addEvent(`mcp.${eventType}`, {
99365
+ "session.id": this.sessionId,
99366
+ ...data2
99367
+ });
99368
+ }
99369
+ /**
99370
+ * Record bash tool events
99371
+ * Tracks command permission checks, allowed/denied commands, and execution
99372
+ */
99373
+ recordBashEvent(eventType, data2 = {}) {
99374
+ if (!this.isEnabled()) return;
99375
+ this.addEvent(`bash.${eventType}`, {
99376
+ "session.id": this.sessionId,
99377
+ ...data2
99378
+ });
99379
+ }
98948
99380
  setAttributes(attributes) {
98949
99381
  if (this.telemetry && this.telemetry.enableConsole) {
98950
99382
  console.log("[Attributes]", attributes);