minovative-mind-cli 2.14.1 → 2.14.3
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 +40 -59
- package/dist/services/agent/slashCommands.js +144 -13
- package/dist/services/agent/toolLoop.js +11 -2
- package/dist/services/agent/types.d.ts +9 -2
- package/dist/services/agent-tools.d.ts +20 -1
- package/dist/services/agent-tools.js +272 -47
- package/dist/services/agent.js +43 -18
- package/dist/services/ai.d.ts +9 -0
- package/dist/services/ai.js +71 -21
- package/dist/services/chatHistoryService.d.ts +5 -0
- package/dist/services/contextAgent.d.ts +14 -6
- package/dist/services/contextAgent.js +36 -10
- package/dist/services/orchestration/investigationAgent.js +31 -7
- package/dist/services/orchestration/investigationCache.js +19 -9
- package/dist/services/orchestration/readCache.d.ts +1 -0
- package/dist/services/orchestration/readCache.js +5 -2
- package/dist/services/orchestration/scopedTools.js +37 -10
- package/dist/services/orchestration/subAgent.js +16 -2
- package/dist/services/proxyClient.d.ts +6 -0
- package/dist/services/proxyClient.js +24 -10
- package/dist/services/sessionSettings.d.ts +66 -0
- package/dist/services/sessionSettings.js +126 -0
- package/dist/services/userProfileService.d.ts +14 -0
- package/dist/services/userProfileService.js +105 -3
- package/dist/services/verificationService.js +24 -2
- package/dist/utils/analysisRunner.d.ts +120 -8
- package/dist/utils/analysisRunner.js +946 -125
- package/dist/utils/antiCheatingGuard.d.ts +21 -0
- package/dist/utils/antiCheatingGuard.js +554 -0
- package/dist/utils/contextPrompts.d.ts +39 -0
- package/dist/utils/contextPrompts.js +81 -9
- package/dist/utils/contextRanker.d.ts +216 -0
- package/dist/utils/contextRanker.js +603 -0
- package/dist/utils/dependencyTracer/modules/graph.d.ts +4 -1
- package/dist/utils/dependencyTracer/modules/graph.js +11 -0
- package/dist/utils/dependencyTracer/modules/types.d.ts +10 -0
- package/dist/utils/dependencyTracer.d.ts +40 -2
- package/dist/utils/dependencyTracer.js +95 -3
- package/dist/utils/fileReadCache.d.ts +58 -0
- package/dist/utils/fileReadCache.js +162 -0
- package/dist/utils/projectStorage.js +2 -1
- package/dist/utils/symbolExtractor.d.ts +12 -0
- package/dist/utils/symbolExtractor.js +111 -15
- package/dist/utils/systemPrompts.d.ts +4 -3
- package/dist/utils/systemPrompts.js +66 -8
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -25,15 +25,18 @@ export interface ToolResult {
|
|
|
25
25
|
mimeType: string;
|
|
26
26
|
data: string;
|
|
27
27
|
};
|
|
28
|
+
/** Optional structured JSON result extracted from the script execution. */
|
|
29
|
+
structuredResult?: unknown;
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* Returns the list of available function declarations for Gemini function calling.
|
|
31
33
|
*
|
|
32
|
-
* @param options - Configuration options such as `isExecutionAgent`.
|
|
34
|
+
* @param options - Configuration options such as `isExecutionAgent` or `isResearchAgent`.
|
|
33
35
|
* @returns An array of Google Generative AI `FunctionDeclaration` objects.
|
|
34
36
|
*/
|
|
35
37
|
export declare function getToolDeclarations(options?: {
|
|
36
38
|
isExecutionAgent?: boolean;
|
|
39
|
+
isResearchAgent?: boolean;
|
|
37
40
|
}): FunctionDeclaration[];
|
|
38
41
|
/**
|
|
39
42
|
* FunctionDeclaration-compatible schema objects that describe
|
|
@@ -138,6 +141,22 @@ export declare function modifyFile(workspaceRoot: string, filePath: string, edit
|
|
|
138
141
|
* @returns A promise resolving to a {@link ToolResult} containing the formatted tree output.
|
|
139
142
|
*/
|
|
140
143
|
export declare function listDirectory(workspaceRoot: string, dirPath: string, maxDepth?: number): Promise<ToolResult>;
|
|
144
|
+
/**
|
|
145
|
+
* Truncates large string output using bounded head and tail windows with a structured metadata receipt.
|
|
146
|
+
* Preserves early context (e.g. invocation, build targets) and late context (e.g. error summaries, exit codes)
|
|
147
|
+
* while bounding total character and line count.
|
|
148
|
+
*
|
|
149
|
+
* @param text - The raw output text to truncate.
|
|
150
|
+
* @param options - Configuration options for character/line limits and custom receipt notices.
|
|
151
|
+
* @returns The windowed text containing head, structured metadata receipt, and tail.
|
|
152
|
+
*/
|
|
153
|
+
export declare function truncateWithHeadTailWindow(text: string, options?: {
|
|
154
|
+
maxChars?: number;
|
|
155
|
+
maxLines?: number;
|
|
156
|
+
headRatio?: number;
|
|
157
|
+
receiptLabel?: string;
|
|
158
|
+
hint?: string;
|
|
159
|
+
}): string;
|
|
141
160
|
/**
|
|
142
161
|
* Condenses verbose command and test failure outputs into high-signal diagnostic error logs
|
|
143
162
|
* with dynamic tail sizing and stack trace boundary snapping.
|
|
@@ -23,13 +23,15 @@ import { findBestMatch, applyMatch } from '../utils/fuzzyMatch.js';
|
|
|
23
23
|
import { localValidate } from '../utils/localSyntaxValidator.js';
|
|
24
24
|
import { validateAndFixSyntax, aiFuzzyMatch } from './agent/syntaxAgent.js';
|
|
25
25
|
import { sanitizeForCDATA } from '../utils/contextPrompts.js';
|
|
26
|
-
import { findDependencies, formatDependencyResult } from '../utils/dependencyTracer.js';
|
|
26
|
+
import { findDependencies, formatDependencyResult, invalidateDependencyGraph } from '../utils/dependencyTracer.js';
|
|
27
27
|
import { atomicWriteFile } from '../utils/atomicWrite.js';
|
|
28
28
|
import { EXCLUDED_EXTENSIONS } from '../utils/excludedExtensions.js';
|
|
29
29
|
import { extractSymbols } from '../utils/symbolExtractor.js';
|
|
30
30
|
import { getMetricCollector, recordRecoveryPrunedLogVolume } from './metrics.js';
|
|
31
31
|
import { getCurrentAgentId } from '../utils/asyncContext.js';
|
|
32
32
|
import { recordFileRead, hasAgentReadFile } from '../utils/fileReadGuard.js';
|
|
33
|
+
import { getCachedFileContent, invalidateFileReadCache, clearFileReadCache } from '../utils/fileReadCache.js';
|
|
34
|
+
import { detectAntiCheatingViolations, isCriticalConfigFile } from '../utils/antiCheatingGuard.js';
|
|
33
35
|
import { runFuzzProbe, checkHeapDelta as runCheckHeapDelta, checkBehavioralDrift as runCheckBehavioralDrift, runEphemeralScript, } from '../utils/analysisRunner.js';
|
|
34
36
|
import ignore from 'ignore';
|
|
35
37
|
const execAsync = promisify(exec);
|
|
@@ -37,13 +39,24 @@ const execAsync = promisify(exec);
|
|
|
37
39
|
/**
|
|
38
40
|
* Returns the list of available function declarations for Gemini function calling.
|
|
39
41
|
*
|
|
40
|
-
* @param options - Configuration options such as `isExecutionAgent`.
|
|
42
|
+
* @param options - Configuration options such as `isExecutionAgent` or `isResearchAgent`.
|
|
41
43
|
* @returns An array of Google Generative AI `FunctionDeclaration` objects.
|
|
42
44
|
*/
|
|
43
45
|
export function getToolDeclarations(options) {
|
|
44
46
|
if (options?.isExecutionAgent) {
|
|
45
47
|
return toolDeclarations;
|
|
46
48
|
}
|
|
49
|
+
if (options?.isResearchAgent) {
|
|
50
|
+
const excludedForResearch = new Set([
|
|
51
|
+
'modify_file',
|
|
52
|
+
'write_file',
|
|
53
|
+
'delete_file',
|
|
54
|
+
'rename_file',
|
|
55
|
+
'create_todo_list',
|
|
56
|
+
'update_todo_status',
|
|
57
|
+
]);
|
|
58
|
+
return toolDeclarations.filter((tool) => !excludedForResearch.has(tool.name));
|
|
59
|
+
}
|
|
47
60
|
return toolDeclarations.filter((tool) => tool.name !== 'create_todo_list' && tool.name !== 'update_todo_status' && tool.name !== 'finish_task');
|
|
48
61
|
}
|
|
49
62
|
/**
|
|
@@ -278,11 +291,15 @@ export const toolDeclarations = [
|
|
|
278
291
|
},
|
|
279
292
|
{
|
|
280
293
|
name: 'run_debug_script',
|
|
281
|
-
description: 'Write a disposable script to a sandboxed temporary file in os.tmpdir(), execute it using the specified runtime, and return the exact standard output and standard error without polluting the workspace or triggering IDE file watchers. ' +
|
|
282
|
-
'
|
|
283
|
-
'(
|
|
284
|
-
'(
|
|
285
|
-
'(
|
|
294
|
+
description: 'Write a disposable script to a sandboxed temporary file in os.tmpdir(), execute it using the specified runtime, and return the exact standard output, structured JSON result, and standard error without polluting the workspace or triggering IDE file watchers. ' +
|
|
295
|
+
'Sandbox Features: ' +
|
|
296
|
+
'(1) Preloaded Helpers: "emitResult(data)" / "__emitResult(data)" outputs structured JSON payloads parsed directly into structuredResult; "inspectSymbols(target)" / "inspectObject(target)" inspects functions, classes, and properties. ' +
|
|
297
|
+
'(2) Module & Path Resolution: Automatically inherits tsconfig.json path aliases (@/*), NODE_PATH, and workspace virtualenvs (.venv, venv) for Python. ' +
|
|
298
|
+
'(3) Diagnostics Middleware: Provides actionable [SANDBOX DIAGNOSTIC] advisories for missing imports, ESM/CJS interop, and compiler errors. ' +
|
|
299
|
+
'Use this to: (a) actively debug the codebase by inspecting variables or logging values, ' +
|
|
300
|
+
'(b) validate your changes by importing the modified module and asserting expected behavior with edge-case inputs, ' +
|
|
301
|
+
'(c) run quick sanity checks (e.g., verify a config file parses correctly, confirm exports are intact after a refactor, or check that a function returns the expected output), ' +
|
|
302
|
+
'(d) write lightweight ML scripts to solve complex problems — e.g., polynomial regression to estimate Big-O complexity, fuzz testing with Markov chain input generation, ' +
|
|
286
303
|
'output regression detection via statistical similarity scoring, or error message classification with Naive Bayes. ' +
|
|
287
304
|
'Default to "node" for generic tasks as a safe baseline, but act like a native inhabitant of the host environment — if Python, Go, Rust, or host-native libraries are active in the project, leverage the host\'s native runtimes for maximum efficiency. ' +
|
|
288
305
|
'Do not guess whether your code works — test it directly!',
|
|
@@ -622,7 +639,7 @@ export async function readFile(workspaceRoot, filePath, startLine, endLine, targ
|
|
|
622
639
|
};
|
|
623
640
|
}
|
|
624
641
|
}
|
|
625
|
-
let content = await
|
|
642
|
+
let content = await getCachedFileContent(absPath);
|
|
626
643
|
// Handle Jupyter Notebooks natively
|
|
627
644
|
if (filePath.toLowerCase().endsWith('.ipynb')) {
|
|
628
645
|
try {
|
|
@@ -825,9 +842,15 @@ export async function writeFile(workspaceRoot, filePath, content) {
|
|
|
825
842
|
};
|
|
826
843
|
}
|
|
827
844
|
}
|
|
845
|
+
const antiCheatError = detectAntiCheatingViolations(filePath, finalContent, existingContent ?? undefined);
|
|
846
|
+
if (antiCheatError) {
|
|
847
|
+
return { output: '', error: antiCheatError };
|
|
848
|
+
}
|
|
828
849
|
await fs.mkdir(path.dirname(absPath), { recursive: true });
|
|
829
850
|
changeLogger.logChange(filePath, existingContent, existingContent !== null ? 'modify' : 'create');
|
|
830
851
|
await atomicWriteFile(absPath, finalContent, 'utf-8');
|
|
852
|
+
invalidateFileReadCache(absPath);
|
|
853
|
+
invalidateDependencyGraph(workspaceRoot);
|
|
831
854
|
return { output: `Successfully wrote to "${filePath}".` };
|
|
832
855
|
}
|
|
833
856
|
catch (err) {
|
|
@@ -850,6 +873,12 @@ export async function writeFile(workspaceRoot, filePath, content) {
|
|
|
850
873
|
*/
|
|
851
874
|
export async function deleteFile(workspaceRoot, filePath) {
|
|
852
875
|
try {
|
|
876
|
+
if (isCriticalConfigFile(filePath)) {
|
|
877
|
+
return {
|
|
878
|
+
output: '',
|
|
879
|
+
error: `Anti-cheating violation: Deleting critical configuration file "${filePath}" is strictly prohibited. You must fix all build, compiler, type, and lint errors head-on in the source code.`,
|
|
880
|
+
};
|
|
881
|
+
}
|
|
853
882
|
const absPath = resolveAndValidatePath(workspaceRoot, filePath);
|
|
854
883
|
let existingContent = null;
|
|
855
884
|
try {
|
|
@@ -860,6 +889,8 @@ export async function deleteFile(workspaceRoot, filePath) {
|
|
|
860
889
|
}
|
|
861
890
|
changeLogger.logChange(filePath, existingContent, 'delete');
|
|
862
891
|
await fs.rm(absPath, { force: true });
|
|
892
|
+
invalidateFileReadCache(absPath);
|
|
893
|
+
invalidateDependencyGraph(workspaceRoot);
|
|
863
894
|
return { output: `Successfully deleted "${filePath}".` };
|
|
864
895
|
}
|
|
865
896
|
catch (err) {
|
|
@@ -877,6 +908,12 @@ export async function deleteFile(workspaceRoot, filePath) {
|
|
|
877
908
|
*/
|
|
878
909
|
export async function renameFile(workspaceRoot, sourcePath, targetPath) {
|
|
879
910
|
try {
|
|
911
|
+
if (isCriticalConfigFile(sourcePath)) {
|
|
912
|
+
return {
|
|
913
|
+
output: '',
|
|
914
|
+
error: `Anti-cheating violation: Moving or renaming critical configuration file "${sourcePath}" is strictly prohibited. You must fix all build, compiler, type, and lint errors head-on in the source code.`,
|
|
915
|
+
};
|
|
916
|
+
}
|
|
880
917
|
const absSource = resolveAndValidatePath(workspaceRoot, sourcePath);
|
|
881
918
|
const absTarget = resolveAndValidatePath(workspaceRoot, targetPath);
|
|
882
919
|
let existingContent = null;
|
|
@@ -892,6 +929,9 @@ export async function renameFile(workspaceRoot, sourcePath, targetPath) {
|
|
|
892
929
|
changeLogger.logChange(targetPath, null, 'create');
|
|
893
930
|
await fs.mkdir(path.dirname(absTarget), { recursive: true });
|
|
894
931
|
await fs.rename(absSource, absTarget);
|
|
932
|
+
invalidateFileReadCache(absSource);
|
|
933
|
+
invalidateFileReadCache(absTarget);
|
|
934
|
+
invalidateDependencyGraph(workspaceRoot);
|
|
895
935
|
return { output: `Successfully moved/renamed "${sourcePath}" to "${targetPath}".` };
|
|
896
936
|
}
|
|
897
937
|
catch (err) {
|
|
@@ -1014,10 +1054,16 @@ export async function modifyFile(workspaceRoot, filePath, edits) {
|
|
|
1014
1054
|
};
|
|
1015
1055
|
}
|
|
1016
1056
|
}
|
|
1057
|
+
const antiCheatError = detectAntiCheatingViolations(filePath, finalModified, existing);
|
|
1058
|
+
if (antiCheatError) {
|
|
1059
|
+
return { output: '', error: antiCheatError };
|
|
1060
|
+
}
|
|
1017
1061
|
changeLogger.logChange(filePath, existing, 'modify');
|
|
1018
1062
|
await atomicWriteFile(absPath, finalModified, 'utf-8');
|
|
1063
|
+
invalidateFileReadCache(absPath);
|
|
1064
|
+
invalidateDependencyGraph(workspaceRoot);
|
|
1019
1065
|
return {
|
|
1020
|
-
output: `Successfully applied ${edits.length} edit(s) to "${filePath}"
|
|
1066
|
+
output: `Successfully applied ${edits.length} edit(s) to "${filePath}".\nStrategies used:\n${strategies.join('\n')}`,
|
|
1021
1067
|
};
|
|
1022
1068
|
}
|
|
1023
1069
|
catch (err) {
|
|
@@ -1124,6 +1170,7 @@ const HIGH_SIGNAL_ERROR_PATTERNS = [
|
|
|
1124
1170
|
/undefined reference/i,
|
|
1125
1171
|
/ld: symbol\(s\) not found/i,
|
|
1126
1172
|
/clang: error:/i,
|
|
1173
|
+
/gcc: error:/i,
|
|
1127
1174
|
/referenced from:/i,
|
|
1128
1175
|
/note: expanded from macro/i,
|
|
1129
1176
|
/note: candidate:/i,
|
|
@@ -1133,29 +1180,59 @@ const HIGH_SIGNAL_ERROR_PATTERNS = [
|
|
|
1133
1180
|
/TypeError:/i,
|
|
1134
1181
|
/ImportError:/i,
|
|
1135
1182
|
/ModuleNotFoundError:/i,
|
|
1183
|
+
/AttributeError:/i,
|
|
1184
|
+
/IndexError:/i,
|
|
1185
|
+
/KeyError:/i,
|
|
1186
|
+
/ValueError:/i,
|
|
1136
1187
|
/FAILED \(failures=/i,
|
|
1137
1188
|
/\bE\s{3}\b/,
|
|
1138
1189
|
/\bFAIL\b/,
|
|
1139
|
-
|
|
1190
|
+
/\bFAILED\b/,
|
|
1191
|
+
/=== FAILURES ===/i,
|
|
1192
|
+
/=== ERRORS ===/i,
|
|
1193
|
+
// Sandbox Diagnostics & Structured Results
|
|
1194
|
+
/\[SANDBOX DIAGNOSTIC\]/i,
|
|
1195
|
+
/\[STRUCTURED RESULT\]/i,
|
|
1196
|
+
/Module resolution failure/i,
|
|
1197
|
+
/CommonJS require\(\) attempted to load an ES Module/i,
|
|
1198
|
+
// Node.js / TypeScript / Jest / Mocha / Vitest
|
|
1199
|
+
/TS\d+:/i,
|
|
1200
|
+
/TS\d{4,5}:/i,
|
|
1140
1201
|
/SyntaxError:/i,
|
|
1141
1202
|
/ReferenceError:/i,
|
|
1203
|
+
/TypeError:/i,
|
|
1204
|
+
/RangeError:/i,
|
|
1205
|
+
/URIError:/i,
|
|
1142
1206
|
/Error: Cannot find module/i,
|
|
1207
|
+
/Cannot find module/i,
|
|
1208
|
+
/Cannot find name/i,
|
|
1209
|
+
/Type '.*' is not assignable to type/i,
|
|
1210
|
+
/Property '.*' does not exist on type/i,
|
|
1143
1211
|
/\bFAIL\b/,
|
|
1144
1212
|
/✕\s/,
|
|
1145
|
-
/TS\d{4}:/,
|
|
1146
1213
|
// Rust / Cargo
|
|
1147
1214
|
/error\[E\d+\]:/i,
|
|
1148
1215
|
/fatal runtime error:/i,
|
|
1149
1216
|
/panicked at/i,
|
|
1217
|
+
/thread '.*' panicked at/i,
|
|
1218
|
+
/-->\s+.*:\d+:\d+/i,
|
|
1219
|
+
/could not compile/i,
|
|
1150
1220
|
// Go
|
|
1151
1221
|
/panic:/i,
|
|
1152
1222
|
/cannot find package/i,
|
|
1153
1223
|
/undefined:/i,
|
|
1154
1224
|
/FAIL\t/,
|
|
1155
|
-
// Java / JVM
|
|
1225
|
+
// Java / JVM / Kotlin / Scala
|
|
1156
1226
|
/Exception in thread/i,
|
|
1157
1227
|
/java\.lang\./i,
|
|
1158
1228
|
/error: cannot find symbol/i,
|
|
1229
|
+
/NullPointerException/i,
|
|
1230
|
+
/ClassNotFoundException/i,
|
|
1231
|
+
// C# / .NET
|
|
1232
|
+
/CS\d{4}:/i,
|
|
1233
|
+
/Unhandled exception/i,
|
|
1234
|
+
// Swift
|
|
1235
|
+
/error:\s+/i,
|
|
1159
1236
|
// Generic Fallbacks
|
|
1160
1237
|
/\b(cannot|unable to|not found|command not found|no such file)\b/i,
|
|
1161
1238
|
];
|
|
@@ -1170,8 +1247,77 @@ const STACK_TRACE_START_PATTERNS = [
|
|
|
1170
1247
|
/Traceback \(most recent call last\):/i,
|
|
1171
1248
|
/Exception in thread/i,
|
|
1172
1249
|
/panic:/i,
|
|
1250
|
+
/thread '.*' panicked at/i,
|
|
1251
|
+
/panicked at/i,
|
|
1173
1252
|
/Error:\s*$/i,
|
|
1253
|
+
/TypeError:\s*$/i,
|
|
1254
|
+
/ReferenceError:\s*$/i,
|
|
1255
|
+
/AssertionError:\s*$/i,
|
|
1256
|
+
/=== FAILURES ===/i,
|
|
1257
|
+
/=== ERRORS ===/i,
|
|
1174
1258
|
];
|
|
1259
|
+
/**
|
|
1260
|
+
* Truncates large string output using bounded head and tail windows with a structured metadata receipt.
|
|
1261
|
+
* Preserves early context (e.g. invocation, build targets) and late context (e.g. error summaries, exit codes)
|
|
1262
|
+
* while bounding total character and line count.
|
|
1263
|
+
*
|
|
1264
|
+
* @param text - The raw output text to truncate.
|
|
1265
|
+
* @param options - Configuration options for character/line limits and custom receipt notices.
|
|
1266
|
+
* @returns The windowed text containing head, structured metadata receipt, and tail.
|
|
1267
|
+
*/
|
|
1268
|
+
export function truncateWithHeadTailWindow(text, options) {
|
|
1269
|
+
if (!text || typeof text !== 'string')
|
|
1270
|
+
return '';
|
|
1271
|
+
const maxChars = options?.maxChars ?? 30_000;
|
|
1272
|
+
const maxLines = options?.maxLines ?? 500;
|
|
1273
|
+
const headRatio = options?.headRatio ?? 0.35;
|
|
1274
|
+
const receiptLabel = options?.receiptLabel ?? 'Output';
|
|
1275
|
+
const hint = options?.hint ?? 'Pipe command to a file and read it in chunks, or use grep.';
|
|
1276
|
+
const lines = text.split(/\r?\n/);
|
|
1277
|
+
if (text.length <= maxChars && lines.length <= maxLines) {
|
|
1278
|
+
return text;
|
|
1279
|
+
}
|
|
1280
|
+
const totalBytes = text.length;
|
|
1281
|
+
const totalLines = lines.length;
|
|
1282
|
+
// Calculate target characters for head and tail windows
|
|
1283
|
+
const receiptReserve = 500;
|
|
1284
|
+
const availableChars = Math.max(1000, maxChars - receiptReserve);
|
|
1285
|
+
const targetHeadChars = Math.floor(availableChars * headRatio);
|
|
1286
|
+
const targetTailChars = availableChars - targetHeadChars;
|
|
1287
|
+
// Max lines for head and tail
|
|
1288
|
+
const targetHeadLines = Math.max(5, Math.floor(maxLines * headRatio));
|
|
1289
|
+
const targetTailLines = Math.max(10, maxLines - targetHeadLines);
|
|
1290
|
+
// Accumulate head lines
|
|
1291
|
+
const headLines = [];
|
|
1292
|
+
let headCharsCount = 0;
|
|
1293
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1294
|
+
const line = lines[i];
|
|
1295
|
+
if (headLines.length >= targetHeadLines ||
|
|
1296
|
+
(headLines.length > 0 && headCharsCount + line.length + 1 > targetHeadChars)) {
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
headLines.push(line);
|
|
1300
|
+
headCharsCount += line.length + 1;
|
|
1301
|
+
}
|
|
1302
|
+
// Accumulate tail lines backwards (ensuring no overlap with head)
|
|
1303
|
+
const tailLines = [];
|
|
1304
|
+
let tailCharsCount = 0;
|
|
1305
|
+
for (let i = lines.length - 1; i >= headLines.length; i--) {
|
|
1306
|
+
const line = lines[i];
|
|
1307
|
+
if (tailLines.length >= targetTailLines ||
|
|
1308
|
+
(tailLines.length > 0 && tailCharsCount + line.length + 1 > targetTailChars)) {
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
tailLines.unshift(line);
|
|
1312
|
+
tailCharsCount += line.length + 1;
|
|
1313
|
+
}
|
|
1314
|
+
const omittedLines = Math.max(0, totalLines - headLines.length - tailLines.length);
|
|
1315
|
+
const headText = headLines.join('\n');
|
|
1316
|
+
const tailText = tailLines.join('\n');
|
|
1317
|
+
const omittedBytes = Math.max(0, totalBytes - headText.length - tailText.length);
|
|
1318
|
+
const receipt = `\n\n... [${receiptLabel} truncated: ${omittedLines} lines (${omittedBytes} bytes) omitted. Showing ${headLines.length} head lines and ${tailLines.length} tail lines. Total: ${totalLines} lines (${totalBytes} bytes). ${hint}] ...\n\n`;
|
|
1319
|
+
return headText + receipt + tailText;
|
|
1320
|
+
}
|
|
1175
1321
|
/**
|
|
1176
1322
|
* Condenses verbose command and test failure outputs into high-signal diagnostic error logs
|
|
1177
1323
|
* with dynamic tail sizing and stack trace boundary snapping.
|
|
@@ -1189,14 +1335,30 @@ export function extractHighSignalError(rawError) {
|
|
|
1189
1335
|
}
|
|
1190
1336
|
// Filter out noise lines (e.g. repetitive compiler warning flags)
|
|
1191
1337
|
const nonNoiseLines = lines.filter((l) => !COMPILER_WARNING_NOISE_PATTERN.test(l));
|
|
1192
|
-
// Find high-signal error lines
|
|
1338
|
+
// Find high-signal error lines and associated compiler frames/context lines
|
|
1193
1339
|
const highSignalLines = [];
|
|
1194
1340
|
const highSignalIndices = new Set();
|
|
1195
1341
|
for (let i = 0; i < nonNoiseLines.length; i++) {
|
|
1196
1342
|
const line = nonNoiseLines[i];
|
|
1197
1343
|
if (HIGH_SIGNAL_ERROR_PATTERNS.some((p) => p.test(line))) {
|
|
1198
|
-
|
|
1199
|
-
|
|
1344
|
+
if (!highSignalIndices.has(i)) {
|
|
1345
|
+
highSignalLines.push(line);
|
|
1346
|
+
highSignalIndices.add(i);
|
|
1347
|
+
}
|
|
1348
|
+
// Capture up to 5 immediate compiler frame continuation lines (e.g. code squiggles, expected/found, arrows)
|
|
1349
|
+
for (let j = 1; j <= 5 && i + j < nonNoiseLines.length; j++) {
|
|
1350
|
+
const nextLine = nonNoiseLines[i + j];
|
|
1351
|
+
if (/^\s*(\||-->|\^|~|expected|found|note:|candidate:)/i.test(nextLine) ||
|
|
1352
|
+
/^\s*\d+\s*\|/.test(nextLine)) {
|
|
1353
|
+
if (!highSignalIndices.has(i + j)) {
|
|
1354
|
+
highSignalLines.push(nextLine);
|
|
1355
|
+
highSignalIndices.add(i + j);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
else {
|
|
1359
|
+
break;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1200
1362
|
}
|
|
1201
1363
|
}
|
|
1202
1364
|
// Dynamic tail sizing: Math.min(50, Math.max(15, Math.floor(totalLines * 0.15)))
|
|
@@ -1233,7 +1395,15 @@ export function extractHighSignalError(rawError) {
|
|
|
1233
1395
|
else {
|
|
1234
1396
|
resultBlocks.push(tailLines.join('\n'));
|
|
1235
1397
|
}
|
|
1236
|
-
|
|
1398
|
+
let condensedResult = resultBlocks.join('\n\n');
|
|
1399
|
+
// Bounded head/tail window check on condensed result if still oversized
|
|
1400
|
+
if (condensedResult.length > 30_000) {
|
|
1401
|
+
condensedResult = truncateWithHeadTailWindow(condensedResult, {
|
|
1402
|
+
maxChars: 30_000,
|
|
1403
|
+
receiptLabel: 'Diagnostic output',
|
|
1404
|
+
hint: 'Pipe to a file if you need full logs.',
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1237
1407
|
const condensedLines = condensedResult.split(/\r?\n/).length;
|
|
1238
1408
|
const linesPruned = Math.max(0, lines.length - condensedLines);
|
|
1239
1409
|
const charsPruned = Math.max(0, rawError.length - condensedResult.length);
|
|
@@ -1257,26 +1427,31 @@ export async function runCommand(workspaceRoot, command, abortSignal) {
|
|
|
1257
1427
|
maxBuffer: 1024 * 1024 * 2, // 2 MB buffer
|
|
1258
1428
|
signal: abortSignal,
|
|
1259
1429
|
});
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1430
|
+
// Invalidate caches as shell command may have modified the workspace filesystem
|
|
1431
|
+
clearFileReadCache();
|
|
1432
|
+
invalidateDependencyGraph(workspaceRoot);
|
|
1433
|
+
const output = [stdout, stderr].filter(Boolean).join('\n');
|
|
1434
|
+
// Bounded head/tail window truncation with structured metadata receipt
|
|
1435
|
+
const windowedOutput = truncateWithHeadTailWindow(output, {
|
|
1436
|
+
maxChars: 30_000,
|
|
1437
|
+
receiptLabel: 'Output',
|
|
1438
|
+
hint: 'To view the rest, pipe the command to a file and read it in chunks, or use grep.',
|
|
1439
|
+
});
|
|
1440
|
+
return { output: windowedOutput || '(command produced no output)' };
|
|
1269
1441
|
}
|
|
1270
1442
|
catch (err) {
|
|
1443
|
+
// Also invalidate in case of partial command execution or errors that wrote files
|
|
1444
|
+
clearFileReadCache();
|
|
1445
|
+
invalidateDependencyGraph(workspaceRoot);
|
|
1271
1446
|
const message = err instanceof Error ? err.message : String(err);
|
|
1272
1447
|
// Condense error output with high-signal extraction and dynamic tail sizing
|
|
1273
1448
|
const condensedError = extractHighSignalError(message);
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
return { output: '', error: `Command failed: ${
|
|
1449
|
+
const windowedError = truncateWithHeadTailWindow(condensedError, {
|
|
1450
|
+
maxChars: 30_000,
|
|
1451
|
+
receiptLabel: 'Error output',
|
|
1452
|
+
hint: 'Pipe to a file if you need full logs.',
|
|
1453
|
+
});
|
|
1454
|
+
return { output: '', error: `Command failed: ${windowedError}` };
|
|
1280
1455
|
}
|
|
1281
1456
|
}
|
|
1282
1457
|
/**
|
|
@@ -1305,14 +1480,18 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, fixedStrings,
|
|
|
1305
1480
|
}
|
|
1306
1481
|
// Use ignore package to evaluate path globs (e.g. src/**/*.ts)
|
|
1307
1482
|
const globMatcher = fileGlob ? ignore().add(fileGlob) : null;
|
|
1483
|
+
const MAX_SHOWN_MATCHES = 50;
|
|
1484
|
+
const MAX_SCAN_MATCHES = 500;
|
|
1308
1485
|
const results = [];
|
|
1486
|
+
let totalMatches = 0;
|
|
1487
|
+
const matchedFiles = new Set();
|
|
1309
1488
|
/**
|
|
1310
1489
|
* Recursively walks directory entries to search for pattern matches.
|
|
1311
1490
|
*
|
|
1312
1491
|
* @param dir - Absolute path to the current directory being searched.
|
|
1313
1492
|
*/
|
|
1314
1493
|
async function walk(dir) {
|
|
1315
|
-
if (
|
|
1494
|
+
if (totalMatches >= MAX_SCAN_MATCHES)
|
|
1316
1495
|
return;
|
|
1317
1496
|
if (abortSignal?.aborted)
|
|
1318
1497
|
return;
|
|
@@ -1324,7 +1503,7 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, fixedStrings,
|
|
|
1324
1503
|
return;
|
|
1325
1504
|
}
|
|
1326
1505
|
for (const entry of entries) {
|
|
1327
|
-
if (
|
|
1506
|
+
if (totalMatches >= MAX_SCAN_MATCHES)
|
|
1328
1507
|
return;
|
|
1329
1508
|
if (abortSignal?.aborted)
|
|
1330
1509
|
return;
|
|
@@ -1351,12 +1530,16 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, fixedStrings,
|
|
|
1351
1530
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
1352
1531
|
const lines = content.split(/\r?\n/);
|
|
1353
1532
|
for (let i = 0; i < lines.length; i++) {
|
|
1354
|
-
if (
|
|
1533
|
+
if (totalMatches >= MAX_SCAN_MATCHES)
|
|
1355
1534
|
break;
|
|
1356
1535
|
regexPattern.lastIndex = 0;
|
|
1357
1536
|
if (regexPattern.test(lines[i])) {
|
|
1358
|
-
|
|
1359
|
-
|
|
1537
|
+
totalMatches++;
|
|
1538
|
+
matchedFiles.add(relPath);
|
|
1539
|
+
if (results.length < MAX_SHOWN_MATCHES) {
|
|
1540
|
+
const relativePath = path.relative(workspaceRoot, filePath).replace(/\\/g, '/');
|
|
1541
|
+
results.push(`${relativePath}:${i + 1}:${lines[i]}`);
|
|
1542
|
+
}
|
|
1360
1543
|
}
|
|
1361
1544
|
}
|
|
1362
1545
|
}
|
|
@@ -1373,8 +1556,18 @@ export async function grepSearch(workspaceRoot, pattern, fileGlob, fixedStrings,
|
|
|
1373
1556
|
}
|
|
1374
1557
|
if (results.length === 0)
|
|
1375
1558
|
return { output: `No matches found for "${pattern}".` };
|
|
1376
|
-
|
|
1377
|
-
|
|
1559
|
+
let resultText = results.join('\n');
|
|
1560
|
+
if (totalMatches > results.length) {
|
|
1561
|
+
const moreSuffix = totalMatches >= MAX_SCAN_MATCHES ? '+' : '';
|
|
1562
|
+
resultText += `\n\n[Showing ${results.length}/${totalMatches}${moreSuffix} matches across ${matchedFiles.size} files. Refine query with fileGlob or dirPath to narrow results.]`;
|
|
1563
|
+
}
|
|
1564
|
+
// Window large grep text if character count is huge
|
|
1565
|
+
const windowedResult = truncateWithHeadTailWindow(resultText, {
|
|
1566
|
+
maxChars: 30_000,
|
|
1567
|
+
receiptLabel: 'Grep search results',
|
|
1568
|
+
hint: 'Refine query with fileGlob or dirPath to narrow results.',
|
|
1569
|
+
});
|
|
1570
|
+
const wrappedResult = `<workspace_file path="grep_search_results">\n<content_data><![CDATA[\n${sanitizeForCDATA(windowedResult)}\n]]\\u200B></content_data>\n</workspace_file>`;
|
|
1378
1571
|
return { output: wrappedResult };
|
|
1379
1572
|
}
|
|
1380
1573
|
catch (err) {
|
|
@@ -1513,6 +1706,12 @@ export async function runDebugScript(workspaceRoot, language, code, abortSignal)
|
|
|
1513
1706
|
if (res.exitCode !== 0) {
|
|
1514
1707
|
finalOutput += `Script failed with exit code ${res.exitCode}.\n`;
|
|
1515
1708
|
}
|
|
1709
|
+
if (res.structuredResult !== undefined) {
|
|
1710
|
+
const formattedStructured = typeof res.structuredResult === 'string'
|
|
1711
|
+
? res.structuredResult
|
|
1712
|
+
: JSON.stringify(res.structuredResult, null, 2);
|
|
1713
|
+
finalOutput += `[STRUCTURED RESULT]\n${formattedStructured}\n`;
|
|
1714
|
+
}
|
|
1516
1715
|
if (res.stdout) {
|
|
1517
1716
|
finalOutput += `[STDOUT]\n${res.stdout}\n`;
|
|
1518
1717
|
}
|
|
@@ -1523,11 +1722,17 @@ export async function runDebugScript(workspaceRoot, language, code, abortSignal)
|
|
|
1523
1722
|
finalOutput = 'Script executed successfully with no output.';
|
|
1524
1723
|
}
|
|
1525
1724
|
const condensedOutput = extractHighSignalError(finalOutput.trim());
|
|
1725
|
+
const windowedOutput = truncateWithHeadTailWindow(condensedOutput, {
|
|
1726
|
+
maxChars: 30_000,
|
|
1727
|
+
receiptLabel: 'Debug script output',
|
|
1728
|
+
hint: 'Inspect specific variables or refine script if full logs are needed.',
|
|
1729
|
+
});
|
|
1526
1730
|
return {
|
|
1527
|
-
output: `<test_results>\n${sanitizeForCDATA(
|
|
1731
|
+
output: `<test_results>\n${sanitizeForCDATA(windowedOutput)}\n</test_results>`,
|
|
1732
|
+
...(res.structuredResult !== undefined ? { structuredResult: res.structuredResult } : {}),
|
|
1528
1733
|
...(res.exitCode !== 0
|
|
1529
1734
|
? {
|
|
1530
|
-
error: `Script failed with exit code ${res.exitCode}:\n${extractHighSignalError(res.stderr || res.stdout || '')}`,
|
|
1735
|
+
error: `Script failed with exit code ${res.exitCode}:\n${truncateWithHeadTailWindow(extractHighSignalError(res.stderr || res.stdout || ''), { maxChars: 30_000, receiptLabel: 'Debug script error' })}`,
|
|
1531
1736
|
}
|
|
1532
1737
|
: {}),
|
|
1533
1738
|
};
|
|
@@ -1573,6 +1778,7 @@ export async function executeFuzzProbe(workspaceRoot, code, language = 'node', c
|
|
|
1573
1778
|
output += `\n[STDERR]\n${res.stderr}\n`;
|
|
1574
1779
|
return {
|
|
1575
1780
|
output: output.trim(),
|
|
1781
|
+
...(res.structuredResult !== undefined ? { structuredResult: res.structuredResult } : {}),
|
|
1576
1782
|
...(res.exitCode !== 0 && !res.passed ? { error: `Fuzz probe failed with exit code ${res.exitCode}` } : {}),
|
|
1577
1783
|
};
|
|
1578
1784
|
}
|
|
@@ -1616,6 +1822,7 @@ export async function executeHeapDelta(workspaceRoot, code, language = 'node', c
|
|
|
1616
1822
|
output += `\n[STDERR]\n${res.stderr}\n`;
|
|
1617
1823
|
return {
|
|
1618
1824
|
output: output.trim(),
|
|
1825
|
+
...(res.structuredResult !== undefined ? { structuredResult: res.structuredResult } : {}),
|
|
1619
1826
|
...(res.exitCode !== 0 ? { error: `Heap delta failed with exit code ${res.exitCode}` } : {}),
|
|
1620
1827
|
};
|
|
1621
1828
|
}
|
|
@@ -1661,6 +1868,7 @@ export async function executeBehavioralDrift(workspaceRoot, baselineCode, candid
|
|
|
1661
1868
|
output += `\n[STDERR]\n${res.stderr}\n`;
|
|
1662
1869
|
return {
|
|
1663
1870
|
output: output.trim(),
|
|
1871
|
+
...(res.structuredResult !== undefined ? { structuredResult: res.structuredResult } : {}),
|
|
1664
1872
|
...(res.exitCode !== 0 ? { error: `Behavioral drift check failed with exit code ${res.exitCode}` } : {}),
|
|
1665
1873
|
};
|
|
1666
1874
|
}
|
|
@@ -1727,6 +1935,9 @@ function resolveWorkspaceArgs(primaryRoot, toolName, args) {
|
|
|
1727
1935
|
async function crossWorkspaceGrep(primaryRoot, pattern, fileGlob, fixedStrings, dirPath, abortSignal) {
|
|
1728
1936
|
const allRoots = workspaceRegistry.getAllRoots(primaryRoot);
|
|
1729
1937
|
const allResults = [];
|
|
1938
|
+
let totalMatches = 0;
|
|
1939
|
+
const matchedFiles = new Set();
|
|
1940
|
+
const MAX_SHOWN_MATCHES = 80;
|
|
1730
1941
|
for (const { alias, root } of allRoots) {
|
|
1731
1942
|
const result = await grepSearch(root, pattern, fileGlob, fixedStrings, dirPath, abortSignal);
|
|
1732
1943
|
if (result.error)
|
|
@@ -1740,10 +1951,14 @@ async function crossWorkspaceGrep(primaryRoot, pattern, fileGlob, fixedStrings,
|
|
|
1740
1951
|
const prefix = alias ? `@${alias}/` : '';
|
|
1741
1952
|
const lines = rawText.split('\n').filter((l) => l.trim());
|
|
1742
1953
|
for (const line of lines) {
|
|
1743
|
-
if (line.startsWith('...')) {
|
|
1744
|
-
|
|
1954
|
+
if (line.startsWith('...') || line.startsWith('[')) {
|
|
1955
|
+
continue;
|
|
1745
1956
|
}
|
|
1746
|
-
|
|
1957
|
+
totalMatches++;
|
|
1958
|
+
const matchFile = line.split(':')[0];
|
|
1959
|
+
if (matchFile)
|
|
1960
|
+
matchedFiles.add(prefix ? `${prefix}${matchFile}` : matchFile);
|
|
1961
|
+
if (allResults.length < MAX_SHOWN_MATCHES) {
|
|
1747
1962
|
// Lines are in format "./path:lineNum:content" — prepend alias prefix
|
|
1748
1963
|
allResults.push(prefix ? line.replace(/^\.?\//, `@${alias}/`) : line);
|
|
1749
1964
|
}
|
|
@@ -1752,9 +1967,16 @@ async function crossWorkspaceGrep(primaryRoot, pattern, fileGlob, fixedStrings,
|
|
|
1752
1967
|
if (allResults.length === 0) {
|
|
1753
1968
|
return { output: `No matches found for "${pattern}" across all workspaces.` };
|
|
1754
1969
|
}
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1970
|
+
let resultText = allResults.join('\n');
|
|
1971
|
+
if (totalMatches > allResults.length) {
|
|
1972
|
+
resultText += `\n\n[Showing ${allResults.length}/${totalMatches} matches across ${matchedFiles.size} files in registered workspaces. Refine query to narrow results.]`;
|
|
1973
|
+
}
|
|
1974
|
+
const windowedResult = truncateWithHeadTailWindow(resultText, {
|
|
1975
|
+
maxChars: 30_000,
|
|
1976
|
+
receiptLabel: 'Cross-workspace grep results',
|
|
1977
|
+
hint: 'Refine query to narrow results.',
|
|
1978
|
+
});
|
|
1979
|
+
const wrappedResult = `<workspace_file path="grep_search_results">\n<content_data><![CDATA[\n${sanitizeForCDATA(windowedResult)}\n]]\\u200B></content_data>\n</workspace_file>`;
|
|
1758
1980
|
return { output: wrappedResult };
|
|
1759
1981
|
}
|
|
1760
1982
|
const currentTasksByAgent = new Map();
|
|
@@ -1946,7 +2168,10 @@ export async function executeTool(workspaceRoot, toolName, args, abortSignal) {
|
|
|
1946
2168
|
result = await runCommand(effectiveRoot, resolvedArgs.command, abortSignal);
|
|
1947
2169
|
break;
|
|
1948
2170
|
case 'run_debug_script':
|
|
1949
|
-
|
|
2171
|
+
case 'debug_script':
|
|
2172
|
+
case 'run_analysis_script':
|
|
2173
|
+
case 'analysis_script':
|
|
2174
|
+
result = await runDebugScript(effectiveRoot, resolvedArgs.language || 'auto', resolvedArgs.code, abortSignal);
|
|
1950
2175
|
break;
|
|
1951
2176
|
case 'grep_search': {
|
|
1952
2177
|
const wsParam = resolvedArgs.workspace;
|