minovative-mind-cli 2.8.4 → 2.9.1

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.
@@ -139,14 +139,19 @@ async function detectProjectType(workspaceRoot) {
139
139
  }
140
140
  return types.join(' / ');
141
141
  }
142
- export async function routeIntent(userRequest, chatHistory = '') {
142
+ export async function routeIntent(userRequest, chatHistory = '', abortSignal) {
143
+ if (abortSignal?.aborted) {
144
+ const err = new Error('Operation aborted');
145
+ err.name = 'AbortError';
146
+ throw err;
147
+ }
143
148
  try {
144
149
  const session = createIntentRouterSession();
145
150
  let prompt = `User Request: "${userRequest}"`;
146
151
  if (chatHistory) {
147
152
  prompt = `Previous Conversation Context:\n${chatHistory}\n\n${prompt}`;
148
153
  }
149
- const result = await session.sendMessage(prompt);
154
+ const result = await session.sendMessage(prompt, undefined, abortSignal);
150
155
  const text = result.response.text()?.trim() || '{}';
151
156
  const parsed = JSON.parse(text);
152
157
  debugLog(`Intent Router Parsed: ${JSON.stringify(parsed)}`);
@@ -156,12 +161,22 @@ export async function routeIntent(userRequest, chatHistory = '') {
156
161
  };
157
162
  }
158
163
  catch (e) {
164
+ if (abortSignal?.aborted || e?.name === 'AbortError' || e?.message?.includes('abort')) {
165
+ const err = new Error('Operation aborted');
166
+ err.name = 'AbortError';
167
+ throw err;
168
+ }
159
169
  debugLog(`Intent Router failed to parse JSON, falling back to EXECUTE. Error: ${String(e)}`);
160
170
  // Fallback to searching if the router fails
161
171
  return { needsContext: true, targetAgent: 'EXECUTE' };
162
172
  }
163
173
  }
164
- export async function evaluateExecutionComplexity(userRequest, investigationSummary, numRelevantFiles, chatHistory = '') {
174
+ export async function evaluateExecutionComplexity(userRequest, investigationSummary, numRelevantFiles, chatHistory = '', abortSignal) {
175
+ if (abortSignal?.aborted) {
176
+ const err = new Error('Operation aborted');
177
+ err.name = 'AbortError';
178
+ throw err;
179
+ }
165
180
  try {
166
181
  const session = createExecutionComplexitySession();
167
182
  let prompt = `User Request: "${userRequest}"
@@ -170,24 +185,39 @@ Number of Relevant Files: ${numRelevantFiles}`;
170
185
  if (chatHistory) {
171
186
  prompt = `Previous Conversation Context:\n${chatHistory}\n\n${prompt}`;
172
187
  }
173
- const result = await session.sendMessage(prompt);
188
+ const result = await session.sendMessage(prompt, undefined, abortSignal);
174
189
  const text = result.response.text()?.trim() || '{}';
175
190
  const parsed = JSON.parse(text);
176
191
  debugLog(`Execution Complexity Parsed: ${JSON.stringify(parsed)}`);
177
192
  return parsed.complexity === 'EASY' ? 'EASY' : 'HARD';
178
193
  }
179
194
  catch (e) {
195
+ if (abortSignal?.aborted || e?.name === 'AbortError' || e?.message?.includes('abort')) {
196
+ const err = new Error('Operation aborted');
197
+ err.name = 'AbortError';
198
+ throw err;
199
+ }
180
200
  debugLog(`Execution Complexity Router failed, falling back to HARD. Error: ${String(e)}`);
181
201
  return 'HARD';
182
202
  }
183
203
  }
184
204
  export async function gatherContext(workspaceRoot, userRequest, chatHistory = '', inputHandler, abortSignal, onProgress, onToolCall) {
205
+ if (abortSignal.aborted) {
206
+ const err = new Error('Operation aborted');
207
+ err.name = 'AbortError';
208
+ throw err;
209
+ }
185
210
  // Always skip slash commands for zero latency
186
211
  if (userRequest.startsWith('/')) {
187
212
  return { contextResult: null, targetAgent: 'EXECUTE', chainedMessages: [] };
188
213
  }
189
214
  // Use the AI Intent Router to decide if we need to search
190
- const { needsContext, targetAgent } = await routeIntent(userRequest, chatHistory);
215
+ const { needsContext, targetAgent } = await routeIntent(userRequest, chatHistory, abortSignal);
216
+ if (abortSignal.aborted) {
217
+ const err = new Error('Operation aborted');
218
+ err.name = 'AbortError';
219
+ throw err;
220
+ }
191
221
  debugLog(`GatherContext Route: needsContext=${needsContext}, targetAgent=${targetAgent}`);
192
222
  if (!needsContext) {
193
223
  return { contextResult: null, targetAgent, chainedMessages: [] };
@@ -197,17 +227,22 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
197
227
  let projectTree = '';
198
228
  let primaryProjectType = 'Unknown';
199
229
  for (const { alias, root } of allRoots) {
230
+ if (abortSignal.aborted) {
231
+ const err = new Error('Operation aborted');
232
+ err.name = 'AbortError';
233
+ throw err;
234
+ }
200
235
  const label = alias ? `@${alias} (${root})` : `Primary Workspace (${root})`;
201
236
  const treeResult = await executeTool(root, 'list_directory', { dirPath: '.', maxDepth: 10 });
202
237
  let tree = treeResult.output;
203
238
  if (tree.length > 30000) {
204
- tree = tree.substring(0, 30000) + '\\n... (Project tree truncated due to size)';
239
+ tree = tree.substring(0, 30000) + '\n... (Project tree truncated due to size)';
205
240
  }
206
241
  const type = await detectProjectType(root);
207
242
  if (!alias) {
208
243
  primaryProjectType = type;
209
244
  }
210
- projectTree += `=== ${label} ===\\nProject Type: ${type}\\n${tree}\\n\\n`;
245
+ projectTree += `=== ${label} ===\nProject Type: ${type}\n${tree}\n\n`;
211
246
  }
212
247
  const projectType = primaryProjectType;
213
248
  const { lookupInvestigation, saveInvestigation } = await import('./orchestration/investigationCache.js');
@@ -217,6 +252,11 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
217
252
  onProgress(`⚡ Memory Bank HIT — loaded ${cacheHit.entry.relevantFiles.length} files from cache`);
218
253
  const cachedFiles = new Map();
219
254
  for (const filePath of cacheHit.entry.relevantFiles) {
255
+ if (abortSignal.aborted) {
256
+ const err = new Error('Operation aborted');
257
+ err.name = 'AbortError';
258
+ throw err;
259
+ }
220
260
  const readResult = await executeTool(workspaceRoot, 'read_file', { filePath });
221
261
  if (!readResult.error) {
222
262
  cachedFiles.set(filePath, { text: readResult.output, inlineData: readResult.inlineData });
@@ -227,11 +267,15 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
227
267
  const { resolveAndValidateMultiWorkspacePath } = await import('../utils/pathSecurity.js');
228
268
  const autoDiscovered = new Set();
229
269
  for (const filePath of cacheHit.entry.relevantFiles) {
270
+ if (abortSignal.aborted)
271
+ break;
230
272
  try {
231
273
  const resolved = resolveAndValidateMultiWorkspacePath(workspaceRoot, filePath);
232
274
  const graph = await buildDependencyGraph(resolved.workspaceRoot);
233
275
  const reverseDeps = graph.getImportedBy(resolved.relativePath);
234
276
  for (const dep of reverseDeps) {
277
+ if (abortSignal.aborted)
278
+ break;
235
279
  if (cachedFiles.has(dep) || autoDiscovered.has(dep))
236
280
  continue;
237
281
  if (cachedFiles.size + autoDiscovered.size >= MAX_TOTAL_FILES)
@@ -265,7 +309,7 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
265
309
  if (isSubAgentsEnabled()) {
266
310
  // Determine complexity and domain breakdown
267
311
  const approxFiles = projectTree.split('\n').length;
268
- const complexity = await evaluateInvestigationComplexity(userRequest, projectType, approxFiles, chatHistory);
312
+ const complexity = await evaluateInvestigationComplexity(userRequest, projectType, approxFiles, chatHistory, abortSignal);
269
313
  if (complexity.strategy === 'PARALLEL' && complexity.agentAssignments.length > 0) {
270
314
  const orchestrator = new InvestigationOrchestrator();
271
315
  const parallelResult = await orchestrator.runParallelInvestigation(userRequest, complexity.agentAssignments, workspaceRoot, projectTree, projectType, chatHistory, abortSignal, onProgress, onToolCall);
@@ -292,6 +336,11 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
292
336
  currentMessage += `\n\nStart investigating to find relevant files.`;
293
337
  const MAX_TURNS = Infinity;
294
338
  for (let turn = 0; turn < MAX_TURNS; turn++) {
339
+ if (abortSignal.aborted) {
340
+ const err = new Error('Operation aborted');
341
+ err.name = 'AbortError';
342
+ throw err;
343
+ }
295
344
  await inputHandler.waitForPrompt();
296
345
  const queuedMsg = inputHandler.getAndClear();
297
346
  let additionalText = undefined;
@@ -308,8 +357,10 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
308
357
  result = await session.sendMessage(currentMessage, additionalText, abortSignal);
309
358
  }
310
359
  catch (e) {
311
- if (e.name === 'AbortError' || e.message?.includes('abort')) {
312
- break;
360
+ if (abortSignal.aborted || e.name === 'AbortError' || e.message?.includes('abort')) {
361
+ const err = new Error('Operation aborted');
362
+ err.name = 'AbortError';
363
+ throw err;
313
364
  }
314
365
  throw e;
315
366
  }
@@ -42,4 +42,4 @@ export interface InvestigationComplexityResult {
42
42
  * @param chatHistory - Recent conversation history for context.
43
43
  * @returns The complexity classification with domain decomposition.
44
44
  */
45
- export declare function evaluateInvestigationComplexity(userRequest: string, projectType: string, approximateFileCount: number, chatHistory?: string): Promise<InvestigationComplexityResult>;
45
+ export declare function evaluateInvestigationComplexity(userRequest: string, projectType: string, approximateFileCount: number, chatHistory?: string, abortSignal?: AbortSignal): Promise<InvestigationComplexityResult>;
@@ -27,7 +27,12 @@ import { debugLog } from '../utils/logger.js';
27
27
  * @param chatHistory - Recent conversation history for context.
28
28
  * @returns The complexity classification with domain decomposition.
29
29
  */
30
- export async function evaluateInvestigationComplexity(userRequest, projectType, approximateFileCount, chatHistory = '') {
30
+ export async function evaluateInvestigationComplexity(userRequest, projectType, approximateFileCount, chatHistory = '', abortSignal) {
31
+ if (abortSignal?.aborted) {
32
+ const err = new Error('Operation aborted');
33
+ err.name = 'AbortError';
34
+ throw err;
35
+ }
31
36
  try {
32
37
  const session = createInvestigationComplexitySession();
33
38
  let prompt = `User Request: "${userRequest}"
@@ -36,7 +41,7 @@ Approximate File Count: ${approximateFileCount}`;
36
41
  if (chatHistory) {
37
42
  prompt = `Previous Conversation Context:\n${chatHistory}\n\n${prompt}`;
38
43
  }
39
- const result = await session.sendMessage(prompt);
44
+ const result = await session.sendMessage(prompt, undefined, abortSignal);
40
45
  const text = result.response.text()?.trim() || '{}';
41
46
  const parsed = JSON.parse(text);
42
47
  debugLog(`Investigation Complexity Parsed: ${JSON.stringify(parsed)}`);
@@ -80,6 +85,11 @@ Approximate File Count: ${approximateFileCount}`;
80
85
  };
81
86
  }
82
87
  catch (e) {
88
+ if (abortSignal?.aborted || e?.name === 'AbortError' || e?.message?.includes('abort')) {
89
+ const err = new Error('Operation aborted');
90
+ err.name = 'AbortError';
91
+ throw err;
92
+ }
83
93
  debugLog(`Investigation Complexity Router failed, falling back to SINGLE. Error: ${String(e)}`);
84
94
  return {
85
95
  strategy: 'SINGLE',
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Main Orchestrator for Sub-Agent Dispatch and Coordination.
3
3
  *
4
4
  * The orchestrator acts as the "PM Kernel", responsible for:
5
- * 1. Task Decomposition (using gemini-3.6-flash)
5
+ * 1. Task Decomposition (using gemini-3.7-flash)
6
6
  * 2. Graph Validation (Cycle detection via Kahn's algorithm)
7
7
  * 3. Lock Ordering (Conflict resolution across parallel waves)
8
8
  * 4. Parallel Dispatch (Executing waves sequentially, agents in parallel)
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Main Orchestrator for Sub-Agent Dispatch and Coordination.
3
3
  *
4
4
  * The orchestrator acts as the "PM Kernel", responsible for:
5
- * 1. Task Decomposition (using gemini-3.6-flash)
5
+ * 1. Task Decomposition (using gemini-3.7-flash)
6
6
  * 2. Graph Validation (Cycle detection via Kahn's algorithm)
7
7
  * 3. Lock Ordering (Conflict resolution across parallel waves)
8
8
  * 4. Parallel Dispatch (Executing waves sequentially, agents in parallel)
@@ -43,7 +43,7 @@ export class SubAgentRunner {
43
43
  let model = getGlobalActiveModel();
44
44
  if (model === GEMINI_MODELS.AUTO)
45
45
  model = GEMINI_MODELS.FLASH;
46
- // Sub-agents default to flash-3.6 for better reasoning capabilities
46
+ // Sub-agents default to flash-3.7 for better reasoning capabilities
47
47
  this.chat = new ProxyChatSession(model, this.buildSystemInstruction(), [{ functionDeclarations: getScopedToolDeclarations() }], {
48
48
  maxOutputTokens: MAX_OUTPUT_TOKENS,
49
49
  temperature: 0.3, // Lower temperature for more focused execution
@@ -22,12 +22,14 @@ async function delay(ms, abortSignal) {
22
22
  let timeout;
23
23
  const abortHandler = () => {
24
24
  clearTimeout(timeout);
25
- reject(new Error('Operation aborted'));
25
+ const err = new Error('Operation aborted');
26
+ err.name = 'AbortError';
27
+ reject(err);
26
28
  };
27
29
  if (abortSignal?.aborted) {
28
30
  return abortHandler();
29
31
  }
30
- abortSignal?.addEventListener('abort', abortHandler);
32
+ abortSignal?.addEventListener('abort', abortHandler, { once: true });
31
33
  timeout = setTimeout(() => {
32
34
  abortSignal?.removeEventListener('abort', abortHandler);
33
35
  resolve();
@@ -97,6 +99,11 @@ export class ProxyClient {
97
99
  const MAX_DELAY_MS = 30000;
98
100
  let attempt = 0;
99
101
  retryLoop: while (true) {
102
+ if (abortSignal?.aborted) {
103
+ const err = new Error('Operation aborted');
104
+ err.name = 'AbortError';
105
+ throw err;
106
+ }
100
107
  const response = await fetch(this.PROXY_URL, {
101
108
  method: 'POST',
102
109
  headers: {
@@ -115,11 +122,21 @@ export class ProxyClient {
115
122
  });
116
123
  debugLog(`Proxy Request to ${modelName} complete. Status: ${response.status} ${response.statusText}`);
117
124
  if ((response.status === 429 || response.status === 503 || response.status === 502 || response.status === 500 || response.status === 504) && attempt < MAX_RETRIES) {
125
+ if (abortSignal?.aborted) {
126
+ const err = new Error('Operation aborted');
127
+ err.name = 'AbortError';
128
+ throw err;
129
+ }
118
130
  const exponentialDelay = Math.min(MAX_DELAY_MS, BASE_DELAY_MS * Math.pow(2, attempt));
119
131
  const delayTime = Math.round(exponentialDelay * (1.0 + Math.random() * 0.5));
120
132
  process.stdout.write('\n');
121
133
  console.warn(`Server error or rate limit hit (${response.status}). Retrying in ${(delayTime / 1000).toFixed(1)}s... (Attempt ${attempt + 1}/${MAX_RETRIES})`);
122
134
  await delay(delayTime, abortSignal);
135
+ if (abortSignal?.aborted) {
136
+ const err = new Error('Operation aborted');
137
+ err.name = 'AbortError';
138
+ throw err;
139
+ }
123
140
  attempt++;
124
141
  continue;
125
142
  }
@@ -231,6 +248,11 @@ export class ProxyClient {
231
248
  }
232
249
  }
233
250
  catch (streamError) {
251
+ if (abortSignal?.aborted || streamError.name === 'AbortError' || streamError.message?.includes('abort')) {
252
+ const err = new Error('Operation aborted');
253
+ err.name = 'AbortError';
254
+ throw err;
255
+ }
234
256
  if (streamError.message?.includes('429') ||
235
257
  streamError.message?.includes('502') ||
236
258
  streamError.message?.includes('503') ||
@@ -245,6 +267,11 @@ export class ProxyClient {
245
267
  process.stdout.write('\n');
246
268
  console.warn(`Server error or rate limit hit during stream. Retrying in ${(delayTime / 1000).toFixed(1)}s... (Attempt ${attempt + 1}/${MAX_RETRIES})`);
247
269
  await delay(delayTime, abortSignal);
270
+ if (abortSignal?.aborted) {
271
+ const err = new Error('Operation aborted');
272
+ err.name = 'AbortError';
273
+ throw err;
274
+ }
248
275
  attempt++;
249
276
  continue retryLoop;
250
277
  }
@@ -287,6 +314,11 @@ export class ProxyClient {
287
314
  const MAX_DELAY_MS = 30000;
288
315
  let attempt = 0;
289
316
  while (true) {
317
+ if (abortSignal?.aborted) {
318
+ const err = new Error('Operation aborted');
319
+ err.name = 'AbortError';
320
+ throw err;
321
+ }
290
322
  const response = await fetch(url, {
291
323
  method: 'POST',
292
324
  headers: { 'Content-Type': 'application/json' },
@@ -300,11 +332,21 @@ export class ProxyClient {
300
332
  response.status === 500 ||
301
333
  response.status === 504) &&
302
334
  attempt < MAX_RETRIES) {
335
+ if (abortSignal?.aborted) {
336
+ const err = new Error('Operation aborted');
337
+ err.name = 'AbortError';
338
+ throw err;
339
+ }
303
340
  const exponentialDelay = Math.min(MAX_DELAY_MS, BASE_DELAY_MS * Math.pow(2, attempt));
304
341
  const delayTime = Math.round(exponentialDelay * (1.0 + Math.random() * 0.5));
305
342
  process.stdout.write('\n');
306
343
  console.warn(`Server error or rate limit hit (${response.status}). Retrying in ${(delayTime / 1000).toFixed(1)}s... (Attempt ${attempt + 1}/${MAX_RETRIES})`);
307
344
  await delay(delayTime, abortSignal);
345
+ if (abortSignal?.aborted) {
346
+ const err = new Error('Operation aborted');
347
+ err.name = 'AbortError';
348
+ throw err;
349
+ }
308
350
  attempt++;
309
351
  continue;
310
352
  }
@@ -17,7 +17,9 @@ export declare const GEMINI_MODELS: {
17
17
  readonly CLAUDE_OPUS: "claude-opus-5";
18
18
  readonly PRO: "gemini-3.1-pro-preview";
19
19
  readonly CLAUDE_SONNET: "claude-sonnet-5";
20
- readonly FLASH: "gemini-3.6-flash";
20
+ readonly FLASH_3_7: "gemini-3.7-flash";
21
+ readonly FLASH: "gemini-3.7-flash";
22
+ readonly FLASH_3_6: "gemini-3.6-flash";
21
23
  readonly FLASH_LITE: "gemini-3.5-flash-lite";
22
24
  readonly AUTO: "auto";
23
25
  };
@@ -17,7 +17,9 @@ export const GEMINI_MODELS = {
17
17
  CLAUDE_OPUS: 'claude-opus-5',
18
18
  PRO: 'gemini-3.1-pro-preview',
19
19
  CLAUDE_SONNET: 'claude-sonnet-5',
20
- FLASH: 'gemini-3.6-flash',
20
+ FLASH_3_7: 'gemini-3.7-flash',
21
+ FLASH: 'gemini-3.7-flash',
22
+ FLASH_3_6: 'gemini-3.6-flash',
21
23
  FLASH_LITE: 'gemini-3.5-flash-lite',
22
24
  AUTO: 'auto',
23
25
  };
@@ -65,5 +65,5 @@
65
65
  ]
66
66
  }
67
67
  },
68
- "version": "2.8.4"
68
+ "version": "2.9.1"
69
69
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "minovative-mind-cli",
3
3
  "description": "An automated AI agent powered by Vertex AI that helps you write software",
4
- "version": "2.8.4",
4
+ "version": "2.9.1",
5
5
  "author": "Daniel Ward",
6
6
  "bin": {
7
7
  "minovative-mind-cli": "bin/run.js"