minovative-mind-cli 2.9.0 → 2.10.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/inputHandler.d.ts +18 -3
- package/dist/services/agent/inputHandler.js +54 -29
- package/dist/services/agent/slashCommands.js +12 -2
- package/dist/services/agent.js +3 -0
- package/dist/services/ai.d.ts +2 -1
- package/dist/services/ai.js +130 -12
- package/dist/services/contextAgent.js +35 -4
- package/dist/services/investigationComplexity.d.ts +1 -1
- package/dist/services/investigationComplexity.js +1 -1
- package/dist/services/orchestration/investigationCache.d.ts +80 -5
- package/dist/services/orchestration/investigationCache.js +570 -41
- package/dist/services/orchestration/investigationOrchestrator.js +11 -2
- package/dist/utils/contextPrompts.d.ts +1 -1
- package/dist/utils/contextPrompts.js +18 -1
- package/dist/utils/systemPrompts.d.ts +2 -1
- package/dist/utils/systemPrompts.js +36 -7
- 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
|
|
|
@@ -31,6 +31,8 @@ export declare class AsyncInputHandler {
|
|
|
31
31
|
private stopped;
|
|
32
32
|
/** Reference to the AbortController controlling the active AI request to trigger cancellations */
|
|
33
33
|
private ac;
|
|
34
|
+
/** Timeout reference for reattaching listener after prompting to avoid race conditions */
|
|
35
|
+
private reattachTimeout;
|
|
34
36
|
/**
|
|
35
37
|
* Registers the active AbortController for the current AI request.
|
|
36
38
|
* This is triggered when the user commands a process cancel (e.g., typing "stop").
|
|
@@ -45,6 +47,14 @@ export declare class AsyncInputHandler {
|
|
|
45
47
|
* @returns True if a text prompt is currently displayed, false otherwise.
|
|
46
48
|
*/
|
|
47
49
|
isCurrentlyPrompting(): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Checks if there are any queued user messages pending.
|
|
52
|
+
*/
|
|
53
|
+
hasQueuedMessages(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Returns a copy of currently queued messages without clearing them.
|
|
56
|
+
*/
|
|
57
|
+
peek(): string[];
|
|
48
58
|
/**
|
|
49
59
|
* Blocks and waits until any active prompting action is completed.
|
|
50
60
|
* Guarantees terminal stdout is clean before resuming logs.
|
|
@@ -63,16 +73,21 @@ export declare class AsyncInputHandler {
|
|
|
63
73
|
private onData;
|
|
64
74
|
/**
|
|
65
75
|
* Starts intercepting keystrokes and enables raw terminal processing.
|
|
66
|
-
*
|
|
76
|
+
* Clears any stale queued messages and saves the original raw mode configuration.
|
|
67
77
|
*
|
|
68
78
|
* @param spinner - The current active Clack spinner UI reference, if any.
|
|
69
79
|
*/
|
|
70
80
|
start(spinner?: ReturnType<typeof p.spinner>): void;
|
|
71
81
|
/**
|
|
72
|
-
* Disables raw mode, stops intercepting keystrokes,
|
|
73
|
-
* the terminal stdin stream to its original raw/cooked state
|
|
82
|
+
* Disables raw mode, stops intercepting keystrokes, restores
|
|
83
|
+
* the terminal stdin stream to its original raw/cooked state, and
|
|
84
|
+
* completely clears the queued messages.
|
|
74
85
|
*/
|
|
75
86
|
stop(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Clears all pending queued messages without modifying stream state.
|
|
89
|
+
*/
|
|
90
|
+
clear(): void;
|
|
76
91
|
/**
|
|
77
92
|
* Clears the spinner reference without stopping the input handler.
|
|
78
93
|
*/
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { truncateForTerminal } from '../../utils/terminal.js';
|
|
4
|
+
if (process.stdin.setMaxListeners) {
|
|
5
|
+
process.stdin.setMaxListeners(50);
|
|
6
|
+
}
|
|
7
|
+
if (process.stdout.setMaxListeners) {
|
|
8
|
+
process.stdout.setMaxListeners(50);
|
|
9
|
+
}
|
|
4
10
|
/**
|
|
5
11
|
* Asynchronous Input Handler (AsyncInputHandler)
|
|
6
12
|
*
|
|
@@ -33,6 +39,8 @@ export class AsyncInputHandler {
|
|
|
33
39
|
stopped = true;
|
|
34
40
|
/** Reference to the AbortController controlling the active AI request to trigger cancellations */
|
|
35
41
|
ac = null;
|
|
42
|
+
/** Timeout reference for reattaching listener after prompting to avoid race conditions */
|
|
43
|
+
reattachTimeout = null;
|
|
36
44
|
/**
|
|
37
45
|
* Registers the active AbortController for the current AI request.
|
|
38
46
|
* This is triggered when the user commands a process cancel (e.g., typing "stop").
|
|
@@ -51,6 +59,18 @@ export class AsyncInputHandler {
|
|
|
51
59
|
isCurrentlyPrompting() {
|
|
52
60
|
return this.isPrompting;
|
|
53
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Checks if there are any queued user messages pending.
|
|
64
|
+
*/
|
|
65
|
+
hasQueuedMessages() {
|
|
66
|
+
return this.queue.length > 0;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns a copy of currently queued messages without clearing them.
|
|
70
|
+
*/
|
|
71
|
+
peek() {
|
|
72
|
+
return [...this.queue];
|
|
73
|
+
}
|
|
54
74
|
/**
|
|
55
75
|
* Blocks and waits until any active prompting action is completed.
|
|
56
76
|
* Guarantees terminal stdout is clean before resuming logs.
|
|
@@ -72,7 +92,7 @@ export class AsyncInputHandler {
|
|
|
72
92
|
*/
|
|
73
93
|
onData = async (chunk) => {
|
|
74
94
|
try {
|
|
75
|
-
if (this.isPrompting)
|
|
95
|
+
if (this.stopped || this.isPrompting)
|
|
76
96
|
return;
|
|
77
97
|
const char = chunk.toString();
|
|
78
98
|
// Handle Ctrl+C (End of Text ASCII 0x03) immediately
|
|
@@ -99,6 +119,9 @@ export class AsyncInputHandler {
|
|
|
99
119
|
placeholder: '(Leave blank and press Enter to cancel)',
|
|
100
120
|
initialValue: char,
|
|
101
121
|
});
|
|
122
|
+
if (this.stopped) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
102
125
|
let wasAborted = false;
|
|
103
126
|
if (!p.isCancel(userInput) && userInput.trim()) {
|
|
104
127
|
const text = userInput.trim();
|
|
@@ -114,7 +137,7 @@ export class AsyncInputHandler {
|
|
|
114
137
|
p.log.info(pc.cyan(`📥 Queued message: "${text}"`));
|
|
115
138
|
}
|
|
116
139
|
}
|
|
117
|
-
if (this.spinner && !wasAborted) {
|
|
140
|
+
if (this.spinner && !wasAborted && !this.stopped) {
|
|
118
141
|
const resumeMsg = this.spinner._lastMessage || 'Resuming execution...';
|
|
119
142
|
this.spinner.start(resumeMsg);
|
|
120
143
|
}
|
|
@@ -128,9 +151,14 @@ export class AsyncInputHandler {
|
|
|
128
151
|
process.stdin.setRawMode(true);
|
|
129
152
|
}
|
|
130
153
|
process.stdin.resume();
|
|
154
|
+
if (this.reattachTimeout) {
|
|
155
|
+
clearTimeout(this.reattachTimeout);
|
|
156
|
+
}
|
|
131
157
|
// Small delay before reattaching listener to avoid capturing duplicate keypress frames
|
|
132
|
-
setTimeout(() => {
|
|
158
|
+
this.reattachTimeout = setTimeout(() => {
|
|
159
|
+
this.reattachTimeout = null;
|
|
133
160
|
if (!this.stopped) {
|
|
161
|
+
process.stdin.removeListener('data', this.onData);
|
|
134
162
|
process.stdin.on('data', this.onData);
|
|
135
163
|
}
|
|
136
164
|
this.isPrompting = false;
|
|
@@ -143,56 +171,53 @@ export class AsyncInputHandler {
|
|
|
143
171
|
};
|
|
144
172
|
/**
|
|
145
173
|
* Starts intercepting keystrokes and enables raw terminal processing.
|
|
146
|
-
*
|
|
174
|
+
* Clears any stale queued messages and saves the original raw mode configuration.
|
|
147
175
|
*
|
|
148
176
|
* @param spinner - The current active Clack spinner UI reference, if any.
|
|
149
177
|
*/
|
|
150
178
|
start(spinner) {
|
|
179
|
+
if (this.reattachTimeout) {
|
|
180
|
+
clearTimeout(this.reattachTimeout);
|
|
181
|
+
this.reattachTimeout = null;
|
|
182
|
+
}
|
|
151
183
|
this.stopped = false;
|
|
184
|
+
this.queue = [];
|
|
152
185
|
if (spinner) {
|
|
153
|
-
this.spinner
|
|
154
|
-
// Monkey-patch to track the latest message for un-pausing
|
|
155
|
-
if (!spinner._isPatched) {
|
|
156
|
-
;
|
|
157
|
-
spinner._isPatched = true;
|
|
158
|
-
spinner._lastMessage = 'Executing...';
|
|
159
|
-
const originalMessage = spinner.message.bind(spinner);
|
|
160
|
-
spinner.message = (msg) => {
|
|
161
|
-
if (msg) {
|
|
162
|
-
;
|
|
163
|
-
spinner._lastMessage = msg;
|
|
164
|
-
}
|
|
165
|
-
originalMessage(msg ? truncateForTerminal(msg) : msg);
|
|
166
|
-
};
|
|
167
|
-
const originalStart = spinner.start.bind(spinner);
|
|
168
|
-
spinner.start = (msg) => {
|
|
169
|
-
if (msg) {
|
|
170
|
-
;
|
|
171
|
-
spinner._lastMessage = msg;
|
|
172
|
-
}
|
|
173
|
-
originalStart(msg ? truncateForTerminal(msg) : msg);
|
|
174
|
-
};
|
|
175
|
-
}
|
|
186
|
+
this.setSpinner(spinner);
|
|
176
187
|
}
|
|
177
188
|
if (process.stdin.isTTY) {
|
|
178
189
|
this.originalRawMode = process.stdin.isRaw;
|
|
179
190
|
process.stdin.setRawMode(true);
|
|
180
191
|
}
|
|
181
192
|
process.stdin.resume();
|
|
193
|
+
process.stdin.removeListener('data', this.onData);
|
|
182
194
|
process.stdin.on('data', this.onData);
|
|
183
195
|
}
|
|
184
196
|
/**
|
|
185
|
-
* Disables raw mode, stops intercepting keystrokes,
|
|
186
|
-
* the terminal stdin stream to its original raw/cooked state
|
|
197
|
+
* Disables raw mode, stops intercepting keystrokes, restores
|
|
198
|
+
* the terminal stdin stream to its original raw/cooked state, and
|
|
199
|
+
* completely clears the queued messages.
|
|
187
200
|
*/
|
|
188
201
|
stop() {
|
|
189
202
|
this.stopped = true;
|
|
203
|
+
this.isPrompting = false;
|
|
204
|
+
if (this.reattachTimeout) {
|
|
205
|
+
clearTimeout(this.reattachTimeout);
|
|
206
|
+
this.reattachTimeout = null;
|
|
207
|
+
}
|
|
190
208
|
process.stdin.removeListener('data', this.onData);
|
|
191
209
|
if (process.stdin.isTTY) {
|
|
192
210
|
process.stdin.setRawMode(this.originalRawMode);
|
|
193
211
|
}
|
|
194
212
|
process.stdin.resume();
|
|
195
213
|
this.spinner = null;
|
|
214
|
+
this.queue = [];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Clears all pending queued messages without modifying stream state.
|
|
218
|
+
*/
|
|
219
|
+
clear() {
|
|
220
|
+
this.queue = [];
|
|
196
221
|
}
|
|
197
222
|
/**
|
|
198
223
|
* Clears the spinner reference without stopping the input handler.
|
|
@@ -715,7 +715,7 @@ export async function handleSlashCommand(command, context) {
|
|
|
715
715
|
}
|
|
716
716
|
else if (chatsMenu === 'bulk-delete') {
|
|
717
717
|
const selectedIds = await p['multiselect']({
|
|
718
|
-
message:
|
|
718
|
+
message: `Select chat sessions to delete: ${pc.dim('(Press Esc to go back / cancel)')}`,
|
|
719
719
|
options: sessions
|
|
720
720
|
.slice()
|
|
721
721
|
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
|
|
@@ -723,7 +723,7 @@ export async function handleSlashCommand(command, context) {
|
|
|
723
723
|
value: s.id,
|
|
724
724
|
label: `${truncate(s.title, 50)} (${new Date(s.timestamp).toLocaleString()})`,
|
|
725
725
|
})),
|
|
726
|
-
required:
|
|
726
|
+
required: false,
|
|
727
727
|
});
|
|
728
728
|
if (p.isCancel(selectedIds) || !Array.isArray(selectedIds) || selectedIds.length === 0) {
|
|
729
729
|
p.log.warn('Bulk delete canceled.');
|
|
@@ -735,6 +735,16 @@ export async function handleSlashCommand(command, context) {
|
|
|
735
735
|
if (confirm) {
|
|
736
736
|
await chatHistoryService.bulkDeleteSessions(selectedIds);
|
|
737
737
|
p.log.success(`Successfully deleted ${selectedIds.length} session(s).`);
|
|
738
|
+
if (chatSessionState && selectedIds.includes(chatSessionState.id)) {
|
|
739
|
+
chat.clearHistory();
|
|
740
|
+
process.stdout.write('\x1B[2J\x1B[3J\x1B[H'); // Hard clear screen and scrollback
|
|
741
|
+
printLogo();
|
|
742
|
+
p.intro(`${brandBg(' Minovative Mind CLI ')} ${pc.dim('v' + version)}`);
|
|
743
|
+
p.log.info(`${pc.dim('Workspace:')} ${brandFg(workspaceRoot)}`);
|
|
744
|
+
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.`);
|
|
745
|
+
p.log.warn('Active session was deleted. Chat history cleared.');
|
|
746
|
+
console.log(pc.dim('\nType your coding request below. Type "exit" or "quit" to leave.\n'));
|
|
747
|
+
}
|
|
738
748
|
}
|
|
739
749
|
else {
|
|
740
750
|
p.log.warn('Bulk delete canceled.');
|
package/dist/services/agent.js
CHANGED
|
@@ -504,6 +504,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
|
|
|
504
504
|
const orchestrator = new Orchestrator(workspaceRoot, chatSessionState.id, inputHandler);
|
|
505
505
|
const handledByOrchestrator = await orchestrator.runOrchestration(finalInput, dynamicSystemInstruction, ac.signal);
|
|
506
506
|
if (typeof handledByOrchestrator === 'string') {
|
|
507
|
+
inputHandler.stop();
|
|
507
508
|
const isStopped = handledByOrchestrator.includes('Generation stopped') ||
|
|
508
509
|
handledByOrchestrator.includes('[Generation stopped');
|
|
509
510
|
if (isStopped) {
|
|
@@ -654,6 +655,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
|
|
|
654
655
|
// Stage 2: Recursive Tool Loops and Automated Self-Correction (delegated to a helper function to avoid nested loop warning)
|
|
655
656
|
const correctionRes = await executeSelfCorrectionLoop(chat, result, workspaceRoot, inputHandler, effectiveTargetAgent, ac.signal, spinner, userInput, isPlanMode);
|
|
656
657
|
const finalText = correctionRes.finalText;
|
|
658
|
+
inputHandler.stop();
|
|
657
659
|
// Update latest usage metadata to reflect all completed turns
|
|
658
660
|
const usage = getAndResetTurnUsage();
|
|
659
661
|
const postExecutionChanges = changeLogger.getCurrentChangeSet()?.changes || [];
|
|
@@ -890,6 +892,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
|
|
|
890
892
|
p.log.error(`${pc.red('Error:')} ${message}`);
|
|
891
893
|
}
|
|
892
894
|
finally {
|
|
895
|
+
inputHandler.stop();
|
|
893
896
|
spinner.stop();
|
|
894
897
|
process.stdout.write('\x1b[2K\r');
|
|
895
898
|
// Persist any verified or partial changes into our session change ledger.
|
package/dist/services/ai.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export declare function getPlanModeConfig(): {
|
|
|
77
77
|
tools: never[];
|
|
78
78
|
};
|
|
79
79
|
/**
|
|
80
|
-
* Compresses a large string of text using
|
|
80
|
+
* Compresses a large string of text using Gemini Flash.
|
|
81
81
|
* Used for shrinking context payloads to prevent OOM/choking.
|
|
82
82
|
*/
|
|
83
83
|
export declare function compressTextUsingFlashLite(text: string, instruction?: string, inlineData?: any, force?: boolean, abortSignal?: AbortSignal): Promise<string>;
|
|
@@ -91,6 +91,7 @@ export declare function createContextAgentSession(): any;
|
|
|
91
91
|
export declare function createIntentRouterSession(): any;
|
|
92
92
|
export declare function createExecutionComplexitySession(): any;
|
|
93
93
|
export declare function createInvestigationComplexitySession(): any;
|
|
94
|
+
export declare function createInvestigationSemanticSession(): any;
|
|
94
95
|
export declare function createWebSearchAgentSession(): any;
|
|
95
96
|
export declare function createHistorySummarizerSession(): any;
|
|
96
97
|
/**
|
package/dist/services/ai.js
CHANGED
|
@@ -5,7 +5,7 @@ import { getMetricCollector } from './metrics.js';
|
|
|
5
5
|
import { getAuthorizedIdToken, checkByokSubscription } from './auth.js';
|
|
6
6
|
import { debugLog } from '../utils/logger.js';
|
|
7
7
|
import { readCache, writeCache } from '../utils/projectStorage.js';
|
|
8
|
-
import { GENERAL_CHAT_INSTRUCTION, PLAN_EXECUTION_INSTRUCTION, PLAN_MODE_INSTRUCTION, CONTEXT_SYSTEM_INSTRUCTION, INTENT_ROUTER_SYSTEM_INSTRUCTION, WEB_SEARCH_SYSTEM_INSTRUCTION, EXECUTION_COMPLEXITY_SYSTEM_INSTRUCTION, INVESTIGATION_COMPLEXITY_SYSTEM_INSTRUCTION, HISTORY_SUMMARIZER_SYSTEM_INSTRUCTION, } from '../utils/systemPrompts.js';
|
|
8
|
+
import { GENERAL_CHAT_INSTRUCTION, PLAN_EXECUTION_INSTRUCTION, PLAN_MODE_INSTRUCTION, CONTEXT_SYSTEM_INSTRUCTION, INTENT_ROUTER_SYSTEM_INSTRUCTION, WEB_SEARCH_SYSTEM_INSTRUCTION, EXECUTION_COMPLEXITY_SYSTEM_INSTRUCTION, INVESTIGATION_COMPLEXITY_SYSTEM_INSTRUCTION, INVESTIGATION_SEMANTIC_SYSTEM_INSTRUCTION, HISTORY_SUMMARIZER_SYSTEM_INSTRUCTION, } from '../utils/systemPrompts.js';
|
|
9
9
|
import { workspaceRegistry } from './workspaceRegistry.js';
|
|
10
10
|
import { loadCredentials } from '../utils/credentialStore.js';
|
|
11
11
|
import { ProxyClient } from './proxyClient.js';
|
|
@@ -345,7 +345,7 @@ export function getPlanModeConfig() {
|
|
|
345
345
|
};
|
|
346
346
|
}
|
|
347
347
|
/**
|
|
348
|
-
* Compresses a large string of text using
|
|
348
|
+
* Compresses a large string of text using Gemini Flash.
|
|
349
349
|
* Used for shrinking context payloads to prevent OOM/choking.
|
|
350
350
|
*/
|
|
351
351
|
export async function compressTextUsingFlashLite(text, instruction = "<directives>\nSummarize the following text concisely. Preserve the most critical technical details, function names, and architecture logic. Make sure it's understandable without the fluff.\n</directives>", inlineData, force = false, abortSignal) {
|
|
@@ -357,7 +357,7 @@ export async function compressTextUsingFlashLite(text, instruction = "<directive
|
|
|
357
357
|
const idToken = await getAuthorizedIdToken();
|
|
358
358
|
if (!idToken)
|
|
359
359
|
return text;
|
|
360
|
-
let model = GEMINI_MODELS.
|
|
360
|
+
let model = GEMINI_MODELS.FLASH;
|
|
361
361
|
const parts = [{ text }];
|
|
362
362
|
if (inlineData) {
|
|
363
363
|
parts.push({ inlineData });
|
|
@@ -397,7 +397,7 @@ export async function compressTextUsingFlashLite(text, instruction = "<directive
|
|
|
397
397
|
if (abortSignal?.aborted || error?.name === 'AbortError' || error?.message?.includes('abort')) {
|
|
398
398
|
return text;
|
|
399
399
|
}
|
|
400
|
-
debugLog(`Failed to compress text using flash
|
|
400
|
+
debugLog(`Failed to compress text using flash: ${error}`);
|
|
401
401
|
if (error?.status === 401 ||
|
|
402
402
|
error?.status === 403 ||
|
|
403
403
|
error?.message?.includes('API_KEY_INVALID') ||
|
|
@@ -568,25 +568,143 @@ export function createContextAgentSession() {
|
|
|
568
568
|
export function createIntentRouterSession() {
|
|
569
569
|
let model = getGlobalActiveModel();
|
|
570
570
|
if (model === 'auto' || model.includes('claude'))
|
|
571
|
-
model = GEMINI_MODELS.
|
|
571
|
+
model = GEMINI_MODELS.FLASH;
|
|
572
572
|
return new ProxyChatSession(model, INTENT_ROUTER_SYSTEM_INSTRUCTION, [], // no tools
|
|
573
|
-
{
|
|
573
|
+
{
|
|
574
|
+
temperature: 0,
|
|
575
|
+
responseMimeType: 'application/json',
|
|
576
|
+
responseSchema: {
|
|
577
|
+
type: SchemaType.OBJECT,
|
|
578
|
+
properties: {
|
|
579
|
+
context: {
|
|
580
|
+
type: SchemaType.STRING,
|
|
581
|
+
enum: ['SEARCH', 'SKIP'],
|
|
582
|
+
description: 'Whether workspace context search is required',
|
|
583
|
+
},
|
|
584
|
+
agent: {
|
|
585
|
+
type: SchemaType.STRING,
|
|
586
|
+
enum: ['CHAT', 'EXECUTE'],
|
|
587
|
+
description: 'Target agent to route the request to',
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
required: ['context', 'agent'],
|
|
591
|
+
},
|
|
592
|
+
});
|
|
574
593
|
}
|
|
575
594
|
// ─── Execution Complexity Router Service ──────────────────────────────
|
|
576
595
|
export function createExecutionComplexitySession() {
|
|
577
596
|
let model = getGlobalActiveModel();
|
|
578
597
|
if (model === 'auto' || model.includes('claude'))
|
|
579
|
-
model = GEMINI_MODELS.
|
|
598
|
+
model = GEMINI_MODELS.FLASH;
|
|
580
599
|
return new ProxyChatSession(model, EXECUTION_COMPLEXITY_SYSTEM_INSTRUCTION, [], // no tools
|
|
581
|
-
{
|
|
600
|
+
{
|
|
601
|
+
temperature: 0,
|
|
602
|
+
responseMimeType: 'application/json',
|
|
603
|
+
responseSchema: {
|
|
604
|
+
type: SchemaType.OBJECT,
|
|
605
|
+
properties: {
|
|
606
|
+
complexity: {
|
|
607
|
+
type: SchemaType.STRING,
|
|
608
|
+
enum: ['EASY', 'HARD'],
|
|
609
|
+
description: 'Execution task complexity rating',
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
required: ['complexity'],
|
|
613
|
+
},
|
|
614
|
+
});
|
|
582
615
|
}
|
|
583
616
|
// ─── Investigation Complexity Router Service ─────────────────────────
|
|
584
617
|
export function createInvestigationComplexitySession() {
|
|
585
618
|
let model = getGlobalActiveModel();
|
|
586
619
|
if (model === 'auto' || model.includes('claude'))
|
|
587
|
-
model = GEMINI_MODELS.
|
|
620
|
+
model = GEMINI_MODELS.FLASH;
|
|
588
621
|
return new ProxyChatSession(model, INVESTIGATION_COMPLEXITY_SYSTEM_INSTRUCTION, [], // no tools
|
|
589
|
-
{
|
|
622
|
+
{
|
|
623
|
+
temperature: 0,
|
|
624
|
+
responseMimeType: 'application/json',
|
|
625
|
+
responseSchema: {
|
|
626
|
+
type: SchemaType.OBJECT,
|
|
627
|
+
properties: {
|
|
628
|
+
strategy: {
|
|
629
|
+
type: SchemaType.STRING,
|
|
630
|
+
enum: ['SINGLE', 'PARALLEL'],
|
|
631
|
+
description: 'Investigation strategy (SINGLE or PARALLEL)',
|
|
632
|
+
},
|
|
633
|
+
domains: {
|
|
634
|
+
type: SchemaType.ARRAY,
|
|
635
|
+
items: {
|
|
636
|
+
type: SchemaType.STRING,
|
|
637
|
+
},
|
|
638
|
+
description: 'All identified investigation domains',
|
|
639
|
+
},
|
|
640
|
+
agentAssignments: {
|
|
641
|
+
type: SchemaType.ARRAY,
|
|
642
|
+
items: {
|
|
643
|
+
type: SchemaType.OBJECT,
|
|
644
|
+
properties: {
|
|
645
|
+
agentLabel: {
|
|
646
|
+
type: SchemaType.STRING,
|
|
647
|
+
description: 'Label of the sub-agent',
|
|
648
|
+
},
|
|
649
|
+
domains: {
|
|
650
|
+
type: SchemaType.ARRAY,
|
|
651
|
+
items: {
|
|
652
|
+
type: SchemaType.STRING,
|
|
653
|
+
},
|
|
654
|
+
description: 'Domains assigned to this sub-agent',
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
required: ['agentLabel', 'domains'],
|
|
658
|
+
},
|
|
659
|
+
description: 'Sub-agent domain group assignments',
|
|
660
|
+
},
|
|
661
|
+
reasoning: {
|
|
662
|
+
type: SchemaType.STRING,
|
|
663
|
+
description: 'Brief justification for the chosen strategy and grouping',
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
required: ['strategy', 'domains', 'agentAssignments', 'reasoning'],
|
|
667
|
+
},
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
// ─── Investigation Semantic Router Service ───────────────────────────
|
|
671
|
+
export function createInvestigationSemanticSession() {
|
|
672
|
+
let model = getGlobalActiveModel();
|
|
673
|
+
if (model === 'auto' || model.includes('claude'))
|
|
674
|
+
model = GEMINI_MODELS.FLASH_LITE;
|
|
675
|
+
return new ProxyChatSession(model, INVESTIGATION_SEMANTIC_SYSTEM_INSTRUCTION, [], // no tools
|
|
676
|
+
{
|
|
677
|
+
temperature: 0,
|
|
678
|
+
responseMimeType: 'application/json',
|
|
679
|
+
responseSchema: {
|
|
680
|
+
type: SchemaType.OBJECT,
|
|
681
|
+
properties: {
|
|
682
|
+
topics: {
|
|
683
|
+
type: SchemaType.ARRAY,
|
|
684
|
+
items: {
|
|
685
|
+
type: SchemaType.STRING,
|
|
686
|
+
},
|
|
687
|
+
description: 'Technical topic or domain identifiers',
|
|
688
|
+
},
|
|
689
|
+
components: {
|
|
690
|
+
type: SchemaType.ARRAY,
|
|
691
|
+
items: {
|
|
692
|
+
type: SchemaType.STRING,
|
|
693
|
+
},
|
|
694
|
+
description: 'Target component, file, function, or endpoint names',
|
|
695
|
+
},
|
|
696
|
+
intent: {
|
|
697
|
+
type: SchemaType.STRING,
|
|
698
|
+
description: 'Primary intent category (e.g. bug_fix, feature_addition, explanation, refactoring)',
|
|
699
|
+
},
|
|
700
|
+
reasoning: {
|
|
701
|
+
type: SchemaType.STRING,
|
|
702
|
+
description: 'Brief justification for the extracted metadata',
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
required: ['topics', 'components', 'intent'],
|
|
706
|
+
},
|
|
707
|
+
});
|
|
590
708
|
}
|
|
591
709
|
// ─── Web Search Agent Service ───────────────────────────────────────────
|
|
592
710
|
export function createWebSearchAgentSession() {
|
|
@@ -604,7 +722,7 @@ export function createWebSearchAgentSession() {
|
|
|
604
722
|
export function createHistorySummarizerSession() {
|
|
605
723
|
let model = getGlobalActiveModel();
|
|
606
724
|
if (model === 'auto' || model.includes('claude'))
|
|
607
|
-
model = GEMINI_MODELS.
|
|
725
|
+
model = GEMINI_MODELS.FLASH;
|
|
608
726
|
return new ProxyChatSession(model, HISTORY_SUMMARIZER_SYSTEM_INSTRUCTION, [], {
|
|
609
727
|
temperature: 0.2,
|
|
610
728
|
maxOutputTokens: MAX_OUTPUT_TOKENS,
|
|
@@ -655,7 +773,7 @@ export async function generateChatTitle(firstMessage, abortSignal) {
|
|
|
655
773
|
const contents = [{ role: 'user', parts: [{ text: firstMessage.substring(0, 500) }] }];
|
|
656
774
|
let model = getGlobalActiveModel();
|
|
657
775
|
if (model === 'auto' || model.includes('claude'))
|
|
658
|
-
model = GEMINI_MODELS.
|
|
776
|
+
model = GEMINI_MODELS.FLASH;
|
|
659
777
|
const byokEnabled = await isByokEnabled();
|
|
660
778
|
let result;
|
|
661
779
|
if (byokEnabled) {
|
|
@@ -246,7 +246,31 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
246
246
|
}
|
|
247
247
|
const projectType = primaryProjectType;
|
|
248
248
|
const { lookupInvestigation, saveInvestigation } = await import('./orchestration/investigationCache.js');
|
|
249
|
-
const
|
|
249
|
+
const collector = getMetricCollector();
|
|
250
|
+
const lookupStart = Date.now();
|
|
251
|
+
let cacheHit = null;
|
|
252
|
+
try {
|
|
253
|
+
cacheHit = await lookupInvestigation(workspaceRoot, userRequest, {
|
|
254
|
+
abortSignal,
|
|
255
|
+
onProgress,
|
|
256
|
+
allowSemanticFallback: true,
|
|
257
|
+
});
|
|
258
|
+
if (collector) {
|
|
259
|
+
collector.recordCachePerformance('investigation', Date.now() - lookupStart);
|
|
260
|
+
if (cacheHit) {
|
|
261
|
+
collector.recordCacheHit('investigation');
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
collector.recordCacheMiss('investigation');
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
if (err?.name === 'AbortError' || abortSignal?.aborted) {
|
|
270
|
+
throw err;
|
|
271
|
+
}
|
|
272
|
+
debugLog(`Investigation cache lookup error: ${err?.message || err}`);
|
|
273
|
+
}
|
|
250
274
|
if (cacheHit) {
|
|
251
275
|
if (onProgress)
|
|
252
276
|
onProgress(`⚡ Memory Bank HIT — loaded ${cacheHit.entry.relevantFiles.length} files from cache`);
|
|
@@ -596,7 +620,6 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
596
620
|
// Prepare next turn
|
|
597
621
|
currentMessage = functionResponses;
|
|
598
622
|
}
|
|
599
|
-
const collector = getMetricCollector();
|
|
600
623
|
if (collector) {
|
|
601
624
|
collector.recordContextSelectedFiles(Array.from(relevantFiles.keys()));
|
|
602
625
|
if (!isInvestigationFinished || relevantFiles.size === 0) {
|
|
@@ -604,8 +627,16 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
|
|
|
604
627
|
}
|
|
605
628
|
}
|
|
606
629
|
if (isInvestigationFinished && relevantFiles.size > 0) {
|
|
607
|
-
|
|
608
|
-
|
|
630
|
+
try {
|
|
631
|
+
const { saveInvestigation } = await import('./orchestration/investigationCache.js');
|
|
632
|
+
await saveInvestigation(workspaceRoot, userRequest, Array.from(relevantFiles.keys()), summary, undefined, abortSignal);
|
|
633
|
+
}
|
|
634
|
+
catch (err) {
|
|
635
|
+
if (err?.name === 'AbortError' || abortSignal?.aborted) {
|
|
636
|
+
throw err;
|
|
637
|
+
}
|
|
638
|
+
debugLog(`Failed to save investigation to cache: ${err?.message || err}`);
|
|
639
|
+
}
|
|
609
640
|
}
|
|
610
641
|
return {
|
|
611
642
|
contextResult: { projectTree, projectType, relevantFiles, summary, webSearchSummary, isParallel: false },
|
|
@@ -31,7 +31,7 @@ export interface InvestigationComplexityResult {
|
|
|
31
31
|
/**
|
|
32
32
|
* Evaluates whether the investigation phase should be parallelized.
|
|
33
33
|
*
|
|
34
|
-
* Uses `gemini-3.
|
|
34
|
+
* Uses `gemini-3.7-flash` (under auto mode) at temperature 0 to classify the prompt's
|
|
35
35
|
* investigation complexity. The PM dynamically identifies domains and groups
|
|
36
36
|
* them into agent assignments. Agent count = `agentAssignments.length`, which
|
|
37
37
|
* may be fewer than `domains.length` when related domains are batched together.
|
|
@@ -16,7 +16,7 @@ import { debugLog } from '../utils/logger.js';
|
|
|
16
16
|
/**
|
|
17
17
|
* Evaluates whether the investigation phase should be parallelized.
|
|
18
18
|
*
|
|
19
|
-
* Uses `gemini-3.
|
|
19
|
+
* Uses `gemini-3.7-flash` (under auto mode) at temperature 0 to classify the prompt's
|
|
20
20
|
* investigation complexity. The PM dynamically identifies domains and groups
|
|
21
21
|
* them into agent assignments. Agent count = `agentAssignments.length`, which
|
|
22
22
|
* may be fewer than `domains.length` when related domains are batched together.
|