minovative-mind-cli 2.9.1 → 2.11.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 +14 -6
- package/dist/services/agent/commandApproval.js +5 -2
- package/dist/services/agent/slashCommands.js +90 -53
- 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 +11 -15
- package/dist/services/ai.d.ts +21 -1
- package/dist/services/ai.js +236 -11
- package/dist/services/chatHistoryService.d.ts +95 -2
- package/dist/services/chatHistoryService.js +236 -9
- package/dist/services/contextAgent.js +196 -81
- package/dist/services/investigationComplexity.d.ts +1 -1
- package/dist/services/investigationComplexity.js +1 -1
- package/dist/services/orchestration/investigationAgent.js +101 -84
- package/dist/services/orchestration/investigationCache.d.ts +80 -5
- package/dist/services/orchestration/investigationCache.js +570 -41
- package/dist/services/orchestration/investigationOrchestrator.js +17 -4
- 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/utils/analysisRunner.d.ts +29 -0
- package/dist/utils/analysisRunner.js +200 -5
- package/dist/utils/contextPrompts.d.ts +20 -4
- package/dist/utils/contextPrompts.js +158 -23
- 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 +5 -4
- package/dist/utils/systemPrompts.js +46 -14
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ Hot-swap during a session using `/models`:
|
|
|
89
89
|
| Model | Best for |
|
|
90
90
|
| ------------------------- | --------------------------------------------------- |
|
|
91
91
|
| **Auto** (default) | Automatically selects (3.7 Flash or 3.5 Flash Lite) |
|
|
92
|
-
| **Gemini 3.7 Flash** | Next-gen performance, reasoning & fast execution
|
|
92
|
+
| **Gemini 3.7 Flash** | Next-gen performance, reasoning & fast execution |
|
|
93
93
|
| **Gemini 3.6 Flash** | Everyday coding — fast and accurate |
|
|
94
94
|
| **Gemini 3.1 Pro** | Complex architectural changes |
|
|
95
95
|
| **Gemini 3.5 Flash Lite** | Best for speed and cost efficiency |
|
|
@@ -101,11 +101,19 @@ If you prefer to use your own API key instead of credits, you can configure it v
|
|
|
101
101
|
- **Configuration:** Use `/config-key` in the chat session to set and manage your API key.
|
|
102
102
|
- **Error Handling:** If your key is invalid, expired, or you hit rate limits, the CLI will report a `BYOK AI Error`. Please check your API key status in the [Google AI Studio dashboard](https://aistudio.google.com).
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
### Auxiliary Model Routing Defaults
|
|
105
|
+
|
|
106
|
+
Background tasks automatically route to dedicated auxiliary models with native `responseSchema` constraints for optimal latency, cost efficiency, and structured output reliability:
|
|
107
|
+
|
|
108
|
+
- **Intent Router (`routeIntent`)**: `Gemini 3.7 Flash` (Temp 0, native `responseSchema`) — zero-temperature classification of `SEARCH` vs `SKIP` and `CHAT` vs `EXECUTE`.
|
|
109
|
+
- **Complexity Evaluators**: `Gemini 3.7 Flash` (Temp 0, native `responseSchema`) — domain partitioning and parallel execution wave feasibility.
|
|
110
|
+
- **Context Compressor**: `Gemini 3.7 Flash` (Temp 0.2) — surgical code distillation for files exceeding 2,000 characters.
|
|
111
|
+
- **Session Titling**: `Gemini 3.7 Flash` (Temp 0.7, native `responseSchema`) — automated concise chat session titling.
|
|
112
|
+
- **Semantic Cache Classifier**: `Gemini 3.5 Flash Lite` (Temp 0, native `responseSchema`) — intent and topic classification for cache hits.
|
|
113
|
+
- **History Summarizer**: `Gemini 3.7 Flash` (Temp 0.2) — 3-part structured conversation compression preserving recent history.
|
|
114
|
+
- **Commit Generator**: `Gemini 3.5 Flash Lite` — conventional commit message synthesis.
|
|
115
|
+
|
|
116
|
+
> You pay for auxiliary model background AI operations and for your selected model during chat and code execution. Use `/debug` to inspect real-time routing diagnostics.
|
|
109
117
|
|
|
110
118
|
---
|
|
111
119
|
|
|
@@ -33,6 +33,9 @@ export function isCommandDestructive(command) {
|
|
|
33
33
|
// Massive permission shifts (chmod 777, recursive chowns)
|
|
34
34
|
/\bchmod\s+(?:-[R\w]*\s+)?777\b/i,
|
|
35
35
|
/\bchown\s+-[R\w]*\b/i,
|
|
36
|
+
// Privileged root escalation commands (require explicit interactive password / system-level access)
|
|
37
|
+
/\bsudo\b/i,
|
|
38
|
+
/\bsu\s+/i,
|
|
36
39
|
];
|
|
37
40
|
return destructivePatterns.some((pattern) => pattern.test(command));
|
|
38
41
|
}
|
|
@@ -49,10 +52,10 @@ export function isCommandSafe(command) {
|
|
|
49
52
|
/^\s*echo\b/i,
|
|
50
53
|
/^\s*grep\b/i,
|
|
51
54
|
/^\s*find\b/i,
|
|
52
|
-
/^\s*npm\s+(install|i|ci|run\b)/i,
|
|
55
|
+
/^\s*npm\s+(install|i|ci|run\b|test\b|t\b)/i,
|
|
53
56
|
/^\s*yarn\s+(install|add|build|lint|test|run\b)/i,
|
|
54
57
|
/^\s*pnpm\s+(install|i|add|build|lint|test|run\b)/i,
|
|
55
|
-
/^\s*bun\s+(install|i|add|run\b)/i,
|
|
58
|
+
/^\s*bun\s+(install|i|add|run\b|test\b)/i,
|
|
56
59
|
/^\s*cargo\s+(build|check|add|test|run\b)/i,
|
|
57
60
|
/^\s*go\s+(mod|get|build|test|run\b)/i,
|
|
58
61
|
/^\s*pip\s+(install|list|show)\b/i,
|
|
@@ -563,73 +563,87 @@ export async function handleSlashCommand(command, context) {
|
|
|
563
563
|
p.log.info(`${pc.dim('Workspace:')} ${brandFg(workspaceRoot)}`);
|
|
564
564
|
p.log.info(`${pc.dim('Commands:')} Type ${pc.yellow('/')} to open the command menu and "${pc.yellow('stop')}" to stop the ai generation. Type ${pc.yellow('exit')} to leave.`);
|
|
565
565
|
p.log.success(`Resumed session: ${session.title}`);
|
|
566
|
-
// Print the loaded history
|
|
567
|
-
|
|
566
|
+
// Print the loaded history cleanly: only user and AI conversational messages,
|
|
567
|
+
// with a clean indicator label if the turn involved tool/code changes.
|
|
568
|
+
let turnHadModifications = false;
|
|
569
|
+
let turnHadInspections = false;
|
|
570
|
+
let renderedIndicatorForTurn = false;
|
|
568
571
|
for (const item of session.history) {
|
|
569
572
|
if (item.role === 'user') {
|
|
573
|
+
const hasFunctionResponse = item.parts?.some((p) => p.functionResponse);
|
|
574
|
+
if (hasFunctionResponse) {
|
|
575
|
+
// Internal tool response from the execution loop — do not display as a user message
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
570
578
|
const textParts = item.parts
|
|
571
|
-
|
|
579
|
+
?.filter((p) => p.text)
|
|
572
580
|
.map((p) => p.text)
|
|
573
581
|
.join('');
|
|
574
|
-
if (textParts &&
|
|
582
|
+
if (textParts &&
|
|
583
|
+
!textParts.includes('SYSTEM CHECK: Please objectively verify') &&
|
|
584
|
+
!textParts.includes('[PREVIOUS CONVERSATION SUMMARY]') &&
|
|
585
|
+
!textParts.startsWith('[USER INTERRUPTION]')) {
|
|
586
|
+
// Reset turn tool flags for the new user prompt
|
|
587
|
+
turnHadModifications = false;
|
|
588
|
+
turnHadInspections = false;
|
|
589
|
+
renderedIndicatorForTurn = false;
|
|
575
590
|
p.log.step(pc.bgBlue(pc.white(` ${textParts} `)));
|
|
576
591
|
}
|
|
577
592
|
}
|
|
578
593
|
else if (item.role === 'model') {
|
|
579
|
-
//
|
|
580
|
-
for (const part of item.parts) {
|
|
594
|
+
// Track if any tool actions were called in this turn
|
|
595
|
+
for (const part of item.parts || []) {
|
|
581
596
|
if (part.functionCall) {
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
p.log.message(` ${pc.dim(`[ ] ${index + 1}.`)} ${task}`);
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
else if (call.name === 'update_todo_status') {
|
|
593
|
-
const status = args.status;
|
|
594
|
-
const taskIndex = args.taskIndex;
|
|
595
|
-
const taskText = sessionTasks[taskIndex - 1] || 'completed';
|
|
596
|
-
let icon = '[ ]';
|
|
597
|
-
let color = pc.dim;
|
|
598
|
-
if (status?.toLowerCase() === 'completed' || status?.toLowerCase() === 'done') {
|
|
599
|
-
icon = '[x]';
|
|
600
|
-
color = pc.green;
|
|
601
|
-
}
|
|
602
|
-
else if (status?.toLowerCase() === 'in_progress' || status?.toLowerCase() === 'started') {
|
|
603
|
-
icon = '[/]';
|
|
604
|
-
color = pc.yellow;
|
|
605
|
-
}
|
|
606
|
-
else if (status?.toLowerCase() === 'failed' || status?.toLowerCase() === 'error') {
|
|
607
|
-
icon = '[!]';
|
|
608
|
-
color = pc.red;
|
|
609
|
-
}
|
|
610
|
-
p.log.message(color(` ${icon} ${taskIndex}. ${taskText}`));
|
|
597
|
+
const name = part.functionCall.name;
|
|
598
|
+
if (name === 'modify_file' ||
|
|
599
|
+
name === 'write_file' ||
|
|
600
|
+
name === 'delete_file' ||
|
|
601
|
+
name === 'rename_file' ||
|
|
602
|
+
name === 'run_command') {
|
|
603
|
+
turnHadModifications = true;
|
|
611
604
|
}
|
|
612
|
-
else
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
catch (e) {
|
|
618
|
-
argsStr = String(call.args);
|
|
619
|
-
}
|
|
620
|
-
const argsPreview = argsStr.slice(0, 50);
|
|
621
|
-
p.log.message(pc.dim(` ├─ Executed tool`));
|
|
605
|
+
else if (name === 'read_file' ||
|
|
606
|
+
name === 'grep_search' ||
|
|
607
|
+
name === 'list_directory' ||
|
|
608
|
+
name === 'find_dependencies') {
|
|
609
|
+
turnHadInspections = true;
|
|
622
610
|
}
|
|
623
611
|
}
|
|
624
612
|
}
|
|
625
|
-
//
|
|
626
|
-
|
|
613
|
+
// Aggregate and extract model conversational text
|
|
614
|
+
let textParts = (item.parts || [])
|
|
627
615
|
.filter((p) => p.text && !p.text.includes('[TASK_FINISHED]'))
|
|
628
616
|
.map((p) => p.text)
|
|
629
617
|
.join('');
|
|
630
|
-
if
|
|
618
|
+
// Backward-compatibility: if text is empty, check finish_task args.summary
|
|
619
|
+
if (!textParts.trim()) {
|
|
620
|
+
const finishCall = (item.parts || []).find((p) => p.functionCall?.name === 'finish_task');
|
|
621
|
+
const finishArgs = finishCall?.functionCall?.args;
|
|
622
|
+
if (finishArgs?.summary) {
|
|
623
|
+
textParts = String(finishArgs.summary);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
if (textParts.includes('[ORCHESTRATOR_EXECUTION]')) {
|
|
627
|
+
turnHadModifications = true;
|
|
628
|
+
}
|
|
629
|
+
const cleanText = textParts
|
|
630
|
+
.replace(/\[TASK_FINISHED\]/g, '')
|
|
631
|
+
.replace(/\[ORCHESTRATOR_EXECUTION\]\n?/g, '')
|
|
632
|
+
.replace(/\[PREVIOUS CONVERSATION SUMMARY\]:[\s\S]*?(?=\n\n|$)/g, '')
|
|
633
|
+
.trim();
|
|
634
|
+
if (cleanText) {
|
|
635
|
+
if (!renderedIndicatorForTurn) {
|
|
636
|
+
if (turnHadModifications) {
|
|
637
|
+
p.log.message(pc.dim(' ⚡ [Mino applied workspace modifications & commands]'));
|
|
638
|
+
renderedIndicatorForTurn = true;
|
|
639
|
+
}
|
|
640
|
+
else if (turnHadInspections) {
|
|
641
|
+
p.log.message(pc.dim(' 🔍 [Inspected workspace context]'));
|
|
642
|
+
renderedIndicatorForTurn = true;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
631
645
|
console.log(`\n${pc.blue('◆')} ${pc.bold('Minovative Mind')} ${pc.dim('(History)')}\n`);
|
|
632
|
-
console.log(renderTerminalMarkdown(
|
|
646
|
+
console.log(renderTerminalMarkdown(cleanText));
|
|
633
647
|
}
|
|
634
648
|
}
|
|
635
649
|
}
|
|
@@ -715,7 +729,7 @@ export async function handleSlashCommand(command, context) {
|
|
|
715
729
|
}
|
|
716
730
|
else if (chatsMenu === 'bulk-delete') {
|
|
717
731
|
const selectedIds = await p['multiselect']({
|
|
718
|
-
message:
|
|
732
|
+
message: `Select chat sessions to delete: ${pc.dim('(Press Esc to go back / cancel)')}`,
|
|
719
733
|
options: sessions
|
|
720
734
|
.slice()
|
|
721
735
|
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
|
|
@@ -723,7 +737,7 @@ export async function handleSlashCommand(command, context) {
|
|
|
723
737
|
value: s.id,
|
|
724
738
|
label: `${truncate(s.title, 50)} (${new Date(s.timestamp).toLocaleString()})`,
|
|
725
739
|
})),
|
|
726
|
-
required:
|
|
740
|
+
required: false,
|
|
727
741
|
});
|
|
728
742
|
if (p.isCancel(selectedIds) || !Array.isArray(selectedIds) || selectedIds.length === 0) {
|
|
729
743
|
p.log.warn('Bulk delete canceled.');
|
|
@@ -735,6 +749,16 @@ export async function handleSlashCommand(command, context) {
|
|
|
735
749
|
if (confirm) {
|
|
736
750
|
await chatHistoryService.bulkDeleteSessions(selectedIds);
|
|
737
751
|
p.log.success(`Successfully deleted ${selectedIds.length} session(s).`);
|
|
752
|
+
if (chatSessionState && selectedIds.includes(chatSessionState.id)) {
|
|
753
|
+
chat.clearHistory();
|
|
754
|
+
process.stdout.write('\x1B[2J\x1B[3J\x1B[H'); // Hard clear screen and scrollback
|
|
755
|
+
printLogo();
|
|
756
|
+
p.intro(`${brandBg(' Minovative Mind CLI ')} ${pc.dim('v' + version)}`);
|
|
757
|
+
p.log.info(`${pc.dim('Workspace:')} ${brandFg(workspaceRoot)}`);
|
|
758
|
+
p.log.info(`${pc.dim('Commands:')} Type ${pc.yellow('/')} to open the command menu and "${pc.yellow('stop')}" to stop the ai generation. Type ${pc.yellow('exit')} to leave.`);
|
|
759
|
+
p.log.warn('Active session was deleted. Chat history cleared.');
|
|
760
|
+
console.log(pc.dim('\nType your coding request below. Type "exit" or "quit" to leave.\n'));
|
|
761
|
+
}
|
|
738
762
|
}
|
|
739
763
|
else {
|
|
740
764
|
p.log.warn('Bulk delete canceled.');
|
|
@@ -1008,10 +1032,23 @@ export async function handleSlashCommand(command, context) {
|
|
|
1008
1032
|
<diff>
|
|
1009
1033
|
${diffOut}
|
|
1010
1034
|
</diff>`;
|
|
1011
|
-
const commitSystemPrompt =
|
|
1035
|
+
const commitSystemPrompt = `You are a strict Conventional Commits generator and expert software engineer.
|
|
1036
|
+
Output ONLY the raw commit message text. Absolutely NO conversational preambles, NO markdown code blocks (e.g. no \`\`\`), and NO post-explanations.
|
|
1037
|
+
|
|
1038
|
+
Strict Formatting Rules:
|
|
1039
|
+
1. First line must follow Conventional Commits format: type(scope): description
|
|
1040
|
+
- Allowed types: feat, fix, refactor, test, docs, chore, perf, style
|
|
1041
|
+
- Subject line must be in imperative mood (e.g. "add support for...", "fix bug in...") and strictly under 50 characters.
|
|
1042
|
+
2. Leave a single blank line after the subject.
|
|
1043
|
+
3. Provide a concise bulleted list (using '-' or '*') explaining the architectural "what" and "why" of the changes based on the provided diff. If diff spans multiple areas, prioritize the primary architectural change.`;
|
|
1012
1044
|
const commitAgent = new ProxyChatSession(GEMINI_MODELS.FLASH_LITE, commitSystemPrompt, [], {});
|
|
1013
1045
|
const commitResult = await commitAgent.sendMessage(prompt);
|
|
1014
|
-
|
|
1046
|
+
let commitMsg = commitResult.response.text().trim();
|
|
1047
|
+
// Sanitize markdown fences if model included them despite instructions
|
|
1048
|
+
commitMsg = commitMsg
|
|
1049
|
+
.replace(/^```[a-zA-Z]*\n?/gm, '')
|
|
1050
|
+
.replace(/```\s*$/gm, '')
|
|
1051
|
+
.trim();
|
|
1015
1052
|
commitSpinner.message('Committing...');
|
|
1016
1053
|
const tmpMsgPath = path.join(workspaceRoot, '.gemini-commit-msg.tmp');
|
|
1017
1054
|
await fs.writeFile(tmpMsgPath, commitMsg, 'utf-8');
|
|
@@ -180,11 +180,12 @@ export declare function traceDependencies(workspaceRoot: string, filePath: strin
|
|
|
180
180
|
*/
|
|
181
181
|
export declare function findRecentChanges(workspaceRoot: string, dirPath?: string, minutes?: number, maxDepth?: number): Promise<ToolResult>;
|
|
182
182
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
183
|
+
* Executes an ephemeral debugging or validation script in the specified language runtime.
|
|
184
|
+
* Delegates to the sandboxed ephemeral script runner in `os.tmpdir()` with automatic ESM/CJS
|
|
185
|
+
* module resolution and multi-language runtime detection.
|
|
185
186
|
*
|
|
186
187
|
* @param workspaceRoot - Absolute path to the workspace root directory.
|
|
187
|
-
* @param language - Runtime language (`'node'`, `'ts-node'`, `'python'`, `'bash'`, `'go'`, or `'
|
|
188
|
+
* @param language - Runtime language (`'node'`, `'ts-node'`, `'python'`, `'bash'`, `'go'`, `'rust'`, `'c'`, `'cpp'`, `'ruby'`, `'php'`, `'java'`, or `'auto'`).
|
|
188
189
|
* @param code - The exact script source code to execute.
|
|
189
190
|
* @param abortSignal - Optional AbortSignal to cancel execution.
|
|
190
191
|
* @returns A promise resolving to a {@link ToolResult} containing execution output.
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { exec } from 'node:child_process';
|
|
15
15
|
import { promises as fs } from 'node:fs';
|
|
16
|
-
import os from 'node:os';
|
|
17
16
|
import path from 'node:path';
|
|
18
17
|
import { promisify } from 'node:util';
|
|
19
18
|
import { SchemaType } from '@google/generative-ai';
|
|
@@ -31,7 +30,7 @@ import { extractSymbols } from '../utils/symbolExtractor.js';
|
|
|
31
30
|
import { getMetricCollector } from './metrics.js';
|
|
32
31
|
import { getCurrentAgentId } from '../utils/asyncContext.js';
|
|
33
32
|
import { recordFileRead, hasAgentReadFile } from '../utils/fileReadGuard.js';
|
|
34
|
-
import { runFuzzProbe, checkHeapDelta as runCheckHeapDelta, checkBehavioralDrift as runCheckBehavioralDrift, } from '../utils/analysisRunner.js';
|
|
33
|
+
import { runFuzzProbe, checkHeapDelta as runCheckHeapDelta, checkBehavioralDrift as runCheckBehavioralDrift, runEphemeralScript, } from '../utils/analysisRunner.js';
|
|
35
34
|
import ignore from 'ignore';
|
|
36
35
|
const execAsync = promisify(exec);
|
|
37
36
|
// ─── Tool Declarations for Gemini Function Calling ───────────────────
|
|
@@ -1112,6 +1111,13 @@ export async function listDirectory(workspaceRoot, dirPath, maxDepth = 3) {
|
|
|
1112
1111
|
* @returns A promise resolving to a {@link ToolResult} containing stdout/stderr or an error.
|
|
1113
1112
|
*/
|
|
1114
1113
|
export async function runCommand(workspaceRoot, command, abortSignal) {
|
|
1114
|
+
// Reject sudo or interactive root commands to prevent non-interactive subshell deadlocks and security escalation
|
|
1115
|
+
if (/\bsudo\b/i.test(command)) {
|
|
1116
|
+
return {
|
|
1117
|
+
output: '',
|
|
1118
|
+
error: "Execution blocked: Commands requiring 'sudo' or root privileges cannot be executed automatically in background tool loops. Please explain to the user in your text response that they must run this command manually in their own terminal.",
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1115
1121
|
try {
|
|
1116
1122
|
const { stdout, stderr } = await execAsync(command, {
|
|
1117
1123
|
cwd: workspaceRoot,
|
|
@@ -1351,102 +1357,49 @@ export async function findRecentChanges(workspaceRoot, dirPath = '.', minutes =
|
|
|
1351
1357
|
}
|
|
1352
1358
|
// ─── Tool Dispatcher ─────────────────────────────────────────────────
|
|
1353
1359
|
/**
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1360
|
+
* Executes an ephemeral debugging or validation script in the specified language runtime.
|
|
1361
|
+
* Delegates to the sandboxed ephemeral script runner in `os.tmpdir()` with automatic ESM/CJS
|
|
1362
|
+
* module resolution and multi-language runtime detection.
|
|
1356
1363
|
*
|
|
1357
1364
|
* @param workspaceRoot - Absolute path to the workspace root directory.
|
|
1358
|
-
* @param language - Runtime language (`'node'`, `'ts-node'`, `'python'`, `'bash'`, `'go'`, or `'
|
|
1365
|
+
* @param language - Runtime language (`'node'`, `'ts-node'`, `'python'`, `'bash'`, `'go'`, `'rust'`, `'c'`, `'cpp'`, `'ruby'`, `'php'`, `'java'`, or `'auto'`).
|
|
1359
1366
|
* @param code - The exact script source code to execute.
|
|
1360
1367
|
* @param abortSignal - Optional AbortSignal to cancel execution.
|
|
1361
1368
|
* @returns A promise resolving to a {@link ToolResult} containing execution output.
|
|
1362
1369
|
*/
|
|
1363
1370
|
export async function runDebugScript(workspaceRoot, language, code, abortSignal) {
|
|
1364
|
-
const extMap = {
|
|
1365
|
-
node: '.js',
|
|
1366
|
-
'ts-node': '.ts',
|
|
1367
|
-
python: '.py',
|
|
1368
|
-
bash: '.sh',
|
|
1369
|
-
go: '.go',
|
|
1370
|
-
rust: '.rs',
|
|
1371
|
-
};
|
|
1372
|
-
const ext = extMap[language.toLowerCase()] || '.txt';
|
|
1373
|
-
const tmpFileName = `.minovative-scratch${ext}`;
|
|
1374
|
-
const absPath = path.join(workspaceRoot, tmpFileName);
|
|
1375
1371
|
try {
|
|
1376
1372
|
const p = await import('@clack/prompts');
|
|
1377
1373
|
const pc = (await import('picocolors')).default;
|
|
1378
|
-
p.log.info(pc.dim(`🛠️ Running temporary ${language} debug script...`));
|
|
1379
|
-
await
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
cmd = `npx ts-node ${tmpFileName}`;
|
|
1387
|
-
break;
|
|
1388
|
-
case 'python':
|
|
1389
|
-
cmd = `python3 ${tmpFileName}`;
|
|
1390
|
-
break;
|
|
1391
|
-
case 'bash':
|
|
1392
|
-
cmd = `bash ${tmpFileName}`;
|
|
1393
|
-
break;
|
|
1394
|
-
case 'go':
|
|
1395
|
-
cmd = `go run ${tmpFileName}`;
|
|
1396
|
-
break;
|
|
1397
|
-
case 'rust': {
|
|
1398
|
-
const binExt = os.platform() === 'win32' ? '.exe' : '';
|
|
1399
|
-
const binPath = tmpFileName.replace('.rs', binExt);
|
|
1400
|
-
const runCmd = os.platform() === 'win32' ? binPath : `./${binPath}`;
|
|
1401
|
-
cmd = `rustc ${tmpFileName} -o ${binPath} && ${runCmd}`;
|
|
1402
|
-
break;
|
|
1403
|
-
}
|
|
1404
|
-
default:
|
|
1405
|
-
return { output: '', error: `Unsupported language runtime: ${language}` };
|
|
1374
|
+
p.log.info(pc.dim(`🛠️ Running temporary ${language || 'script'} debug script...`));
|
|
1375
|
+
const res = await runEphemeralScript(workspaceRoot, language || 'auto', code, {
|
|
1376
|
+
abortSignal,
|
|
1377
|
+
timeoutMs: 60_000,
|
|
1378
|
+
});
|
|
1379
|
+
let finalOutput = '';
|
|
1380
|
+
if (res.exitCode !== 0) {
|
|
1381
|
+
finalOutput += `Script failed with exit code ${res.exitCode}.\n`;
|
|
1406
1382
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
const out = stdout.trim();
|
|
1410
|
-
const errOut = stderr.trim();
|
|
1411
|
-
let finalOutput = '';
|
|
1412
|
-
if (out)
|
|
1413
|
-
finalOutput += `[STDOUT]\n${out}\n`;
|
|
1414
|
-
if (errOut)
|
|
1415
|
-
finalOutput += `[STDERR]\n${errOut}\n`;
|
|
1416
|
-
if (!finalOutput)
|
|
1417
|
-
finalOutput = 'Script executed successfully with no output.';
|
|
1418
|
-
return { output: `<test_results>\n${sanitizeForCDATA(finalOutput)}\n</test_results>` };
|
|
1383
|
+
if (res.stdout) {
|
|
1384
|
+
finalOutput += `[STDOUT]\n${res.stdout}\n`;
|
|
1419
1385
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
const errOut = (err.stderr || '').trim();
|
|
1423
|
-
let finalOutput = `Script failed with exit code ${err.code || 1}.\n`;
|
|
1424
|
-
if (out)
|
|
1425
|
-
finalOutput += `[STDOUT]\n${out}\n`;
|
|
1426
|
-
if (errOut)
|
|
1427
|
-
finalOutput += `[STDERR]\n${errOut}\n`;
|
|
1428
|
-
return { output: `<test_results>\n${sanitizeForCDATA(finalOutput)}\n</test_results>` };
|
|
1386
|
+
if (res.stderr) {
|
|
1387
|
+
finalOutput += `[STDERR]\n${res.stderr}\n`;
|
|
1429
1388
|
}
|
|
1389
|
+
if (!finalOutput.trim()) {
|
|
1390
|
+
finalOutput = 'Script executed successfully with no output.';
|
|
1391
|
+
}
|
|
1392
|
+
return {
|
|
1393
|
+
output: `<test_results>\n${sanitizeForCDATA(finalOutput.trim())}\n</test_results>`,
|
|
1394
|
+
...(res.exitCode !== 0 ? { error: `Script failed with exit code ${res.exitCode}` } : {}),
|
|
1395
|
+
};
|
|
1430
1396
|
}
|
|
1431
1397
|
catch (err) {
|
|
1432
1398
|
return {
|
|
1433
1399
|
output: '',
|
|
1434
|
-
error: `Failed to
|
|
1400
|
+
error: `Failed to execute debug script: ${err instanceof Error ? err.message : String(err)}`,
|
|
1435
1401
|
};
|
|
1436
1402
|
}
|
|
1437
|
-
finally {
|
|
1438
|
-
try {
|
|
1439
|
-
await fs.rm(absPath, { force: true });
|
|
1440
|
-
if (language.toLowerCase() === 'rust') {
|
|
1441
|
-
const binExt = os.platform() === 'win32' ? '.exe' : '';
|
|
1442
|
-
const binPath = absPath.replace('.rs', binExt);
|
|
1443
|
-
await fs.rm(binPath, { force: true });
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
catch {
|
|
1447
|
-
// Ignore cleanup errors
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
1403
|
}
|
|
1451
1404
|
/**
|
|
1452
1405
|
* Executes a fuzz probing analysis script and formats the resulting pass/fail and crash statistics.
|
package/dist/services/agent.d.ts
CHANGED
|
@@ -112,15 +112,14 @@ export declare function executeSingleTurn(workspaceRoot: string, userInput: stri
|
|
|
112
112
|
} | void>;
|
|
113
113
|
/**
|
|
114
114
|
* Raw chat history entry count threshold to trigger chat history summarization.
|
|
115
|
-
*
|
|
115
|
+
* 2 entries = 1 complete conversational user/model turn.
|
|
116
116
|
*/
|
|
117
|
-
export declare const HISTORY_SUMMARIZATION_THRESHOLD =
|
|
117
|
+
export declare const HISTORY_SUMMARIZATION_THRESHOLD = 2;
|
|
118
118
|
/**
|
|
119
|
-
* Summarizes
|
|
120
|
-
* exceeds {@link HISTORY_SUMMARIZATION_THRESHOLD} entries.
|
|
119
|
+
* Summarizes chat history entries starting from the first full user/model turn.
|
|
121
120
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
121
|
+
* Compresses preceding conversation history into a structured high-density summary pair
|
|
122
|
+
* in the active session while permanently preserving uncompressed history in fullHistory.
|
|
124
123
|
*
|
|
125
124
|
* @param chat - The active ProxyChatSession instance.
|
|
126
125
|
* @returns A promise resolving to true if history was summarized and updated; false otherwise.
|
package/dist/services/agent.js
CHANGED
|
@@ -544,7 +544,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
|
|
|
544
544
|
parts: [{ text: userInput }],
|
|
545
545
|
}, {
|
|
546
546
|
role: 'model',
|
|
547
|
-
parts: [{ text: handledByOrchestrator }],
|
|
547
|
+
parts: [{ text: `[ORCHESTRATOR_EXECUTION]\n${handledByOrchestrator}` }],
|
|
548
548
|
});
|
|
549
549
|
// Print the summary text just like single-agent mode
|
|
550
550
|
console.log(`\n${pc.blue('◆')} ${pc.bold('Minovative Mind')} ${pc.dim(`(Orchestrator)`)}\n`);
|
|
@@ -670,6 +670,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
|
|
|
670
670
|
if (cleanFinalText) {
|
|
671
671
|
console.log(`\n${pc.blue('◆')} ${pc.bold('Minovative Mind')} ${pc.dim(`(${chat.getModel()})`)}\n`);
|
|
672
672
|
console.log(renderTerminalMarkdown(cleanFinalText));
|
|
673
|
+
chat.appendFinalSummary(cleanFinalText);
|
|
673
674
|
}
|
|
674
675
|
}
|
|
675
676
|
if (usage) {
|
|
@@ -989,15 +990,14 @@ async function compressContextFiles(workspaceRoot, contextResult) {
|
|
|
989
990
|
}
|
|
990
991
|
/**
|
|
991
992
|
* Raw chat history entry count threshold to trigger chat history summarization.
|
|
992
|
-
*
|
|
993
|
+
* 2 entries = 1 complete conversational user/model turn.
|
|
993
994
|
*/
|
|
994
|
-
export const HISTORY_SUMMARIZATION_THRESHOLD =
|
|
995
|
+
export const HISTORY_SUMMARIZATION_THRESHOLD = 2;
|
|
995
996
|
/**
|
|
996
|
-
* Summarizes
|
|
997
|
-
* exceeds {@link HISTORY_SUMMARIZATION_THRESHOLD} entries.
|
|
997
|
+
* Summarizes chat history entries starting from the first full user/model turn.
|
|
998
998
|
*
|
|
999
|
-
*
|
|
1000
|
-
*
|
|
999
|
+
* Compresses preceding conversation history into a structured high-density summary pair
|
|
1000
|
+
* in the active session while permanently preserving uncompressed history in fullHistory.
|
|
1001
1001
|
*
|
|
1002
1002
|
* @param chat - The active ProxyChatSession instance.
|
|
1003
1003
|
* @returns A promise resolving to true if history was summarized and updated; false otherwise.
|
|
@@ -1008,12 +1008,8 @@ export async function summarizeHistoryIfNeeded(chat, abortSignal) {
|
|
|
1008
1008
|
return false;
|
|
1009
1009
|
}
|
|
1010
1010
|
try {
|
|
1011
|
-
|
|
1012
|
-
const
|
|
1013
|
-
const olderHistory = history.slice(0, history.length - recentCount);
|
|
1014
|
-
const recentHistory = history.slice(history.length - recentCount);
|
|
1015
|
-
debugLog(`Summarizing ${olderHistory.length} older chat history entries...`);
|
|
1016
|
-
const summaryText = await summarizeChatHistory(olderHistory, abortSignal);
|
|
1011
|
+
debugLog(`Summarizing ${history.length} chat history entries (Flash-Lite Summarizer triggered after first turn)...`);
|
|
1012
|
+
const summaryText = await summarizeChatHistory(history, abortSignal);
|
|
1017
1013
|
if (summaryText && summaryText.trim().length > 0) {
|
|
1018
1014
|
const summaryContent = [
|
|
1019
1015
|
{
|
|
@@ -1025,8 +1021,8 @@ export async function summarizeHistoryIfNeeded(chat, abortSignal) {
|
|
|
1025
1021
|
parts: [{ text: 'Understood. I have reviewed and absorbed the summary of our preceding conversation.' }],
|
|
1026
1022
|
},
|
|
1027
1023
|
];
|
|
1028
|
-
chat.loadCompressedHistory(
|
|
1029
|
-
debugLog(`Chat history compressed successfully from ${history.length} to ${summaryContent.length
|
|
1024
|
+
chat.loadCompressedHistory(summaryContent);
|
|
1025
|
+
debugLog(`Chat history compressed successfully from ${history.length} to ${summaryContent.length} entries.`);
|
|
1030
1026
|
return true;
|
|
1031
1027
|
}
|
|
1032
1028
|
}
|
package/dist/services/ai.d.ts
CHANGED
|
@@ -22,6 +22,12 @@ export declare class ProxyChatSession {
|
|
|
22
22
|
loadRawHistory(history: Content[]): void;
|
|
23
23
|
loadCompressedHistory(history: Content[]): void;
|
|
24
24
|
addTurn(userContent: Content, modelContent: Content): void;
|
|
25
|
+
/**
|
|
26
|
+
* Appends or merges the final conversational summary text into the active session history.
|
|
27
|
+
* Ensures that the final response produced by finish_task, PM reconciliation, or final turns
|
|
28
|
+
* is permanently preserved as a model text response in both working history and full transcript.
|
|
29
|
+
*/
|
|
30
|
+
appendFinalSummary(text: string): void;
|
|
25
31
|
/**
|
|
26
32
|
* Retrieves the most recent conversation history as a formatted string.
|
|
27
33
|
* Useful for passing conversation context to stateless background agents.
|
|
@@ -42,6 +48,19 @@ export declare class ProxyChatSession {
|
|
|
42
48
|
* conversational context while preventing OOM crashes.
|
|
43
49
|
*/
|
|
44
50
|
private pruneHistory;
|
|
51
|
+
/**
|
|
52
|
+
* Collapses oversized functionResponse outputs in historical turns (>1 turn old)
|
|
53
|
+
* while strictly maintaining Gemini functionCall and functionResponse pairing.
|
|
54
|
+
*
|
|
55
|
+
* Gemini requires every functionCall in a model turn to have a matching functionResponse
|
|
56
|
+
* in the immediately following user turn. Deleting parts or entries breaks this invariant and
|
|
57
|
+
* causes 400 Bad Request errors. This method mutates oversized response outputs in-place
|
|
58
|
+
* on older turns to drastically conserve context window tokens.
|
|
59
|
+
*
|
|
60
|
+
* @param threshold Maximum characters allowed for a historical tool response before collapsing (default: 1500).
|
|
61
|
+
* @param turnsToKeep Number of recent turns to preserve unpruned (default: 1).
|
|
62
|
+
*/
|
|
63
|
+
pruneToolOutputHistory(threshold?: number, turnsToKeep?: number): void;
|
|
45
64
|
sendMessage(message: string | Array<{
|
|
46
65
|
functionResponse: {
|
|
47
66
|
name: string;
|
|
@@ -77,7 +96,7 @@ export declare function getPlanModeConfig(): {
|
|
|
77
96
|
tools: never[];
|
|
78
97
|
};
|
|
79
98
|
/**
|
|
80
|
-
* Compresses a large string of text using
|
|
99
|
+
* Compresses a large string of text using Gemini Flash.
|
|
81
100
|
* Used for shrinking context payloads to prevent OOM/choking.
|
|
82
101
|
*/
|
|
83
102
|
export declare function compressTextUsingFlashLite(text: string, instruction?: string, inlineData?: any, force?: boolean, abortSignal?: AbortSignal): Promise<string>;
|
|
@@ -91,6 +110,7 @@ export declare function createContextAgentSession(): any;
|
|
|
91
110
|
export declare function createIntentRouterSession(): any;
|
|
92
111
|
export declare function createExecutionComplexitySession(): any;
|
|
93
112
|
export declare function createInvestigationComplexitySession(): any;
|
|
113
|
+
export declare function createInvestigationSemanticSession(): any;
|
|
94
114
|
export declare function createWebSearchAgentSession(): any;
|
|
95
115
|
export declare function createHistorySummarizerSession(): any;
|
|
96
116
|
/**
|