@probelabs/probe 0.6.0-rc198 → 0.6.0-rc200

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.
@@ -3,6 +3,8 @@
3
3
  * This module contains the core logic for thinking tag removal and attempt_complete recovery
4
4
  */
5
5
 
6
+ import { DEFAULT_VALID_TOOLS, buildToolTagPattern } from '../tools/common.js';
7
+
6
8
  /**
7
9
  * Remove thinking tags and their content from XML string
8
10
  * Handles both closed and unclosed thinking tags
@@ -24,7 +26,8 @@ export function removeThinkingTags(xmlString) {
24
26
  const afterThinking = result.substring(thinkingIndex + '<thinking>'.length);
25
27
 
26
28
  // Look for any tool tags in the remaining content
27
- const toolPattern = /<(search|query|extract|listFiles|searchFiles|implement|attempt_completion|attempt_complete)>/;
29
+ // Use the shared tool list to build the pattern dynamically
30
+ const toolPattern = buildToolTagPattern(DEFAULT_VALID_TOOLS);
28
31
  const toolMatch = afterThinking.match(toolPattern);
29
32
 
30
33
  if (toolMatch) {
@@ -148,8 +151,8 @@ export function checkAttemptCompleteRecovery(cleanedXmlString, validTools = [])
148
151
  * @returns {boolean} - True if other tool tags are found
149
152
  */
