minovative-mind-cli 2.2.4 → 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.
- package/LICENSE.md +2 -0
- package/README.md +19 -20
- package/dist/commands/logout.js +1 -1
- package/dist/services/agent/slashCommands.js +54 -15
- package/dist/services/agent/toolLoop.js +4 -1
- package/dist/services/agent/types.d.ts +4 -0
- package/dist/services/agent-tools.js +55 -27
- package/dist/services/agent.d.ts +4 -0
- package/dist/services/agent.js +79 -62
- package/dist/services/ai.d.ts +1 -2
- package/dist/services/ai.js +21 -19
- package/dist/services/auth.d.ts +1 -1
- package/dist/services/auth.js +7 -28
- package/dist/services/chatHistoryService.d.ts +8 -0
- package/dist/services/contextAgent.js +18 -1
- package/dist/services/investigationComplexity.d.ts +1 -1
- package/dist/services/investigationComplexity.js +1 -1
- package/dist/services/orchestration/investigationAgent.d.ts +6 -2
- package/dist/services/orchestration/investigationAgent.js +35 -10
- package/dist/services/orchestration/investigationOrchestrator.js +3 -1
- package/dist/services/orchestration/orchestrator.d.ts +1 -1
- package/dist/services/orchestration/orchestrator.js +39 -16
- package/dist/services/orchestration/subAgent.d.ts +5 -1
- package/dist/services/orchestration/subAgent.js +33 -17
- package/dist/services/proxyClient.d.ts +16 -0
- package/dist/services/proxyClient.js +32 -0
- package/dist/utils/analysisRunner.js +2 -2
- package/dist/utils/config.d.ts +3 -4
- package/dist/utils/config.js +3 -4
- package/dist/utils/credentialStore.d.ts +30 -0
- package/dist/utils/credentialStore.js +540 -0
- package/dist/utils/projectStorage.js +70 -0
- package/dist/utils/systemPrompts.d.ts +3 -3
- package/dist/utils/systemPrompts.js +4 -4
- package/oclif.manifest.json +1 -1
- package/package.json +2 -1
|
@@ -38,8 +38,10 @@ export class InvestigationAgentRunner {
|
|
|
38
38
|
chat;
|
|
39
39
|
lastHeartbeat = Date.now();
|
|
40
40
|
creditsUsed = 0;
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
inputTokens = 0;
|
|
42
|
+
outputTokens = 0;
|
|
43
|
+
/** Stall timeout for investigation agents (300s to allow for heavy vision/web tasks) */
|
|
44
|
+
static STALL_TIMEOUT_MS = 300_000;
|
|
43
45
|
constructor(agentLabel, domains, workspaceRoot, readCache, projectTree, projectType) {
|
|
44
46
|
this.agentLabel = agentLabel;
|
|
45
47
|
this.domains = domains;
|
|
@@ -49,7 +51,7 @@ export class InvestigationAgentRunner {
|
|
|
49
51
|
this.projectType = projectType;
|
|
50
52
|
let model = getGlobalActiveModel();
|
|
51
53
|
if (model === GEMINI_MODELS.AUTO)
|
|
52
|
-
model = GEMINI_MODELS.
|
|
54
|
+
model = GEMINI_MODELS.FLASH;
|
|
53
55
|
this.chat = new ProxyChatSession(model, this.buildSystemInstruction(), getContextToolDeclarations(), {
|
|
54
56
|
maxOutputTokens: MAX_OUTPUT_TOKENS,
|
|
55
57
|
temperature: 1,
|
|
@@ -112,6 +114,8 @@ export class InvestigationAgentRunner {
|
|
|
112
114
|
currentMessage += `\n\nStart investigating to find relevant files within your assigned domains.`;
|
|
113
115
|
let isFinished = false;
|
|
114
116
|
const visitedToolCalls = new Set();
|
|
117
|
+
let consecutiveDuplicates = 0;
|
|
118
|
+
const MAX_CONSECUTIVE_DUPLICATES = 3;
|
|
115
119
|
// Tool loop — mirrors the existing context agent loop in contextAgent.ts
|
|
116
120
|
while (!crashed && !abortSignal.aborted && !isFinished) {
|
|
117
121
|
this.pingHeartbeat();
|
|
@@ -142,14 +146,31 @@ export class InvestigationAgentRunner {
|
|
|
142
146
|
// Deduplicate identical tool calls
|
|
143
147
|
const callSignature = `${call.name}:${JSON.stringify(args)}`;
|
|
144
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
|
+
}
|
|
145
163
|
functionResponses.push({
|
|
146
164
|
functionResponse: {
|
|
147
165
|
name: call.name,
|
|
148
|
-
response: {
|
|
149
|
-
|
|
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
|
+
},
|
|
150
170
|
});
|
|
151
171
|
continue;
|
|
152
172
|
}
|
|
173
|
+
consecutiveDuplicates = 0;
|
|
153
174
|
visitedToolCalls.add(callSignature);
|
|
154
175
|
if (call.name === 'finish_investigation') {
|
|
155
176
|
summary = args.summary || '';
|
|
@@ -322,7 +343,7 @@ export class InvestigationAgentRunner {
|
|
|
322
343
|
crashed = true;
|
|
323
344
|
summary = 'Investigation aborted by user.';
|
|
324
345
|
}
|
|
325
|
-
debugLog(`InvestigationAgent [${this.agentLabel}]: Finished. Success=${success && !crashed}, Files=${relevantFiles.size}, Tokens=${this.creditsUsed}`);
|
|
346
|
+
debugLog(`InvestigationAgent [${this.agentLabel}]: Finished. Success=${success && !crashed}, Files=${relevantFiles.size}, Tokens=${this.creditsUsed} (In: ${this.inputTokens}, Out: ${this.outputTokens})`);
|
|
326
347
|
return {
|
|
327
348
|
domains: this.domains,
|
|
328
349
|
agentLabel: this.agentLabel,
|
|
@@ -330,6 +351,8 @@ export class InvestigationAgentRunner {
|
|
|
330
351
|
summary,
|
|
331
352
|
webSearchSummary,
|
|
332
353
|
creditsUsed: this.creditsUsed,
|
|
354
|
+
inputTokens: this.inputTokens,
|
|
355
|
+
outputTokens: this.outputTokens,
|
|
333
356
|
success: success && !crashed,
|
|
334
357
|
};
|
|
335
358
|
}
|
|
@@ -339,10 +362,12 @@ export class InvestigationAgentRunner {
|
|
|
339
362
|
updateUsage(result) {
|
|
340
363
|
const usage = result.response.usageMetadata?.();
|
|
341
364
|
if (usage) {
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
365
|
+
const input = (usage.promptTokens || 0) + (usage.cachedTokens || 0);
|
|
366
|
+
const output = usage.candidatesTokens || 0;
|
|
367
|
+
const tokens = usage.totalTokenCount || input + output;
|
|
368
|
+
this.inputTokens += input;
|
|
369
|
+
this.outputTokens += output;
|
|
370
|
+
this.creditsUsed += tokens;
|
|
346
371
|
}
|
|
347
372
|
}
|
|
348
373
|
}
|
|
@@ -98,12 +98,14 @@ export class InvestigationOrchestrator {
|
|
|
98
98
|
// 7. Log summary
|
|
99
99
|
const cacheStats = readCache.getStats();
|
|
100
100
|
const totalTokens = results.reduce((sum, r) => sum + r.creditsUsed, 0);
|
|
101
|
+
const totalInputTokens = results.reduce((sum, r) => sum + (r.inputTokens || 0), 0);
|
|
102
|
+
const totalOutputTokens = results.reduce((sum, r) => sum + (r.outputTokens || 0), 0);
|
|
101
103
|
const totalFilesBeforeDedup = results.reduce((sum, r) => sum + r.relevantFiles.size, 0);
|
|
102
104
|
p.log.info(`${pc.green('✓')} Parallel Investigation complete.\n` +
|
|
103
105
|
` Agents: ${agentCount} (${failedResults.length} failed) | ` +
|
|
104
106
|
`Files: ${mergedResult.relevantFiles.size} (deduped from ${totalFilesBeforeDedup}) | ` +
|
|
105
107
|
`Cache hits: ${cacheStats.hitCount}\n` +
|
|
106
|
-
` Duration: ${duration}s | Tokens: ${totalTokens.toLocaleString()}`);
|
|
108
|
+
` Duration: ${duration}s | Tokens: ${totalTokens.toLocaleString()} ${pc.dim(`(Input: ${totalInputTokens.toLocaleString()}, Output: ${totalOutputTokens.toLocaleString()})`)}`);
|
|
107
109
|
return mergedResult;
|
|
108
110
|
}
|
|
109
111
|
/**
|
|
@@ -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
|
+
* 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
|
+
* 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 =
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
101
|
-
|
|
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
|
-
})
|
|
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
|
|
@@ -226,30 +232,47 @@ export class Orchestrator {
|
|
|
226
232
|
p.log.step(pc.cyan('Orchestrator: Reconciling results...'));
|
|
227
233
|
const stats = this.bus.getStats();
|
|
228
234
|
let totalTokens = 0;
|
|
235
|
+
let inputTokens = 0;
|
|
236
|
+
let outputTokens = 0;
|
|
229
237
|
let failedTasks = 0;
|
|
230
238
|
let rawSummaryData = '';
|
|
231
239
|
for (const [taskId, res] of this.agentResults.entries()) {
|
|
232
240
|
totalTokens += res.creditsUsed;
|
|
241
|
+
inputTokens += res.inputTokens || 0;
|
|
242
|
+
outputTokens += res.outputTokens || 0;
|
|
233
243
|
if (!res.success)
|
|
234
244
|
failedTasks++;
|
|
235
245
|
debugLog(`Task ${taskId} summary: ${res.summary.substring(0, 100)}...`);
|
|
236
246
|
rawSummaryData += `Task: ${taskId} | Status: ${res.success ? 'Success' : 'Failed'}\nChanges:\n${res.summary}\n\n`;
|
|
237
247
|
}
|
|
238
248
|
p.log.step(pc.cyan('Orchestrator: Synthesizing final changes overview...'));
|
|
239
|
-
let finalSummary = '
|
|
249
|
+
let finalSummary = '';
|
|
240
250
|
try {
|
|
241
|
-
const
|
|
242
|
-
const
|
|
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);
|
|
243
264
|
finalSummary += synthesized + '\n\n';
|
|
244
265
|
}
|
|
245
266
|
catch (e) {
|
|
246
267
|
debugLog(`Synthesis failed, falling back to raw output: ${e}`);
|
|
247
268
|
finalSummary += rawSummaryData;
|
|
248
269
|
}
|
|
270
|
+
const currentUsage = peekTurnUsage();
|
|
271
|
+
const totalInputTokens = (currentUsage.promptTokens || 0) + (currentUsage.cachedTokens || 0);
|
|
249
272
|
p.log.info(`${pc.green('✓')} Sub-agent execution complete.\n` +
|
|
250
273
|
` Total tasks: ${graph.tasks.length} (${failedTasks} failed)\n` +
|
|
251
274
|
` Bus Activity: ${stats.activityCount} actions, ${stats.signalCount} signals\n` +
|
|
252
|
-
` Total Tokens: ${
|
|
275
|
+
` Total Tokens: ${currentUsage.totalTokenCount.toLocaleString()} ${pc.dim(`(Input: ${totalInputTokens.toLocaleString()}, Output: ${currentUsage.candidatesTokens.toLocaleString()})`)}`);
|
|
253
276
|
// Clear the bus persistence now that orchestration is done
|
|
254
277
|
await this.bus.cleanup();
|
|
255
278
|
this.locks.shutdown();
|
|
@@ -16,6 +16,8 @@ export interface SubAgentResult {
|
|
|
16
16
|
summary: string;
|
|
17
17
|
/** Total credits (tokens) consumed by this agent */
|
|
18
18
|
creditsUsed: number;
|
|
19
|
+
inputTokens?: number;
|
|
20
|
+
outputTokens?: number;
|
|
19
21
|
/** Did the agent hit a stall timeout or crash? */
|
|
20
22
|
crashed: boolean;
|
|
21
23
|
}
|
|
@@ -34,8 +36,10 @@ export declare class SubAgentRunner {
|
|
|
34
36
|
private chat;
|
|
35
37
|
private lastHeartbeat;
|
|
36
38
|
private creditsUsed;
|
|
39
|
+
private inputTokens;
|
|
40
|
+
private outputTokens;
|
|
37
41
|
/** Max time without a tool call or response before the agent is considered stalled */
|
|
38
|
-
static readonly STALL_TIMEOUT_MS =
|
|
42
|
+
static readonly STALL_TIMEOUT_MS = 300000;
|
|
39
43
|
constructor(taskId: string, intent: string, workspaceRoot: string, bus: MessageBus, locks: FileLockRegistry, globalContext: string, onProgress?: ((msg: string) => void) | undefined);
|
|
40
44
|
/**
|
|
41
45
|
* Constructs the base system instruction for this specific agent.
|
|
@@ -24,8 +24,10 @@ export class SubAgentRunner {
|
|
|
24
24
|
chat;
|
|
25
25
|
lastHeartbeat = Date.now();
|
|
26
26
|
creditsUsed = 0;
|
|
27
|
+
inputTokens = 0;
|
|
28
|
+
outputTokens = 0;
|
|
27
29
|
/** Max time without a tool call or response before the agent is considered stalled */
|
|
28
|
-
static STALL_TIMEOUT_MS =
|
|
30
|
+
static STALL_TIMEOUT_MS = 300_000;
|
|
29
31
|
constructor(taskId, intent, workspaceRoot, bus, locks, globalContext, onProgress) {
|
|
30
32
|
this.taskId = taskId;
|
|
31
33
|
this.intent = intent;
|
|
@@ -36,8 +38,8 @@ export class SubAgentRunner {
|
|
|
36
38
|
this.onProgress = onProgress;
|
|
37
39
|
let model = getGlobalActiveModel();
|
|
38
40
|
if (model === GEMINI_MODELS.AUTO)
|
|
39
|
-
model = GEMINI_MODELS.
|
|
40
|
-
// Sub-agents default to flash-3.
|
|
41
|
+
model = GEMINI_MODELS.FLASH;
|
|
42
|
+
// Sub-agents default to flash-3.6 for better reasoning capabilities
|
|
41
43
|
this.chat = new ProxyChatSession(model, this.buildSystemInstruction(), [{ functionDeclarations: getScopedToolDeclarations() }], {
|
|
42
44
|
maxOutputTokens: MAX_OUTPUT_TOKENS,
|
|
43
45
|
temperature: 0.3, // Lower temperature for more focused execution
|
|
@@ -49,18 +51,24 @@ export class SubAgentRunner {
|
|
|
49
51
|
* Constructs the base system instruction for this specific agent.
|
|
50
52
|
*/
|
|
51
53
|
buildSystemInstruction() {
|
|
52
|
-
return (
|
|
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` +
|
|
53
58
|
`Your task ID is: ${this.taskId}\n` +
|
|
54
|
-
`Your objective: ${this.intent}\n
|
|
55
|
-
|
|
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` +
|
|
56
63
|
`${this.globalContext}\n` +
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
`</reference_context>\n\n` +
|
|
65
|
+
`<critical_guidelines>\n` +
|
|
59
66
|
`1. You are ONE worker in a team. You MUST ONLY focus on your specific objective: "${this.intent}".\n` +
|
|
60
67
|
`2. DO NOT attempt to fulfill the entire original user request in the reference context. Other agents are handling the other parts.\n` +
|
|
61
68
|
`3. You are part of a parallelized system. Use 'post_message' to coordinate if you discover breaking changes.\n` +
|
|
62
69
|
`4. When you have completed your objective, stop using tools and provide a final summary of your work.\n` +
|
|
63
|
-
`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>`);
|
|
64
72
|
}
|
|
65
73
|
/**
|
|
66
74
|
* Updates the heartbeat. Passed to `executeScopedTool` to ensure the agent
|
|
@@ -124,8 +132,10 @@ export class SubAgentRunner {
|
|
|
124
132
|
toolResponses.push({
|
|
125
133
|
functionResponse: {
|
|
126
134
|
name: call.name,
|
|
127
|
-
response: {
|
|
128
|
-
|
|
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
|
+
},
|
|
129
139
|
});
|
|
130
140
|
continue;
|
|
131
141
|
}
|
|
@@ -149,7 +159,9 @@ export class SubAgentRunner {
|
|
|
149
159
|
output: responseData.output !== undefined ? responseData.output : responseData,
|
|
150
160
|
...(responseData.error ? { error: responseData.error } : {}),
|
|
151
161
|
...(responseData.inlineData ? { inlineData: responseData.inlineData } : {}),
|
|
152
|
-
...(responseData._orchestrationWarning
|
|
162
|
+
...(responseData._orchestrationWarning
|
|
163
|
+
? { _orchestrationWarning: responseData._orchestrationWarning }
|
|
164
|
+
: {}),
|
|
153
165
|
},
|
|
154
166
|
},
|
|
155
167
|
});
|
|
@@ -180,11 +192,13 @@ export class SubAgentRunner {
|
|
|
180
192
|
crashed = true;
|
|
181
193
|
finalSummary = 'Aborted by orchestrator.';
|
|
182
194
|
}
|
|
183
|
-
debugLog(`SubAgent [${this.taskId}]: Finished. Success=${success}, Crashed=${crashed}, Tokens=${this.creditsUsed}`);
|
|
195
|
+
debugLog(`SubAgent [${this.taskId}]: Finished. Success=${success}, Crashed=${crashed}, Tokens=${this.creditsUsed} (In: ${this.inputTokens}, Out: ${this.outputTokens})`);
|
|
184
196
|
return {
|
|
185
197
|
success: success && !crashed,
|
|
186
198
|
summary: finalSummary,
|
|
187
199
|
creditsUsed: this.creditsUsed,
|
|
200
|
+
inputTokens: this.inputTokens,
|
|
201
|
+
outputTokens: this.outputTokens,
|
|
188
202
|
crashed,
|
|
189
203
|
};
|
|
190
204
|
});
|
|
@@ -195,10 +209,12 @@ export class SubAgentRunner {
|
|
|
195
209
|
updateUsage(result) {
|
|
196
210
|
const usage = result.response.usageMetadata?.();
|
|
197
211
|
if (usage) {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
212
|
+
const input = (usage.promptTokens || 0) + (usage.cachedTokens || 0);
|
|
213
|
+
const output = usage.candidatesTokens || 0;
|
|
214
|
+
const tokens = usage.totalTokenCount || input + output;
|
|
215
|
+
this.inputTokens += input;
|
|
216
|
+
this.outputTokens += output;
|
|
217
|
+
this.creditsUsed += tokens;
|
|
202
218
|
}
|
|
203
219
|
}
|
|
204
220
|
}
|
|
@@ -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 ??
|
|
74
|
-
const maxOutputChars = options?.maxOutputChars ??
|
|
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 {
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -14,10 +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
|
|
18
|
-
readonly
|
|
19
|
-
readonly
|
|
20
|
-
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";
|
|
21
20
|
readonly AUTO: "auto";
|
|
22
21
|
};
|
|
23
22
|
/** Default Gemini model for the coding agent. */
|
package/dist/utils/config.js
CHANGED
|
@@ -14,10 +14,9 @@ export const GITHUB_CLIENT_ID = 'Ov23linFYFfjO3JILG7r';
|
|
|
14
14
|
* Supported Gemini AI models.
|
|
15
15
|
*/
|
|
16
16
|
export const GEMINI_MODELS = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
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',
|
|
21
20
|
AUTO: 'auto',
|
|
22
21
|
};
|
|
23
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>;
|