minovative-mind-cli 2.10.0 → 2.11.2
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 +1 -0
- package/dist/commands/chat.d.ts +1 -1
- package/dist/commands/chat.js +2 -1
- package/dist/services/agent/commandApproval.js +5 -2
- package/dist/services/agent/slashCommands.js +213 -51
- package/dist/services/agent-tools.d.ts +4 -3
- package/dist/services/agent-tools.js +32 -79
- package/dist/services/agent.d.ts +5 -6
- package/dist/services/agent.js +38 -15
- package/dist/services/ai.d.ts +25 -0
- package/dist/services/ai.js +253 -2
- package/dist/services/chatHistoryService.d.ts +95 -2
- package/dist/services/chatHistoryService.js +236 -9
- package/dist/services/contextAgent.js +184 -89
- package/dist/services/orchestration/investigationAgent.js +100 -84
- package/dist/services/orchestration/investigationOrchestrator.js +6 -2
- package/dist/services/orchestration/orchestrator.js +6 -3
- package/dist/services/orchestration/scopedTools.js +5 -0
- package/dist/services/orchestration/subAgent.d.ts +31 -1
- package/dist/services/orchestration/subAgent.js +153 -2
- package/dist/services/userProfileService.d.ts +97 -0
- package/dist/services/userProfileService.js +410 -0
- package/dist/utils/analysisRunner.d.ts +29 -0
- package/dist/utils/analysisRunner.js +200 -5
- package/dist/utils/contextPrompts.d.ts +19 -3
- package/dist/utils/contextPrompts.js +144 -26
- package/dist/utils/historyPrompt.d.ts +92 -1
- package/dist/utils/historyPrompt.js +166 -2
- package/dist/utils/symbolExtractor.d.ts +12 -0
- package/dist/utils/symbolExtractor.js +946 -0
- package/dist/utils/systemPrompts.d.ts +6 -4
- package/dist/utils/systemPrompts.js +77 -9
- package/oclif.manifest.json +2 -2
- package/package.json +1 -1
|
@@ -9,7 +9,48 @@ import { debugLog } from '../utils/logger.js';
|
|
|
9
9
|
import { buildDependencyGraph } from '../utils/dependencyTracer.js';
|
|
10
10
|
import { runEphemeralScript } from '../utils/analysisRunner.js';
|
|
11
11
|
import { getMetricCollector } from './metrics.js';
|
|
12
|
+
import { estimateTokenCount, pruneTextToTokenBudget } from '../utils/historyPrompt.js';
|
|
12
13
|
const metrics = getMetricCollector();
|
|
14
|
+
/** Token budget constants for context optimization */
|
|
15
|
+
const MAX_TREE_TOKENS = 12000;
|
|
16
|
+
const MAX_CHAT_HISTORY_ROUTER_TOKENS = 3000;
|
|
17
|
+
const MAX_CHAT_HISTORY_INVESTIGATION_TOKENS = 4500;
|
|
18
|
+
const MAX_SINGLE_FILE_TOKENS = 25000;
|
|
19
|
+
const MAX_TOTAL_FILE_TOKENS = 150000;
|
|
20
|
+
const MAX_TOTAL_FILES = 30;
|
|
21
|
+
const MAX_TOOL_OUTPUT_TOKENS = 10000;
|
|
22
|
+
/**
|
|
23
|
+
* Bounds individual file content to ensure it does not exceed the single-file token budget.
|
|
24
|
+
*
|
|
25
|
+
* @param filePath The file path.
|
|
26
|
+
* @param content Raw file text.
|
|
27
|
+
* @returns Budget-constrained file text.
|
|
28
|
+
*/
|
|
29
|
+
function boundFileContent(filePath, content) {
|
|
30
|
+
const tokens = estimateTokenCount(content);
|
|
31
|
+
if (tokens <= MAX_SINGLE_FILE_TOKENS) {
|
|
32
|
+
return content;
|
|
33
|
+
}
|
|
34
|
+
debugLog(`Truncating large file context for ${filePath}: ${tokens} tokens -> capped at ${MAX_SINGLE_FILE_TOKENS}`);
|
|
35
|
+
return pruneTextToTokenBudget(content, MAX_SINGLE_FILE_TOKENS, {
|
|
36
|
+
truncationMarker: `\n\n... [File content truncated by Context Agent: ${tokens} estimated tokens exceeded the ${MAX_SINGLE_FILE_TOKENS} token limit. Use read_file with line ranges if specific sections are needed.] ...\n\n`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Bounds tool output text to prevent massive outputs from blowing the context window during investigation turns.
|
|
41
|
+
*
|
|
42
|
+
* @param output Raw tool output string.
|
|
43
|
+
* @returns Budget-constrained tool output.
|
|
44
|
+
*/
|
|
45
|
+
function boundToolOutput(output) {
|
|
46
|
+
const tokens = estimateTokenCount(output);
|
|
47
|
+
if (tokens <= MAX_TOOL_OUTPUT_TOKENS) {
|
|
48
|
+
return output;
|
|
49
|
+
}
|
|
50
|
+
return pruneTextToTokenBudget(output, MAX_TOOL_OUTPUT_TOKENS, {
|
|
51
|
+
truncationMarker: `\n\n... [Tool output truncated: exceeded ${MAX_TOOL_OUTPUT_TOKENS} tokens limit] ...\n`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
13
54
|
async function detectProjectType(workspaceRoot) {
|
|
14
55
|
const types = [];
|
|
15
56
|
const fileExists = async (fileName) => {
|
|
@@ -149,7 +190,8 @@ export async function routeIntent(userRequest, chatHistory = '', abortSignal) {
|
|
|
149
190
|
const session = createIntentRouterSession();
|
|
150
191
|
let prompt = `User Request: "${userRequest}"`;
|
|
151
192
|
if (chatHistory) {
|
|
152
|
-
|
|
193
|
+
const prunedHistory = pruneTextToTokenBudget(chatHistory, MAX_CHAT_HISTORY_ROUTER_TOKENS, { fromStart: true });
|
|
194
|
+
prompt = `Previous Conversation Context:\n${prunedHistory}\n\n${prompt}`;
|
|
153
195
|
}
|
|
154
196
|
const result = await session.sendMessage(prompt, undefined, abortSignal);
|
|
155
197
|
const text = result.response.text()?.trim() || '{}';
|
|
@@ -183,7 +225,8 @@ export async function evaluateExecutionComplexity(userRequest, investigationSumm
|
|
|
183
225
|
Investigation Summary: ${investigationSummary || 'None (0 files needed)'}
|
|
184
226
|
Number of Relevant Files: ${numRelevantFiles}`;
|
|
185
227
|
if (chatHistory) {
|
|
186
|
-
|
|
228
|
+
const prunedHistory = pruneTextToTokenBudget(chatHistory, MAX_CHAT_HISTORY_ROUTER_TOKENS, { fromStart: true });
|
|
229
|
+
prompt = `Previous Conversation Context:\n${prunedHistory}\n\n${prompt}`;
|
|
187
230
|
}
|
|
188
231
|
const result = await session.sendMessage(prompt, undefined, abortSignal);
|
|
189
232
|
const text = result.response.text()?.trim() || '{}';
|
|
@@ -235,8 +278,11 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
235
278
|
const label = alias ? `@${alias} (${root})` : `Primary Workspace (${root})`;
|
|
236
279
|
const treeResult = await executeTool(root, 'list_directory', { dirPath: '.', maxDepth: 10 });
|
|
237
280
|
let tree = treeResult.output;
|
|
238
|
-
|
|
239
|
-
|
|
281
|
+
const treeTokens = estimateTokenCount(tree);
|
|
282
|
+
if (treeTokens > MAX_TREE_TOKENS) {
|
|
283
|
+
tree = pruneTextToTokenBudget(tree, MAX_TREE_TOKENS, {
|
|
284
|
+
truncationMarker: '\n... (Project tree truncated to fit token budget)\n',
|
|
285
|
+
});
|
|
240
286
|
}
|
|
241
287
|
const type = await detectProjectType(root);
|
|
242
288
|
if (!alias) {
|
|
@@ -275,20 +321,27 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
275
321
|
if (onProgress)
|
|
276
322
|
onProgress(`⚡ Memory Bank HIT — loaded ${cacheHit.entry.relevantFiles.length} files from cache`);
|
|
277
323
|
const cachedFiles = new Map();
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
throw err;
|
|
283
|
-
}
|
|
324
|
+
let cumulativeFileTokens = 0;
|
|
325
|
+
const readResults = await Promise.all(cacheHit.entry.relevantFiles.map(async (filePath) => {
|
|
326
|
+
if (abortSignal.aborted)
|
|
327
|
+
return { filePath, error: 'aborted' };
|
|
284
328
|
const readResult = await executeTool(workspaceRoot, 'read_file', { filePath });
|
|
285
|
-
|
|
286
|
-
|
|
329
|
+
return { filePath, readResult };
|
|
330
|
+
}));
|
|
331
|
+
for (const item of readResults) {
|
|
332
|
+
if (item.readResult && !item.readResult.error) {
|
|
333
|
+
const boundedText = boundFileContent(item.filePath, item.readResult.output);
|
|
334
|
+
const tokens = estimateTokenCount(boundedText);
|
|
335
|
+
if (cumulativeFileTokens + tokens <= MAX_TOTAL_FILE_TOKENS) {
|
|
336
|
+
cachedFiles.set(item.filePath, { text: boundedText, inlineData: item.readResult.inlineData });
|
|
337
|
+
cumulativeFileTokens += tokens;
|
|
338
|
+
}
|
|
287
339
|
}
|
|
288
340
|
}
|
|
289
341
|
const MAX_TOTAL_FILES = 30;
|
|
290
342
|
try {
|
|
291
343
|
const { resolveAndValidateMultiWorkspacePath } = await import('../utils/pathSecurity.js');
|
|
344
|
+
const depsToRead = [];
|
|
292
345
|
const autoDiscovered = new Set();
|
|
293
346
|
for (const filePath of cacheHit.entry.relevantFiles) {
|
|
294
347
|
if (abortSignal.aborted)
|
|
@@ -298,21 +351,34 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
298
351
|
const graph = await buildDependencyGraph(resolved.workspaceRoot);
|
|
299
352
|
const reverseDeps = graph.getImportedBy(resolved.relativePath);
|
|
300
353
|
for (const dep of reverseDeps) {
|
|
301
|
-
if (abortSignal.aborted)
|
|
302
|
-
break;
|
|
303
354
|
if (cachedFiles.has(dep) || autoDiscovered.has(dep))
|
|
304
355
|
continue;
|
|
305
356
|
if (cachedFiles.size + autoDiscovered.size >= MAX_TOTAL_FILES)
|
|
306
357
|
break;
|
|
307
358
|
autoDiscovered.add(dep);
|
|
308
|
-
|
|
309
|
-
if (!depReadRes.error) {
|
|
310
|
-
cachedFiles.set(dep, { text: depReadRes.output, inlineData: depReadRes.inlineData });
|
|
311
|
-
}
|
|
359
|
+
depsToRead.push({ dep, resolvedWorkspace: resolved.workspaceRoot });
|
|
312
360
|
}
|
|
313
361
|
}
|
|
314
362
|
catch (e) { }
|
|
315
363
|
}
|
|
364
|
+
if (depsToRead.length > 0) {
|
|
365
|
+
const depResults = await Promise.all(depsToRead.map(async ({ dep, resolvedWorkspace }) => {
|
|
366
|
+
if (abortSignal.aborted)
|
|
367
|
+
return { dep, error: 'aborted' };
|
|
368
|
+
const depReadRes = await executeTool(resolvedWorkspace, 'read_file', { filePath: dep });
|
|
369
|
+
return { dep, depReadRes };
|
|
370
|
+
}));
|
|
371
|
+
for (const item of depResults) {
|
|
372
|
+
if (item.depReadRes && !item.depReadRes.error) {
|
|
373
|
+
const boundedText = boundFileContent(item.dep, item.depReadRes.output);
|
|
374
|
+
const tokens = estimateTokenCount(boundedText);
|
|
375
|
+
if (cumulativeFileTokens + tokens <= MAX_TOTAL_FILE_TOKENS) {
|
|
376
|
+
cachedFiles.set(item.dep, { text: boundedText, inlineData: item.depReadRes.inlineData });
|
|
377
|
+
cumulativeFileTokens += tokens;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
316
382
|
}
|
|
317
383
|
catch (e) { }
|
|
318
384
|
const { accumulateUsage } = await import('./metrics.js');
|
|
@@ -352,10 +418,12 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
352
418
|
let isInvestigationFinished = false;
|
|
353
419
|
let chainedMessages = [];
|
|
354
420
|
let webSearchSummary = '';
|
|
355
|
-
|
|
421
|
+
let cumulativeFileTokens = 0;
|
|
422
|
+
// Initial prompt with history budget management
|
|
356
423
|
let currentMessage = `User Request: "${userRequest}"\n\nProject Type: ${projectType}\n\nProject Structure:\n${projectTree}`;
|
|
357
424
|
if (chatHistory) {
|
|
358
|
-
|
|
425
|
+
const prunedHistory = pruneTextToTokenBudget(chatHistory, MAX_CHAT_HISTORY_INVESTIGATION_TOKENS, { fromStart: true });
|
|
426
|
+
currentMessage = `Previous Conversation Context:\n${prunedHistory}\n\n` + currentMessage;
|
|
359
427
|
}
|
|
360
428
|
currentMessage += `\n\nStart investigating to find relevant files.`;
|
|
361
429
|
const MAX_TURNS = Infinity;
|
|
@@ -403,11 +471,11 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
403
471
|
break;
|
|
404
472
|
}
|
|
405
473
|
let isFinished = false;
|
|
406
|
-
|
|
474
|
+
// Log progress for all requested tool calls in this turn
|
|
407
475
|
for (const call of functionCalls) {
|
|
408
476
|
if (abortSignal.aborted)
|
|
409
477
|
break;
|
|
410
|
-
const args = call.args;
|
|
478
|
+
const args = (call.args || {});
|
|
411
479
|
let logMsg = ` [Context Agent] Executing ${call.name}`;
|
|
412
480
|
if (call.name === 'finish_investigation') {
|
|
413
481
|
const filesToRead = args.relevantFiles || [];
|
|
@@ -434,69 +502,84 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
434
502
|
logMsg = ` [Context Agent] Looking for recently modified files`;
|
|
435
503
|
}
|
|
436
504
|
else if (call.name === 'run_analysis_script') {
|
|
437
|
-
|
|
438
|
-
logMsg = ` [Context Agent] Running analysis script${target}`;
|
|
439
|
-
}
|
|
440
|
-
const cleanMsg = logMsg.trim().replace(/^\[Context Agent\] /, '');
|
|
441
|
-
if (onToolCall) {
|
|
442
|
-
onToolCall(cleanMsg, 'Context Agent');
|
|
505
|
+
logMsg = ` [Context Agent] Running analysis script (${args.language})`;
|
|
443
506
|
}
|
|
444
|
-
|
|
445
|
-
onProgress(
|
|
507
|
+
if (onProgress) {
|
|
508
|
+
onProgress(logMsg.trim());
|
|
446
509
|
}
|
|
447
510
|
else {
|
|
448
511
|
console.log(pc.dim(logMsg));
|
|
449
512
|
}
|
|
513
|
+
}
|
|
514
|
+
// Execute tool calls concurrently while preserving 1:1 Gemini response positional ordering
|
|
515
|
+
const functionResponses = await Promise.all(functionCalls.map(async (call) => {
|
|
516
|
+
if (abortSignal.aborted) {
|
|
517
|
+
return {
|
|
518
|
+
functionResponse: {
|
|
519
|
+
name: call.name,
|
|
520
|
+
response: { error: 'Operation aborted.' },
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
const args = (call.args || {});
|
|
450
525
|
if (call.name === 'finish_investigation') {
|
|
526
|
+
isInvestigationFinished = true;
|
|
527
|
+
isFinished = true;
|
|
451
528
|
summary = args.summary || '';
|
|
452
529
|
const filesToRead = args.relevantFiles || [];
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
530
|
+
const uniqueFilesToRead = filesToRead.filter((fp) => !relevantFiles.has(fp));
|
|
531
|
+
const readResults = await Promise.all(uniqueFilesToRead.map(async (filePath) => {
|
|
532
|
+
const readResult = await executeTool(workspaceRoot, 'read_file', { filePath });
|
|
533
|
+
return { filePath, readResult };
|
|
534
|
+
}));
|
|
535
|
+
for (const { filePath, readResult } of readResults) {
|
|
536
|
+
if (cumulativeFileTokens >= MAX_TOTAL_FILE_TOKENS)
|
|
537
|
+
break;
|
|
538
|
+
if (!readResult.error) {
|
|
539
|
+
const boundedText = boundFileContent(filePath, readResult.output);
|
|
540
|
+
relevantFiles.set(filePath, { text: boundedText, inlineData: readResult.inlineData });
|
|
541
|
+
cumulativeFileTokens += estimateTokenCount(boundedText);
|
|
460
542
|
}
|
|
461
543
|
}
|
|
462
|
-
|
|
463
|
-
isInvestigationFinished = true;
|
|
464
|
-
// ── Auto-trace reverse dependencies ──
|
|
465
|
-
// When the Context Agent finalizes its investigation, we automatically
|
|
466
|
-
// discover files that DEPEND ON the selected files. This ensures the
|
|
467
|
-
// Execution Agent won't break imports when modifying/deleting/renaming.
|
|
468
|
-
const MAX_TOTAL_FILES = 30;
|
|
544
|
+
// Auto-trace reverse dependencies for all selected files to include active importers
|
|
469
545
|
try {
|
|
546
|
+
const { buildDependencyGraph } = await import('../utils/dependencyTracer.js');
|
|
470
547
|
const { resolveAndValidateMultiWorkspacePath } = await import('../utils/pathSecurity.js');
|
|
471
548
|
const autoDiscovered = new Set();
|
|
472
|
-
|
|
549
|
+
await Promise.all(filesToRead.map(async (filePath) => {
|
|
473
550
|
try {
|
|
474
551
|
const resolved = resolveAndValidateMultiWorkspacePath(workspaceRoot, filePath);
|
|
475
552
|
const graph = await buildDependencyGraph(resolved.workspaceRoot);
|
|
476
553
|
const reverseDeps = graph.getImportedBy(resolved.relativePath);
|
|
477
554
|
for (const dep of reverseDeps) {
|
|
478
555
|
const aliasedDep = resolved.alias ? `@${resolved.alias}/${dep}` : dep;
|
|
479
|
-
if (!filesToRead.includes(aliasedDep)
|
|
556
|
+
if (!filesToRead.includes(aliasedDep)) {
|
|
480
557
|
autoDiscovered.add(aliasedDep);
|
|
481
558
|
}
|
|
482
559
|
}
|
|
483
560
|
}
|
|
484
|
-
catch
|
|
561
|
+
catch {
|
|
485
562
|
// Ignore path resolution errors for trace dependencies
|
|
486
563
|
}
|
|
487
|
-
}
|
|
488
|
-
// Merge auto-discovered dependents, respecting
|
|
564
|
+
}));
|
|
565
|
+
// Merge auto-discovered dependents, respecting both file count and token budget caps
|
|
489
566
|
const remaining = MAX_TOTAL_FILES - relevantFiles.size;
|
|
567
|
+
const depsToRead = Array.from(autoDiscovered)
|
|
568
|
+
.filter((dep) => !relevantFiles.has(dep))
|
|
569
|
+
.slice(0, Math.max(0, remaining));
|
|
570
|
+
const depReadResults = await Promise.all(depsToRead.map(async (dep) => {
|
|
571
|
+
const readResult = await executeTool(workspaceRoot, 'read_file', { filePath: dep });
|
|
572
|
+
return { dep, readResult };
|
|
573
|
+
}));
|
|
490
574
|
let added = 0;
|
|
491
|
-
for (const dep of
|
|
492
|
-
if (
|
|
575
|
+
for (const { dep, readResult } of depReadResults) {
|
|
576
|
+
if (relevantFiles.size >= MAX_TOTAL_FILES || cumulativeFileTokens >= MAX_TOTAL_FILE_TOKENS)
|
|
493
577
|
break;
|
|
494
|
-
if (!
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
578
|
+
if (!readResult.error) {
|
|
579
|
+
const boundedText = boundFileContent(dep, readResult.output);
|
|
580
|
+
relevantFiles.set(dep, { text: boundedText, inlineData: readResult.inlineData });
|
|
581
|
+
cumulativeFileTokens += estimateTokenCount(boundedText);
|
|
582
|
+
added++;
|
|
500
583
|
}
|
|
501
584
|
}
|
|
502
585
|
if (added > 0) {
|
|
@@ -510,46 +593,50 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
510
593
|
catch {
|
|
511
594
|
// Dependency tracing is best-effort — don't block investigation
|
|
512
595
|
}
|
|
513
|
-
|
|
596
|
+
return {
|
|
514
597
|
functionResponse: {
|
|
515
598
|
name: call.name,
|
|
516
599
|
response: { output: 'Investigation finished.' },
|
|
517
600
|
},
|
|
518
|
-
}
|
|
519
|
-
break;
|
|
601
|
+
};
|
|
520
602
|
}
|
|
521
603
|
else if (call.name === 'list_directory') {
|
|
522
604
|
const listRes = await executeTool(workspaceRoot, 'list_directory', args);
|
|
523
|
-
|
|
605
|
+
const output = boundToolOutput(listRes.output);
|
|
606
|
+
return {
|
|
524
607
|
functionResponse: {
|
|
525
608
|
name: call.name,
|
|
526
609
|
response: {
|
|
527
|
-
output
|
|
610
|
+
output,
|
|
528
611
|
...(listRes.error ? { error: listRes.error } : {}),
|
|
529
612
|
},
|
|
530
613
|
},
|
|
531
|
-
}
|
|
614
|
+
};
|
|
532
615
|
}
|
|
533
616
|
else if (call.name === 'search_codebase') {
|
|
534
617
|
const grepRes = await executeTool(workspaceRoot, 'grep_search', { ...args, workspace: 'all' });
|
|
535
|
-
|
|
618
|
+
const output = boundToolOutput(grepRes.error ? grepRes.error : grepRes.output);
|
|
619
|
+
return {
|
|
536
620
|
functionResponse: {
|
|
537
621
|
name: call.name,
|
|
538
|
-
response: { output
|
|
622
|
+
response: { output },
|
|
539
623
|
},
|
|
540
|
-
}
|
|
624
|
+
};
|
|
541
625
|
}
|
|
542
626
|
else if (call.name === 'read_file') {
|
|
543
627
|
const readRes = await executeTool(workspaceRoot, 'read_file', args);
|
|
544
628
|
if (!readRes.error) {
|
|
545
|
-
|
|
629
|
+
const boundedText = boundFileContent(args.filePath, readRes.output);
|
|
630
|
+
relevantFiles.set(args.filePath, { text: boundedText, inlineData: readRes.inlineData });
|
|
631
|
+
cumulativeFileTokens += estimateTokenCount(boundedText);
|
|
546
632
|
}
|
|
547
|
-
|
|
633
|
+
const output = boundToolOutput(readRes.error ? readRes.error : readRes.output);
|
|
634
|
+
return {
|
|
548
635
|
functionResponse: {
|
|
549
636
|
name: call.name,
|
|
550
|
-
response: { output
|
|
637
|
+
response: { output },
|
|
551
638
|
},
|
|
552
|
-
}
|
|
639
|
+
};
|
|
553
640
|
}
|
|
554
641
|
else if (call.name === 'perform_web_search') {
|
|
555
642
|
try {
|
|
@@ -564,56 +651,64 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
564
651
|
console.log(pc.dim(` [Web Search] ${webMsg}`));
|
|
565
652
|
}
|
|
566
653
|
const searchSummary = webResult.response.text()?.trim() || 'No relevant information found.';
|
|
567
|
-
|
|
568
|
-
|
|
654
|
+
const boundedSummary = boundToolOutput(searchSummary);
|
|
655
|
+
webSearchSummary += `\nQuery: ${args.query}\nFindings:\n${boundedSummary}\n`;
|
|
656
|
+
return {
|
|
569
657
|
functionResponse: {
|
|
570
658
|
name: call.name,
|
|
571
|
-
response: { output:
|
|
659
|
+
response: { output: boundedSummary },
|
|
572
660
|
},
|
|
573
|
-
}
|
|
661
|
+
};
|
|
574
662
|
}
|
|
575
663
|
catch (e) {
|
|
576
|
-
|
|
664
|
+
return {
|
|
577
665
|
functionResponse: {
|
|
578
666
|
name: call.name,
|
|
579
667
|
response: { error: e.message || 'Failed to search the web' },
|
|
580
668
|
},
|
|
581
|
-
}
|
|
669
|
+
};
|
|
582
670
|
}
|
|
583
671
|
}
|
|
584
672
|
else if (call.name === 'find_dependencies') {
|
|
585
673
|
const depResult = await executeTool(workspaceRoot, 'find_dependencies', args);
|
|
586
|
-
|
|
674
|
+
const output = boundToolOutput(depResult.error ? depResult.error : depResult.output);
|
|
675
|
+
return {
|
|
587
676
|
functionResponse: {
|
|
588
677
|
name: call.name,
|
|
589
|
-
response: { output
|
|
678
|
+
response: { output },
|
|
590
679
|
},
|
|
591
|
-
}
|
|
680
|
+
};
|
|
592
681
|
}
|
|
593
682
|
else if (call.name === 'find_recent_changes') {
|
|
594
683
|
const recentRes = await executeTool(workspaceRoot, 'find_recent_changes', args);
|
|
595
|
-
|
|
684
|
+
const output = boundToolOutput(recentRes.error ? recentRes.error : recentRes.output);
|
|
685
|
+
return {
|
|
596
686
|
functionResponse: {
|
|
597
687
|
name: call.name,
|
|
598
|
-
response: { output
|
|
688
|
+
response: { output },
|
|
599
689
|
},
|
|
600
|
-
}
|
|
690
|
+
};
|
|
601
691
|
}
|
|
602
692
|
else if (call.name === 'run_analysis_script') {
|
|
603
|
-
const analysisResult = await runEphemeralScript(workspaceRoot, args.language, args.code, {
|
|
604
|
-
|
|
605
|
-
});
|
|
606
|
-
const output = analysisResult.exitCode === 0
|
|
693
|
+
const analysisResult = await runEphemeralScript(workspaceRoot, args.language, args.code, { abortSignal });
|
|
694
|
+
const rawOutput = analysisResult.exitCode === 0
|
|
607
695
|
? analysisResult.stdout || '(script produced no output)'
|
|
608
696
|
: `Script failed (exit ${analysisResult.exitCode}):\n${analysisResult.stderr}`;
|
|
609
|
-
|
|
697
|
+
const output = boundToolOutput(rawOutput);
|
|
698
|
+
return {
|
|
610
699
|
functionResponse: {
|
|
611
700
|
name: call.name,
|
|
612
701
|
response: { output },
|
|
613
702
|
},
|
|
614
|
-
}
|
|
703
|
+
};
|
|
615
704
|
}
|
|
616
|
-
|
|
705
|
+
return {
|
|
706
|
+
functionResponse: {
|
|
707
|
+
name: call.name,
|
|
708
|
+
response: { output: `Unknown tool: ${call.name}` },
|
|
709
|
+
},
|
|
710
|
+
};
|
|
711
|
+
}));
|
|
617
712
|
if (isFinished) {
|
|
618
713
|
break;
|
|
619
714
|
}
|