@probelabs/probe 0.6.0-rc224 → 0.6.0-rc225

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.
@@ -12713,6 +12713,183 @@ var init_simpleTelemetry = __esm({
12713
12713
  console.log("[Attributes]", attributes);
12714
12714
  }
12715
12715
  }
12716
+ /**
12717
+ * Hash content for deduplication/comparison purposes
12718
+ * @param {string} content - The content to hash
12719
+ * @returns {string} - Hex string hash
12720
+ */
12721
+ hashContent(content) {
12722
+ let hash = 0;
12723
+ const len = Math.min(content.length, 1e3);
12724
+ for (let i = 0; i < len; i++) {
12725
+ hash = (hash << 5) - hash + content.charCodeAt(i);
12726
+ hash |= 0;
12727
+ }
12728
+ return hash.toString(16);
12729
+ }
12730
+ /**
12731
+ * Record a conversation turn (assistant response or tool result)
12732
+ * @param {string} role - The role (assistant, tool_result)
12733
+ * @param {string} content - The turn content
12734
+ * @param {Object} metadata - Additional metadata
12735
+ */
12736
+ recordConversationTurn(role, content, metadata = {}) {
12737
+ if (!this.isEnabled()) return;
12738
+ this.addEvent(`conversation.turn.${role}`, {
12739
+ "session.id": this.sessionId,
12740
+ "conversation.role": role,
12741
+ "conversation.content": content.substring(0, 1e4),
12742
+ "conversation.content.length": content.length,
12743
+ "conversation.content.hash": this.hashContent(content),
12744
+ ...metadata
12745
+ });
12746
+ }
12747
+ /**
12748
+ * Record error events with classification
12749
+ * @param {string} errorType - The type of error (wrapped_tool, unrecognized_tool, no_tool_call, circuit_breaker, etc.)
12750
+ * @param {Object} errorDetails - Error details including message, stack, context
12751
+ */
12752
+ recordErrorEvent(errorType, errorDetails = {}) {
12753
+ if (!this.isEnabled()) return;
12754
+ this.addEvent(`error.${errorType}`, {
12755
+ "session.id": this.sessionId,
12756
+ "error.type": errorType,
12757
+ "error.message": errorDetails.message?.substring(0, 1e3) || null,
12758
+ "error.stack": errorDetails.stack?.substring(0, 2e3) || null,
12759
+ "error.recoverable": errorDetails.recoverable ?? true,
12760
+ "error.context": JSON.stringify(errorDetails.context || {}).substring(0, 1e3),
12761
+ ...Object.fromEntries(
12762
+ Object.entries(errorDetails).filter(([k]) => !["message", "stack", "context", "recoverable"].includes(k)).map(([k, v]) => [`error.${k}`, v])
12763
+ )
12764
+ });
12765
+ }
12766
+ /**
12767
+ * Record AI thinking/reasoning content
12768
+ * @param {string} thinkingContent - The thinking content from AI response
12769
+ * @param {Object} metadata - Additional metadata
12770
+ */
12771
+ recordThinkingContent(thinkingContent, metadata = {}) {
12772
+ if (!this.isEnabled() || !thinkingContent) return;
12773
+ this.addEvent("ai.thinking", {
12774
+ "session.id": this.sessionId,
12775
+ "ai.thinking.content": thinkingContent.substring(0, 5e4),
12776
+ "ai.thinking.length": thinkingContent.length,
12777
+ "ai.thinking.hash": this.hashContent(thinkingContent),
12778
+ ...metadata
12779
+ });
12780
+ }
12781
+ /**
12782
+ * Record AI tool call decision
12783
+ * @param {string} toolName - The tool name AI decided to call
12784
+ * @param {Object} params - The parameters AI provided
12785
+ * @param {Object} metadata - Additional metadata
12786
+ */
12787
+ recordToolDecision(toolName, params, metadata = {}) {
12788
+ if (!this.isEnabled()) return;
12789
+ this.addEvent("ai.tool_decision", {
12790
+ "session.id": this.sessionId,
12791
+ "ai.tool_decision.name": toolName,
12792
+ "ai.tool_decision.params": JSON.stringify(params || {}).substring(0, 2e3),
12793
+ ...metadata
12794
+ });
12795
+ }
12796
+ /**
12797
+ * Record tool result after execution
12798
+ * @param {string} toolName - The tool that was executed
12799
+ * @param {string|Object} result - The tool result
12800
+ * @param {boolean} success - Whether the tool succeeded
12801
+ * @param {number} durationMs - Execution duration in milliseconds
12802
+ * @param {Object} metadata - Additional metadata
12803
+ */
12804
+ recordToolResult(toolName, result, success, durationMs, metadata = {}) {
12805
+ if (!this.isEnabled()) return;
12806
+ const resultStr = typeof result === "string" ? result : JSON.stringify(result);
12807
+ this.addEvent("tool.result", {
12808
+ "session.id": this.sessionId,
12809
+ "tool.name": toolName,
12810
+ "tool.result": resultStr.substring(0, 1e4),
12811
+ "tool.result.length": resultStr.length,
12812
+ "tool.result.hash": this.hashContent(resultStr),
12813
+ "tool.duration_ms": durationMs,
12814
+ "tool.success": success,
12815
+ ...metadata
12816
+ });
12817
+ }
12818
+ /**
12819
+ * Record MCP tool execution start
12820
+ * @param {string} toolName - MCP tool name
12821
+ * @param {string} serverName - MCP server name
12822
+ * @param {Object} params - Tool parameters
12823
+ * @param {Object} metadata - Additional metadata
12824
+ */
12825
+ recordMcpToolStart(toolName, serverName, params, metadata = {}) {
12826
+ if (!this.isEnabled()) return;
12827
+ this.addEvent("mcp.tool.start", {
12828
+ "session.id": this.sessionId,
12829
+ "mcp.tool.name": toolName,
12830
+ "mcp.tool.server": serverName || "unknown",
12831
+ "mcp.tool.params": JSON.stringify(params || {}).substring(0, 2e3),
12832
+ ...metadata
12833
+ });
12834
+ }
12835
+ /**
12836
+ * Record MCP tool execution end
12837
+ * @param {string} toolName - MCP tool name
12838
+ * @param {string} serverName - MCP server name
12839
+ * @param {string|Object} result - Tool result
12840
+ * @param {boolean} success - Whether succeeded
12841
+ * @param {number} durationMs - Execution duration
12842
+ * @param {string} errorMessage - Error message if failed
12843
+ * @param {Object} metadata - Additional metadata
12844
+ */
12845
+ recordMcpToolEnd(toolName, serverName, result, success, durationMs, errorMessage = null, metadata = {}) {
12846
+ if (!this.isEnabled()) return;
12847
+ const resultStr = typeof result === "string" ? result : JSON.stringify(result || "");
12848
+ this.addEvent("mcp.tool.end", {
12849
+ "session.id": this.sessionId,
12850
+ "mcp.tool.name": toolName,
12851
+ "mcp.tool.server": serverName || "unknown",
12852
+ "mcp.tool.result": resultStr.substring(0, 1e4),
12853
+ "mcp.tool.result.length": resultStr.length,
12854
+ "mcp.tool.duration_ms": durationMs,
12855
+ "mcp.tool.success": success,
12856
+ "mcp.tool.error": errorMessage,
12857
+ ...metadata
12858
+ });
12859
+ }
12860
+ /**
12861
+ * Record iteration lifecycle event
12862
+ * @param {string} eventType - start or end
12863
+ * @param {number} iteration - Iteration number
12864
+ * @param {Object} data - Additional data
12865
+ */
12866
+ recordIterationEvent(eventType, iteration, data = {}) {
12867
+ if (!this.isEnabled()) return;
12868
+ this.addEvent(`iteration.${eventType}`, {
12869
+ "session.id": this.sessionId,
12870
+ "iteration": iteration,
12871
+ ...data
12872
+ });
12873
+ }
12874
+ /**
12875
+ * Record per-turn token breakdown
12876
+ * @param {number} iteration - Iteration number
12877
+ * @param {Object} tokenData - Token metrics
12878
+ */
12879
+ recordTokenTurn(iteration, tokenData = {}) {
12880
+ if (!this.isEnabled()) return;
12881
+ this.addEvent("tokens.turn", {
12882
+ "session.id": this.sessionId,
12883
+ "iteration": iteration,
12884
+ "tokens.input": tokenData.inputTokens || 0,
12885
+ "tokens.output": tokenData.outputTokens || 0,
12886
+ "tokens.total": (tokenData.inputTokens || 0) + (tokenData.outputTokens || 0),
12887
+ "tokens.cache_read": tokenData.cacheReadTokens || 0,
12888
+ "tokens.cache_write": tokenData.cacheWriteTokens || 0,
12889
+ "tokens.context_used": tokenData.contextTokens || 0,
12890
+ "tokens.context_remaining": tokenData.maxContextTokens ? tokenData.maxContextTokens - (tokenData.contextTokens || 0) : null
12891
+ });
12892
+ }
12716
12893
  async withSpan(spanName, fn, attributes = {}) {
12717
12894
  if (!this.isEnabled()) {
12718
12895
  return fn();
@@ -20419,11 +20596,12 @@ function createTools(configOptions) {
20419
20596
  return tools2;
20420
20597
  }
20421
20598
  function parseXmlToolCallWithThinking(xmlString, validTools) {
20422
- const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
20599
+ const { cleanedXmlString, recoveryResult, thinkingContent } = processXmlWithThinkingAndRecovery(xmlString, validTools);
20423
20600
  if (recoveryResult) {
20424
- return recoveryResult;
20601
+ return { ...recoveryResult, thinkingContent };
20425
20602
  }
20426
- return parseXmlToolCall(cleanedXmlString, validTools);
20603
+ const toolCall = parseXmlToolCall(cleanedXmlString, validTools);
20604
+ return toolCall ? { ...toolCall, thinkingContent } : null;
20427
20605
  }
20428
20606
  var implementToolDefinition, listFilesToolDefinition, searchFilesToolDefinition, listSkillsToolDefinition, useSkillToolDefinition, readImageToolDefinition;
20429
20607
  var init_tools2 = __esm({
@@ -59717,25 +59895,27 @@ function parseXmlMcpToolCall(xmlString, mcpToolNames = []) {
59717
59895
  function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge = null) {
59718
59896
  const nativeResult = parseNativeXmlToolWithThinking(xmlString, nativeTools);
59719
59897
  if (nativeResult) {
59720
- return { ...nativeResult, type: "native" };
59898
+ const { thinkingContent, ...rest } = nativeResult;
59899
+ return { ...rest, type: "native", thinkingContent };
59721
59900
  }
59722
59901
  if (mcpBridge) {
59723
59902
  const mcpResult = parseXmlMcpToolCall(xmlString, mcpBridge.getToolNames());
59724
59903
  if (mcpResult) {
59725
- return { ...mcpResult, type: "mcp" };
59904
+ const { thinkingContent } = processXmlWithThinkingAndRecovery(xmlString, []);
59905
+ return { ...mcpResult, type: "mcp", thinkingContent };
59726
59906
  }
59727
59907
  }
59728
59908
  return null;
59729
59909
  }
59730
59910
  function parseNativeXmlToolWithThinking(xmlString, validTools) {
59731
- const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
59911
+ const { cleanedXmlString, recoveryResult, thinkingContent } = processXmlWithThinkingAndRecovery(xmlString, validTools);
59732
59912
  if (recoveryResult) {
59733
- return recoveryResult;
59913
+ return { ...recoveryResult, thinkingContent };
59734
59914
  }
59735
59915
  for (const toolName of validTools) {
59736
59916
  const result = parseNativeXmlTool(cleanedXmlString, toolName);
59737
59917
  if (result) {
59738
- return result;
59918
+ return { ...result, thinkingContent };
59739
59919
  }
59740
59920
  }
59741
59921
  return null;
@@ -70197,6 +70377,181 @@ var init_ProbeAgent = __esm({
70197
70377
  _filterMcpTools(mcpToolNames) {
70198
70378
  return mcpToolNames.filter((toolName) => this._isMcpToolAllowed(toolName));
70199
70379
  }
70380
+ /**
70381
+ * Check if tracer is AppTracer (expects sessionId as first param) vs SimpleAppTracer
70382
+ * @returns {boolean} - True if tracer is AppTracer style (requires sessionId)
70383
+ * @private
70384
+ */
70385
+ _isAppTracerStyle() {
70386
+ return this.tracer && typeof this.tracer.sessionSpans !== "undefined";
70387
+ }
70388
+ /**
70389
+ * Record an error classification event for telemetry
70390
+ * Provides unified error recording across all error types
70391
+ * @param {string} errorType - Error type (wrapped_tool, unrecognized_tool, no_tool_call, circuit_breaker)
70392
+ * @param {string} message - Error message
70393
+ * @param {Object} context - Additional context data
70394
+ * @param {number} iteration - Current iteration number
70395
+ * @private
70396
+ */
70397
+ _recordErrorTelemetry(errorType, message, context, iteration) {
70398
+ if (!this.tracer) return;
70399
+ if (this._isAppTracerStyle() && typeof this.tracer.recordErrorClassification === "function") {
70400
+ this.tracer.recordErrorClassification(this.sessionId, iteration, errorType, {
70401
+ message,
70402
+ context
70403
+ });
70404
+ } else if (typeof this.tracer.recordErrorEvent === "function") {
70405
+ this.tracer.recordErrorEvent(errorType, {
70406
+ message,
70407
+ context: { ...context, iteration }
70408
+ });
70409
+ } else {
70410
+ this.tracer.addEvent(`error.${errorType}`, {
70411
+ "error.type": errorType,
70412
+ "error.message": message,
70413
+ "error.recoverable": errorType !== "circuit_breaker",
70414
+ "error.context": JSON.stringify(context).substring(0, 1e3),
70415
+ "iteration": iteration
70416
+ });
70417
+ }
70418
+ }
70419
+ /**
70420
+ * Record AI thinking content for telemetry
70421
+ * @param {string} thinkingContent - The thinking content
70422
+ * @param {number} iteration - Current iteration number
70423
+ * @private
70424
+ */
70425
+ _recordThinkingTelemetry(thinkingContent, iteration) {
70426
+ if (!this.tracer || !thinkingContent) return;
70427
+ if (this._isAppTracerStyle() && typeof this.tracer.recordThinkingContent === "function") {
70428
+ this.tracer.recordThinkingContent(this.sessionId, iteration, thinkingContent);
70429
+ } else if (typeof this.tracer.recordThinkingContent === "function") {
70430
+ this.tracer.recordThinkingContent(thinkingContent, { iteration });
70431
+ } else {
70432
+ this.tracer.addEvent("ai.thinking", {
70433
+ "ai.thinking.content": thinkingContent.substring(0, 5e4),
70434
+ "ai.thinking.length": thinkingContent.length,
70435
+ "iteration": iteration
70436
+ });
70437
+ }
70438
+ }
70439
+ /**
70440
+ * Record AI tool decision for telemetry
70441
+ * @param {string} toolName - The tool name
70442
+ * @param {Object} params - Tool parameters
70443
+ * @param {number} responseLength - Length of AI response
70444
+ * @param {number} iteration - Current iteration number
70445
+ * @private
70446
+ */
70447
+ _recordToolDecisionTelemetry(toolName, params, responseLength, iteration) {
70448
+ if (!this.tracer) return;
70449
+ if (this._isAppTracerStyle() && typeof this.tracer.recordAIToolDecision === "function") {
70450
+ this.tracer.recordAIToolDecision(this.sessionId, iteration, toolName, params);
70451
+ } else if (typeof this.tracer.recordToolDecision === "function") {
70452
+ this.tracer.recordToolDecision(toolName, params, {
70453
+ iteration,
70454
+ "ai.tool_decision.raw_response_length": responseLength
70455
+ });
70456
+ } else {
70457
+ this.tracer.addEvent("ai.tool_decision", {
70458
+ "ai.tool_decision.name": toolName,
70459
+ "ai.tool_decision.params": JSON.stringify(params || {}).substring(0, 2e3),
70460
+ "ai.tool_decision.raw_response_length": responseLength,
70461
+ "iteration": iteration
70462
+ });
70463
+ }
70464
+ }
70465
+ /**
70466
+ * Record tool result for telemetry
70467
+ * @param {string} toolName - The tool name
70468
+ * @param {string|Object} result - Tool result
70469
+ * @param {boolean} success - Whether tool succeeded
70470
+ * @param {number} durationMs - Execution duration in milliseconds
70471
+ * @param {number} iteration - Current iteration number
70472
+ * @private
70473
+ */
70474
+ _recordToolResultTelemetry(toolName, result, success, durationMs, iteration) {
70475
+ if (!this.tracer) return;
70476
+ if (this._isAppTracerStyle() && typeof this.tracer.recordToolResult === "function") {
70477
+ this.tracer.recordToolResult(this.sessionId, iteration, toolName, result, success, durationMs);
70478
+ } else if (typeof this.tracer.recordToolResult === "function") {
70479
+ this.tracer.recordToolResult(toolName, result, success, durationMs, { iteration });
70480
+ } else {
70481
+ const resultStr = typeof result === "string" ? result : JSON.stringify(result || "");
70482
+ this.tracer.addEvent("tool.result", {
70483
+ "tool.name": toolName,
70484
+ "tool.result": resultStr.substring(0, 1e4),
70485
+ "tool.result.length": resultStr.length,
70486
+ "tool.duration_ms": durationMs,
70487
+ "tool.success": success,
70488
+ "iteration": iteration
70489
+ });
70490
+ }
70491
+ }
70492
+ /**
70493
+ * Record MCP tool lifecycle event for telemetry
70494
+ * @param {string} phase - 'start' or 'end'
70495
+ * @param {string} toolName - MCP tool name
70496
+ * @param {Object} params - Tool parameters (for start) or null (for end)
70497
+ * @param {number} iteration - Current iteration number
70498
+ * @param {Object} [endData] - Additional data for end phase (result, success, durationMs, error)
70499
+ * @private
70500
+ */
70501
+ _recordMcpToolTelemetry(phase, toolName, params, iteration, endData = null) {
70502
+ if (!this.tracer) return;
70503
+ if (phase === "start") {
70504
+ if (this._isAppTracerStyle() && typeof this.tracer.recordMcpToolStart === "function") {
70505
+ this.tracer.recordMcpToolStart(this.sessionId, iteration, toolName, "mcp", params);
70506
+ } else if (typeof this.tracer.recordMcpToolStart === "function") {
70507
+ this.tracer.recordMcpToolStart(toolName, "mcp", params, { iteration });
70508
+ } else {
70509
+ this.tracer.addEvent("mcp.tool.start", {
70510
+ "mcp.tool.name": toolName,
70511
+ "mcp.tool.server": "mcp",
70512
+ "mcp.tool.params": JSON.stringify(params || {}).substring(0, 2e3),
70513
+ "iteration": iteration
70514
+ });
70515
+ }
70516
+ } else if (phase === "end" && endData) {
70517
+ const { result, success, durationMs, error } = endData;
70518
+ if (this._isAppTracerStyle() && typeof this.tracer.recordMcpToolEnd === "function") {
70519
+ this.tracer.recordMcpToolEnd(this.sessionId, iteration, toolName, "mcp", result, success, durationMs, error);
70520
+ } else if (typeof this.tracer.recordMcpToolEnd === "function") {
70521
+ this.tracer.recordMcpToolEnd(toolName, "mcp", result, success, durationMs, error, { iteration });
70522
+ } else {
70523
+ const resultStr = typeof result === "string" ? result : JSON.stringify(result || "");
70524
+ this.tracer.addEvent("mcp.tool.end", {
70525
+ "mcp.tool.name": toolName,
70526
+ "mcp.tool.server": "mcp",
70527
+ "mcp.tool.result": resultStr.substring(0, 1e4),
70528
+ "mcp.tool.result.length": resultStr.length,
70529
+ "mcp.tool.duration_ms": durationMs,
70530
+ "mcp.tool.success": success,
70531
+ "mcp.tool.error": error,
70532
+ "iteration": iteration
70533
+ });
70534
+ }
70535
+ }
70536
+ }
70537
+ /**
70538
+ * Record iteration lifecycle event for telemetry
70539
+ * @param {string} phase - 'end' (start is already handled elsewhere)
70540
+ * @param {number} iteration - Current iteration number
70541
+ * @param {Object} data - Additional iteration data
70542
+ * @private
70543
+ */
70544
+ _recordIterationTelemetry(phase, iteration, data = {}) {
70545
+ if (!this.tracer) return;
70546
+ if (typeof this.tracer.recordIterationEvent === "function") {
70547
+ this.tracer.recordIterationEvent(phase, iteration, data);
70548
+ } else {
70549
+ this.tracer.addEvent(`iteration.${phase}`, {
70550
+ "iteration": iteration,
70551
+ ...data
70552
+ });
70553
+ }
70554
+ }
70200
70555
  /**
70201
70556
  * Initialize the agent asynchronously (must be called after constructor)
70202
70557
  * This method initializes MCP and merges MCP tools into the tool list, and loads history from storage
@@ -72151,8 +72506,12 @@ You are working with a repository located at: ${searchDirectory}
72151
72506
  }
72152
72507
  const nativeTools = validTools;
72153
72508
  const parsedTool = this.mcpBridge && !options._disableTools ? parseHybridXmlToolCall(assistantResponseContent, nativeTools, this.mcpBridge) : parseXmlToolCallWithThinking(assistantResponseContent, validTools);
72509
+ if (parsedTool?.thinkingContent) {
72510
+ this._recordThinkingTelemetry(parsedTool.thinkingContent, currentIteration);
72511
+ }
72154
72512
  if (parsedTool) {
72155
72513
  const { toolName, params } = parsedTool;
72514
+ this._recordToolDecisionTelemetry(toolName, params, assistantResponseContent.length, currentIteration);
72156
72515
  if (this.debug) console.log(`[DEBUG] Parsed tool call: ${toolName} with params:`, params);
72157
72516
  if (toolName === "attempt_completion") {
72158
72517
  completionAttempted = true;
@@ -72229,6 +72588,8 @@ You are working with a repository located at: ${searchDirectory}
72229
72588
  } else {
72230
72589
  const { type } = parsedTool;
72231
72590
  if (type === "mcp" && this.mcpBridge && this.mcpBridge.isMcpTool(toolName)) {
72591
+ const mcpStartTime = Date.now();
72592
+ this._recordMcpToolTelemetry("start", toolName, params, currentIteration);
72232
72593
  try {
72233
72594
  if (this.debug) {
72234
72595
  console.error(`
@@ -72258,6 +72619,13 @@ You are working with a repository located at: ${searchDirectory}
72258
72619
  } catch (truncateError) {
72259
72620
  console.error(`[WARN] Tool output truncation failed: ${truncateError.message}`);
72260
72621
  }
72622
+ const mcpDurationMs = Date.now() - mcpStartTime;
72623
+ this._recordMcpToolTelemetry("end", toolName, null, currentIteration, {
72624
+ result: toolResultContent,
72625
+ success: true,
72626
+ durationMs: mcpDurationMs,
72627
+ error: null
72628
+ });
72261
72629
  if (this.debug) {
72262
72630
  const preview = toolResultContent.length > 500 ? toolResultContent.substring(0, 500) + "..." : toolResultContent;
72263
72631
  console.error(`[DEBUG] ========================================`);
@@ -72271,6 +72639,13 @@ You are working with a repository located at: ${searchDirectory}
72271
72639
  ${toolResultContent}
72272
72640
  </tool_result>` });
72273
72641
  } catch (error) {
72642
+ const mcpDurationMs = Date.now() - mcpStartTime;
72643
+ this._recordMcpToolTelemetry("end", toolName, null, currentIteration, {
72644
+ result: null,
72645
+ success: false,
72646
+ durationMs: mcpDurationMs,
72647
+ error: error.message
72648
+ });
72274
72649
  console.error(`Error executing MCP tool ${toolName}:`, error);
72275
72650
  if (this.debug) {
72276
72651
  console.error(`[DEBUG] ========================================`);
@@ -72362,6 +72737,7 @@ ${errorXml}
72362
72737
  return await this.toolImplementations[toolName].execute(toolParams);
72363
72738
  };
72364
72739
  let toolResult;
72740
+ const toolStartTime = Date.now();
72365
72741
  try {
72366
72742
  if (this.tracer) {
72367
72743
  toolResult = await this.tracer.withSpan("tool.call", executeToolCall, {
@@ -72372,6 +72748,8 @@ ${errorXml}
72372
72748
  } else {
72373
72749
  toolResult = await executeToolCall();
72374
72750
  }
72751
+ const toolDurationMs = Date.now() - toolStartTime;
72752
+ this._recordToolResultTelemetry(toolName, toolResult, true, toolDurationMs, currentIteration);
72375
72753
  if (this.debug) {
72376
72754
  const resultPreview = typeof toolResult === "string" ? toolResult.length > 500 ? toolResult.substring(0, 500) + "..." : toolResult : toolResult ? JSON.stringify(toolResult, null, 2).substring(0, 500) + "..." : "No Result";
72377
72755
  console.error(`[DEBUG] ========================================`);
@@ -72428,6 +72806,20 @@ ${toolResultContent}
72428
72806
  role: "user",
72429
72807
  content: toolResultMessage
72430
72808
  });
72809
+ if (this.tracer) {
72810
+ if (typeof this.tracer.recordConversationTurn === "function") {
72811
+ this.tracer.recordConversationTurn("assistant", assistantResponseContent, {
72812
+ iteration: currentIteration,
72813
+ has_tool_call: true,
72814
+ tool_name: toolName
72815
+ });
72816
+ this.tracer.recordConversationTurn("tool_result", toolResultContent, {
72817
+ iteration: currentIteration,
72818
+ tool_name: toolName,
72819
+ tool_success: true
72820
+ });
72821
+ }
72822
+ }
72431
72823
  if (this.debug) {
72432
72824
  console.log(`[DEBUG] Tool ${toolName} executed successfully. Result length: ${typeof toolResult === "string" ? toolResult.length : JSON.stringify(toolResult).length}`);
72433
72825
  }
@@ -72498,6 +72890,7 @@ ${errorXml}
72498
72890
  if (this.debug) {
72499
72891
  console.log(`[DEBUG] Detected wrapped tool '${wrappedToolName}' in assistant response - wrong XML format.`);
72500
72892
  }
72893
+ this._recordErrorTelemetry("wrapped_tool", "Tool call wrapped in markdown", { toolName: wrappedToolName }, currentIteration);
72501
72894
  const toolError = new ParameterError(
72502
72895
  `Tool '${wrappedToolName}' found but in WRONG FORMAT - do not wrap tools in other XML tags.`,
72503
72896
  {
@@ -72523,6 +72916,7 @@ ${formatErrorForAI(toolError)}
72523
72916
  if (this.debug) {
72524
72917
  console.log(`[DEBUG] Detected unrecognized tool '${unrecognizedTool}' in assistant response.`);
72525
72918
  }
72919
+ this._recordErrorTelemetry("unrecognized_tool", `Unknown tool: ${unrecognizedTool}`, { toolName: unrecognizedTool, validTools }, currentIteration);
72526
72920
  const toolError = new ParameterError(`Tool '${unrecognizedTool}' is not available in this context.`, {
72527
72921
  suggestion: `Available tools: ${validTools.join(", ")}. Please use one of these tools instead.`
72528
72922
  });
@@ -72530,6 +72924,7 @@ ${formatErrorForAI(toolError)}
72530
72924
  ${formatErrorForAI(toolError)}
72531
72925
  </tool_result>`;
72532
72926
  } else {
72927
+ this._recordErrorTelemetry("no_tool_call", "AI response did not contain tool call", { responsePreview: assistantResponseContent.substring(0, 500) }, currentIteration);
72533
72928
  if (currentIteration >= maxIterations) {
72534
72929
  let cleanedResponse = assistantResponseContent;
72535
72930
  cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").trim();
@@ -72605,6 +73000,7 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
72605
73000
  sameFormatErrorCount++;
72606
73001
  if (sameFormatErrorCount >= MAX_REPEATED_FORMAT_ERRORS) {
72607
73002
  const errorDesc = isWrapped ? "wrapped tool format" : unrecognizedTool;
73003
+ this._recordErrorTelemetry("circuit_breaker", "Format error limit exceeded", { formatErrorCount: sameFormatErrorCount, errorCategory }, currentIteration);
72608
73004
  console.error(`[ERROR] Format error category '${errorCategory}' repeated ${sameFormatErrorCount} times. Breaking loop early to prevent infinite iteration.`);
72609
73005
  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.`;
72610
73006
  break;
@@ -72618,6 +73014,10 @@ Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant messa
72618
73014
  sameFormatErrorCount = 0;
72619
73015
  }
72620
73016
  }
73017
+ this._recordIterationTelemetry("end", currentIteration, {
73018
+ "iteration.completed": completionAttempted,
73019
+ "iteration.message_count": currentMessages.length
73020
+ });
72621
73021
  if (currentMessages.length > MAX_HISTORY_MESSAGES) {
72622
73022
  const messagesBefore = currentMessages.length;
72623
73023
  const systemMsg = currentMessages[0];
@@ -321,14 +321,17 @@ export function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge =
321
321
  // This includes thinking tag removal and attempt_complete recovery logic
322
322
  const nativeResult = parseNativeXmlToolWithThinking(xmlString, nativeTools);
323
323
  if (nativeResult) {
324
- return { ...nativeResult, type: 'native' };
324
+ const { thinkingContent, ...rest } = nativeResult;
325
+ return { ...rest, type: 'native', thinkingContent };
325
326
  }
326
327
 
327
328
  // Then try MCP tools if bridge is available
328
329
  if (mcpBridge) {
329
330
  const mcpResult = parseXmlMcpToolCall(xmlString, mcpBridge.getToolNames());
330
331
  if (mcpResult) {
331
- return { ...mcpResult, type: 'mcp' };
332
+ // Extract thinking content for MCP tools as well
333
+ const { thinkingContent } = processXmlWithThinkingAndRecovery(xmlString, []);
334
+ return { ...mcpResult, type: 'mcp', thinkingContent };
332
335
  }
333
336
  }
334
337
 
@@ -344,18 +347,18 @@ export function parseHybridXmlToolCall(xmlString, nativeTools = [], mcpBridge =
344
347
  */
345
348
  function parseNativeXmlToolWithThinking(xmlString, validTools) {
346
349
  // Use the shared processing logic
347
- const { cleanedXmlString, recoveryResult } = processXmlWithThinkingAndRecovery(xmlString, validTools);
348
-
349
- // If recovery found an attempt_complete pattern, return it
350
+ const { cleanedXmlString, recoveryResult, thinkingContent } = processXmlWithThinkingAndRecovery(xmlString, validTools);
351
+
352
+ // If recovery found an attempt_complete pattern, return it with thinking content
350
353
  if (recoveryResult) {
351
- return recoveryResult;
354
+ return { ...recoveryResult, thinkingContent };
352
355
  }
353
356
 
354
357
  // Use the original parseNativeXmlTool function to parse the cleaned XML string
355
358
  for (const toolName of validTools) {
356
359
  const result = parseNativeXmlTool(cleanedXmlString, toolName);
357
360
  if (result) {
358
- return result;
361
+ return { ...result, thinkingContent };
359
362
  }
360
363
  }
361
364