minovative-mind-cli 2.2.5 → 2.3.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.
Files changed (35) hide show
  1. package/LICENSE.md +2 -0
  2. package/README.md +8 -8
  3. package/dist/commands/logout.js +1 -1
  4. package/dist/services/agent/slashCommands.js +53 -14
  5. package/dist/services/agent/toolLoop.js +4 -1
  6. package/dist/services/agent/types.d.ts +4 -0
  7. package/dist/services/agent-tools.js +55 -27
  8. package/dist/services/agent.d.ts +4 -0
  9. package/dist/services/agent.js +77 -60
  10. package/dist/services/ai.d.ts +1 -2
  11. package/dist/services/ai.js +21 -19
  12. package/dist/services/auth.d.ts +1 -1
  13. package/dist/services/auth.js +7 -28
  14. package/dist/services/chatHistoryService.d.ts +8 -0
  15. package/dist/services/contextAgent.js +18 -1
  16. package/dist/services/investigationComplexity.d.ts +1 -1
  17. package/dist/services/investigationComplexity.js +1 -1
  18. package/dist/services/orchestration/investigationAgent.d.ts +2 -2
  19. package/dist/services/orchestration/investigationAgent.js +25 -6
  20. package/dist/services/orchestration/orchestrator.d.ts +1 -1
  21. package/dist/services/orchestration/orchestrator.js +37 -18
  22. package/dist/services/orchestration/subAgent.d.ts +1 -1
  23. package/dist/services/orchestration/subAgent.js +23 -13
  24. package/dist/services/proxyClient.d.ts +16 -0
  25. package/dist/services/proxyClient.js +32 -0
  26. package/dist/utils/analysisRunner.js +2 -2
  27. package/dist/utils/config.d.ts +3 -3
  28. package/dist/utils/config.js +3 -3
  29. package/dist/utils/credentialStore.d.ts +30 -0
  30. package/dist/utils/credentialStore.js +540 -0
  31. package/dist/utils/projectStorage.js +70 -0
  32. package/dist/utils/systemPrompts.d.ts +3 -3
  33. package/dist/utils/systemPrompts.js +4 -4
  34. package/oclif.manifest.json +1 -1
  35. package/package.json +2 -1
