@probelabs/probe 0.6.0-rc253 → 0.6.0-rc255

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 (53) hide show
  1. package/README.md +166 -3
  2. package/bin/binaries/probe-v0.6.0-rc255-aarch64-apple-darwin.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc255-aarch64-unknown-linux-musl.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc255-x86_64-apple-darwin.tar.gz +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc255-x86_64-pc-windows-msvc.zip +0 -0
  6. package/bin/binaries/probe-v0.6.0-rc255-x86_64-unknown-linux-musl.tar.gz +0 -0
  7. package/build/agent/ProbeAgent.d.ts +1 -1
  8. package/build/agent/ProbeAgent.js +51 -16
  9. package/build/agent/acp/tools.js +2 -1
  10. package/build/agent/acp/tools.test.js +2 -1
  11. package/build/agent/dsl/environment.js +19 -0
  12. package/build/agent/index.js +1512 -413
  13. package/build/agent/schemaUtils.js +91 -2
  14. package/build/agent/tools.js +0 -28
  15. package/build/delegate.js +3 -0
  16. package/build/index.js +2 -0
  17. package/build/tools/common.js +6 -5
  18. package/build/tools/edit.js +457 -65
  19. package/build/tools/executePlan.js +3 -1
  20. package/build/tools/fileTracker.js +318 -0
  21. package/build/tools/fuzzyMatch.js +271 -0
  22. package/build/tools/hashline.js +131 -0
  23. package/build/tools/lineEditHeuristics.js +138 -0
  24. package/build/tools/symbolEdit.js +119 -0
  25. package/build/tools/vercel.js +40 -9
  26. package/cjs/agent/ProbeAgent.cjs +1615 -517
  27. package/cjs/index.cjs +1643 -543
  28. package/index.d.ts +189 -1
  29. package/package.json +1 -1
  30. package/src/agent/ProbeAgent.d.ts +1 -1
  31. package/src/agent/ProbeAgent.js +51 -16
  32. package/src/agent/acp/tools.js +2 -1
  33. package/src/agent/acp/tools.test.js +2 -1
  34. package/src/agent/dsl/environment.js +19 -0
  35. package/src/agent/index.js +14 -3
  36. package/src/agent/schemaUtils.js +91 -2
  37. package/src/agent/tools.js +0 -28
  38. package/src/delegate.js +3 -0
  39. package/src/index.js +2 -0
  40. package/src/tools/common.js +6 -5
  41. package/src/tools/edit.js +457 -65
  42. package/src/tools/executePlan.js +3 -1
  43. package/src/tools/fileTracker.js +318 -0
  44. package/src/tools/fuzzyMatch.js +271 -0
  45. package/src/tools/hashline.js +131 -0
  46. package/src/tools/lineEditHeuristics.js +138 -0
  47. package/src/tools/symbolEdit.js +119 -0
  48. package/src/tools/vercel.js +40 -9
  49. package/bin/binaries/probe-v0.6.0-rc253-aarch64-apple-darwin.tar.gz +0 -0
  50. package/bin/binaries/probe-v0.6.0-rc253-aarch64-unknown-linux-musl.tar.gz +0 -0
  51. package/bin/binaries/probe-v0.6.0-rc253-x86_64-apple-darwin.tar.gz +0 -0
  52. package/bin/binaries/probe-v0.6.0-rc253-x86_64-pc-windows-msvc.zip +0 -0
  53. package/bin/binaries/probe-v0.6.0-rc253-x86_64-unknown-linux-musl.tar.gz +0 -0
@@ -165,6 +165,39 @@ export function decodeHtmlEntities(text) {
165
165
  return decoded;
166
166
  }
167
167
 
