@probelabs/probe 0.6.0-rc67 → 0.6.0-rc70

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.
@@ -115,9 +115,13 @@ export class ProbeAgent {
115
115
  search: wrappedTools.searchToolInstance,
116
116
  query: wrappedTools.queryToolInstance,
117
117
  extract: wrappedTools.extractToolInstance,
118
+ delegate: wrappedTools.delegateToolInstance,
118
119
  listFiles: listFilesToolInstance,
119
120
  searchFiles: searchFilesToolInstance,
120
121
  };
122
+
123
+ // Store wrapped tools for ACP system
124
+ this.wrappedTools = wrappedTools;
121
125
  }
122
126
 
123
127
  /**
@@ -580,8 +584,15 @@ When troubleshooting:
580
584
  throw new Error(finalResult);
581
585
  }
582
586
 
583
- // Parse tool call from response
584
- const parsedTool = parseXmlToolCallWithThinking(assistantResponseContent);
587
+ // Parse tool call from response with valid tools list
588
+ const validTools = [
589
+ 'search', 'query', 'extract', 'listFiles', 'searchFiles', 'attempt_completion'
590
+ ];
591
+ if (this.allowEdit) {
592
+ validTools.push('implement');
593
+ }
594
+
595
+ const parsedTool = parseXmlToolCallWithThinking(assistantResponseContent, validTools);
585
596
  if (parsedTool) {
586
597
  const { toolName, params } = parsedTool;
587
598
  if (this.debug) console.log(`[DEBUG] Parsed tool call: ${toolName} with params:`, params);
@@ -614,6 +625,22 @@ When troubleshooting:
614
625
 
615
626
  // Execute tool with tracing if available
616
627
  const executeToolCall = async () => {
628
+ // For delegate tool, pass current iteration and max iterations
629
+ if (toolName === 'delegate') {
630
+ const enhancedParams = {
631
+ ...toolParams,
632
+ currentIteration,
633
+ maxIterations,
634
+ debug: this.debug
635
+ };
636
+
637
+ if (this.debug) {
638
+ console.log(`[DEBUG] Executing delegate tool at iteration ${currentIteration}/${maxIterations}`);
639
+ console.log(`[DEBUG] Delegate task: ${toolParams.task?.substring(0, 100)}...`);
640
+ }
641
+
642
+ return await this.toolImplementations[toolName].execute(enhancedParams);
643
+ }
617
644
  return await this.toolImplementations[toolName].execute(toolParams);
618
645
  };
619
646
 
@@ -722,7 +749,8 @@ When troubleshooting:
722
749
  this.tokenCounter.updateHistory(this.history);
723
750
 
724
751
  // Schema handling - format response according to provided schema
725
- if (options.schema && !options._schemaFormatted) {
752
+ // Skip schema processing if result came from attempt_completion tool
753
+ if (options.schema && !options._schemaFormatted && !completionAttempted) {
726
754
  if (this.debug) {
727
755
  console.log('[DEBUG] Schema provided, applying automatic formatting...');
728
756
  }
@@ -822,6 +850,38 @@ Convert your previous response content into this JSON format now. Return nothing
822
850
  console.error('[ERROR] Schema formatting failed:', error);
823
851
  // Return the original result if schema formatting fails
824
852
  }
853
+ } else if (completionAttempted && options.schema) {
854
+ // For attempt_completion results with schema, still clean markdown if needed
855
+ try {
856
+ finalResult = cleanSchemaResponse(finalResult);
857
+
858
+ // Validate and fix Mermaid diagrams if present
859
+ const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
860
+ debug: this.debug,
861
+ path: this.allowedFolders[0],
862
+ provider: this.clientApiProvider,
863
+ model: this.model
864
+ });
865
+
866
+ if (mermaidValidation.wasFixed) {
867
+ finalResult = mermaidValidation.fixedResponse;
868
+ if (this.debug) {
869
+ console.log(`[DEBUG] Mermaid diagrams fixed in attempt_completion result`);
870
+ }
871
+ }
872
+
873
+ // Validate JSON if schema expects JSON
874
+ if (isJsonSchema(options.schema)) {
875
+ const validation = validateJsonResponse(finalResult);
876
+ if (!validation.isValid && this.debug) {
877
+ console.log(`[DEBUG] attempt_completion result JSON validation failed: ${validation.error}`);
878
+ }
879
+ }
880
+ } catch (error) {
881
+ if (this.debug) {
882
+ console.log(`[DEBUG] attempt_completion result cleanup failed: ${error.message}`);
883
+ }
884
+ }
825
885
  }
826
886
 
827
887
  return finalResult;
@@ -160,6 +160,8 @@ export class ACPToolManager {
160
160
  return ToolCallKind.query;
161
161
  case 'extract':
162
162
  return ToolCallKind.extract;
163
+ case 'delegate':
164
+ return ToolCallKind.execute;
163
165
  case 'implement':
164
166
  return ToolCallKind.edit;
165
167
  default:
@@ -202,6 +204,15 @@ export class ACPToolManager {
202
204
  sessionId: this.probeAgent.sessionId
203
205
  });
204
206
 
207
+ case 'delegate':
208
+ if (!tools.delegateToolInstance) {
209
+ throw new Error('Delegate tool not available');
210
+ }
211
+ return await tools.delegateToolInstance.execute({
212
+ ...params,
213
+ sessionId: this.probeAgent.sessionId
214
+ });
215
+
205
216
  default:
206
217
  throw new Error(`Unknown tool: ${toolName}`);
207
218
  }
@@ -336,6 +347,21 @@ export class ACPToolManager {
336
347
  },
337
348
  required: ['files']
338
349
  }
350
+ },
351
+ {
352
+ name: 'delegate',
353
+ description: 'Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Use when complex requests can be broken into focused, parallel tasks.',
354
+ kind: ToolCallKind.execute,
355
+ parameters: {
356
+ type: 'object',
357
+ properties: {
358
+ task: {
359
+ type: 'string',
360
+ description: 'A complete, self-contained task that can be executed independently by a subagent. Should be specific and focused on one area of expertise.'
361
+ }
362
+ },
363
+ required: ['task']
364
+ }
339
365
  }
340
366
  ];
341
367
  }
@@ -101,6 +101,9 @@ describe('ACPToolManager', () => {
101
101
  },
102
102
  extractToolInstance: {
103
103
  execute: jest.fn().mockResolvedValue('extract result')
104
+ },
105
+ delegateToolInstance: {
106
+ execute: jest.fn().mockResolvedValue('delegate result')
104
107
  }
105
108
  }
106
109
  };
@@ -113,6 +116,7 @@ describe('ACPToolManager', () => {
113
116
  expect(toolManager.getToolKind('search')).toBe(ToolCallKind.search);
114
117
  expect(toolManager.getToolKind('query')).toBe(ToolCallKind.query);
115
118
  expect(toolManager.getToolKind('extract')).toBe(ToolCallKind.extract);
119
+ expect(toolManager.getToolKind('delegate')).toBe(ToolCallKind.execute);
116
120
  expect(toolManager.getToolKind('implement')).toBe(ToolCallKind.edit);
117
121
  expect(toolManager.getToolKind('unknown')).toBe(ToolCallKind.execute);
118
122
  });
@@ -167,6 +171,18 @@ describe('ACPToolManager', () => {
167
171
  });
168
172
  });
169
173
 
174
+ test('should execute delegate tool successfully', async () => {
175
+ const params = { task: 'Analyze security vulnerabilities in authentication code' };
176
+
177
+ const result = await toolManager.executeToolCall('session-123', 'delegate', params);
178
+
179
+ expect(result).toBe('delegate result');
180
+ expect(mockProbeAgent.wrappedTools.delegateToolInstance.execute).toHaveBeenCalledWith({
181
+ ...params,
182
+ sessionId: 'test-session'
183
+ });
184
+ });
185
+
170
186
  test('should handle tool execution errors', async () => {
171
187
  const error = new Error('Tool execution failed');
172
188
  mockProbeAgent.wrappedTools.searchToolInstance.execute.mockRejectedValue(error);
@@ -187,6 +203,10 @@ describe('ACPToolManager', () => {
187
203
  mockProbeAgent.wrappedTools.searchToolInstance = null;
188
204
 
189
205
  await expect(toolManager.executeToolCall('session-123', 'search', {})).rejects.toThrow('Search tool not available');
206
+
207
+ mockProbeAgent.wrappedTools.delegateToolInstance = null;
208
+
209
+ await expect(toolManager.executeToolCall('session-123', 'delegate', {})).rejects.toThrow('Delegate tool not available');
190
210
  });
191
211
  });
192
212
 
@@ -263,7 +283,7 @@ describe('ACPToolManager', () => {
263
283
  test('should provide correct tool definitions', () => {
264
284
  const definitions = ACPToolManager.getToolDefinitions();
265
285
 
266
- expect(definitions).toHaveLength(3);
286
+ expect(definitions).toHaveLength(4);
267
287
 
268
288
  const searchTool = definitions.find(d => d.name === 'search');
269
289
  expect(searchTool).toBeDefined();
@@ -282,6 +302,12 @@ describe('ACPToolManager', () => {
282
302
  expect(extractTool.kind).toBe(ToolCallKind.extract);
283
303
  expect(extractTool.parameters.properties.files).toBeDefined();
284
304
  expect(extractTool.parameters.required).toContain('files');
305
+
306
+ const delegateTool = definitions.find(d => d.name === 'delegate');
307
+ expect(delegateTool).toBeDefined();
308
+ expect(delegateTool.kind).toBe(ToolCallKind.execute);
309
+ expect(delegateTool.parameters.properties.task).toBeDefined();
310
+ expect(delegateTool.parameters.required).toContain('task');
285
311
  });
286
312
  });
287
313