@probelabs/probe 0.6.0-rc258 → 0.6.0-rc259
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-rc258-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc259-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/probe-v0.6.0-rc259-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/{probe-v0.6.0-rc258-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc259-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/probe-v0.6.0-rc259-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc259-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +13 -0
- package/build/agent/index.js +177 -19
- package/build/agent/probeTool.js +9 -0
- 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 +177 -19
- package/cjs/index.cjs +187 -19
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +13 -0
- package/src/agent/probeTool.js +9 -0
- 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-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc258-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc258-x86_64-unknown-linux-musl.tar.gz +0 -0
package/cjs/index.cjs
CHANGED
|
@@ -35867,7 +35867,7 @@ async function handleLineEdit({ resolvedPath: resolvedPath2, file_path, start_li
|
|
|
35867
35867
|
return buildLineEditResponse(file_path, startLine, endLine, newLines.length, fileLines, startLine - 1, action, modifications);
|
|
35868
35868
|
}
|
|
35869
35869
|
}
|
|
35870
|
-
var import_ai, import_fs4, import_path5, import_fs5, editTool, createTool, editSchema, createSchema, editDescription, createDescription, editToolDefinition, createToolDefinition;
|
|
35870
|
+
var import_ai, import_fs4, import_path5, import_fs5, editTool, createTool, multiEditTool, editSchema, createSchema, multiEditSchema, editDescription, createDescription, multiEditDescription, editToolDefinition, createToolDefinition, multiEditToolDefinition;
|
|
35871
35871
|
var init_edit = __esm({
|
|
35872
35872
|
"src/tools/edit.js"() {
|
|
35873
35873
|
"use strict";
|
|
@@ -35941,7 +35941,7 @@ Parameters:
|
|
|
35941
35941
|
},
|
|
35942
35942
|
required: ["file_path", "new_string"]
|
|
35943
35943
|
},
|
|
35944
|
-
execute: async ({ file_path, old_string, new_string, replace_all = false, symbol: symbol15, position, start_line, end_line }) => {
|
|
35944
|
+
execute: async ({ file_path, old_string, new_string, replace_all = false, symbol: symbol15, position, start_line, end_line, workingDirectory }) => {
|
|
35945
35945
|
try {
|
|
35946
35946
|
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
35947
35947
|
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").`;
|
|
@@ -35949,7 +35949,8 @@ Parameters:
|
|
|
35949
35949
|
if (new_string === void 0 || new_string === null || typeof new_string !== "string") {
|
|
35950
35950
|
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).`;
|
|
35951
35951
|
}
|
|
35952
|
-
const
|
|
35952
|
+
const effectiveCwd = workingDirectory || cwd || process.cwd();
|
|
35953
|
+
const resolvedPath2 = (0, import_path5.isAbsolute)(file_path) ? file_path : (0, import_path5.resolve)(effectiveCwd, file_path);
|
|
35953
35954
|
if (debug) {
|
|
35954
35955
|
console.error(`[Edit] Attempting to edit file: ${resolvedPath2}`);
|
|
35955
35956
|
}
|
|
@@ -36056,7 +36057,7 @@ Important:
|
|
|
36056
36057
|
},
|
|
36057
36058
|
required: ["file_path", "content"]
|
|
36058
36059
|
},
|
|
36059
|
-
execute: async ({ file_path, content, overwrite = false }) => {
|
|
36060
|
+
execute: async ({ file_path, content, overwrite = false, workingDirectory }) => {
|
|
36060
36061
|
try {
|
|
36061
36062
|
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
36062
36063
|
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").`;
|
|
@@ -36064,7 +36065,8 @@ Important:
|
|
|
36064
36065
|
if (content === void 0 || content === null || typeof content !== "string") {
|
|
36065
36066
|
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).`;
|
|
36066
36067
|
}
|
|
36067
|
-
const
|
|
36068
|
+
const effectiveCwd = workingDirectory || cwd || process.cwd();
|
|
36069
|
+
const resolvedPath2 = (0, import_path5.isAbsolute)(file_path) ? file_path : (0, import_path5.resolve)(effectiveCwd, file_path);
|
|
36068
36070
|
if (debug) {
|
|
36069
36071
|
console.error(`[Create] Attempting to create file: ${resolvedPath2}`);
|
|
36070
36072
|
}
|
|
@@ -36093,6 +36095,70 @@ Important:
|
|
|
36093
36095
|
}
|
|
36094
36096
|
});
|
|
36095
36097
|
};
|
|
36098
|
+
multiEditTool = (options = {}) => {
|
|
36099
|
+
const editInstance = editTool(options);
|
|
36100
|
+
return (0, import_ai.tool)({
|
|
36101
|
+
name: "multi_edit",
|
|
36102
|
+
description: "Apply multiple file edits in a single tool call. Accepts a JSON array of edit operations.",
|
|
36103
|
+
inputSchema: {
|
|
36104
|
+
type: "object",
|
|
36105
|
+
properties: {
|
|
36106
|
+
edits: {
|
|
36107
|
+
type: "string",
|
|
36108
|
+
description: "JSON array of edit operations. Each object supports: file_path, old_string, new_string, replace_all, symbol, position, start_line, end_line."
|
|
36109
|
+
}
|
|
36110
|
+
},
|
|
36111
|
+
required: ["edits"]
|
|
36112
|
+
},
|
|
36113
|
+
execute: async ({ edits: rawEdits }) => {
|
|
36114
|
+
let edits;
|
|
36115
|
+
if (typeof rawEdits === "string") {
|
|
36116
|
+
try {
|
|
36117
|
+
edits = JSON.parse(rawEdits);
|
|
36118
|
+
} catch (e5) {
|
|
36119
|
+
return `Error: Invalid JSON in edits parameter - ${e5.message}. Provide a raw JSON array between <edits> tags.`;
|
|
36120
|
+
}
|
|
36121
|
+
} else if (Array.isArray(rawEdits)) {
|
|
36122
|
+
edits = rawEdits;
|
|
36123
|
+
} else {
|
|
36124
|
+
return "Error: edits must be a JSON array of edit operations.";
|
|
36125
|
+
}
|
|
36126
|
+
if (!Array.isArray(edits) || edits.length === 0) {
|
|
36127
|
+
return "Error: edits must be a non-empty JSON array.";
|
|
36128
|
+
}
|
|
36129
|
+
if (edits.length > 50) {
|
|
36130
|
+
return `Error: Too many edits (${edits.length}). Maximum 50 per batch.`;
|
|
36131
|
+
}
|
|
36132
|
+
const results = [];
|
|
36133
|
+
let successCount = 0;
|
|
36134
|
+
let failCount = 0;
|
|
36135
|
+
for (let i5 = 0; i5 < edits.length; i5++) {
|
|
36136
|
+
const editOp = edits[i5];
|
|
36137
|
+
if (!editOp || typeof editOp !== "object" || Array.isArray(editOp)) {
|
|
36138
|
+
results.push(`[${i5 + 1}] FAIL: Invalid edit operation - must be an object`);
|
|
36139
|
+
failCount++;
|
|
36140
|
+
continue;
|
|
36141
|
+
}
|
|
36142
|
+
try {
|
|
36143
|
+
const result = await editInstance.execute(editOp);
|
|
36144
|
+
const isError = typeof result === "string" && result.startsWith("Error");
|
|
36145
|
+
if (isError) {
|
|
36146
|
+
results.push(`[${i5 + 1}] FAIL: ${result}`);
|
|
36147
|
+
failCount++;
|
|
36148
|
+
} else {
|
|
36149
|
+
results.push(`[${i5 + 1}] OK: ${result}`);
|
|
36150
|
+
successCount++;
|
|
36151
|
+
}
|
|
36152
|
+
} catch (error2) {
|
|
36153
|
+
results.push(`[${i5 + 1}] FAIL: ${error2.message}`);
|
|
36154
|
+
failCount++;
|
|
36155
|
+
}
|
|
36156
|
+
}
|
|
36157
|
+
const summary = `Multi-edit: ${successCount}/${edits.length} succeeded` + (failCount > 0 ? `, ${failCount} failed` : "");
|
|
36158
|
+
return summary + "\n\n" + results.join("\n");
|
|
36159
|
+
}
|
|
36160
|
+
});
|
|
36161
|
+
};
|
|
36096
36162
|
editSchema = {
|
|
36097
36163
|
type: "object",
|
|
36098
36164
|
properties: {
|
|
@@ -36150,8 +36216,19 @@ Important:
|
|
|
36150
36216
|
},
|
|
36151
36217
|
required: ["file_path", "content"]
|
|
36152
36218
|
};
|
|
36219
|
+
multiEditSchema = {
|
|
36220
|
+
type: "object",
|
|
36221
|
+
properties: {
|
|
36222
|
+
edits: {
|
|
36223
|
+
type: "string",
|
|
36224
|
+
description: "JSON array of edit operations"
|
|
36225
|
+
}
|
|
36226
|
+
},
|
|
36227
|
+
required: ["edits"]
|
|
36228
|
+
};
|
|
36153
36229
|
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.";
|
|
36154
36230
|
createDescription = "Create new files with specified content. Will create parent directories if needed.";
|
|
36231
|
+
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.";
|
|
36155
36232
|
editToolDefinition = `
|
|
36156
36233
|
## edit
|
|
36157
36234
|
Description: ${editDescription}
|
|
@@ -36307,6 +36384,44 @@ Examples:
|
|
|
36307
36384
|
This is a new project.</content>
|
|
36308
36385
|
<overwrite>true</overwrite>
|
|
36309
36386
|
</create>`;
|
|
36387
|
+
multiEditToolDefinition = `
|
|
36388
|
+
## multi_edit
|
|
36389
|
+
Description: ${multiEditDescription}
|
|
36390
|
+
|
|
36391
|
+
Apply multiple edits in one call. Each operation in the array uses the same parameters as the edit tool:
|
|
36392
|
+
- file_path, old_string, new_string (text mode)
|
|
36393
|
+
- file_path, symbol, new_string (symbol replace)
|
|
36394
|
+
- file_path, symbol, new_string, position (symbol insert)
|
|
36395
|
+
- file_path, start_line, new_string (line-targeted)
|
|
36396
|
+
|
|
36397
|
+
Edits are applied sequentially. Failures do not stop remaining edits. Maximum 50 edits per call.
|
|
36398
|
+
|
|
36399
|
+
Parameters:
|
|
36400
|
+
- edits: (required) JSON array of edit objects. Place raw JSON between tags.
|
|
36401
|
+
|
|
36402
|
+
When to use multi_edit vs edit:
|
|
36403
|
+
- Use edit for a single change to one file
|
|
36404
|
+
- Use multi_edit when making 2+ related changes across files (e.g., rename a function and update all call sites)
|
|
36405
|
+
- Use multi_edit for coordinated multi-file refactoring where order matters
|
|
36406
|
+
|
|
36407
|
+
Examples:
|
|
36408
|
+
|
|
36409
|
+
Multiple text replacements across files:
|
|
36410
|
+
<multi_edit>
|
|
36411
|
+
<edits>[
|
|
36412
|
+
{"file_path": "src/main.js", "old_string": "return false;", "new_string": "return true;"},
|
|
36413
|
+
{"file_path": "src/config.js", "old_string": "debug: false", "new_string": "debug: true"}
|
|
36414
|
+
]</edits>
|
|
36415
|
+
</multi_edit>
|
|
36416
|
+
|
|
36417
|
+
Mixed edit modes in one batch:
|
|
36418
|
+
<multi_edit>
|
|
36419
|
+
<edits>[
|
|
36420
|
+
{"file_path": "src/utils.js", "symbol": "oldHelper", "new_string": "function newHelper() { return 42; }"},
|
|
36421
|
+
{"file_path": "src/main.js", "old_string": "oldHelper()", "new_string": "newHelper()", "replace_all": true},
|
|
36422
|
+
{"file_path": "src/index.js", "start_line": "10", "end_line": "12", "new_string": "export { newHelper };"}
|
|
36423
|
+
]</edits>
|
|
36424
|
+
</multi_edit>`;
|
|
36310
36425
|
}
|
|
36311
36426
|
});
|
|
36312
36427
|
|
|
@@ -36799,7 +36914,8 @@ function getValidParamsForTool(toolName) {
|
|
|
36799
36914
|
task: taskSchema,
|
|
36800
36915
|
attempt_completion: attemptCompletionSchema,
|
|
36801
36916
|
edit: editSchema,
|
|
36802
|
-
create: createSchema
|
|
36917
|
+
create: createSchema,
|
|
36918
|
+
multi_edit: multiEditSchema
|
|
36803
36919
|
};
|
|
36804
36920
|
const schema = schemaMap[toolName];
|
|
36805
36921
|
if (!schema) {
|
|
@@ -36839,7 +36955,7 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
36839
36955
|
const closeTag = `</${toolName}>`;
|
|
36840
36956
|
const openIndex = earliestOpenIndex;
|
|
36841
36957
|
let closeIndex;
|
|
36842
|
-
if (toolName
|
|
36958
|
+
if (LAST_INDEX_TOOLS.has(toolName)) {
|
|
36843
36959
|
closeIndex = xmlString.lastIndexOf(closeTag);
|
|
36844
36960
|
if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
|
|
36845
36961
|
closeIndex = -1;
|
|
@@ -36864,7 +36980,15 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
36864
36980
|
if (paramOpenIndex === -1) {
|
|
36865
36981
|
continue;
|
|
36866
36982
|
}
|
|
36867
|
-
let paramCloseIndex
|
|
36983
|
+
let paramCloseIndex;
|
|
36984
|
+
if (RAW_CONTENT_PARAMS.has(paramName)) {
|
|
36985
|
+
paramCloseIndex = innerContent.lastIndexOf(paramCloseTag);
|
|
36986
|
+
if (paramCloseIndex !== -1 && paramCloseIndex <= paramOpenIndex + paramOpenTag.length) {
|
|
36987
|
+
paramCloseIndex = -1;
|
|
36988
|
+
}
|
|
36989
|
+
} else {
|
|
36990
|
+
paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
|
|
36991
|
+
}
|
|
36868
36992
|
if (paramCloseIndex === -1) {
|
|
36869
36993
|
let nextTagIndex = innerContent.length;
|
|
36870
36994
|
for (const nextParam of validParams) {
|
|
@@ -36876,18 +37000,26 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
36876
37000
|
}
|
|
36877
37001
|
paramCloseIndex = nextTagIndex;
|
|
36878
37002
|
}
|
|
36879
|
-
|
|
37003
|
+
const rawValue = innerContent.substring(
|
|
36880
37004
|
paramOpenIndex + paramOpenTag.length,
|
|
36881
37005
|
paramCloseIndex
|
|
36882
|
-
)
|
|
36883
|
-
|
|
36884
|
-
|
|
36885
|
-
|
|
36886
|
-
|
|
36887
|
-
|
|
36888
|
-
|
|
36889
|
-
|
|
36890
|
-
|
|
37006
|
+
);
|
|
37007
|
+
let paramValue;
|
|
37008
|
+
if (RAW_CONTENT_PARAMS.has(paramName)) {
|
|
37009
|
+
paramValue = unescapeXmlEntities(rawValue.replace(/^\n/, "").replace(/\n$/, ""));
|
|
37010
|
+
} else {
|
|
37011
|
+
paramValue = unescapeXmlEntities(rawValue.trim());
|
|
37012
|
+
}
|
|
37013
|
+
if (!RAW_CONTENT_PARAMS.has(paramName)) {
|
|
37014
|
+
if (paramValue.toLowerCase() === "true") {
|
|
37015
|
+
paramValue = true;
|
|
37016
|
+
} else if (paramValue.toLowerCase() === "false") {
|
|
37017
|
+
paramValue = false;
|
|
37018
|
+
} else if (!isNaN(paramValue) && paramValue.trim() !== "") {
|
|
37019
|
+
const num = Number(paramValue);
|
|
37020
|
+
if (Number.isFinite(num)) {
|
|
37021
|
+
paramValue = num;
|
|
37022
|
+
}
|
|
36891
37023
|
}
|
|
36892
37024
|
}
|
|
36893
37025
|
params[paramName] = paramValue;
|
|
@@ -36930,6 +37062,7 @@ function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
36930
37062
|
"readImage",
|
|
36931
37063
|
"edit",
|
|
36932
37064
|
"create",
|
|
37065
|
+
"multi_edit",
|
|
36933
37066
|
"delegate",
|
|
36934
37067
|
"bash",
|
|
36935
37068
|
"task",
|
|
@@ -37059,7 +37192,7 @@ function resolveTargetPath(target, cwd) {
|
|
|
37059
37192
|
}
|
|
37060
37193
|
return filePart + suffix;
|
|
37061
37194
|
}
|
|
37062
|
-
var import_path6, 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, bashDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
|
|
37195
|
+
var import_path6, 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, bashDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS, RAW_CONTENT_PARAMS, LAST_INDEX_TOOLS;
|
|
37063
37196
|
var init_common2 = __esm({
|
|
37064
37197
|
"src/tools/common.js"() {
|
|
37065
37198
|
"use strict";
|
|
@@ -37479,6 +37612,8 @@ Capabilities:
|
|
|
37479
37612
|
"task",
|
|
37480
37613
|
"attempt_completion"
|
|
37481
37614
|
];
|
|
37615
|
+
RAW_CONTENT_PARAMS = /* @__PURE__ */ new Set(["content", "new_string", "old_string"]);
|
|
37616
|
+
LAST_INDEX_TOOLS = /* @__PURE__ */ new Set(["attempt_completion", "create", "edit"]);
|
|
37482
37617
|
}
|
|
37483
37618
|
});
|
|
37484
37619
|
|
|
@@ -38144,6 +38279,9 @@ function createTools(configOptions) {
|
|
|
38144
38279
|
if (configOptions.allowEdit && isToolAllowed("create")) {
|
|
38145
38280
|
tools2.createTool = createTool(configOptions);
|
|
38146
38281
|
}
|
|
38282
|
+
if (configOptions.allowEdit && isToolAllowed("multi_edit")) {
|
|
38283
|
+
tools2.multiEditTool = multiEditTool(configOptions);
|
|
38284
|
+
}
|
|
38147
38285
|
return tools2;
|
|
38148
38286
|
}
|
|
38149
38287
|
function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
@@ -45529,6 +45667,13 @@ function createWrappedTools(baseTools) {
|
|
|
45529
45667
|
baseTools.createTool.execute
|
|
45530
45668
|
);
|
|
45531
45669
|
}
|
|
45670
|
+
if (baseTools.multiEditTool) {
|
|
45671
|
+
wrappedTools.multiEditToolInstance = wrapToolWithEmitter(
|
|
45672
|
+
baseTools.multiEditTool,
|
|
45673
|
+
"multi_edit",
|
|
45674
|
+
baseTools.multiEditTool.execute
|
|
45675
|
+
);
|
|
45676
|
+
}
|
|
45532
45677
|
return wrappedTools;
|
|
45533
45678
|
}
|
|
45534
45679
|
var import_child_process6, import_util11, import_crypto4, import_events, import_fs7, import_fs8, import_path8, toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
|
|
@@ -108617,6 +108762,9 @@ var init_ProbeAgent = __esm({
|
|
|
108617
108762
|
if (wrappedTools.createToolInstance && isToolAllowed("create")) {
|
|
108618
108763
|
this.toolImplementations.create = wrappedTools.createToolInstance;
|
|
108619
108764
|
}
|
|
108765
|
+
if (wrappedTools.multiEditToolInstance && isToolAllowed("multi_edit")) {
|
|
108766
|
+
this.toolImplementations.multi_edit = wrappedTools.multiEditToolInstance;
|
|
108767
|
+
}
|
|
108620
108768
|
}
|
|
108621
108769
|
this.wrappedTools = wrappedTools;
|
|
108622
108770
|
if (this.debug) {
|
|
@@ -109906,6 +110054,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
109906
110054
|
}
|
|
109907
110055
|
if (this.allowEdit && isToolAllowed("create")) {
|
|
109908
110056
|
toolDefinitions += `${createToolDefinition}
|
|
110057
|
+
`;
|
|
110058
|
+
}
|
|
110059
|
+
if (this.allowEdit && isToolAllowed("multi_edit")) {
|
|
110060
|
+
toolDefinitions += `${multiEditToolDefinition}
|
|
109909
110061
|
`;
|
|
109910
110062
|
}
|
|
109911
110063
|
if (this.enableBash && isToolAllowed("bash")) {
|
|
@@ -110002,6 +110154,9 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
|
|
|
110002
110154
|
if (this.allowEdit && isToolAllowed("create")) {
|
|
110003
110155
|
availableToolsList += "- create: Create new files with specified content.\n";
|
|
110004
110156
|
}
|
|
110157
|
+
if (this.allowEdit && isToolAllowed("multi_edit")) {
|
|
110158
|
+
availableToolsList += "- multi_edit: Apply multiple file edits in one call using a JSON array of operations.\n";
|
|
110159
|
+
}
|
|
110005
110160
|
if (this.enableDelegate && isToolAllowed("delegate")) {
|
|
110006
110161
|
availableToolsList += "- delegate: Delegate big distinct tasks to specialized probe subagents.\n";
|
|
110007
110162
|
}
|
|
@@ -110596,6 +110751,9 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
110596
110751
|
if (this.allowEdit && this.allowedTools.isEnabled("create")) {
|
|
110597
110752
|
validTools.push("create");
|
|
110598
110753
|
}
|
|
110754
|
+
if (this.allowEdit && this.allowedTools.isEnabled("multi_edit")) {
|
|
110755
|
+
validTools.push("multi_edit");
|
|
110756
|
+
}
|
|
110599
110757
|
if (this.enableBash && this.allowedTools.isEnabled("bash")) {
|
|
110600
110758
|
validTools.push("bash");
|
|
110601
110759
|
}
|
|
@@ -113702,6 +113860,10 @@ __export(tools_exports, {
|
|
|
113702
113860
|
extractTool: () => extractTool,
|
|
113703
113861
|
getCleanupExecutePlanToolDefinition: () => getCleanupExecutePlanToolDefinition,
|
|
113704
113862
|
getExecutePlanToolDefinition: () => getExecutePlanToolDefinition,
|
|
113863
|
+
multiEditDescription: () => multiEditDescription,
|
|
113864
|
+
multiEditSchema: () => multiEditSchema,
|
|
113865
|
+
multiEditTool: () => multiEditTool,
|
|
113866
|
+
multiEditToolDefinition: () => multiEditToolDefinition,
|
|
113705
113867
|
parseAndResolvePaths: () => parseAndResolvePaths,
|
|
113706
113868
|
querySchema: () => querySchema,
|
|
113707
113869
|
queryTool: () => queryTool,
|
|
@@ -114353,6 +114515,9 @@ __export(index_exports, {
|
|
|
114353
114515
|
initializeSimpleTelemetryFromOptions: () => initializeSimpleTelemetryFromOptions,
|
|
114354
114516
|
listFilesByLevel: () => listFilesByLevel,
|
|
114355
114517
|
listFilesToolInstance: () => listFilesToolInstance,
|
|
114518
|
+
multiEditSchema: () => multiEditSchema,
|
|
114519
|
+
multiEditTool: () => multiEditTool,
|
|
114520
|
+
multiEditToolDefinition: () => multiEditToolDefinition,
|
|
114356
114521
|
parseXmlToolCall: () => parseXmlToolCall,
|
|
114357
114522
|
query: () => query,
|
|
114358
114523
|
querySchema: () => querySchema,
|
|
@@ -114448,6 +114613,9 @@ init_index();
|
|
|
114448
114613
|
initializeSimpleTelemetryFromOptions,
|
|
114449
114614
|
listFilesByLevel,
|
|
114450
114615
|
listFilesToolInstance,
|
|
114616
|
+
multiEditSchema,
|
|
114617
|
+
multiEditTool,
|
|
114618
|
+
multiEditToolDefinition,
|
|
114451
114619
|
parseXmlToolCall,
|
|
114452
114620
|
query,
|
|
114453
114621
|
querySchema,
|
package/package.json
CHANGED
package/src/agent/ProbeAgent.js
CHANGED
|
@@ -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');
|
package/src/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
|
|
package/src/agent/tools.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
bashTool,
|
|
11
11
|
editTool,
|
|
12
12
|
createTool,
|
|
13
|
+
multiEditTool,
|
|
13
14
|
DEFAULT_SYSTEM_MESSAGE,
|
|
14
15
|
attemptCompletionSchema,
|
|
15
16
|
attemptCompletionToolDefinition,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
bashSchema,
|
|
24
25
|
editSchema,
|
|
25
26
|
createSchema,
|
|
27
|
+
multiEditSchema,
|
|
26
28
|
searchToolDefinition,
|
|
27
29
|
queryToolDefinition,
|
|
28
30
|
extractToolDefinition,
|
|
@@ -33,6 +35,7 @@ import {
|
|
|
33
35
|
bashToolDefinition,
|
|
34
36
|
editToolDefinition,
|
|
35
37
|
createToolDefinition,
|
|
38
|
+
multiEditToolDefinition,
|
|
36
39
|
googleSearchToolDefinition,
|
|
37
40
|
urlContextToolDefinition,
|
|
38
41
|
parseXmlToolCall
|
|
@@ -87,6 +90,9 @@ export function createTools(configOptions) {
|
|
|
87
90
|
if (configOptions.allowEdit && isToolAllowed('create')) {
|
|
88
91
|
tools.createTool = createTool(configOptions);
|
|
89
92
|
}
|
|
93
|
+
if (configOptions.allowEdit && isToolAllowed('multi_edit')) {
|
|
94
|
+
tools.multiEditTool = multiEditTool(configOptions);
|
|
95
|
+
}
|
|
90
96
|
return tools;
|
|
91
97
|
}
|
|
92
98
|
|
|
@@ -114,6 +120,7 @@ export {
|
|
|
114
120
|
bashSchema,
|
|
115
121
|
editSchema,
|
|
116
122
|
createSchema,
|
|
123
|
+
multiEditSchema,
|
|
117
124
|
attemptCompletionSchema,
|
|
118
125
|
searchToolDefinition,
|
|
119
126
|
queryToolDefinition,
|
|
@@ -125,6 +132,7 @@ export {
|
|
|
125
132
|
bashToolDefinition,
|
|
126
133
|
editToolDefinition,
|
|
127
134
|
createToolDefinition,
|
|
135
|
+
multiEditToolDefinition,
|
|
128
136
|
attemptCompletionToolDefinition,
|
|
129
137
|
googleSearchToolDefinition,
|
|
130
138
|
urlContextToolDefinition,
|
package/src/index.js
CHANGED
|
@@ -44,13 +44,15 @@ import {
|
|
|
44
44
|
import {
|
|
45
45
|
editSchema,
|
|
46
46
|
createSchema,
|
|
47
|
+
multiEditSchema,
|
|
47
48
|
editToolDefinition,
|
|
48
|
-
createToolDefinition
|
|
49
|
+
createToolDefinition,
|
|
50
|
+
multiEditToolDefinition
|
|
49
51
|
} from './tools/edit.js';
|
|
50
52
|
import { searchTool, queryTool, extractTool, delegateTool, analyzeAllTool } from './tools/vercel.js';
|
|
51
53
|
import { createExecutePlanTool, getExecutePlanToolDefinition, createCleanupExecutePlanTool, getCleanupExecutePlanToolDefinition } from './tools/executePlan.js';
|
|
52
54
|
import { bashTool } from './tools/bash.js';
|
|
53
|
-
import { editTool, createTool } from './tools/edit.js';
|
|
55
|
+
import { editTool, createTool, multiEditTool } from './tools/edit.js';
|
|
54
56
|
import { FileTracker } from './tools/fileTracker.js';
|
|
55
57
|
import { ProbeAgent } from './agent/ProbeAgent.js';
|
|
56
58
|
import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
|
|
@@ -99,6 +101,7 @@ export {
|
|
|
99
101
|
bashTool,
|
|
100
102
|
editTool,
|
|
101
103
|
createTool,
|
|
104
|
+
multiEditTool,
|
|
102
105
|
FileTracker,
|
|
103
106
|
// Export tool instances
|
|
104
107
|
listFilesToolInstance,
|
|
@@ -115,6 +118,7 @@ export {
|
|
|
115
118
|
bashSchema,
|
|
116
119
|
editSchema,
|
|
117
120
|
createSchema,
|
|
121
|
+
multiEditSchema,
|
|
118
122
|
// Export tool definitions
|
|
119
123
|
searchToolDefinition,
|
|
120
124
|
queryToolDefinition,
|
|
@@ -127,6 +131,7 @@ export {
|
|
|
127
131
|
bashToolDefinition,
|
|
128
132
|
editToolDefinition,
|
|
129
133
|
createToolDefinition,
|
|
134
|
+
multiEditToolDefinition,
|
|
130
135
|
googleSearchToolDefinition,
|
|
131
136
|
urlContextToolDefinition,
|
|
132
137
|
// Export parser function
|
package/src/tools/common.js
CHANGED
|
@@ -5,7 +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
|
+
import { editSchema, createSchema, multiEditSchema } from './edit.js';
|
|
9
9
|
import { taskSchema } from '../agent/tasks/taskTool.js';
|
|
10
10
|
|
|
11
11
|
// Common schemas for tool parameters (used for internal execution after XML parsing)
|
|
@@ -492,7 +492,8 @@ function getValidParamsForTool(toolName) {
|
|
|
492
492
|
task: taskSchema,
|
|
493
493
|
attempt_completion: attemptCompletionSchema,
|
|
494
494
|
edit: editSchema,
|
|
495
|
-
create: createSchema
|
|
495
|
+
create: createSchema,
|
|
496
|
+
multi_edit: multiEditSchema
|
|
496
497
|
};
|
|
497
498
|
|
|
498
499
|
const schema = schemaMap[toolName];
|
|
@@ -538,6 +539,15 @@ export function unescapeXmlEntities(str) {
|
|
|
538
539
|
.replace(/&/g, '&');
|
|
539
540
|
}
|
|
540
541
|
|
|
542
|
+
// Parameters that contain arbitrary code/file content — use lastIndexOf for closing tag
|
|
543
|
+
// to handle cases where the content itself contains the closing tag string.
|
|
544
|
+
const RAW_CONTENT_PARAMS = new Set(['content', 'new_string', 'old_string']);
|
|
545
|
+
|
|
546
|
+
// Tools whose content can include their own closing tag string (e.g., file content
|
|
547
|
+
// containing </create> or </edit>). Use lastIndexOf for outer tag boundary, same
|
|
548
|
+
// strategy already used for attempt_completion.
|
|
549
|
+
const LAST_INDEX_TOOLS = new Set(['attempt_completion', 'create', 'edit']);
|
|
550
|
+
|
|
541
551
|
// Simple XML parser helper - safer string-based approach
|
|
542
552
|
export function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
543
553
|
// Find the tool that appears EARLIEST in the string
|
|
@@ -564,13 +574,13 @@ export function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
564
574
|
const closeTag = `</${toolName}>`;
|
|
565
575
|
const openIndex = earliestOpenIndex;
|
|
566
576
|
|
|
567
|
-
// For
|
|
568
|
-
//
|
|
569
|
-
//
|
|
577
|
+
// For tools that contain arbitrary content (file content, code), use lastIndexOf
|
|
578
|
+
// to find the LAST occurrence of the closing tag. This prevents issues where the
|
|
579
|
+
// content itself contains the closing tag string (e.g., file content with </create>).
|
|
580
|
+
// For other tools, use indexOf from the opening tag position.
|
|
570
581
|
let closeIndex;
|
|
571
|
-
if (toolName
|
|
582
|
+
if (LAST_INDEX_TOOLS.has(toolName)) {
|
|
572
583
|
// Find the last occurrence of the closing tag in the entire string
|
|
573
|
-
// This assumes attempt_completion doesn't have nested tags of the same name
|
|
574
584
|
closeIndex = xmlString.lastIndexOf(closeTag);
|
|
575
585
|
// Make sure the closing tag is after the opening tag
|
|
576
586
|
if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
|
|
@@ -610,7 +620,19 @@ export function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
610
620
|
continue; // Parameter not found
|
|
611
621
|
}
|
|
612
622
|
|
|
613
|
-
|
|
623
|
+
// For raw content params (file content, code), use lastIndexOf to find the
|
|
624
|
+
// LAST closing tag — the content itself may contain the closing tag string.
|
|
625
|
+
// For other params (file_path, overwrite, etc.), use indexOf (first match).
|
|
626
|
+
let paramCloseIndex;
|
|
627
|
+
if (RAW_CONTENT_PARAMS.has(paramName)) {
|
|
628
|
+
paramCloseIndex = innerContent.lastIndexOf(paramCloseTag);
|
|
629
|
+
// Ensure it's after the opening tag
|
|
630
|
+
if (paramCloseIndex !== -1 && paramCloseIndex <= paramOpenIndex + paramOpenTag.length) {
|
|
631
|
+
paramCloseIndex = -1;
|
|
632
|
+
}
|
|
633
|
+
} else {
|
|
634
|
+
paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
|
|
635
|
+
}
|
|
614
636
|
|
|
615
637
|
// Handle unclosed parameter tags - use content until next tag or end of content
|
|
616
638
|
if (paramCloseIndex === -1) {
|
|
@@ -626,23 +648,34 @@ export function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
626
648
|
paramCloseIndex = nextTagIndex;
|
|
627
649
|
}
|
|
628
650
|
|
|
629
|
-
|
|
651
|
+
const rawValue = innerContent.substring(
|
|
630
652
|
paramOpenIndex + paramOpenTag.length,
|
|
631
653
|
paramCloseIndex
|
|
632
|
-
)
|
|
633
|
-
|
|
634
|
-
//
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
paramValue =
|
|
639
|
-
} else
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
// For raw content params, preserve whitespace (only strip XML formatting newlines).
|
|
657
|
+
// For other params, trim all whitespace.
|
|
658
|
+
let paramValue;
|
|
659
|
+
if (RAW_CONTENT_PARAMS.has(paramName)) {
|
|
660
|
+
paramValue = unescapeXmlEntities(rawValue.replace(/^\n/, '').replace(/\n$/, ''));
|
|
661
|
+
} else {
|
|
662
|
+
paramValue = unescapeXmlEntities(rawValue.trim());
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Type coercion for non-content params only (content/new_string/old_string must stay strings)
|
|
666
|
+
if (!RAW_CONTENT_PARAMS.has(paramName)) {
|
|
667
|
+
if (paramValue.toLowerCase() === 'true') {
|
|
668
|
+
paramValue = true;
|
|
669
|
+
} else if (paramValue.toLowerCase() === 'false') {
|
|
670
|
+
paramValue = false;
|
|
671
|
+
} else if (!isNaN(paramValue) && paramValue.trim() !== '') {
|
|
672
|
+
// Check if it's potentially a number (handle integers and floats)
|
|
673
|
+
const num = Number(paramValue);
|
|
674
|
+
if (Number.isFinite(num)) { // Use Number.isFinite to avoid Infinity/NaN
|
|
675
|
+
paramValue = num;
|
|
676
|
+
}
|
|
677
|
+
// Keep as string if not a valid finite number
|
|
644
678
|
}
|
|
645
|
-
// Keep as string if not a valid finite number
|
|
646
679
|
}
|
|
647
680
|
|
|
648
681
|
params[paramName] = paramValue;
|
|
@@ -707,7 +740,7 @@ export function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
707
740
|
const knownToolNames = [
|
|
708
741
|
'search', 'query', 'extract', 'listFiles', 'searchFiles',
|
|
709
742
|
'listSkills', 'useSkill', 'readImage', 'edit',
|
|
710
|
-
'create', 'delegate', 'bash', 'task', 'attempt_completion',
|
|
743
|
+
'create', 'multi_edit', 'delegate', 'bash', 'task', 'attempt_completion',
|
|
711
744
|
'attempt_complete', 'read_file', 'write_file', 'run_command',
|
|
712
745
|
'grep', 'find', 'cat', 'list_directory'
|
|
713
746
|
];
|