168
+ /**
169
+ * Sanitize Markdown escape sequences in JSON strings
170
+ *
171
+ * Markdown uses backslash escapes like \*, \_, \#, \~ etc. which are NOT valid
172
+ * JSON escape sequences. When AI models produce JSON with Markdown content,
173
+ * these escapes cause JSON.parse() to fail with "Invalid \escape" errors.
174
+ *
175
+ * This function removes the backslash from invalid escape sequences while
176
+ * preserving valid JSON escapes: \\, \", \/, \b, \f, \n, \r, \t, \uXXXX
177
+ *
178
+ * @param {string} jsonString - JSON string that may contain Markdown escapes
179
+ * @returns {string} - JSON string with invalid escapes sanitized
180
+ */
181
+ export function sanitizeMarkdownEscapesInJson(jsonString) {
182
+ if (!jsonString || typeof jsonString !== 'string') {
183
+ return jsonString;
184
+ }
185
+
186
+ // Strategy: Match either:
187
+ // 1. \\\\ (escaped backslash) - preserve as-is
188
+ // 2. \\X where X is NOT a valid JSON escape char - remove the backslash
189
+ //
190
+ // Valid JSON escape chars: " \ / b f n r t u
191
+ // This converts: \* → *, \_ → _, \# → #, \~ → ~, etc.
192
+ // But preserves: \\, \", \n, \t, \r, \b, \f, \/, \uXXXX
193
+ return jsonString.replace(/\\\\|\\([^"\\\/bfnrtu])/g, (match, captured) => {
194
+ if (match === '\\\\') {
195
+ return '\\\\'; // Preserve escaped backslash
196
+ }
197
+ return captured; // Remove backslash from invalid escape
198
+ });
199
+ }
200
+
168
201
  /**
169
202
  * Normalize JavaScript syntax to valid JSON syntax
170
203
  * Converts single quotes to double quotes for strings in JSON-like structures
@@ -261,6 +294,22 @@ export function cleanSchemaResponse(response) {
261
294
  return cleanSchemaResponse(resultWrapperMatch[1]);
262
295
  }
263
296
 
297
+ // Strip <tool_code>...</tool_code> wrapper (Gemini-style code execution format)
298
+ // Issue #443: Gemini sometimes wraps responses in <plan> + <tool_code> tags
299
+ // e.g., <tool_code>print(attempt_completion({"projects": ["repo1"]}))</tool_code>
300
+ const toolCodeMatch = trimmed.match(/<tool_code>\s*([\s\S]*?)\s*<\/tool_code>/);
301
+ if (toolCodeMatch) {
302
+ let innerContent = toolCodeMatch[1].trim();
303
+ // Extract JSON from print() or attempt_completion() wrappers
304
+ // e.g., print({"key": "value"}) or attempt_completion({"key": "value"})
305
+ const funcCallMatch = innerContent.match(/(?:print|attempt_completion)\s*\(\s*([{\[][\s\S]*[}\]])\s*\)/);
306
+ if (funcCallMatch) {
307
+ return cleanSchemaResponse(funcCallMatch[1]);
308
+ }
309
+ // Try cleaning the inner content directly
310
+ return cleanSchemaResponse(innerContent);
311
+ }
312
+
264
313
  // First, look for JSON after code block markers - similar to mermaid extraction
265
314
  // Try with json language specifier
266
315
  const jsonBlockMatch = trimmed.match(/```json\s*\n([\s\S]*?)\n```/);
@@ -370,9 +419,30 @@ export function validateJsonResponse(response, options = {}) {
370
419
  }
371
420
  }
372
421
 
422
+ // Try to parse the response, with fallback to sanitizing Markdown escapes (issue #441)
423
+ let responseToValidate = response;
424
+ try {
425
+ JSON.parse(response);
426
+ } catch (initialError) {
427
+ // Check if the error is due to invalid escape sequences (Markdown escapes like \*, \_)
428
+ if (initialError.message && initialError.message.includes('escape')) {
429
+ const sanitized = sanitizeMarkdownEscapesInJson(response);
430
+ try {
431
+ JSON.parse(sanitized);
432
+ // Sanitized version parses - use it instead
433
+ responseToValidate = sanitized;
434
+ if (debug) {
435
+ console.log(`[DEBUG] JSON validation: Fixed Markdown escapes in JSON (issue #441)`);
436
+ }
437
+ } catch {
438
+ // Sanitization didn't help, continue with original (will fail below with proper error)
439
+ }
440
+ }
441
+ }
442
+
373
443
  try {
374
444
  const parseStart = Date.now();
375
- const parsed = JSON.parse(response);
445
+ const parsed = JSON.parse(responseToValidate);
376
446
  const parseTime = Date.now() - parseStart;
377
447
 
378
448
  if (debug) {
@@ -853,7 +923,26 @@ export function tryAutoWrapForSimpleSchema(response, schema, options = {}) {
853
923
  console.log(`[DEBUG] Auto-wrap: Response is already valid JSON, skipping`);
854
924
  }
855
925
  return null;
856
- } catch {
926
+ } catch (initialError) {
927
+ // Not valid JSON - check if it's due to Markdown escapes (issue #441)
928
+ // AI models sometimes produce JSON with Markdown escapes like \* or \_
929
+ // which are valid Markdown but NOT valid JSON escape sequences
930
+ if (initialError.message && initialError.message.includes('escape')) {
931
+ try {
932
+ const sanitized = sanitizeMarkdownEscapesInJson(response);
933
+ JSON.parse(sanitized);
934
+ // Sanitized JSON is valid! Return it instead of wrapping
935
+ if (debug) {
936
+ console.log(`[DEBUG] Auto-wrap: Fixed Markdown escapes in JSON (issue #441), returning sanitized JSON`);
937
+ }
938
+ return sanitized;
939
+ } catch {
940
+ // Sanitization didn't help, proceed with wrapping
941
+ if (debug) {
942
+ console.log(`[DEBUG] Auto-wrap: Markdown escape sanitization didn't fix JSON, proceeding with wrapping`);
943
+ }
944
+ }
945
+ }
857
946
  // Not valid JSON, proceed with wrapping
858
947
  }
859
948
 
@@ -87,7 +87,6 @@ export function createTools(configOptions) {
87
87
  if (configOptions.allowEdit && isToolAllowed('create')) {
88
88
  tools.createTool = createTool(configOptions);
89
89
  }
90
-
91
90
  return tools;
92
91
  }
93
92
 
@@ -132,33 +131,6 @@ export {
132
131
  parseXmlToolCall
133
132
  };
134
133
 
135
- // Define the implement tool XML definition
136
- export const implementToolDefinition = `
137
- ## implement
138
- Description: Implement a given task. Can modify files. Can be used ONLY if task explicitly stated that something requires modification or implementation.
139
-
140
- Parameters:
141
- - task: (required) The task description. Should be as detailed as possible, ideally pointing to exact files which needs be modified or created.
142
- - autoCommits: (optional) Whether to enable auto-commits in aider. Default is false.
143
-
144
- Usage Example:
145
-
146
- <examples>
147
-
148
- User: Can you implement a function to calculate Fibonacci numbers in main.js?
149
- <implement>
150
- <task>Implement a recursive function to calculate the nth Fibonacci number in main.js</task>
151
- </implement>
152
-
153
- User: Can you implement a function to calculate Fibonacci numbers in main.js with auto-commits?
154
- <implement>
155
- <task>Implement a recursive function to calculate the nth Fibonacci number in main.js</task>
156
- <autoCommits>true</autoCommits>
157
- </implement>
158
-
159
- </examples>
160
- `;
161
-
162
134
  // Define the listFiles tool XML definition
163
135
  export const listFilesToolDefinition = `
164
136
  ## listFiles
package/build/delegate.js CHANGED
@@ -346,6 +346,7 @@ const DEFAULT_DELEGATE_TIMEOUT = parseInt(process.env.DELEGATE_TIMEOUT, 10) || 3
346
346
  * @param {Object} [options.tracer=null] - Telemetry tracer instance
347
347
  * @param {boolean} [options.enableBash=false] - Enable bash tool (inherited from parent)
348
348
  * @param {Object} [options.bashConfig] - Bash configuration (inherited from parent)
349
+ * @param {boolean} [options.allowEdit=false] - Allow edit/create tools (inherited from parent)
349
350
  * @param {string} [options.architectureFileName] - Architecture context filename to embed from repo root
350
351
  * @param {string} [options.promptType='code-researcher'] - Prompt type for the subagent
351
352
  * @param {Array<string>|null} [options.allowedTools] - Allowed tools for the subagent (null = default)
@@ -373,6 +374,7 @@ export async function delegate({
373
374
  model = null,
374
375
  enableBash = false,
375
376
  bashConfig = null,
377
+ allowEdit = false,
376
378
  architectureFileName = null,
377
379
  promptType = 'code-researcher',
378
380
  allowedTools = null,
@@ -462,6 +464,7 @@ export async function delegate({
462
464
  model, // Inherit from parent
463
465
  enableBash, // Inherit from parent
464
466
  bashConfig, // Inherit from parent
467
+ allowEdit, // Inherit from parent
465
468
  architectureFileName,
466
469
  allowedTools,
467
470
  disableTools,
package/build/index.js CHANGED
@@ -51,6 +51,7 @@ import { searchTool, queryTool, extractTool, delegateTool, analyzeAllTool } from
51
51
  import { createExecutePlanTool, getExecutePlanToolDefinition, createCleanupExecutePlanTool, getCleanupExecutePlanToolDefinition } from './tools/executePlan.js';
52
52
  import { bashTool } from './tools/bash.js';
53
53
  import { editTool, createTool } from './tools/edit.js';
54
+ import { FileTracker } from './tools/fileTracker.js';
54
55
  import { ProbeAgent } from './agent/ProbeAgent.js';
55
56
  import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
56
57
  import { listFilesToolInstance, searchFilesToolInstance } from './agent/probeTool.js';
@@ -98,6 +99,7 @@ export {
98
99
  bashTool,
99
100
  editTool,
100
101
  createTool,
102
+ FileTracker,
101
103
  // Export tool instances
102
104
  listFilesToolInstance,
103
105
  searchFilesToolInstance,
@@ -269,6 +269,8 @@ User: Read file inside the dependency
269
269
  </extract>
270
270
 
271
271
  </examples>
272
+
273
+ **Edit Integration:** The line numbers shown in extract output (e.g. "42 | code") can be used directly with the edit tool's start_line/end_line parameters for precise line-targeted editing. To edit inside a large function: extract it by symbol name first (e.g. "file.js#myFunction"), then use the line numbers from the output to make surgical edits with start_line/end_line.
272
274
  `;
273
275
 
274
276
  export const delegateToolDefinition = `
@@ -431,7 +433,7 @@ Capabilities:
431
433
 
432
434
  export const searchDescription = 'Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions.';
433
435
  export const queryDescription = 'Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.';
434
- export const extractDescription = 'Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.';
436
+ export const extractDescription = 'Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. Line numbers from output can be used with edit start_line/end_line for precise editing.';
435
437
  export const delegateDescription = 'Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.';
436
438
  export const bashDescription = 'Execute bash commands for system exploration and development tasks. Secure by default with built-in allow/deny lists.';
437
439
  export const analyzeAllDescription = 'Answer questions that require analyzing ALL matching data in the codebase. Use for aggregate questions like "What features exist?", "List all API endpoints", "Count TODO comments". The AI automatically plans the search strategy, processes all results via map-reduce, and synthesizes a comprehensive answer. WARNING: Slower than search - only use when you need complete coverage.';
@@ -450,7 +452,6 @@ export const DEFAULT_VALID_TOOLS = [
450
452
  'useSkill',
451
453
  'listFiles',
452
454
  'searchFiles',
453
- 'implement',
454
455
  'bash',
455
456
  'task',
456
457
  'attempt_completion'
@@ -496,9 +497,9 @@ function getValidParamsForTool(toolName) {
496
497
 
497
498
  const schema = schemaMap[toolName];
498
499
  if (!schema) {
499
- // For tools without schema (listFiles, searchFiles, implement), return common params
500
+ // For tools without schema (listFiles, searchFiles), return common params
500
501
  // These are the shared params that appear across multiple tools
501
- return ['path', 'directory', 'pattern', 'recursive', 'includeHidden', 'task', 'files', 'autoCommits', 'result'];
502
+ return ['path', 'directory', 'pattern', 'recursive', 'includeHidden', 'task', 'files', 'result'];
502
503
  }
503
504
 
504
505
  // For attempt_completion, it has custom validation, just return 'result'
@@ -688,7 +689,7 @@ export function detectUnrecognizedToolCall(xmlString, validTools) {
688
689
  // Common tool names that AI might try to use (these should appear as top-level tags)
689
690
  const knownToolNames = [
690
691
  'search', 'query', 'extract', 'listFiles', 'searchFiles',
691
- 'listSkills', 'useSkill', 'readImage', 'implement', 'edit',
692
+ 'listSkills', 'useSkill', 'readImage', 'edit',
692
693
  'create', 'delegate', 'bash', 'task', 'attempt_completion',
693
694
  'attempt_complete', 'read_file', 'write_file', 'run_command',
694
695
  'grep', 'find', 'cat', 'list_directory'