150
153
  function hasOtherToolTags(xmlString, validTools = []) {
151
- const defaultTools = ['search', 'query', 'extract', 'listFiles', 'searchFiles', 'implement', 'attempt_completion'];
152
- const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
154
+ // Use the shared canonical tool list as default
155
+ const toolsToCheck = validTools.length > 0 ? validTools : DEFAULT_VALID_TOOLS;
153
156
 
154
157
  // Check for any tool tags other than attempt_complete variants
155
158
  for (const tool of toolsToCheck) {
package/build/delegate.js CHANGED
@@ -229,6 +229,9 @@ export async function delegate({
229
229
  console.error(`[DELEGATE] Using ProbeAgent SDK with code-researcher prompt`);
230
230
  }
231
231
  // Create a new ProbeAgent instance for the delegated task
232
+ // IMPORTANT: We pass both path and cwd set to the same value (workspace root)
233
+ // to prevent path doubling issues. The parent's navigation context should not
234
+ // affect the subagent's path resolution - subagents always work from workspace root.
232
235
  const subagent = new ProbeAgent({
233
236
  sessionId,
234
237
  promptType: 'code-researcher', // Clean prompt, not inherited from parent
@@ -238,7 +241,8 @@ export async function delegate({
238
241
  maxIterations: remainingIterations,
239
242
  debug,
240
243
  tracer,
241
- path, // Inherit from parent
244
+ path, // Workspace root (from delegateTool)
245
+ cwd: path, // Explicitly set cwd to workspace root to prevent path doubling
242
246
  provider, // Inherit from parent
243
247
  model, // Inherit from parent
244
248
  enableBash, // Inherit from parent
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { z } from 'zod';
7
7
  import { resolve, isAbsolute } from 'path';
8
+ import { editSchema, createSchema } from './edit.js';
8
9
 
9
10
  // Common schemas for tool parameters (used for internal execution after XML parsing)
10
11
  export const searchSchema = z.object({
@@ -325,7 +326,8 @@ export const delegateDescription = 'Automatically delegate big distinct tasks to
325
326
  export const bashDescription = 'Execute bash commands for system exploration and development tasks. Secure by default with built-in allow/deny lists.';
326
327
 
327
328
  // Valid tool names that should be parsed as tool calls
328
- const DEFAULT_VALID_TOOLS = [
329
+ // This is the canonical list - all other tool lists should reference this
330
+ export const DEFAULT_VALID_TOOLS = [
329
331
  'search',
330
332
  'query',
331
333
  'extract',
@@ -333,23 +335,41 @@ const DEFAULT_VALID_TOOLS = [
333
335
  'listFiles',
334
336
  'searchFiles',
335
337
  'implement',
338
+ 'bash',
336
339
  'attempt_completion'
337
340
  ];
338
341
 
342
+ /**
343
+ * Build a regex pattern to match any tool tag from the valid tools list
344
+ * @param {string[]} tools - List of tool names (defaults to DEFAULT_VALID_TOOLS)
345
+ * @returns {RegExp} - Regex pattern to match tool opening tags
346
+ */
347
+ export function buildToolTagPattern(tools = DEFAULT_VALID_TOOLS) {
348
+ // Also include attempt_complete as an alias for attempt_completion
349
+ const allTools = [...tools];
350
+ if (allTools.includes('attempt_completion') && !allTools.includes('attempt_complete')) {
351
+ allTools.push('attempt_complete');
352
+ }
353
+ const escaped = allTools.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
354
+ return new RegExp(`<(${escaped.join('|')})>`);
355
+ }
356
+
339
357
  /**
340
358
  * Get valid parameter names for a specific tool from its schema
341
359
  * @param {string} toolName - Name of the tool
342
360
  * @returns {string[]} - Array of valid parameter names for this tool
343
361
  */
344
362
  function getValidParamsForTool(toolName) {
345
- // Map tool names to their schemas
363
+ // Map tool names to their schemas (supports both Zod and JSON Schema formats)
346
364
  const schemaMap = {
347
365
  search: searchSchema,
348
366
  query: querySchema,
349
367
  extract: extractSchema,
350
368
  delegate: delegateSchema,
351
369
  bash: bashSchema,
352
- attempt_completion: attemptCompletionSchema
370
+ attempt_completion: attemptCompletionSchema,
371
+ edit: editSchema,
372
+ create: createSchema
353
373
  };
354
374
 
355
375
  const schema = schemaMap[toolName];
@@ -365,10 +385,15 @@ function getValidParamsForTool(toolName) {
365
385
  }
366
386
 
367
387
  // Extract keys from Zod schema
368
- if (schema && schema._def && schema._def.shape) {
388
+ if (schema._def && schema._def.shape) {
369
389
  return Object.keys(schema._def.shape());
370
390
  }
371
391
 
392
+ // Extract keys from JSON Schema (used by edit and create tools)
393
+ if (schema.properties) {
394
+ return Object.keys(schema.properties);
395
+ }
396
+
372
397
  // Fallback: return empty array if we can't extract schema keys
373
398
  return [];
374
399
  }
@@ -25,7 +25,9 @@ export {
25
25
  attemptCompletionSchema,
26
26
  attemptCompletionToolDefinition,
27
27
  parseAndResolvePaths,
28
- resolveTargetPath
28
+ resolveTargetPath,
29
+ DEFAULT_VALID_TOOLS,
30
+ buildToolTagPattern
29
31
  } from './common.js';
30
32
 
31
33
  // Export edit and create schemas
@@ -295,9 +295,22 @@ export const delegateTool = (options = {}) => {
295
295
  throw new TypeError('model must be a string, null, or undefined');
296
296
  }
297
297
 
298
- // Use inherited path if not specified in AI call
299
- // Priority: explicit path > cwd > first allowedFolder
300
- const effectivePath = path || cwd || (allowedFolders && allowedFolders[0]);
298
+ // Determine the path to pass to the subagent
299
+ // NOTE: Delegation intentionally uses DIFFERENT priority than other tools.
300
+ //
301
+ // Other tools (search, extract, query, bash) use: cwd || allowedFolders[0]
302
+ // Delegation uses: path || allowedFolders[0] || cwd
303
+ //
304
+ // This is intentional because:
305
+ // - Other tools operate within the parent's navigation context (cwd is correct)
306
+ // - Subagents need a FRESH start from workspace root, not parent's navigation state
307
+ // - Using parent's cwd would cause "path doubling" (Issue #348) where paths like
308
+ // /workspace/project/src/internal/build/src/internal/build/file.go get constructed
309
+ //
310
+ // The workspace root (allowedFolders[0]) is the security boundary and correct base
311
+ // for subagent operations. Parent navigation context should not leak to subagents.
312
+ const workspaceRoot = allowedFolders && allowedFolders[0];
313
+ const effectivePath = path || workspaceRoot || cwd;
301
314
 
302
315
  if (debug) {
303
316
  console.error(`Executing delegate with task: "${task.substring(0, 100)}${task.length > 100 ? '...' : ''}"`);
@@ -305,7 +318,7 @@ export const delegateTool = (options = {}) => {
305
318
  console.error(`Parent session: ${parentSessionId}`);
306
319
  }
307
320
  if (effectivePath && effectivePath !== path) {
308
- console.error(`Using inherited path: ${effectivePath}`);
321
+ console.error(`Using workspace root: ${effectivePath} (cwd was: ${cwd || 'not set'})`);
309
322
  }
310
323
  }
311
324