@@ -40,8 +40,8 @@ export class InvestigationAgentRunner {
40
40
  creditsUsed = 0;
41
41
  inputTokens = 0;
42
42
  outputTokens = 0;
43
- /** Shorter stall timeout for investigation agents (120s to allow for heavy vision/web tasks) */
44
- static STALL_TIMEOUT_MS = 120_000;
43
+ /** Stall timeout for investigation agents (300s to allow for heavy vision/web tasks) */
44
+ static STALL_TIMEOUT_MS = 300_000;
45
45
  constructor(agentLabel, domains, workspaceRoot, readCache, projectTree, projectType) {
46
46
  this.agentLabel = agentLabel;
47
47
  this.domains = domains;
@@ -51,7 +51,7 @@ export class InvestigationAgentRunner {
51
51
  this.projectType = projectType;
52
52
  let model = getGlobalActiveModel();
53
53
  if (model === GEMINI_MODELS.AUTO)
54
- model = GEMINI_MODELS.FLASH_3_5;
54
+ model = GEMINI_MODELS.FLASH;
55
55
  this.chat = new ProxyChatSession(model, this.buildSystemInstruction(), getContextToolDeclarations(), {
56
56
  maxOutputTokens: MAX_OUTPUT_TOKENS,
57
57
  temperature: 1,
@@ -114,6 +114,8 @@ export class InvestigationAgentRunner {
114
114
  currentMessage += `\n\nStart investigating to find relevant files within your assigned domains.`;
115
115
  let isFinished = false;
116
116
  const visitedToolCalls = new Set();
117
+ let consecutiveDuplicates = 0;
118
+ const MAX_CONSECUTIVE_DUPLICATES = 3;
117
119
  // Tool loop — mirrors the existing context agent loop in contextAgent.ts
118
120
  while (!crashed && !abortSignal.aborted && !isFinished) {
119
121
  this.pingHeartbeat();
@@ -144,14 +146,31 @@ export class InvestigationAgentRunner {
144
146
  // Deduplicate identical tool calls
145
147
  const callSignature = `${call.name}:${JSON.stringify(args)}`;
146
148
  if (call.name !== 'finish_investigation' && visitedToolCalls.has(callSignature)) {
149
+ consecutiveDuplicates++;
150
+ if (consecutiveDuplicates >= MAX_CONSECUTIVE_DUPLICATES) {
151
+ // Force-finish: the model is stuck in a loop
152
+ const forceMsg = `Investigation auto-completed: the model repeated the same tool call ${MAX_CONSECUTIVE_DUPLICATES} times consecutively.`;
153
+ debugLog(`InvestigationAgent [${this.agentLabel}]: ${forceMsg}`);
154
+ if (onProgress)
155
+ onProgress(`[${this.agentLabel}] ${forceMsg}`);
156
+ if (!summary || summary === 'No relevant context found.') {
157
+ summary = 'Investigation was auto-completed due to repeated duplicate tool calls. Review the gathered files for context.';
158
+ }
159
+ isFinished = true;
160
+ success = relevantFiles.size > 0;
161
+ break;
162
+ }
147
163
  functionResponses.push({
148
164
  functionResponse: {
149
165
  name: call.name,
150
- response: { error: 'You have already made this exact tool call previously. Please review your context history or try a different action.' }
151
- }
166
+ response: {
167
+ error: `DUPLICATE CALL BLOCKED (attempt ${consecutiveDuplicates}/${MAX_CONSECUTIVE_DUPLICATES}): You already executed this exact tool call. Do NOT retry it. Use the results you already have and call finish_investigation now, or try a DIFFERENT tool call with different parameters.`,
168
+ },
169
+ },
152
170
  });
153
171
  continue;
154
172
  }
173
+ consecutiveDuplicates = 0;
155
174
  visitedToolCalls.add(callSignature);
156
175
  if (call.name === 'finish_investigation') {
157
176
  summary = args.summary || '';
@@ -345,7 +364,7 @@ export class InvestigationAgentRunner {
345
364
  if (usage) {
346
365
  const input = (usage.promptTokens || 0) + (usage.cachedTokens || 0);
347
366
  const output = usage.candidatesTokens || 0;
348
- const tokens = usage.totalTokenCount || (input + output);
367
+ const tokens = usage.totalTokenCount || input + output;
349
368
  this.inputTokens += input;
350
369
  this.outputTokens += output;
351
370
  this.creditsUsed += tokens;
@@ -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.5-flash)
5
+ * 1. Task Decomposition (using gemini-3.6-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.5-flash)
5
+ * 1. Task Decomposition (using gemini-3.6-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)
@@ -11,6 +11,7 @@
11
11
  import * as p from '@clack/prompts';
12
12
  import pc from 'picocolors';
13
13
  import { ProxyChatSession, getGlobalActiveModel, compressTextUsingFlashLite } from '../ai.js';
14
+ import { peekTurnUsage } from '../proxyClient.js';
14
15
  import { GEMINI_MODELS, MAX_OUTPUT_TOKENS } from '../../utils/config.js';
15
16
  import { debugLog } from '../../utils/logger.js';
16
17
  import { MessageBus } from './messageBus.js';
@@ -19,19 +20,22 @@ import { SubAgentRunner } from './subAgent.js';
19
20
  import { validateTaskGraph, computeExecutionWaves, detectFileConflicts, resolveFileConflicts, buildCycleCorrectionPrompt, CyclicDependencyError, } from './taskGraph.js';
20
21
  import { trackTask } from '../../utils/taskVisualizer.js';
21
22
  // ─── Constants ───────────────────────────────────────────────────────
22
- const PM_SYSTEM_INSTRUCTION = `You are the PM Agent (Project Manager).
23
+ const PM_SYSTEM_INSTRUCTION = `<identity>
24
+ You are the PM Agent (Project Manager).
23
25
  Your job is to decompose the user's objective into a parallelizable Task Graph for sub-agents.
24
26
  You must output ONLY raw JSON representing the TaskGraph object. Do not wrap in markdown code blocks.
27
+ </identity>
25
28
 
26
- Requirements:
29
+ <requirements>
27
30
  - Break the work down logically based on file isolation and dependency chains.
28
31
  - Tasks that don't depend on each other will run in parallel.
29
32
  - If a task depends on another, it must list its ID in "dependsOn".
30
33
  - Do NOT create cyclic dependencies (A -> B -> A).
31
34
  - If the entire objective is very simple and only requires modifying 1-2 files sequentially, output a graph with exactly 1 task.
32
35
  - Ensure 'targetFiles' lists all files the task will modify.
36
+ </requirements>
33
37
 
34
- JSON Schema:
38
+ <json_schema>
35
39
  {
36
40
  "objective": "string",
37
41
  "tasks": [
@@ -46,7 +50,7 @@ JSON Schema:
46
50
  ],
47
51
  "constraints": ["string"]
48
52
  }
49
- `;
53
+ </json_schema>`;
50
54
  // ─── Orchestrator Implementation ─────────────────────────────────────
51
55
  export class Orchestrator {
52
56
  workspaceRoot;
@@ -64,7 +68,7 @@ export class Orchestrator {
64
68
  this.locks = new FileLockRegistry();
65
69
  let model = getGlobalActiveModel();
66
70
  if (model === GEMINI_MODELS.AUTO)
67
- model = GEMINI_MODELS.FLASH_3_5;
71
+ model = GEMINI_MODELS.FLASH;
68
72
  this.pmChat = new ProxyChatSession(model, PM_SYSTEM_INSTRUCTION, [], {
69
73
  maxOutputTokens: MAX_OUTPUT_TOKENS,
70
74
  temperature: 0.1,
@@ -97,10 +101,12 @@ export class Orchestrator {
97
101
  for (const wave of waves) {
98
102
  if (signal.aborted)
99
103
  break;
100
- const taskDescriptions = wave.taskIds.map(taskId => {
101
- const taskDef = graph.tasks.find(t => t.id === taskId);
104
+ const taskDescriptions = wave.taskIds
105
+ .map((taskId) => {
106
+ const taskDef = graph.tasks.find((t) => t.id === taskId);
102
107
  return ` - ${pc.cyan(taskDef.id)}: ${pc.dim(taskDef.intent)}`;
103
- }).join('\n');
108
+ })
109
+ .join('\n');
104
110
  p.log.step(pc.magenta(`Starting Wave ${wave.depth + 1} (${wave.taskIds.length} tasks):\n${taskDescriptions}`));
105
111
  const s = p.spinner();
106
112
  s.start(`Executing Wave ${wave.depth + 1}...`);
@@ -116,8 +122,8 @@ export class Orchestrator {
116
122
  const MAX_CONCURRENT = 2;
117
123
  for (let i = 0; i < wave.taskIds.length; i += MAX_CONCURRENT) {
118
124
  const chunk = wave.taskIds.slice(i, i + MAX_CONCURRENT);
119
- const wavePromises = chunk.map(taskId => {
120
- const taskDef = graph.tasks.find(t => t.id === taskId);
125
+ const wavePromises = chunk.map((taskId) => {
126
+ const taskDef = graph.tasks.find((t) => t.id === taskId);
121
127
  const globalContext = `Objective:\n${objective}\n\nContext:\n${contextInjection}`;
122
128
  agentStatuses.set(taskDef.id, 'Starting...');
123
129
  updateSpinner();
@@ -131,7 +137,7 @@ export class Orchestrator {
131
137
  }
132
138
  s.stop(`Wave ${wave.depth + 1} execution finished.`);
133
139
  // Post-wave evaluation
134
- const failedCount = results.filter(r => !r.success).length;
140
+ const failedCount = results.filter((r) => !r.success).length;
135
141
  if (failedCount > 0) {
136
142
  p.log.warn(pc.yellow(`Wave ${wave.depth + 1} finished with ${failedCount} failure(s).`));
137
143
  // Phase 3 implementation note: Right now, we continue and let downstream
@@ -232,28 +238,41 @@ export class Orchestrator {
232
238
  let rawSummaryData = '';
233
239
  for (const [taskId, res] of this.agentResults.entries()) {
234
240
  totalTokens += res.creditsUsed;
235
- inputTokens += (res.inputTokens || 0);
236
- outputTokens += (res.outputTokens || 0);
241
+ inputTokens += res.inputTokens || 0;
242
+ outputTokens += res.outputTokens || 0;
237
243
  if (!res.success)
238
244
  failedTasks++;
239
245
  debugLog(`Task ${taskId} summary: ${res.summary.substring(0, 100)}...`);
240
246
  rawSummaryData += `Task: ${taskId} | Status: ${res.success ? 'Success' : 'Failed'}\nChanges:\n${res.summary}\n\n`;
241
247
  }
242
248
  p.log.step(pc.cyan('Orchestrator: Synthesizing final changes overview...'));
243
- let finalSummary = '[Sub-Agent Orchestration Completed]\n\n';
249
+ let finalSummary = '';
244
250
  try {
245
- const instruction = 'You are an expert technical orchestrator. The user has delegated a complex task to multiple sub-agents. Below are their individual task completion summaries. Synthesize these summaries into ONE cohesive, unified overview of all changes made. DO NOT list them by task name or separate them by agent. Just provide a single seamless summary of what was accomplished overall. Use clear bullet points and markdown. Be concise but complete.';
246
- const synthesized = await compressTextUsingFlashLite(rawSummaryData, instruction);
251
+ const payloadWithContext = `<original_request>\n${graph.objective}\n</original_request>\n\n<task_summaries>\n${rawSummaryData}\n</task_summaries>`;
252
+ const instruction = '<identity>\n' +
253
+ "You are Mino, a Senior software developer. You just delegated the user's complex request to multiple parallel sub-agents, and they have finished. " +
254
+ "Below is the user's original request, followed by the raw task completion summaries from the sub-agents.\n" +
255
+ '</identity>\n\n' +
256
+ '<directives>\n' +
257
+ 'Synthesize these summaries into a natural, conversational response back to the user. ' +
258
+ 'Address their original request directly (e.g., "I have completed your request to..."). ' +
259
+ 'Provide ONE cohesive, unified overview of all changes made, using clear bullet points. ' +
260
+ 'DO NOT list changes by task name or separate them by agent. ' +
261
+ 'Be concise, helpful, and conclude by asking if they need any further adjustments.\n' +
262
+ '</directives>';
263
+ const synthesized = await compressTextUsingFlashLite(payloadWithContext, instruction);
247
264
  finalSummary += synthesized + '\n\n';
248
265
  }
249
266
  catch (e) {
250
267
  debugLog(`Synthesis failed, falling back to raw output: ${e}`);
251
268
  finalSummary += rawSummaryData;
252
269
  }
270
+ const currentUsage = peekTurnUsage();
271
+ const totalInputTokens = (currentUsage.promptTokens || 0) + (currentUsage.cachedTokens || 0);
253
272
  p.log.info(`${pc.green('✓')} Sub-agent execution complete.\n` +
254
273
  ` Total tasks: ${graph.tasks.length} (${failedTasks} failed)\n` +
255
274
  ` Bus Activity: ${stats.activityCount} actions, ${stats.signalCount} signals\n` +
256
- ` Total Tokens: ${totalTokens.toLocaleString()} ${pc.dim(`(Input: ${inputTokens.toLocaleString()}, Output: ${outputTokens.toLocaleString()})`)}`);
275
+ ` Total Tokens: ${currentUsage.totalTokenCount.toLocaleString()} ${pc.dim(`(Input: ${totalInputTokens.toLocaleString()}, Output: ${currentUsage.candidatesTokens.toLocaleString()})`)}`);
257
276
  // Clear the bus persistence now that orchestration is done
258
277
  await this.bus.cleanup();
259
278
  this.locks.shutdown();
@@ -39,7 +39,7 @@ export declare class SubAgentRunner {
39
39
  private inputTokens;
40
40
  private outputTokens;
41
41
  /** Max time without a tool call or response before the agent is considered stalled */
42
- static readonly STALL_TIMEOUT_MS = 60000;
42
+ static readonly STALL_TIMEOUT_MS = 300000;
43
43
  constructor(taskId: string, intent: string, workspaceRoot: string, bus: MessageBus, locks: FileLockRegistry, globalContext: string, onProgress?: ((msg: string) => void) | undefined);
44
44
  /**
45
45
  * Constructs the base system instruction for this specific agent.
@@ -27,7 +27,7 @@ export class SubAgentRunner {
27
27
  inputTokens = 0;
28
28
  outputTokens = 0;
29
29
  /** Max time without a tool call or response before the agent is considered stalled */
30
- static STALL_TIMEOUT_MS = 60_000;
30
+ static STALL_TIMEOUT_MS = 300_000;
31
31
  constructor(taskId, intent, workspaceRoot, bus, locks, globalContext, onProgress) {
32
32
  this.taskId = taskId;
33
33
  this.intent = intent;
@@ -38,8 +38,8 @@ export class SubAgentRunner {
38
38
  this.onProgress = onProgress;
39
39
  let model = getGlobalActiveModel();
40
40
  if (model === GEMINI_MODELS.AUTO)
41
- model = GEMINI_MODELS.FLASH_3_5;
42
- // Sub-agents default to flash-3.5 for better reasoning capabilities
41
+ model = GEMINI_MODELS.FLASH;
42
+ // Sub-agents default to flash-3.6 for better reasoning capabilities
43
43
  this.chat = new ProxyChatSession(model, this.buildSystemInstruction(), [{ functionDeclarations: getScopedToolDeclarations() }], {
44
44
  maxOutputTokens: MAX_OUTPUT_TOKENS,
45
45
  temperature: 0.3, // Lower temperature for more focused execution
@@ -51,18 +51,24 @@ export class SubAgentRunner {
51
51
  * Constructs the base system instruction for this specific agent.
52
52
  */
53
53
  buildSystemInstruction() {
54
- return (`You are an autonomous sub-agent executing a specific portion of a larger task.\n` +
54
+ return (`<identity>\n` +
55
+ `You are an autonomous Senior software developer sub-agent executing a specific portion of a larger task.\n` +
56
+ `</identity>\n\n` +
57
+ `<task_info>\n` +
55
58
  `Your task ID is: ${this.taskId}\n` +
56
- `Your objective: ${this.intent}\n\n` +
57
- `=== REFERENCE CONTEXT (DO NOT IMPLEMENT THIS FULL REQUEST) ===\n` +
59
+ `Your objective: ${this.intent}\n` +
60
+ `</task_info>\n\n` +
61
+ `<reference_context>\n` +
62
+ `DO NOT IMPLEMENT THIS FULL REQUEST. THIS IS JUST FOR CONTEXT.\n\n` +
58
63
  `${this.globalContext}\n` +
59
- `==============================================================\n\n` +
60
- `CRITICAL GUIDELINES:\n` +
64
+ `</reference_context>\n\n` +
65
+ `<critical_guidelines>\n` +
61
66
  `1. You are ONE worker in a team. You MUST ONLY focus on your specific objective: "${this.intent}".\n` +
62
67
  `2. DO NOT attempt to fulfill the entire original user request in the reference context. Other agents are handling the other parts.\n` +
63
68
  `3. You are part of a parallelized system. Use 'post_message' to coordinate if you discover breaking changes.\n` +
64
69
  `4. When you have completed your objective, stop using tools and provide a final summary of your work.\n` +
65
- `5. If you encounter an insurmountable error, provide a summary of what went wrong so the orchestrator can re-assign or fix it.`);
70
+ `5. If you encounter an insurmountable error, provide a summary of what went wrong so the orchestrator can re-assign or fix it.\n` +
71
+ `</critical_guidelines>`);
66
72
  }
67
73
  /**
68
74
  * Updates the heartbeat. Passed to `executeScopedTool` to ensure the agent
@@ -126,8 +132,10 @@ export class SubAgentRunner {
126
132
  toolResponses.push({
127
133
  functionResponse: {
128
134
  name: call.name,
129
- response: { error: 'You have already made this exact tool call previously. Please review your context history or try a different action.' }
130
- }
135
+ response: {
136
+ error: 'You have already made this exact tool call previously. Please review your context history or try a different action.',
137
+ },
138
+ },
131
139
  });
132
140
  continue;
133
141
  }
@@ -151,7 +159,9 @@ export class SubAgentRunner {
151
159
  output: responseData.output !== undefined ? responseData.output : responseData,
152
160
  ...(responseData.error ? { error: responseData.error } : {}),
153
161
  ...(responseData.inlineData ? { inlineData: responseData.inlineData } : {}),
154
- ...(responseData._orchestrationWarning ? { _orchestrationWarning: responseData._orchestrationWarning } : {}),
162
+ ...(responseData._orchestrationWarning
163
+ ? { _orchestrationWarning: responseData._orchestrationWarning }
164
+ : {}),
155
165
  },
156
166
  },
157
167
  });
@@ -201,7 +211,7 @@ export class SubAgentRunner {
201
211
  if (usage) {
202
212
  const input = (usage.promptTokens || 0) + (usage.cachedTokens || 0);
203
213
  const output = usage.candidatesTokens || 0;
204
- const tokens = usage.totalTokenCount || (input + output);
214
+ const tokens = usage.totalTokenCount || input + output;
205
215
  this.inputTokens += input;
206
216
  this.outputTokens += output;
207
217
  this.creditsUsed += tokens;
@@ -28,6 +28,22 @@ export interface ProxyUsageMetadata {
28
28
  /** The remaining balance left in the user's account. */
29
29
  remainingBalance: number;
30
30
  }
31
+ export declare function getAndResetTurnUsage(): {
32
+ promptTokens: number;
33
+ candidatesTokens: number;
34
+ cachedTokens: number;
35
+ totalTokenCount: number;
36
+ creditsUsed: number;
37
+ remainingBalance: number | undefined;
38
+ };
39
+ export declare function peekTurnUsage(): {
40
+ promptTokens: number;
41
+ candidatesTokens: number;
42
+ cachedTokens: number;
43
+ totalTokenCount: number;
44
+ creditsUsed: number;
45
+ remainingBalance: number | undefined;
46
+ };
31
47
  /**
32
48
  * Client service interacting directly with the serverless Gemini proxy endpoint.
33
49
  * Ensures authorization via Firebase token passing and parses streamed content.
@@ -1,4 +1,27 @@
1
1
  import { debugLog } from '../utils/logger.js';
2
+ let globalSessionAccumulatedUsage = {
3
+ promptTokens: 0,
4
+ candidatesTokens: 0,
5
+ cachedTokens: 0,
6
+ totalTokenCount: 0,
7
+ creditsUsed: 0,
8
+ remainingBalance: undefined
9
+ };
10
+ export function getAndResetTurnUsage() {
11
+ const current = { ...globalSessionAccumulatedUsage };
12
+ globalSessionAccumulatedUsage = {
13
+ promptTokens: 0,
14
+ candidatesTokens: 0,
15
+ cachedTokens: 0,
16
+ totalTokenCount: 0,
17
+ creditsUsed: 0,
18
+ remainingBalance: undefined
19
+ };
20
+ return current;
21
+ }
22
+ export function peekTurnUsage() {
23
+ return { ...globalSessionAccumulatedUsage };
24
+ }
2
25
  /**
3
26
  * Client service interacting directly with the serverless Gemini proxy endpoint.
4
27
  * Ensures authorization via Firebase token passing and parses streamed content.
@@ -111,6 +134,15 @@ export class ProxyClient {
111
134
  else if (data.type === 'done') {
112
135
  if (data.usage) {
113
136
  usageMetadata = data.usage;
137
+ globalSessionAccumulatedUsage.promptTokens += data.usage.promptTokens || 0;
138
+ globalSessionAccumulatedUsage.candidatesTokens += data.usage.candidatesTokens || 0;
139
+ globalSessionAccumulatedUsage.cachedTokens += data.usage.cachedTokens || 0;
140
+ globalSessionAccumulatedUsage.creditsUsed += data.usage.creditsUsed || 0;
141
+ globalSessionAccumulatedUsage.totalTokenCount +=
142
+ (data.usage.promptTokens || 0) + (data.usage.cachedTokens || 0) + (data.usage.candidatesTokens || 0);
143
+ if (data.usage.remainingBalance !== undefined) {
144
+ globalSessionAccumulatedUsage.remainingBalance = data.usage.remainingBalance;
145
+ }
114
146
  }
115
147
  if (data.groundingMetadata) {
116
148
  groundingMetadata = data.groundingMetadata;
@@ -70,8 +70,8 @@ function truncateOutput(text, max) {
70
70
  * @returns Captured stdout, stderr, and exit code.
71
71
  */
72
72
  export async function runEphemeralScript(workspaceRoot, language, code, options) {
73
- const timeoutMs = options?.timeoutMs ?? 10_000;
74
- const maxOutputChars = options?.maxOutputChars ?? 30_000;
73
+ const timeoutMs = options?.timeoutMs ?? 60_000;
74
+ const maxOutputChars = options?.maxOutputChars ?? 100_000;
75
75
  const ext = LANGUAGE_EXTENSIONS[language.toLowerCase()];
76
76
  if (!ext) {
77
77
  return {
@@ -14,9 +14,9 @@ export declare const GITHUB_CLIENT_ID = "Ov23linFYFfjO3JILG7r";
14
14
  * Supported Gemini AI models.
15
15
  */
16
16
  export declare const GEMINI_MODELS: {
17
- readonly PRO_3_1: "gemini-3.1-pro-preview";
18
- readonly FLASH_3_5: "gemini-3.5-flash";
19
- readonly FLASH_LITE_3_1: "gemini-3.1-flash-lite";
17
+ readonly PRO: "gemini-3.1-pro-preview";
18
+ readonly FLASH: "gemini-3.6-flash";
19
+ readonly FLASH_LITE: "gemini-3.5-flash-lite";
20
20
  readonly AUTO: "auto";
21
21
  };
22
22
  /** Default Gemini model for the coding agent. */
@@ -14,9 +14,9 @@ export const GITHUB_CLIENT_ID = 'Ov23linFYFfjO3JILG7r';
14
14
  * Supported Gemini AI models.
15
15
  */
16
16
  export const GEMINI_MODELS = {
17
- PRO_3_1: 'gemini-3.1-pro-preview',
18
- FLASH_3_5: 'gemini-3.5-flash',
19
- FLASH_LITE_3_1: 'gemini-3.1-flash-lite',
17
+ PRO: 'gemini-3.1-pro-preview',
18
+ FLASH: 'gemini-3.6-flash',
19
+ FLASH_LITE: 'gemini-3.5-flash-lite',
20
20
  AUTO: 'auto',
21
21
  };
22
22
  /** Default Gemini model for the coding agent. */
@@ -0,0 +1,30 @@
1
+ /** Legacy plaintext file path (for migration). */
2
+ export declare const LEGACY_CONFIG_FILE: string;
3
+ export interface StoredCredentials {
4
+ idToken?: string;
5
+ refreshToken?: string;
6
+ idTokenExpiry?: number;
7
+ }
8
+ /**
9
+ * Persists authentication credentials to the most secure available store.
10
+ *
11
+ * Strategy (tried in order):
12
+ * 1. macOS Keychain (`security` CLI)
13
+ * 2. Linux libsecret (`secret-tool` CLI)
14
+ * 3. Windows DPAPI (PowerShell)
15
+ * 4. AES-256-GCM encrypted file with 0600 permissions
16
+ */
17
+ export declare function saveCredentials(data: StoredCredentials): Promise<void>;
18
+ /**
19
+ * Loads authentication credentials from the secure store.
20
+ * Returns an empty object if no credentials are found.
21
+ *
22
+ * On first run after upgrade, silently migrates any legacy plaintext
23
+ * `~/.minovative-mind-cli.json` into the secure store and deletes the old file.
24
+ */
25
+ export declare function loadCredentials(): Promise<StoredCredentials>;
26
+ /**
27
+ * Removes all stored credentials from the secure store.
28
+ * Also cleans up any legacy plaintext file if it still exists.
29
+ */
30
+ export declare function clearCredentials(): Promise<void>;