@probelabs/probe 0.6.0-rc258 → 0.6.0-rc260
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.
- package/bin/binaries/probe-v0.6.0-rc260-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc260-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc260-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/{probe-v0.6.0-rc258-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc260-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/probe-v0.6.0-rc260-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +13 -0
- package/build/agent/bashCommandUtils.js +3 -0
- package/build/agent/bashPermissions.js +23 -0
- package/build/agent/index.js +223 -26
- package/build/agent/probeTool.js +9 -0
- package/build/agent/schemaUtils.js +40 -4
- package/build/agent/tools.js +8 -0
- package/build/index.js +7 -2
- package/build/tools/common.js +56 -23
- package/build/tools/edit.js +139 -6
- package/build/tools/index.js +5 -2
- package/cjs/agent/ProbeAgent.cjs +296 -99
- package/cjs/index.cjs +306 -99
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +13 -0
- package/src/agent/bashCommandUtils.js +3 -0
- package/src/agent/bashPermissions.js +23 -0
- package/src/agent/probeTool.js +9 -0
- package/src/agent/schemaUtils.js +40 -4
- package/src/agent/tools.js +8 -0
- package/src/index.js +7 -2
- package/src/tools/common.js +56 -23
- package/src/tools/edit.js +139 -6
- package/src/tools/index.js +5 -2
- package/bin/binaries/probe-v0.6.0-rc258-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc258-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc258-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc258-x86_64-unknown-linux-musl.tar.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
attemptCompletionToolDefinition,
|
|
60
60
|
editToolDefinition,
|
|
61
61
|
createToolDefinition,
|
|
62
|
+
multiEditToolDefinition,
|
|
62
63
|
googleSearchToolDefinition,
|
|
63
64
|
urlContextToolDefinition,
|
|
64
65
|
attemptCompletionSchema,
|
|
@@ -964,6 +965,9 @@ export class ProbeAgent {
|
|
|
964
965
|
if (wrappedTools.createToolInstance && isToolAllowed('create')) {
|
|
965
966
|
this.toolImplementations.create = wrappedTools.createToolInstance;
|
|
966
967
|
}
|
|
968
|
+
if (wrappedTools.multiEditToolInstance && isToolAllowed('multi_edit')) {
|
|
969
|
+
this.toolImplementations.multi_edit = wrappedTools.multiEditToolInstance;
|
|
970
|
+
}
|
|
967
971
|
}
|
|
968
972
|
|
|
969
973
|
// Store wrapped tools for ACP system
|
|
@@ -2565,6 +2569,9 @@ ${extractGuidance}
|
|
|
2565
2569
|
if (this.allowEdit && isToolAllowed('create')) {
|
|
2566
2570
|
toolDefinitions += `${createToolDefinition}\n`;
|
|
2567
2571
|
}
|
|
2572
|
+
if (this.allowEdit && isToolAllowed('multi_edit')) {
|
|
2573
|
+
toolDefinitions += `${multiEditToolDefinition}\n`;
|
|
2574
|
+
}
|
|
2568
2575
|
// Bash tool (require both enableBash flag AND allowedTools permission)
|
|
2569
2576
|
if (this.enableBash && isToolAllowed('bash')) {
|
|
2570
2577
|
toolDefinitions += `${bashToolDefinition}\n`;
|
|
@@ -2670,6 +2677,9 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
|
|
|
2670
2677
|
if (this.allowEdit && isToolAllowed('create')) {
|
|
2671
2678
|
availableToolsList += '- create: Create new files with specified content.\n';
|
|
2672
2679
|
}
|
|
2680
|
+
if (this.allowEdit && isToolAllowed('multi_edit')) {
|
|
2681
|
+
availableToolsList += '- multi_edit: Apply multiple file edits in one call using a JSON array of operations.\n';
|
|
2682
|
+
}
|
|
2673
2683
|
if (this.enableDelegate && isToolAllowed('delegate')) {
|
|
2674
2684
|
availableToolsList += '- delegate: Delegate big distinct tasks to specialized probe subagents.\n';
|
|
2675
2685
|
}
|
|
@@ -3430,6 +3440,9 @@ Follow these instructions carefully:
|
|
|
3430
3440
|
if (this.allowEdit && this.allowedTools.isEnabled('create')) {
|
|
3431
3441
|
validTools.push('create');
|
|
3432
3442
|
}
|
|
3443
|
+
if (this.allowEdit && this.allowedTools.isEnabled('multi_edit')) {
|
|
3444
|
+
validTools.push('multi_edit');
|
|
3445
|
+
}
|
|
3433
3446
|
// Bash tool (require both enableBash flag AND allowedTools permission)
|
|
3434
3447
|
if (this.enableBash && this.allowedTools.isEnabled('bash')) {
|
|
3435
3448
|
validTools.push('bash');
|
|
@@ -127,6 +127,8 @@ export function parseSimpleCommand(command) {
|
|
|
127
127
|
/&&/, // Logical AND
|
|
128
128
|
/\|\|/, // Logical OR
|
|
129
129
|
/(?<!\\);/, // Command separator (but not escaped \;)
|
|
130
|
+
/\n/, // Newline command separator (multi-line commands)
|
|
131
|
+
/\r/, // Carriage return (CRLF line endings)
|
|
130
132
|
/&$/, // Background execution
|
|
131
133
|
/\$\(/, // Command substitution $()
|
|
132
134
|
/`/, // Command substitution ``
|
|
@@ -260,6 +262,7 @@ export function isComplexPattern(pattern) {
|
|
|
260
262
|
/&&/, // Logical AND
|
|
261
263
|
/\|\|/, // Logical OR
|
|
262
264
|
/;/, // Command separator
|
|
265
|
+
/\n/, // Newline command separator
|
|
263
266
|
/&$/, // Background execution
|
|
264
267
|
/\$\(/, // Command substitution $()
|
|
265
268
|
/`/, // Command substitution ``
|
|
@@ -402,6 +402,29 @@ export class BashPermissionChecker {
|
|
|
402
402
|
i++;
|
|
403
403
|
continue;
|
|
404
404
|
}
|
|
405
|
+
// Check for newline (command separator in multi-line scripts)
|
|
406
|
+
// Also handle \r\n (CRLF) line endings
|
|
407
|
+
if (char === '\n' || (char === '\r' && nextChar === '\n')) {
|
|
408
|
+
if (current.trim()) {
|
|
409
|
+
components.push(current.trim());
|
|
410
|
+
}
|
|
411
|
+
current = '';
|
|
412
|
+
if (char === '\r') {
|
|
413
|
+
i += 2; // Skip \r\n
|
|
414
|
+
} else {
|
|
415
|
+
i++;
|
|
416
|
+
}
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (char === '\r') {
|
|
420
|
+
// Bare \r without \n
|
|
421
|
+
if (current.trim()) {
|
|
422
|
+
components.push(current.trim());
|
|
423
|
+
}
|
|
424
|
+
current = '';
|
|
425
|
+
i++;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
405
428
|
}
|
|
406
429
|
|
|
407
430
|
current += char;
|
package/build/agent/index.js
CHANGED
|
@@ -9427,7 +9427,7 @@ async function handleLineEdit({ resolvedPath, file_path, start_line, end_line, n
|
|
|
9427
9427
|
return buildLineEditResponse(file_path, startLine, endLine, newLines.length, fileLines, startLine - 1, action, modifications);
|
|
9428
9428
|
}
|
|
9429
9429
|
}
|
|
9430
|
-
var editTool, createTool, editSchema, createSchema, editDescription, createDescription, editToolDefinition, createToolDefinition;
|
|
9430
|
+
var editTool, createTool, multiEditTool, editSchema, createSchema, multiEditSchema, editDescription, createDescription, multiEditDescription, editToolDefinition, createToolDefinition, multiEditToolDefinition;
|
|
9431
9431
|
var init_edit = __esm({
|
|
9432
9432
|
"src/tools/edit.js"() {
|
|
9433
9433
|
"use strict";
|
|
@@ -9497,7 +9497,7 @@ Parameters:
|
|
|
9497
9497
|
},
|
|
9498
9498
|
required: ["file_path", "new_string"]
|
|
9499
9499
|
},
|
|
9500
|
-
execute: async ({ file_path, old_string, new_string, replace_all = false, symbol, position, start_line, end_line }) => {
|
|
9500
|
+
execute: async ({ file_path, old_string, new_string, replace_all = false, symbol, position, start_line, end_line, workingDirectory }) => {
|
|
9501
9501
|
try {
|
|
9502
9502
|
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
9503
9503
|
return `Error editing file: Invalid file_path - must be a non-empty string. Provide an absolute path or a path relative to the working directory (e.g. "src/main.js").`;
|
|
@@ -9505,7 +9505,8 @@ Parameters:
|
|
|
9505
9505
|
if (new_string === void 0 || new_string === null || typeof new_string !== "string") {
|
|
9506
9506
|
return `Error editing file: Invalid new_string - must be a string. Provide the replacement content as a string value (empty string "" is valid for deletions).`;
|
|
9507
9507
|
}
|
|
9508
|
-
const
|
|
9508
|
+
const effectiveCwd = workingDirectory || cwd || process.cwd();
|
|
9509
|
+
const resolvedPath = isAbsolute(file_path) ? file_path : resolve(effectiveCwd, file_path);
|
|
9509
9510
|
if (debug) {
|
|
9510
9511
|
console.error(`[Edit] Attempting to edit file: ${resolvedPath}`);
|
|
9511
9512
|
}
|
|
@@ -9612,7 +9613,7 @@ Important:
|
|
|
9612
9613
|
},
|
|
9613
9614
|
required: ["file_path", "content"]
|
|
9614
9615
|
},
|
|
9615
|
-
execute: async ({ file_path, content, overwrite = false }) => {
|
|
9616
|
+
execute: async ({ file_path, content, overwrite = false, workingDirectory }) => {
|
|
9616
9617
|
try {
|
|
9617
9618
|
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
9618
9619
|
return `Error creating file: Invalid file_path - must be a non-empty string. Provide an absolute path or a path relative to the working directory (e.g. "src/newFile.js").`;
|
|
@@ -9620,7 +9621,8 @@ Important:
|
|
|
9620
9621
|
if (content === void 0 || content === null || typeof content !== "string") {
|
|
9621
9622
|
return `Error creating file: Invalid content - must be a string. Provide the file content as a string value (empty string "" is valid for an empty file).`;
|
|
9622
9623
|
}
|
|
9623
|
-
const
|
|
9624
|
+
const effectiveCwd = workingDirectory || cwd || process.cwd();
|
|
9625
|
+
const resolvedPath = isAbsolute(file_path) ? file_path : resolve(effectiveCwd, file_path);
|
|
9624
9626
|
if (debug) {
|
|
9625
9627
|
console.error(`[Create] Attempting to create file: ${resolvedPath}`);
|
|
9626
9628
|
}
|
|
@@ -9649,6 +9651,70 @@ Important:
|
|
|
9649
9651
|
}
|
|
9650
9652
|
});
|
|
9651
9653
|
};
|
|
9654
|
+
multiEditTool = (options = {}) => {
|
|
9655
|
+
const editInstance = editTool(options);
|
|
9656
|
+
return tool({
|
|
9657
|
+
name: "multi_edit",
|
|
9658
|
+
description: "Apply multiple file edits in a single tool call. Accepts a JSON array of edit operations.",
|
|
9659
|
+
inputSchema: {
|
|
9660
|
+
type: "object",
|
|
9661
|
+
properties: {
|
|
9662
|
+
edits: {
|
|
9663
|
+
type: "string",
|
|
9664
|
+
description: "JSON array of edit operations. Each object supports: file_path, old_string, new_string, replace_all, symbol, position, start_line, end_line."
|
|
9665
|
+
}
|
|
9666
|
+
},
|
|
9667
|
+
required: ["edits"]
|
|
9668
|
+
},
|
|
9669
|
+
execute: async ({ edits: rawEdits }) => {
|
|
9670
|
+
let edits;
|
|
9671
|
+
if (typeof rawEdits === "string") {
|
|
9672
|
+
try {
|
|
9673
|
+
edits = JSON.parse(rawEdits);
|
|
9674
|
+
} catch (e) {
|
|
9675
|
+
return `Error: Invalid JSON in edits parameter - ${e.message}. Provide a raw JSON array between <edits> tags.`;
|
|
9676
|
+
}
|
|
9677
|
+
} else if (Array.isArray(rawEdits)) {
|
|
9678
|
+
edits = rawEdits;
|
|
9679
|
+
} else {
|
|
9680
|
+
return "Error: edits must be a JSON array of edit operations.";
|
|
9681
|
+
}
|
|
9682
|
+
if (!Array.isArray(edits) || edits.length === 0) {
|
|
9683
|
+
return "Error: edits must be a non-empty JSON array.";
|
|
9684
|
+
}
|
|
9685
|
+
if (edits.length > 50) {
|
|
9686
|
+
return `Error: Too many edits (${edits.length}). Maximum 50 per batch.`;
|
|
9687
|
+
}
|
|
9688
|
+
const results = [];
|
|
9689
|
+
let successCount = 0;
|
|
9690
|
+
let failCount = 0;
|
|
9691
|
+
for (let i = 0; i < edits.length; i++) {
|
|
9692
|
+
const editOp = edits[i];
|
|
9693
|
+
if (!editOp || typeof editOp !== "object" || Array.isArray(editOp)) {
|
|
9694
|
+
results.push(`[${i + 1}] FAIL: Invalid edit operation - must be an object`);
|
|
9695
|
+
failCount++;
|
|
9696
|
+
continue;
|
|
9697
|
+
}
|
|
9698
|
+
try {
|
|
9699
|
+
const result = await editInstance.execute(editOp);
|
|
9700
|
+
const isError = typeof result === "string" && result.startsWith("Error");
|
|
9701
|
+
if (isError) {
|
|
9702
|
+
results.push(`[${i + 1}] FAIL: ${result}`);
|
|
9703
|
+
failCount++;
|
|
9704
|
+
} else {
|
|
9705
|
+
results.push(`[${i + 1}] OK: ${result}`);
|
|
9706
|
+
successCount++;
|
|
9707
|
+
}
|
|
9708
|
+
} catch (error) {
|
|
9709
|
+
results.push(`[${i + 1}] FAIL: ${error.message}`);
|
|
9710
|
+
failCount++;
|
|
9711
|
+
}
|
|
9712
|
+
}
|
|
9713
|
+
const summary = `Multi-edit: ${successCount}/${edits.length} succeeded` + (failCount > 0 ? `, ${failCount} failed` : "");
|
|
9714
|
+
return summary + "\n\n" + results.join("\n");
|
|
9715
|
+
}
|
|
9716
|
+
});
|
|
9717
|
+
};
|
|
9652
9718
|
editSchema = {
|
|
9653
9719
|
type: "object",
|
|
9654
9720
|
properties: {
|
|
@@ -9706,8 +9772,19 @@ Important:
|
|
|
9706
9772
|
},
|
|
9707
9773
|
required: ["file_path", "content"]
|
|
9708
9774
|
};
|
|
9775
|
+
multiEditSchema = {
|
|
9776
|
+
type: "object",
|
|
9777
|
+
properties: {
|
|
9778
|
+
edits: {
|
|
9779
|
+
type: "string",
|
|
9780
|
+
description: "JSON array of edit operations"
|
|
9781
|
+
}
|
|
9782
|
+
},
|
|
9783
|
+
required: ["edits"]
|
|
9784
|
+
};
|
|
9709
9785
|
editDescription = "Edit files using text replacement, AST-aware symbol operations, or line-targeted editing. Supports fuzzy matching for text edits and optional hash-based integrity verification for line edits.";
|
|
9710
9786
|
createDescription = "Create new files with specified content. Will create parent directories if needed.";
|
|
9787
|
+
multiEditDescription = "Apply multiple file edits in a single tool call. Accepts a JSON array of edit operations, each supporting the same modes as the edit tool.";
|
|
9711
9788
|
editToolDefinition = `
|
|
9712
9789
|
## edit
|
|
9713
9790
|
Description: ${editDescription}
|
|
@@ -9863,6 +9940,44 @@ Examples:
|
|
|
9863
9940
|
This is a new project.</content>
|
|
9864
9941
|
<overwrite>true</overwrite>
|
|
9865
9942
|
</create>`;
|
|
9943
|
+
multiEditToolDefinition = `
|
|
9944
|
+
## multi_edit
|
|
9945
|
+
Description: ${multiEditDescription}
|
|
9946
|
+
|
|
9947
|
+
Apply multiple edits in one call. Each operation in the array uses the same parameters as the edit tool:
|
|
9948
|
+
- file_path, old_string, new_string (text mode)
|
|
9949
|
+
- file_path, symbol, new_string (symbol replace)
|
|
9950
|
+
- file_path, symbol, new_string, position (symbol insert)
|
|
9951
|
+
- file_path, start_line, new_string (line-targeted)
|
|
9952
|
+
|
|
9953
|
+
Edits are applied sequentially. Failures do not stop remaining edits. Maximum 50 edits per call.
|
|
9954
|
+
|
|
9955
|
+
Parameters:
|
|
9956
|
+
- edits: (required) JSON array of edit objects. Place raw JSON between tags.
|
|
9957
|
+
|
|
9958
|
+
When to use multi_edit vs edit:
|
|
9959
|
+
- Use edit for a single change to one file
|
|
9960
|
+
- Use multi_edit when making 2+ related changes across files (e.g., rename a function and update all call sites)
|
|
9961
|
+
- Use multi_edit for coordinated multi-file refactoring where order matters
|
|
9962
|
+
|
|
9963
|
+
Examples:
|
|
9964
|
+
|
|
9965
|
+
Multiple text replacements across files:
|
|
9966
|
+
<multi_edit>
|
|
9967
|
+
<edits>[
|
|
9968
|
+
{"file_path": "src/main.js", "old_string": "return false;", "new_string": "return true;"},
|
|
9969
|
+
{"file_path": "src/config.js", "old_string": "debug: false", "new_string": "debug: true"}
|
|
9970
|
+
]</edits>
|
|
9971
|
+
</multi_edit>
|
|
9972
|
+
|
|
9973
|
+
Mixed edit modes in one batch:
|
|
9974
|
+
<multi_edit>
|
|
9975
|
+
<edits>[
|
|
9976
|
+
{"file_path": "src/utils.js", "symbol": "oldHelper", "new_string": "function newHelper() { return 42; }"},
|
|
9977
|
+
{"file_path": "src/main.js", "old_string": "oldHelper()", "new_string": "newHelper()", "replace_all": true},
|
|
9978
|
+
{"file_path": "src/index.js", "start_line": "10", "end_line": "12", "new_string": "export { newHelper };"}
|
|
9979
|
+
]</edits>
|
|
9980
|
+
</multi_edit>`;
|
|
9866
9981
|
}
|
|
9867
9982
|
});
|
|
9868
9983
|
|
|
@@ -10356,7 +10471,8 @@ function getValidParamsForTool(toolName) {
|
|
|
10356
10471
|
task: taskSchema,
|
|
10357
10472
|
attempt_completion: attemptCompletionSchema,
|
|
10358
10473
|
edit: editSchema,
|
|
10359
|
-
create: createSchema
|
|
10474
|
+
create: createSchema,
|
|
10475
|
+
multi_edit: multiEditSchema
|
|
10360
10476
|
};
|
|
10361
10477
|
const schema = schemaMap[toolName];
|
|
10362
10478
|
if (!schema) {
|
|
@@ -10396,7 +10512,7 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
10396
10512
|
const closeTag = `</${toolName}>`;
|
|
10397
10513
|
const openIndex = earliestOpenIndex;
|
|
10398
10514
|
let closeIndex;
|
|
10399
|
-
if (toolName
|
|
10515
|
+
if (LAST_INDEX_TOOLS.has(toolName)) {
|
|
10400
10516
|
closeIndex = xmlString.lastIndexOf(closeTag);
|
|
10401
10517
|
if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
|
|
10402
10518
|
closeIndex = -1;
|
|
@@ -10421,7 +10537,15 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
10421
10537
|
if (paramOpenIndex === -1) {
|
|
10422
10538
|
continue;
|
|
10423
10539
|
}
|
|
10424
|
-
let paramCloseIndex
|
|
10540
|
+
let paramCloseIndex;
|
|
10541
|
+
if (RAW_CONTENT_PARAMS.has(paramName)) {
|
|
10542
|
+
paramCloseIndex = innerContent.lastIndexOf(paramCloseTag);
|
|
10543
|
+
if (paramCloseIndex !== -1 && paramCloseIndex <= paramOpenIndex + paramOpenTag.length) {
|
|
10544
|
+
paramCloseIndex = -1;
|
|
10545
|
+
}
|
|
10546
|
+
} else {
|
|
10547
|
+
paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
|
|
10548
|
+
}
|
|
10425
10549
|
if (paramCloseIndex === -1) {
|
|
10426
10550
|
let nextTagIndex = innerContent.length;
|
|
10427
10551
|
for (const nextParam of validParams) {
|
|
@@ -10433,18 +10557,26 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
10433
10557
|
}
|
|
10434
10558
|
paramCloseIndex = nextTagIndex;
|
|
10435
10559
|
}
|
|
10436
|
-
|
|
10560
|
+
const rawValue = innerContent.substring(
|
|
10437
10561
|
paramOpenIndex + paramOpenTag.length,
|
|
10438
10562
|
paramCloseIndex
|
|
10439
|
-
)
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10563
|
+
);
|
|
10564
|
+
let paramValue;
|
|
10565
|
+
if (RAW_CONTENT_PARAMS.has(paramName)) {
|
|
10566
|
+
paramValue = unescapeXmlEntities(rawValue.replace(/^\n/, "").replace(/\n$/, ""));
|
|
10567
|
+
} else {
|
|
10568
|
+
paramValue = unescapeXmlEntities(rawValue.trim());
|
|
10569
|
+
}
|
|
10570
|
+
if (!RAW_CONTENT_PARAMS.has(paramName)) {
|
|
10571
|
+
if (paramValue.toLowerCase() === "true") {
|
|
10572
|
+
paramValue = true;
|
|
10573
|
+
} else if (paramValue.toLowerCase() === "false") {
|
|
10574
|
+
paramValue = false;
|
|
10575
|
+
} else if (!isNaN(paramValue) && paramValue.trim() !== "") {
|
|
10576
|
+
const num = Number(paramValue);
|
|
10577
|
+
if (Number.isFinite(num)) {
|
|
10578
|
+
paramValue = num;
|
|
10579
|
+
}
|
|
10448
10580
|
}
|
|
10449
10581
|
}
|
|
10450
10582
|
params[paramName] = paramValue;
|
|
@@ -10487,6 +10619,7 @@ function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
10487
10619
|
"readImage",
|
|
10488
10620
|
"edit",
|
|
10489
10621
|
"create",
|
|
10622
|
+
"multi_edit",
|
|
10490
10623
|
"delegate",
|
|
10491
10624
|
"bash",
|
|
10492
10625
|
"task",
|
|
@@ -10616,7 +10749,7 @@ function resolveTargetPath(target, cwd) {
|
|
|
10616
10749
|
}
|
|
10617
10750
|
return filePart + suffix;
|
|
10618
10751
|
}
|
|
10619
|
-
var searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, googleSearchToolDefinition, urlContextToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
|
|
10752
|
+
var searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, googleSearchToolDefinition, urlContextToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS, RAW_CONTENT_PARAMS, LAST_INDEX_TOOLS;
|
|
10620
10753
|
var init_common = __esm({
|
|
10621
10754
|
"src/tools/common.js"() {
|
|
10622
10755
|
"use strict";
|
|
@@ -11034,6 +11167,8 @@ Capabilities:
|
|
|
11034
11167
|
"task",
|
|
11035
11168
|
"attempt_completion"
|
|
11036
11169
|
];
|
|
11170
|
+
RAW_CONTENT_PARAMS = /* @__PURE__ */ new Set(["content", "new_string", "old_string"]);
|
|
11171
|
+
LAST_INDEX_TOOLS = /* @__PURE__ */ new Set(["attempt_completion", "create", "edit"]);
|
|
11037
11172
|
}
|
|
11038
11173
|
});
|
|
11039
11174
|
|
|
@@ -12103,6 +12238,10 @@ function parseSimpleCommand(command) {
|
|
|
12103
12238
|
// Logical OR
|
|
12104
12239
|
/(?<!\\);/,
|
|
12105
12240
|
// Command separator (but not escaped \;)
|
|
12241
|
+
/\n/,
|
|
12242
|
+
// Newline command separator (multi-line commands)
|
|
12243
|
+
/\r/,
|
|
12244
|
+
// Carriage return (CRLF line endings)
|
|
12106
12245
|
/&$/,
|
|
12107
12246
|
// Background execution
|
|
12108
12247
|
/\$\(/,
|
|
@@ -12209,6 +12348,8 @@ function isComplexPattern(pattern) {
|
|
|
12209
12348
|
// Logical OR
|
|
12210
12349
|
/;/,
|
|
12211
12350
|
// Command separator
|
|
12351
|
+
/\n/,
|
|
12352
|
+
// Newline command separator
|
|
12212
12353
|
/&$/,
|
|
12213
12354
|
// Background execution
|
|
12214
12355
|
/\$\(/,
|
|
@@ -12581,6 +12722,26 @@ var init_bashPermissions = __esm({
|
|
|
12581
12722
|
i++;
|
|
12582
12723
|
continue;
|
|
12583
12724
|
}
|
|
12725
|
+
if (char === "\n" || char === "\r" && nextChar === "\n") {
|
|
12726
|
+
if (current2.trim()) {
|
|
12727
|
+
components.push(current2.trim());
|
|
12728
|
+
}
|
|
12729
|
+
current2 = "";
|
|
12730
|
+
if (char === "\r") {
|
|
12731
|
+
i += 2;
|
|
12732
|
+
} else {
|
|
12733
|
+
i++;
|
|
12734
|
+
}
|
|
12735
|
+
continue;
|
|
12736
|
+
}
|
|
12737
|
+
if (char === "\r") {
|
|
12738
|
+
if (current2.trim()) {
|
|
12739
|
+
components.push(current2.trim());
|
|
12740
|
+
}
|
|
12741
|
+
current2 = "";
|
|
12742
|
+
i++;
|
|
12743
|
+
continue;
|
|
12744
|
+
}
|
|
12584
12745
|
}
|
|
12585
12746
|
current2 += char;
|
|
12586
12747
|
i++;
|
|
@@ -24659,7 +24820,11 @@ var init_esm3 = __esm({
|
|
|
24659
24820
|
#matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
|
|
24660
24821
|
const firstgs = pattern.indexOf(GLOBSTAR, patternIndex);
|
|
24661
24822
|
const lastgs = pattern.lastIndexOf(GLOBSTAR);
|
|
24662
|
-
const [head2, body, tail] = [
|
|
24823
|
+
const [head2, body, tail] = partial ? [
|
|
24824
|
+
pattern.slice(patternIndex, firstgs),
|
|
24825
|
+
pattern.slice(firstgs + 1),
|
|
24826
|
+
[]
|
|
24827
|
+
] : [
|
|
24663
24828
|
pattern.slice(patternIndex, firstgs),
|
|
24664
24829
|
pattern.slice(firstgs + 1, lastgs),
|
|
24665
24830
|
pattern.slice(lastgs + 1)
|
|
@@ -24696,7 +24861,7 @@ var init_esm3 = __esm({
|
|
|
24696
24861
|
return false;
|
|
24697
24862
|
}
|
|
24698
24863
|
}
|
|
24699
|
-
return sawSome;
|
|
24864
|
+
return partial || sawSome;
|
|
24700
24865
|
}
|
|
24701
24866
|
const bodySegments = [[[], 0]];
|
|
24702
24867
|
let currentBody = bodySegments[0];
|
|
@@ -24745,7 +24910,7 @@ var init_esm3 = __esm({
|
|
|
24745
24910
|
}
|
|
24746
24911
|
fileIndex++;
|
|
24747
24912
|
}
|
|
24748
|
-
return null;
|
|
24913
|
+
return partial || null;
|
|
24749
24914
|
}
|
|
24750
24915
|
#matchOne(file, pattern, partial, fileIndex, patternIndex) {
|
|
24751
24916
|
let fi;
|
|
@@ -32036,6 +32201,13 @@ function createWrappedTools(baseTools) {
|
|
|
32036
32201
|
baseTools.createTool.execute
|
|
32037
32202
|
);
|
|
32038
32203
|
}
|
|
32204
|
+
if (baseTools.multiEditTool) {
|
|
32205
|
+
wrappedTools.multiEditToolInstance = wrapToolWithEmitter(
|
|
32206
|
+
baseTools.multiEditTool,
|
|
32207
|
+
"multi_edit",
|
|
32208
|
+
baseTools.multiEditTool.execute
|
|
32209
|
+
);
|
|
32210
|
+
}
|
|
32039
32211
|
return wrappedTools;
|
|
32040
32212
|
}
|
|
32041
32213
|
var toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
|
|
@@ -32954,6 +33126,9 @@ function createTools(configOptions) {
|
|
|
32954
33126
|
if (configOptions.allowEdit && isToolAllowed("create")) {
|
|
32955
33127
|
tools2.createTool = createTool(configOptions);
|
|
32956
33128
|
}
|
|
33129
|
+
if (configOptions.allowEdit && isToolAllowed("multi_edit")) {
|
|
33130
|
+
tools2.multiEditTool = multiEditTool(configOptions);
|
|
33131
|
+
}
|
|
32957
33132
|
return tools2;
|
|
32958
33133
|
}
|
|
32959
33134
|
function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
@@ -69965,6 +70140,15 @@ function normalizeJsonQuotes(str) {
|
|
|
69965
70140
|
}
|
|
69966
70141
|
return result;
|
|
69967
70142
|
}
|
|
70143
|
+
function isCodeBlockEmbeddedInDocument(text, match2) {
|
|
70144
|
+
const beforeBlock = text.substring(0, match2.index).trim();
|
|
70145
|
+
const afterBlock = text.substring(match2.index + match2[0].length).trim();
|
|
70146
|
+
const hasMarkdownHeadings = /^#{1,6}\s/m.test(beforeBlock) || /^#{1,6}\s/m.test(afterBlock);
|
|
70147
|
+
if (hasMarkdownHeadings) {
|
|
70148
|
+
return true;
|
|
70149
|
+
}
|
|
70150
|
+
return false;
|
|
70151
|
+
}
|
|
69968
70152
|
function cleanSchemaResponse(response) {
|
|
69969
70153
|
if (!response || typeof response !== "string") {
|
|
69970
70154
|
return response;
|
|
@@ -69984,11 +70168,11 @@ function cleanSchemaResponse(response) {
|
|
|
69984
70168
|
return cleanSchemaResponse(innerContent);
|
|
69985
70169
|
}
|
|
69986
70170
|
const jsonBlockMatch = trimmed.match(/```json\s*\n([\s\S]*?)\n```/);
|
|
69987
|
-
if (jsonBlockMatch) {
|
|
70171
|
+
if (jsonBlockMatch && !isCodeBlockEmbeddedInDocument(trimmed, jsonBlockMatch)) {
|
|
69988
70172
|
return normalizeJsonQuotes(jsonBlockMatch[1].trim());
|
|
69989
70173
|
}
|
|
69990
70174
|
const anyBlockMatch = trimmed.match(/```\s*\n([{\[][\s\S]*?[}\]])\s*```/);
|
|
69991
|
-
if (anyBlockMatch) {
|
|
70175
|
+
if (anyBlockMatch && !isCodeBlockEmbeddedInDocument(trimmed, anyBlockMatch)) {
|
|
69992
70176
|
return normalizeJsonQuotes(anyBlockMatch[1].trim());
|
|
69993
70177
|
}
|
|
69994
70178
|
const codeBlockPatterns = [
|
|
@@ -69997,7 +70181,7 @@ function cleanSchemaResponse(response) {
|
|
|
69997
70181
|
];
|
|
69998
70182
|
for (const pattern of codeBlockPatterns) {
|
|
69999
70183
|
const match2 = trimmed.match(pattern);
|
|
70000
|
-
if (match2) {
|
|
70184
|
+
if (match2 && !isCodeBlockEmbeddedInDocument(trimmed, match2)) {
|
|
70001
70185
|
return normalizeJsonQuotes(match2[1].trim());
|
|
70002
70186
|
}
|
|
70003
70187
|
}
|
|
@@ -70008,7 +70192,7 @@ function cleanSchemaResponse(response) {
|
|
|
70008
70192
|
}
|
|
70009
70193
|
const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
|
|
70010
70194
|
const codeBlockMatch = trimmed.match(codeBlockStartPattern);
|
|
70011
|
-
if (codeBlockMatch) {
|
|
70195
|
+
if (codeBlockMatch && !isCodeBlockEmbeddedInDocument(trimmed, codeBlockMatch)) {
|
|
70012
70196
|
const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1;
|
|
70013
70197
|
const openChar = codeBlockMatch[1];
|
|
70014
70198
|
const closeChar = openChar === "{" ? "}" : "]";
|
|
@@ -83452,6 +83636,9 @@ var init_ProbeAgent = __esm({
|
|
|
83452
83636
|
if (wrappedTools.createToolInstance && isToolAllowed("create")) {
|
|
83453
83637
|
this.toolImplementations.create = wrappedTools.createToolInstance;
|
|
83454
83638
|
}
|
|
83639
|
+
if (wrappedTools.multiEditToolInstance && isToolAllowed("multi_edit")) {
|
|
83640
|
+
this.toolImplementations.multi_edit = wrappedTools.multiEditToolInstance;
|
|
83641
|
+
}
|
|
83455
83642
|
}
|
|
83456
83643
|
this.wrappedTools = wrappedTools;
|
|
83457
83644
|
if (this.debug) {
|
|
@@ -84741,6 +84928,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
84741
84928
|
}
|
|
84742
84929
|
if (this.allowEdit && isToolAllowed("create")) {
|
|
84743
84930
|
toolDefinitions += `${createToolDefinition}
|
|
84931
|
+
`;
|
|
84932
|
+
}
|
|
84933
|
+
if (this.allowEdit && isToolAllowed("multi_edit")) {
|
|
84934
|
+
toolDefinitions += `${multiEditToolDefinition}
|
|
84744
84935
|
`;
|
|
84745
84936
|
}
|
|
84746
84937
|
if (this.enableBash && isToolAllowed("bash")) {
|
|
@@ -84837,6 +85028,9 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
|
|
|
84837
85028
|
if (this.allowEdit && isToolAllowed("create")) {
|
|
84838
85029
|
availableToolsList += "- create: Create new files with specified content.\n";
|
|
84839
85030
|
}
|
|
85031
|
+
if (this.allowEdit && isToolAllowed("multi_edit")) {
|
|
85032
|
+
availableToolsList += "- multi_edit: Apply multiple file edits in one call using a JSON array of operations.\n";
|
|
85033
|
+
}
|
|
84840
85034
|
if (this.enableDelegate && isToolAllowed("delegate")) {
|
|
84841
85035
|
availableToolsList += "- delegate: Delegate big distinct tasks to specialized probe subagents.\n";
|
|
84842
85036
|
}
|
|
@@ -85431,6 +85625,9 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
85431
85625
|
if (this.allowEdit && this.allowedTools.isEnabled("create")) {
|
|
85432
85626
|
validTools.push("create");
|
|
85433
85627
|
}
|
|
85628
|
+
if (this.allowEdit && this.allowedTools.isEnabled("multi_edit")) {
|
|
85629
|
+
validTools.push("multi_edit");
|
|
85630
|
+
}
|
|
85434
85631
|
if (this.enableBash && this.allowedTools.isEnabled("bash")) {
|
|
85435
85632
|
validTools.push("bash");
|
|
85436
85633
|
}
|
package/build/agent/probeTool.js
CHANGED
|
@@ -256,6 +256,15 @@ export function createWrappedTools(baseTools) {
|
|
|
256
256
|
);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
// Wrap multi_edit tool
|
|
260
|
+
if (baseTools.multiEditTool) {
|
|
261
|
+
wrappedTools.multiEditToolInstance = wrapToolWithEmitter(
|
|
262
|
+
baseTools.multiEditTool,
|
|
263
|
+
'multi_edit',
|
|
264
|
+
baseTools.multiEditTool.execute
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
259
268
|
return wrappedTools;
|
|
260
269
|
}
|
|
261
270
|
|
|
@@ -266,6 +266,36 @@ function normalizeJsonQuotes(str) {
|
|
|
266
266
|
return result;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Check if a code block is embedded within a structured markdown document.
|
|
271
|
+
* Used to avoid extracting embedded JSON examples from markdown text.
|
|
272
|
+
* Issue #456: attempt_completion results containing markdown with JSON code block
|
|
273
|
+
* documentation examples were having the examples extracted as structured output.
|
|
274
|
+
*
|
|
275
|
+
* Normal AI behavior: AI wraps JSON answer in a code block with brief explanation text.
|
|
276
|
+
* Issue #456: AI returns a markdown document that contains JSON code blocks as examples.
|
|
277
|
+
*
|
|
278
|
+
* We distinguish these by checking if the surrounding text contains markdown structural
|
|
279
|
+
* elements (headings) which indicate a document rather than a brief explanation.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} text - The full trimmed text
|
|
282
|
+
* @param {RegExpMatchArray} match - The regex match for the code block
|
|
283
|
+
* @returns {boolean} - true if the code block is embedded in a markdown document
|
|
284
|
+
*/
|
|
285
|
+
function isCodeBlockEmbeddedInDocument(text, match) {
|
|
286
|
+
const beforeBlock = text.substring(0, match.index).trim();
|
|
287
|
+
const afterBlock = text.substring(match.index + match[0].length).trim();
|
|
288
|
+
|
|
289
|
+
// Check if surrounding text contains markdown headings (lines starting with #)
|
|
290
|
+
// This is a strong signal that the content is a structured document, not just explanation text
|
|
291
|
+
const hasMarkdownHeadings = /^#{1,6}\s/m.test(beforeBlock) || /^#{1,6}\s/m.test(afterBlock);
|
|
292
|
+
if (hasMarkdownHeadings) {
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
|
|
269
299
|
/**
|
|
270
300
|
* Clean AI response by extracting JSON content when response contains JSON
|
|
271
301
|
* Only processes responses that contain JSON structures { or [
|
|
@@ -311,15 +341,20 @@ export function cleanSchemaResponse(response) {
|
|
|
311
341
|
}
|
|
312
342
|
|
|
313
343
|
// First, look for JSON after code block markers - similar to mermaid extraction
|
|
344
|
+
// Only extract from code blocks when they are the primary content (not embedded examples in markdown).
|
|
345
|
+
// Issue #456: When attempt_completion result contains markdown with embedded JSON code blocks
|
|
346
|
+
// as documentation examples, extracting those blocks produces wrong structured output.
|
|
347
|
+
// We check that there's no significant text content outside the code block.
|
|
348
|
+
|
|
314
349
|
// Try with json language specifier
|
|
315
350
|
const jsonBlockMatch = trimmed.match(/```json\s*\n([\s\S]*?)\n```/);
|
|
316
|
-
if (jsonBlockMatch) {
|
|
351
|
+
if (jsonBlockMatch && !isCodeBlockEmbeddedInDocument(trimmed, jsonBlockMatch)) {
|
|
317
352
|
return normalizeJsonQuotes(jsonBlockMatch[1].trim());
|
|
318
353
|
}
|
|
319
354
|
|
|
320
355
|
// Try any code block with JSON content
|
|
321
356
|
const anyBlockMatch = trimmed.match(/```\s*\n([{\[][\s\S]*?[}\]])\s*```/);
|
|
322
|
-
if (anyBlockMatch) {
|
|
357
|
+
if (anyBlockMatch && !isCodeBlockEmbeddedInDocument(trimmed, anyBlockMatch)) {
|
|
323
358
|
return normalizeJsonQuotes(anyBlockMatch[1].trim());
|
|
324
359
|
}
|
|
325
360
|
|
|
@@ -331,7 +366,7 @@ export function cleanSchemaResponse(response) {
|
|
|
331
366
|
|
|
332
367
|
for (const pattern of codeBlockPatterns) {
|
|
333
368
|
const match = trimmed.match(pattern);
|
|
334
|
-
if (match) {
|
|
369
|
+
if (match && !isCodeBlockEmbeddedInDocument(trimmed, match)) {
|
|
335
370
|
return normalizeJsonQuotes(match[1].trim());
|
|
336
371
|
}
|
|
337
372
|
}
|
|
@@ -345,10 +380,11 @@ export function cleanSchemaResponse(response) {
|
|
|
345
380
|
}
|
|
346
381
|
|
|
347
382
|
// Look for code block start followed immediately by JSON
|
|
383
|
+
// Only extract if the code block is not embedded in a structured markdown document
|
|
348
384
|
const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
|
|
349
385
|
const codeBlockMatch = trimmed.match(codeBlockStartPattern);
|
|
350
386
|
|
|
351
|
-
if (codeBlockMatch) {
|
|
387
|
+
if (codeBlockMatch && !isCodeBlockEmbeddedInDocument(trimmed, codeBlockMatch)) {
|
|
352
388
|
const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1; // Position of the bracket
|
|
353
389
|
|
|
354
390
|
// Find the matching closing bracket
|