neoagent 2.5.2-beta.9 → 3.0.0
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/README.md +5 -1
- package/docs/integrations.md +11 -7
- package/package.json +2 -2
- package/server/public/.last_build_id +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +4 -4
- package/server/services/ai/deliverables/artifact_helpers.js +92 -9
- package/server/services/ai/deliverables/selector.js +17 -0
- package/server/services/ai/loop/blank_recovery.js +37 -0
- package/server/services/ai/loop/conversation_loop.js +345 -242
- package/server/services/ai/loop/error_recovery.js +38 -0
- package/server/services/ai/loop/messaging_delivery.js +52 -6
- package/server/services/ai/loop/progress_classification.js +178 -0
- package/server/services/ai/loop/progress_monitor.js +6 -5
- package/server/services/ai/loop/tool_dispatch.js +1 -0
- package/server/services/ai/loopPolicy.js +15 -6
- package/server/services/ai/messagingFallback.js +23 -1
- package/server/services/ai/preModelCompaction.js +31 -0
- package/server/services/ai/repetitionGuard.js +47 -2
- package/server/services/ai/systemPrompt.js +6 -0
- package/server/services/ai/taskAnalysis.js +10 -0
- package/server/services/ai/toolEvidence.js +15 -3
- package/server/services/ai/toolResult.js +29 -0
- package/server/services/ai/toolSelector.js +20 -3
- package/server/services/ai/tools.js +196 -26
- package/server/services/integrations/github/common.js +2 -2
- package/server/services/integrations/github/repos.js +123 -55
- package/server/services/runtime/docker-vm-manager.js +21 -1
- package/server/services/workspace/manager.js +56 -0
|
@@ -91,6 +91,35 @@ function compactToolResult(toolName, toolArgs = {}, toolResult, options = {}) {
|
|
|
91
91
|
});
|
|
92
92
|
break;
|
|
93
93
|
|
|
94
|
+
case 'read_files':
|
|
95
|
+
envelope = trimObject({
|
|
96
|
+
tool: toolName,
|
|
97
|
+
count: toolResult?.count || 0,
|
|
98
|
+
truncated: toolResult?.truncated || false,
|
|
99
|
+
results: (toolResult?.results || []).slice(0, 6).map((item) => trimObject({
|
|
100
|
+
path: item.path || item.requestedPath,
|
|
101
|
+
requestedPath: item.requestedPath,
|
|
102
|
+
rangeShown: item.rangeShown,
|
|
103
|
+
error: item.error,
|
|
104
|
+
content: lineExcerpt(item.content || '', 10, Math.floor(softLimit * 0.22))
|
|
105
|
+
}))
|
|
106
|
+
});
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case 'replace_file_range':
|
|
110
|
+
envelope = trimObject({
|
|
111
|
+
tool: toolName,
|
|
112
|
+
status: toolResult?.success === false || toolResult?.error ? 'error' : 'ok',
|
|
113
|
+
path: toolResult?.path || toolArgs.path,
|
|
114
|
+
startLine: toolResult?.startLine || toolArgs.start_line,
|
|
115
|
+
endLine: toolResult?.endLine || toolArgs.end_line,
|
|
116
|
+
replacedLines: toolResult?.replacedLines,
|
|
117
|
+
insertedLines: toolResult?.insertedLines,
|
|
118
|
+
totalLines: toolResult?.totalLines,
|
|
119
|
+
error: toolResult?.error
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
|
|
94
123
|
case 'search_files':
|
|
95
124
|
envelope = trimObject({
|
|
96
125
|
tool: toolName,
|
|
@@ -15,6 +15,22 @@ const ALWAYS_INCLUDE_BUILT_INS = [
|
|
|
15
15
|
'send_message',
|
|
16
16
|
'send_interim_update',
|
|
17
17
|
];
|
|
18
|
+
const CORE_FILE_TOOLS = [
|
|
19
|
+
'read_file',
|
|
20
|
+
'read_files',
|
|
21
|
+
'list_directory',
|
|
22
|
+
'search_files',
|
|
23
|
+
'edit_file',
|
|
24
|
+
'replace_file_range',
|
|
25
|
+
'write_file',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function requiredToolNames(options = {}) {
|
|
29
|
+
const requiredNames = [...ALWAYS_INCLUDE_BUILT_INS];
|
|
30
|
+
if (options.widgetId) requiredNames.push('save_widget_snapshot');
|
|
31
|
+
if (options.includeCoreFileTools) requiredNames.push(...CORE_FILE_TOOLS);
|
|
32
|
+
return requiredNames;
|
|
33
|
+
}
|
|
18
34
|
|
|
19
35
|
function compactDescription(value, maxChars = 180) {
|
|
20
36
|
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
@@ -37,8 +53,7 @@ function buildToolCatalog(tools = []) {
|
|
|
37
53
|
|
|
38
54
|
function ensureRequiredTools(selectedTools = [], builtInTools = [], options = {}) {
|
|
39
55
|
const limit = Number(options.maxTools) || MAX_TOOLS;
|
|
40
|
-
const requiredNames =
|
|
41
|
-
if (options.widgetId) requiredNames.push('save_widget_snapshot');
|
|
56
|
+
const requiredNames = requiredToolNames(options);
|
|
42
57
|
if (!requiredNames.length) return selectedTools;
|
|
43
58
|
|
|
44
59
|
const selected = Array.isArray(selectedTools) ? [...selectedTools] : [];
|
|
@@ -83,6 +98,7 @@ function selectInitialTools(allTools = [], suggestedNames = [], options = {}) {
|
|
|
83
98
|
|
|
84
99
|
function activateTools(currentTools = [], allTools = [], requestedNames = [], options = {}) {
|
|
85
100
|
const knownByName = new Map(allTools.map((tool) => [tool?.name, tool]));
|
|
101
|
+
const requiredNames = requiredToolNames(options);
|
|
86
102
|
let next = ensureRequiredTools(currentTools, allTools, options);
|
|
87
103
|
const activated = [];
|
|
88
104
|
const evicted = [];
|
|
@@ -102,7 +118,7 @@ function activateTools(currentTools = [], allTools = [], requestedNames = [], op
|
|
|
102
118
|
if (next.some((item) => item?.name === name)) continue;
|
|
103
119
|
if (next.length >= MAX_TOOLS) {
|
|
104
120
|
const replaceIndex = next.findIndex((item) => (
|
|
105
|
-
!
|
|
121
|
+
!requiredNames.includes(item?.name)
|
|
106
122
|
&& !requested.includes(item?.name)
|
|
107
123
|
));
|
|
108
124
|
if (replaceIndex === -1) {
|
|
@@ -136,6 +152,7 @@ function selectToolsForTask(task, builtInTools = [], mcpTools = [], _options = {
|
|
|
136
152
|
}
|
|
137
153
|
|
|
138
154
|
module.exports = {
|
|
155
|
+
CORE_FILE_TOOLS,
|
|
139
156
|
MAX_TOOLS,
|
|
140
157
|
activateTools,
|
|
141
158
|
buildToolCatalog,
|
|
@@ -881,16 +881,57 @@ function getAvailableTools(app, options = {}) {
|
|
|
881
881
|
},
|
|
882
882
|
{
|
|
883
883
|
name: 'read_file',
|
|
884
|
-
description: 'Read
|
|
884
|
+
description: 'Read one workspace file. Supports line ranges for large files. Use workspace file tools for code inspection whenever the files are in the shared workspace.',
|
|
885
885
|
parameters: {
|
|
886
886
|
type: 'object',
|
|
887
887
|
properties: {
|
|
888
|
-
path: { type: 'string', description: 'Absolute or relative
|
|
888
|
+
path: { type: 'string', description: 'Absolute or relative path inside the per-user workspace' },
|
|
889
|
+
file_path: { type: 'string', description: 'Alias for path; accepted for compatibility with model tool calls' },
|
|
889
890
|
start_line: { type: 'number', description: 'Starting line number (1-indexed, inclusive)' },
|
|
890
891
|
end_line: { type: 'number', description: 'Ending line number (1-indexed, inclusive)' },
|
|
892
|
+
line_start: { type: 'number', description: 'Alias for start_line; accepted for compatibility with model tool calls' },
|
|
893
|
+
line_count: { type: 'number', description: 'Number of lines to read starting at start_line/line_start' },
|
|
891
894
|
encoding: { type: 'string', description: 'File encoding (default utf-8)' }
|
|
892
895
|
},
|
|
893
|
-
required: [
|
|
896
|
+
required: []
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
name: 'read_files',
|
|
901
|
+
description: 'Read multiple workspace files or line ranges in one call. Prefer this over repeated single-file reads or shell snippets when inspecting related files.',
|
|
902
|
+
parameters: {
|
|
903
|
+
type: 'object',
|
|
904
|
+
properties: {
|
|
905
|
+
files: {
|
|
906
|
+
type: 'array',
|
|
907
|
+
description: 'Files to read. Each item may be a path string or an object with path/file_path plus optional start_line/end_line or line_start/line_count.',
|
|
908
|
+
items: {
|
|
909
|
+
oneOf: [
|
|
910
|
+
{ type: 'string', description: 'Path inside the per-user workspace' },
|
|
911
|
+
{
|
|
912
|
+
type: 'object',
|
|
913
|
+
properties: {
|
|
914
|
+
path: { type: 'string', description: 'Absolute or relative path inside the per-user workspace' },
|
|
915
|
+
file_path: { type: 'string', description: 'Alias for path' },
|
|
916
|
+
start_line: { type: 'number', description: 'Starting line number (1-indexed, inclusive)' },
|
|
917
|
+
end_line: { type: 'number', description: 'Ending line number (1-indexed, inclusive)' },
|
|
918
|
+
line_start: { type: 'number', description: 'Alias for start_line' },
|
|
919
|
+
line_count: { type: 'number', description: 'Number of lines to read from start_line/line_start' },
|
|
920
|
+
encoding: { type: 'string', description: 'File encoding (default utf-8)' }
|
|
921
|
+
},
|
|
922
|
+
required: []
|
|
923
|
+
}
|
|
924
|
+
]
|
|
925
|
+
}
|
|
926
|
+
},
|
|
927
|
+
paths: {
|
|
928
|
+
type: 'array',
|
|
929
|
+
items: { type: 'string' },
|
|
930
|
+
description: 'Convenience alias for reading whole files by path.'
|
|
931
|
+
},
|
|
932
|
+
encoding: { type: 'string', description: 'Default file encoding for items without encoding (default utf-8)' }
|
|
933
|
+
},
|
|
934
|
+
required: []
|
|
894
935
|
}
|
|
895
936
|
},
|
|
896
937
|
{
|
|
@@ -900,10 +941,11 @@ function getAvailableTools(app, options = {}) {
|
|
|
900
941
|
type: 'object',
|
|
901
942
|
properties: {
|
|
902
943
|
path: { type: 'string', description: 'File path' },
|
|
944
|
+
file_path: { type: 'string', description: 'Alias for path; accepted for compatibility with model tool calls' },
|
|
903
945
|
content: { type: 'string', description: 'Content to write' },
|
|
904
946
|
mode: { type: 'string', enum: ['write', 'append'], description: 'Write mode (default write)' }
|
|
905
947
|
},
|
|
906
|
-
required: ['
|
|
948
|
+
required: ['content']
|
|
907
949
|
}
|
|
908
950
|
},
|
|
909
951
|
{
|
|
@@ -913,46 +955,68 @@ function getAvailableTools(app, options = {}) {
|
|
|
913
955
|
type: 'object',
|
|
914
956
|
properties: {
|
|
915
957
|
path: { type: 'string', description: 'File path' },
|
|
958
|
+
file_path: { type: 'string', description: 'Alias for path; accepted for compatibility with model tool calls' },
|
|
916
959
|
edits: {
|
|
917
960
|
type: 'array',
|
|
918
961
|
items: {
|
|
919
962
|
type: 'object',
|
|
920
963
|
properties: {
|
|
921
964
|
oldText: { type: 'string', description: 'The exact text to replace.' },
|
|
922
|
-
newText: { type: 'string', description: 'The replacement text.' }
|
|
965
|
+
newText: { type: 'string', description: 'The replacement text.' },
|
|
966
|
+
old_text: { type: 'string', description: 'Alias for oldText' },
|
|
967
|
+
new_text: { type: 'string', description: 'Alias for newText' }
|
|
923
968
|
},
|
|
924
|
-
required: [
|
|
969
|
+
required: []
|
|
925
970
|
},
|
|
926
971
|
description: 'List of text replacements to apply.'
|
|
927
972
|
}
|
|
928
973
|
},
|
|
929
|
-
required: ['
|
|
974
|
+
required: ['edits']
|
|
975
|
+
}
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
name: 'replace_file_range',
|
|
979
|
+
description: 'Replace a 1-indexed inclusive line range in a workspace file. Use this when you know line numbers and exact-text edit_file would be brittle. Read the target range first, then replace only the intended lines.',
|
|
980
|
+
parameters: {
|
|
981
|
+
type: 'object',
|
|
982
|
+
properties: {
|
|
983
|
+
path: { type: 'string', description: 'File path inside the per-user workspace' },
|
|
984
|
+
file_path: { type: 'string', description: 'Alias for path; accepted for compatibility with model tool calls' },
|
|
985
|
+
start_line: { type: 'number', description: 'Starting line number (1-indexed, inclusive)' },
|
|
986
|
+
end_line: { type: 'number', description: 'Ending line number (1-indexed, inclusive)' },
|
|
987
|
+
startLine: { type: 'number', description: 'Alias for start_line' },
|
|
988
|
+
endLine: { type: 'number', description: 'Alias for end_line' },
|
|
989
|
+
content: { type: 'string', description: 'Replacement content for the range. Empty string deletes the range.' }
|
|
990
|
+
},
|
|
991
|
+
required: ['content']
|
|
930
992
|
}
|
|
931
993
|
},
|
|
932
994
|
{
|
|
933
995
|
name: 'list_directory',
|
|
934
|
-
description: 'List files and directories with metadata
|
|
996
|
+
description: 'List workspace files and directories with metadata. Omit path to list the workspace root.',
|
|
935
997
|
parameters: {
|
|
936
998
|
type: 'object',
|
|
937
999
|
properties: {
|
|
938
|
-
path: { type: 'string', description: 'Directory path' },
|
|
1000
|
+
path: { type: 'string', description: 'Directory path inside the workspace (default ".")' },
|
|
939
1001
|
recursive: { type: 'boolean', description: 'List recursively' },
|
|
940
1002
|
depth: { type: 'number', description: 'Maximum recursion depth (default 1, max 5)' }
|
|
941
1003
|
},
|
|
942
|
-
required: [
|
|
1004
|
+
required: []
|
|
943
1005
|
}
|
|
944
1006
|
},
|
|
945
1007
|
{
|
|
946
1008
|
name: 'search_files',
|
|
947
|
-
description: 'Search for text
|
|
1009
|
+
description: 'Search for text across workspace files recursively. Omit path to search the workspace root.',
|
|
948
1010
|
parameters: {
|
|
949
1011
|
type: 'object',
|
|
950
1012
|
properties: {
|
|
951
|
-
path: { type: 'string', description: 'Directory to search in' },
|
|
1013
|
+
path: { type: 'string', description: 'Directory to search in (default ".")' },
|
|
952
1014
|
query: { type: 'string', description: 'Text or regex pattern to search for' },
|
|
953
|
-
include: { type: 'string', description: 'Glob pattern for files to include (e.g. "*.js")' }
|
|
1015
|
+
include: { type: 'string', description: 'Glob pattern for files to include (e.g. "*.js")' },
|
|
1016
|
+
maxDepth: { type: 'number', description: 'Maximum directory depth (default 5, max 10)' },
|
|
1017
|
+
maxFileSize: { type: 'number', description: 'Maximum file size in bytes to scan (default 1048576)' }
|
|
954
1018
|
},
|
|
955
|
-
required: ['
|
|
1019
|
+
required: ['query']
|
|
956
1020
|
}
|
|
957
1021
|
},
|
|
958
1022
|
{
|
|
@@ -1141,7 +1205,7 @@ function getAvailableTools(app, options = {}) {
|
|
|
1141
1205
|
},
|
|
1142
1206
|
{
|
|
1143
1207
|
name: 'create_task',
|
|
1144
|
-
description: 'Create a background
|
|
1208
|
+
description: 'Create a future, recurring, monitored, or background automation with a named trigger and self-contained prompt. Do not use for immediate short answers.',
|
|
1145
1209
|
parameters: {
|
|
1146
1210
|
type: 'object',
|
|
1147
1211
|
properties: {
|
|
@@ -1160,12 +1224,12 @@ function getAvailableTools(app, options = {}) {
|
|
|
1160
1224
|
},
|
|
1161
1225
|
{
|
|
1162
1226
|
name: 'list_tasks',
|
|
1163
|
-
description: 'List
|
|
1227
|
+
description: 'List saved background/scheduled tasks for this user and agent. Do not use for ordinary immediate replies.',
|
|
1164
1228
|
parameters: { type: 'object', properties: {} }
|
|
1165
1229
|
},
|
|
1166
1230
|
{
|
|
1167
1231
|
name: 'delete_task',
|
|
1168
|
-
description: 'Delete a task by its ID.',
|
|
1232
|
+
description: 'Delete a saved background/scheduled task by its ID.',
|
|
1169
1233
|
parameters: {
|
|
1170
1234
|
type: 'object',
|
|
1171
1235
|
properties: {
|
|
@@ -1176,7 +1240,7 @@ function getAvailableTools(app, options = {}) {
|
|
|
1176
1240
|
},
|
|
1177
1241
|
{
|
|
1178
1242
|
name: 'update_task',
|
|
1179
|
-
description: 'Update an existing task, including its trigger, prompt, or enabled state.',
|
|
1243
|
+
description: 'Update an existing saved background/scheduled task, including its trigger, prompt, or enabled state.',
|
|
1180
1244
|
parameters: {
|
|
1181
1245
|
type: 'object',
|
|
1182
1246
|
properties: {
|
|
@@ -1500,6 +1564,33 @@ function getAvailableTools(app, options = {}) {
|
|
|
1500
1564
|
return compacted;
|
|
1501
1565
|
}
|
|
1502
1566
|
|
|
1567
|
+
function firstStringArg(args = {}, names = []) {
|
|
1568
|
+
for (const name of names) {
|
|
1569
|
+
const value = args?.[name];
|
|
1570
|
+
if (typeof value === 'string' && value.trim()) return value;
|
|
1571
|
+
}
|
|
1572
|
+
return '';
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
function normalizeReadFileArgs(args = {}) {
|
|
1576
|
+
const startLine = args.start_line ?? args.line_start ?? args.startLine ?? args.lineStart;
|
|
1577
|
+
let endLine = args.end_line ?? args.line_end ?? args.endLine ?? args.lineEnd;
|
|
1578
|
+
const lineCount = args.line_count ?? args.lineCount;
|
|
1579
|
+
if (endLine == null && startLine != null && lineCount != null) {
|
|
1580
|
+
const start = Number(startLine);
|
|
1581
|
+
const count = Number(lineCount);
|
|
1582
|
+
if (Number.isInteger(start) && Number.isInteger(count) && count > 0) {
|
|
1583
|
+
endLine = start + count - 1;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
return {
|
|
1587
|
+
path: firstStringArg(args, ['path', 'file_path', 'filePath', 'filename']),
|
|
1588
|
+
encoding: args.encoding || 'utf-8',
|
|
1589
|
+
start_line: startLine,
|
|
1590
|
+
end_line: endLine,
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1503
1594
|
/**
|
|
1504
1595
|
* Executes a tool by name.
|
|
1505
1596
|
* @param {string} toolName - Name of the tool.
|
|
@@ -2297,12 +2388,58 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2297
2388
|
try {
|
|
2298
2389
|
const workspace = wc();
|
|
2299
2390
|
if (!workspace) return { error: 'Workspace service is unavailable.' };
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2391
|
+
const normalizedArgs = normalizeReadFileArgs(args);
|
|
2392
|
+
if (!normalizedArgs.path) {
|
|
2393
|
+
return {
|
|
2394
|
+
error: 'read_file requires path or file_path. Keep source files in the workspace; for repository work clone or move the checkout into the workspace before using file tools.',
|
|
2395
|
+
};
|
|
2396
|
+
}
|
|
2397
|
+
return workspace.readFile(userId, normalizedArgs);
|
|
2398
|
+
} catch (err) {
|
|
2399
|
+
return { error: err.message };
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
case 'read_files': {
|
|
2404
|
+
try {
|
|
2405
|
+
const workspace = wc();
|
|
2406
|
+
if (!workspace) return { error: 'Workspace service is unavailable.' };
|
|
2407
|
+
const entries = Array.isArray(args.files)
|
|
2408
|
+
? args.files
|
|
2409
|
+
: (Array.isArray(args.paths) ? args.paths : []);
|
|
2410
|
+
if (!entries.length) {
|
|
2411
|
+
return { error: 'read_files requires a non-empty files or paths array.' };
|
|
2412
|
+
}
|
|
2413
|
+
const maxFiles = 8;
|
|
2414
|
+
const results = entries.slice(0, maxFiles).map((entry, index) => {
|
|
2415
|
+
const fileArgs = typeof entry === 'string'
|
|
2416
|
+
? { path: entry }
|
|
2417
|
+
: (entry && typeof entry === 'object' ? entry : {});
|
|
2418
|
+
const normalizedArgs = normalizeReadFileArgs({
|
|
2419
|
+
...fileArgs,
|
|
2420
|
+
encoding: fileArgs.encoding || args.encoding || 'utf-8',
|
|
2421
|
+
});
|
|
2422
|
+
if (!normalizedArgs.path) {
|
|
2423
|
+
return {
|
|
2424
|
+
index,
|
|
2425
|
+
success: false,
|
|
2426
|
+
error: 'Each read_files item requires path or file_path.',
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
const result = workspace.readFile(userId, normalizedArgs);
|
|
2430
|
+
return {
|
|
2431
|
+
index,
|
|
2432
|
+
requestedPath: normalizedArgs.path,
|
|
2433
|
+
success: !result?.error,
|
|
2434
|
+
...result,
|
|
2435
|
+
};
|
|
2305
2436
|
});
|
|
2437
|
+
return {
|
|
2438
|
+
success: results.every((result) => result.success !== false && !result.error),
|
|
2439
|
+
count: results.length,
|
|
2440
|
+
truncated: entries.length > maxFiles,
|
|
2441
|
+
results,
|
|
2442
|
+
};
|
|
2306
2443
|
} catch (err) {
|
|
2307
2444
|
return { error: err.message };
|
|
2308
2445
|
}
|
|
@@ -2312,8 +2449,10 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2312
2449
|
try {
|
|
2313
2450
|
const workspace = wc();
|
|
2314
2451
|
if (!workspace) return { error: 'Workspace service is unavailable.' };
|
|
2452
|
+
const targetPath = args.path || args.file_path;
|
|
2453
|
+
if (!targetPath) return { success: false, error: 'write_file requires path or file_path.' };
|
|
2315
2454
|
return workspace.writeFile(userId, {
|
|
2316
|
-
path:
|
|
2455
|
+
path: targetPath,
|
|
2317
2456
|
content: args.content,
|
|
2318
2457
|
mode: args.mode,
|
|
2319
2458
|
});
|
|
@@ -2326,9 +2465,38 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2326
2465
|
try {
|
|
2327
2466
|
const workspace = wc();
|
|
2328
2467
|
if (!workspace) return { error: 'Workspace service is unavailable.' };
|
|
2468
|
+
const targetPath = args.path || args.file_path;
|
|
2469
|
+
if (!targetPath) return { success: false, error: 'edit_file requires path or file_path.' };
|
|
2470
|
+
const edits = Array.isArray(args.edits)
|
|
2471
|
+
? args.edits.map((edit) => ({
|
|
2472
|
+
...edit,
|
|
2473
|
+
oldText: edit?.oldText ?? edit?.old_text,
|
|
2474
|
+
newText: edit?.newText ?? edit?.new_text,
|
|
2475
|
+
}))
|
|
2476
|
+
: [];
|
|
2329
2477
|
return workspace.editFile(userId, {
|
|
2330
|
-
path:
|
|
2331
|
-
edits
|
|
2478
|
+
path: targetPath,
|
|
2479
|
+
edits,
|
|
2480
|
+
});
|
|
2481
|
+
} catch (err) {
|
|
2482
|
+
return { error: err.message };
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
case 'replace_file_range': {
|
|
2487
|
+
try {
|
|
2488
|
+
const workspace = wc();
|
|
2489
|
+
if (!workspace) return { error: 'Workspace service is unavailable.' };
|
|
2490
|
+
if (typeof workspace.replaceFileRange !== 'function') {
|
|
2491
|
+
return { error: 'Workspace service does not support replace_file_range.' };
|
|
2492
|
+
}
|
|
2493
|
+
const targetPath = args.path || args.file_path;
|
|
2494
|
+
if (!targetPath) return { success: false, error: 'replace_file_range requires path or file_path.' };
|
|
2495
|
+
return workspace.replaceFileRange(userId, {
|
|
2496
|
+
path: targetPath,
|
|
2497
|
+
start_line: args.start_line ?? args.startLine,
|
|
2498
|
+
end_line: args.end_line ?? args.endLine,
|
|
2499
|
+
content: args.content,
|
|
2332
2500
|
});
|
|
2333
2501
|
} catch (err) {
|
|
2334
2502
|
return { error: err.message };
|
|
@@ -2357,6 +2525,8 @@ async function executeTool(toolName, args, context, engine) {
|
|
|
2357
2525
|
path: args.path,
|
|
2358
2526
|
query: args.query,
|
|
2359
2527
|
include: args.include,
|
|
2528
|
+
maxDepth: args.maxDepth ?? args.max_depth,
|
|
2529
|
+
maxFileSize: args.maxFileSize ?? args.max_file_size,
|
|
2360
2530
|
});
|
|
2361
2531
|
} catch (err) {
|
|
2362
2532
|
return { error: err.message };
|
|
@@ -42,7 +42,7 @@ async function githubApiRequest(auth, options = {}) {
|
|
|
42
42
|
'X-GitHub-Api-Version': '2022-11-28',
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
if (body
|
|
45
|
+
if (body) {
|
|
46
46
|
headers['Content-Type'] = 'application/json';
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -103,4 +103,4 @@ module.exports = {
|
|
|
103
103
|
buildPaginationParams,
|
|
104
104
|
githubApiRequest,
|
|
105
105
|
parseOwnerRepo,
|
|
106
|
-
};
|
|
106
|
+
};
|