minovative-mind-cli 1.5.0 → 2.0.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 (76) hide show
  1. package/README.md +51 -45
  2. package/dist/commands/chat.js +10 -5
  3. package/dist/services/agent/slashCommands.js +163 -37
  4. package/dist/services/agent/toolLoop.d.ts +1 -1
  5. package/dist/services/agent/toolLoop.js +7 -2
  6. package/dist/services/agent/types.d.ts +2 -0
  7. package/dist/services/agent-tools.d.ts +9 -4
  8. package/dist/services/agent-tools.js +145 -21
  9. package/dist/services/agent.d.ts +8 -0
  10. package/dist/services/agent.js +294 -38
  11. package/dist/services/ai.d.ts +19 -5
  12. package/dist/services/ai.js +167 -35
  13. package/dist/services/changeLogger.d.ts +142 -0
  14. package/dist/services/changeLogger.js +132 -3
  15. package/dist/services/contextAgent.d.ts +6 -1
  16. package/dist/services/contextAgent.js +95 -14
  17. package/dist/services/embeddingIndex.d.ts +82 -0
  18. package/dist/services/embeddingIndex.js +613 -0
  19. package/dist/services/investigationComplexity.d.ts +45 -0
  20. package/dist/services/investigationComplexity.js +91 -0
  21. package/dist/services/metrics.d.ts +18 -0
  22. package/dist/services/metrics.js +7 -0
  23. package/dist/services/orchestration/fileLockRegistry.d.ts +125 -0
  24. package/dist/services/orchestration/fileLockRegistry.js +276 -0
  25. package/dist/services/orchestration/investigationAgent.d.ts +85 -0
  26. package/dist/services/orchestration/investigationAgent.js +359 -0
  27. package/dist/services/orchestration/investigationOrchestrator.d.ts +53 -0
  28. package/dist/services/orchestration/investigationOrchestrator.js +180 -0
  29. package/dist/services/orchestration/messageBus.d.ts +162 -0
  30. package/dist/services/orchestration/messageBus.js +225 -0
  31. package/dist/services/orchestration/orchestrator.d.ts +45 -0
  32. package/dist/services/orchestration/orchestrator.js +214 -0
  33. package/dist/services/orchestration/readCache.d.ts +79 -0
  34. package/dist/services/orchestration/readCache.js +108 -0
  35. package/dist/services/orchestration/scopedTools.d.ts +57 -0
  36. package/dist/services/orchestration/scopedTools.js +172 -0
  37. package/dist/services/orchestration/subAgent.d.ts +58 -0
  38. package/dist/services/orchestration/subAgent.js +187 -0
  39. package/dist/services/orchestration/taskGraph.d.ts +129 -0
  40. package/dist/services/orchestration/taskGraph.js +254 -0
  41. package/dist/services/proxyClient.d.ts +25 -0
  42. package/dist/services/proxyClient.js +60 -0
  43. package/dist/utils/asyncContext.d.ts +16 -0
  44. package/dist/utils/asyncContext.js +25 -0
  45. package/dist/utils/config.d.ts +3 -1
  46. package/dist/utils/config.js +3 -1
  47. package/dist/utils/contextPrompts.js +3 -2
  48. package/dist/utils/dependencyTracer/modules/api.d.ts +9 -0
  49. package/dist/utils/dependencyTracer/modules/api.js +62 -0
  50. package/dist/utils/dependencyTracer/modules/graph.d.ts +9 -0
  51. package/dist/utils/dependencyTracer/modules/graph.js +23 -0
  52. package/dist/utils/dependencyTracer/modules/profiles.d.ts +7 -0
  53. package/dist/utils/dependencyTracer/modules/profiles.js +120 -0
  54. package/dist/utils/dependencyTracer/modules/resolver.d.ts +7 -0
  55. package/dist/utils/dependencyTracer/modules/resolver.js +51 -0
  56. package/dist/utils/dependencyTracer/modules/types.d.ts +4 -0
  57. package/dist/utils/dependencyTracer/modules/types.js +1 -0
  58. package/dist/utils/dependencyTracer/modules/walker.d.ts +1 -0
  59. package/dist/utils/dependencyTracer/modules/walker.js +48 -0
  60. package/dist/utils/dependencyTracer.js +31 -17
  61. package/dist/utils/excludedExtensions.js +0 -1
  62. package/dist/utils/historyPrompt.d.ts +9 -0
  63. package/dist/utils/historyPrompt.js +87 -0
  64. package/dist/utils/logo.d.ts +2 -0
  65. package/dist/utils/logo.js +31 -10
  66. package/dist/utils/paste.d.ts +21 -0
  67. package/dist/utils/paste.js +22 -1
  68. package/dist/utils/profiles.d.ts +2 -0
  69. package/dist/utils/profiles.js +44 -0
  70. package/dist/utils/projectStorage.js +10 -7
  71. package/dist/utils/systemPrompts.d.ts +6 -3
  72. package/dist/utils/systemPrompts.js +106 -5
  73. package/dist/utils/types.d.ts +33 -0
  74. package/dist/utils/types.js +1 -0
  75. package/oclif.manifest.json +2 -2
  76. package/package.json +5 -3
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @fileoverview Task Graph for Sub-Agent Orchestration.
3
+ *
4
+ * Defines the data structures and algorithms for decomposing a user request into
5
+ * a directed acyclic graph (DAG) of parallelizable sub-tasks.
6
+ *
7
+ * Key capabilities:
8
+ * - **Kahn's Algorithm** for topological sort with cycle detection
9
+ * - **Execution Wave computation** for maximally parallel dispatch scheduling
10
+ * - **File conflict detection** for lock pre-allocation ordering
11
+ * - **Disconnected graph support** — floating tasks launch immediately in Wave 1
12
+ */
13
+ /**
14
+ * A structured plan produced by the PM Agent. Contains all the information
15
+ * needed to dispatch, schedule, and coordinate parallel sub-agents.
16
+ */
17
+ export interface TaskGraph {
18
+ /** High-level objective from the user */
19
+ objective: string;
20
+ /** Ordered list of sub-tasks. Each task can span multiple files. */
21
+ tasks: SubTask[];
22
+ /** Global constraints all sub-agents must respect */
23
+ constraints: string[];
24
+ }
25
+ /**
26
+ * A single unit of work assigned to one sub-agent.
27
+ * Defined by intent (what to do), scope (which files), and ordering (dependencies).
28
+ */
29
+ export interface SubTask {
30
+ /** Unique identifier for this task (e.g., 'task-1', 'setup-redis') */
31
+ id: string;
32
+ /** Human-readable description of what this sub-agent must accomplish */
33
+ intent: string;
34
+ /** All files this sub-agent will work on (can be many) */
35
+ targetFiles: string[];
36
+ /** Files this sub-agent may read but NOT modify */
37
+ readOnlyFiles: string[];
38
+ /** IDs of tasks that must complete before this one starts */
39
+ dependsOn: string[];
40
+ /** What this sub-agent should export for downstream tasks */
41
+ exports: string[];
42
+ }
43
+ /**
44
+ * An execution wave groups tasks at the same depth in the dependency graph.
45
+ * All tasks within a wave can run in parallel. Waves execute sequentially.
46
+ */
47
+ export interface ExecutionWave {
48
+ /** 0-indexed wave depth */
49
+ depth: number;
50
+ /** Task IDs that can run in parallel at this depth */
51
+ taskIds: string[];
52
+ }
53
+ /**
54
+ * Thrown when Kahn's algorithm detects a cyclic dependency in the task graph.
55
+ * Contains the specific nodes involved so the PM can self-correct.
56
+ */
57
+ export declare class CyclicDependencyError extends Error {
58
+ /** Human-readable description of the cycle nodes and their dependencies */
59
+ readonly cycleNodes: string[];
60
+ constructor(
61
+ /** Human-readable description of the cycle nodes and their dependencies */
62
+ cycleNodes: string[]);
63
+ }
64
+ /**
65
+ * Thrown when a task references a dependency that doesn't exist in the graph.
66
+ */
67
+ export declare class InvalidDependencyError extends Error {
68
+ readonly taskId: string;
69
+ readonly missingDependencyId: string;
70
+ constructor(taskId: string, missingDependencyId: string);
71
+ }
72
+ /**
73
+ * Validates a TaskGraph for cyclic dependencies using Kahn's Algorithm
74
+ * for topological sorting.
75
+ *
76
+ * - **Time complexity**: O(V + E) where V = tasks, E = dependency edges.
77
+ * - **Disconnected graph support**: Tasks with no dependencies AND no dependents
78
+ * naturally get in-degree 0 and appear immediately in the sorted output.
79
+ *
80
+ * @param graph - The task graph to validate.
81
+ * @returns The topological execution order (array of task IDs).
82
+ * @throws {CyclicDependencyError} If the graph contains a cycle.
83
+ * @throws {InvalidDependencyError} If a task references a non-existent dependency.
84
+ */
85
+ export declare function validateTaskGraph(graph: TaskGraph): string[];
86
+ /**
87
+ * Computes execution waves from a validated task graph. Each wave is a set of
88
+ * tasks that can run in parallel — all their dependencies are satisfied by
89
+ * tasks in earlier waves.
90
+ *
91
+ * **Disconnected tasks** (no deps, no one depends on them) land in Wave 0.
92
+ *
93
+ * @param graph - A validated (acyclic) task graph.
94
+ * @returns Array of execution waves ordered by depth.
95
+ */
96
+ export declare function computeExecutionWaves(graph: TaskGraph): ExecutionWave[];
97
+ /**
98
+ * Detects file-level conflicts between tasks in the same execution wave.
99
+ * If two tasks in the same wave share any `targetFiles`, they CANNOT run in
100
+ * parallel (circular wait deadlock risk). The orchestrator must serialize them.
101
+ *
102
+ * @param graph - A validated task graph.
103
+ * @param waves - The computed execution waves.
104
+ * @returns A map of wave depth → array of conflicting task ID pairs with their shared files.
105
+ */
106
+ export declare function detectFileConflicts(graph: TaskGraph, waves: ExecutionWave[]): Map<number, Array<{
107
+ taskA: string;
108
+ taskB: string;
109
+ sharedFiles: string[];
110
+ }>>;
111
+ /**
112
+ * Resolves file conflicts by splitting conflicted tasks out of their parallel
113
+ * wave and serializing them. Returns a new wave schedule where conflicting
114
+ * tasks are placed into sequential sub-waves.
115
+ *
116
+ * @param waves - The original execution waves.
117
+ * @param conflicts - The detected conflicts from `detectFileConflicts`.
118
+ * @returns A new array of execution waves with conflicts resolved.
119
+ */
120
+ export declare function resolveFileConflicts(waves: ExecutionWave[], conflicts: Map<number, Array<{
121
+ taskA: string;
122
+ taskB: string;
123
+ sharedFiles: string[];
124
+ }>>): ExecutionWave[];
125
+ /**
126
+ * Generates the system prompt sent to the PM Agent when Kahn's algorithm
127
+ * detects a cycle, requesting it to fix its own task graph.
128
+ */
129
+ export declare function buildCycleCorrectionPrompt(cycleNodes: string[]): string;
@@ -0,0 +1,254 @@
1
+ /**
2
+ * @fileoverview Task Graph for Sub-Agent Orchestration.
3
+ *
4
+ * Defines the data structures and algorithms for decomposing a user request into
5
+ * a directed acyclic graph (DAG) of parallelizable sub-tasks.
6
+ *
7
+ * Key capabilities:
8
+ * - **Kahn's Algorithm** for topological sort with cycle detection
9
+ * - **Execution Wave computation** for maximally parallel dispatch scheduling
10
+ * - **File conflict detection** for lock pre-allocation ordering
11
+ * - **Disconnected graph support** — floating tasks launch immediately in Wave 1
12
+ */
13
+ import { debugLog } from '../../utils/logger.js';
14
+ // ─── Errors ──────────────────────────────────────────────────────────
15
+ /**
16
+ * Thrown when Kahn's algorithm detects a cyclic dependency in the task graph.
17
+ * Contains the specific nodes involved so the PM can self-correct.
18
+ */
19
+ export class CyclicDependencyError extends Error {
20
+ cycleNodes;
21
+ constructor(
22
+ /** Human-readable description of the cycle nodes and their dependencies */
23
+ cycleNodes) {
24
+ super(`Task graph contains a cyclic dependency:\n${cycleNodes.join('\n')}\n\n` +
25
+ `Tasks cannot depend on each other circularly.`);
26
+ this.cycleNodes = cycleNodes;
27
+ this.name = 'CyclicDependencyError';
28
+ }
29
+ }
30
+ /**
31
+ * Thrown when a task references a dependency that doesn't exist in the graph.
32
+ */
33
+ export class InvalidDependencyError extends Error {
34
+ taskId;
35
+ missingDependencyId;
36
+ constructor(taskId, missingDependencyId) {
37
+ super(`Task "${taskId}" depends on "${missingDependencyId}" which does not exist in the graph.`);
38
+ this.taskId = taskId;
39
+ this.missingDependencyId = missingDependencyId;
40
+ this.name = 'InvalidDependencyError';
41
+ }
42
+ }
43
+ // ─── Validation (Kahn's Algorithm) ───────────────────────────────────
44
+ /**
45
+ * Validates a TaskGraph for cyclic dependencies using Kahn's Algorithm
46
+ * for topological sorting.
47
+ *
48
+ * - **Time complexity**: O(V + E) where V = tasks, E = dependency edges.
49
+ * - **Disconnected graph support**: Tasks with no dependencies AND no dependents
50
+ * naturally get in-degree 0 and appear immediately in the sorted output.
51
+ *
52
+ * @param graph - The task graph to validate.
53
+ * @returns The topological execution order (array of task IDs).
54
+ * @throws {CyclicDependencyError} If the graph contains a cycle.
55
+ * @throws {InvalidDependencyError} If a task references a non-existent dependency.
56
+ */
57
+ export function validateTaskGraph(graph) {
58
+ const taskIds = new Set(graph.tasks.map(t => t.id));
59
+ const inDegree = new Map();
60
+ const adjacency = new Map();
61
+ // Initialize all nodes
62
+ for (const task of graph.tasks) {
63
+ inDegree.set(task.id, 0);
64
+ adjacency.set(task.id, []);
65
+ }
66
+ // Build adjacency list and compute in-degrees
67
+ for (const task of graph.tasks) {
68
+ for (const dep of task.dependsOn) {
69
+ // Validate that the dependency actually exists
70
+ if (!taskIds.has(dep)) {
71
+ throw new InvalidDependencyError(task.id, dep);
72
+ }
73
+ adjacency.get(dep).push(task.id);
74
+ inDegree.set(task.id, (inDegree.get(task.id) ?? 0) + 1);
75
+ }
76
+ }
77
+ // Seed the queue with all zero-dependency tasks (Wave 1 candidates)
78
+ const queue = [];
79
+ for (const [id, degree] of inDegree) {
80
+ if (degree === 0)
81
+ queue.push(id);
82
+ }
83
+ // Process the queue
84
+ const sorted = [];
85
+ while (queue.length > 0) {
86
+ const current = queue.shift();
87
+ sorted.push(current);
88
+ for (const neighbor of adjacency.get(current) ?? []) {
89
+ const newDegree = (inDegree.get(neighbor) ?? 1) - 1;
90
+ inDegree.set(neighbor, newDegree);
91
+ if (newDegree === 0)
92
+ queue.push(neighbor);
93
+ }
94
+ }
95
+ // If not all nodes were processed, we have a cycle
96
+ if (sorted.length !== graph.tasks.length) {
97
+ const cycleNodes = graph.tasks
98
+ .filter(t => !sorted.includes(t.id))
99
+ .map(t => ` ${t.id} (depends on: ${t.dependsOn.join(', ')})`);
100
+ debugLog(`TaskGraph: Cycle detected in ${cycleNodes.length} nodes.`);
101
+ throw new CyclicDependencyError(cycleNodes);
102
+ }
103
+ debugLog(`TaskGraph: Valid topological order: [${sorted.join(' → ')}]`);
104
+ return sorted;
105
+ }
106
+ // ─── Execution Wave Computation ──────────────────────────────────────
107
+ /**
108
+ * Computes execution waves from a validated task graph. Each wave is a set of
109
+ * tasks that can run in parallel — all their dependencies are satisfied by
110
+ * tasks in earlier waves.
111
+ *
112
+ * **Disconnected tasks** (no deps, no one depends on them) land in Wave 0.
113
+ *
114
+ * @param graph - A validated (acyclic) task graph.
115
+ * @returns Array of execution waves ordered by depth.
116
+ */
117
+ export function computeExecutionWaves(graph) {
118
+ const taskMap = new Map(graph.tasks.map(t => [t.id, t]));
119
+ const depths = new Map();
120
+ // Compute the depth of each task (longest path from any root)
121
+ function computeDepth(taskId, visited = new Set()) {
122
+ if (depths.has(taskId))
123
+ return depths.get(taskId);
124
+ if (visited.has(taskId))
125
+ return 0; // Safety guard (should never hit post-validation)
126
+ visited.add(taskId);
127
+ const task = taskMap.get(taskId);
128
+ if (task.dependsOn.length === 0) {
129
+ depths.set(taskId, 0);
130
+ return 0;
131
+ }
132
+ const maxDepDep = Math.max(...task.dependsOn.map(dep => computeDepth(dep, visited)));
133
+ const depth = maxDepDep + 1;
134
+ depths.set(taskId, depth);
135
+ return depth;
136
+ }
137
+ for (const task of graph.tasks) {
138
+ computeDepth(task.id);
139
+ }
140
+ // Group tasks by depth into waves
141
+ const waveMap = new Map();
142
+ for (const [taskId, depth] of depths) {
143
+ if (!waveMap.has(depth))
144
+ waveMap.set(depth, []);
145
+ waveMap.get(depth).push(taskId);
146
+ }
147
+ // Sort waves by depth and return
148
+ const waves = Array.from(waveMap.entries())
149
+ .sort(([a], [b]) => a - b)
150
+ .map(([depth, taskIds]) => ({ depth, taskIds }));
151
+ debugLog(`TaskGraph: ${waves.length} execution wave(s): ` +
152
+ waves.map(w => `Wave ${w.depth} [${w.taskIds.join(', ')}]`).join(' → '));
153
+ return waves;
154
+ }
155
+ // ─── File Conflict Detection (Lock Pre-Allocation) ───────────────────
156
+ /**
157
+ * Detects file-level conflicts between tasks in the same execution wave.
158
+ * If two tasks in the same wave share any `targetFiles`, they CANNOT run in
159
+ * parallel (circular wait deadlock risk). The orchestrator must serialize them.
160
+ *
161
+ * @param graph - A validated task graph.
162
+ * @param waves - The computed execution waves.
163
+ * @returns A map of wave depth → array of conflicting task ID pairs with their shared files.
164
+ */
165
+ export function detectFileConflicts(graph, waves) {
166
+ const taskMap = new Map(graph.tasks.map(t => [t.id, t]));
167
+ const conflicts = new Map();
168
+ for (const wave of waves) {
169
+ if (wave.taskIds.length < 2)
170
+ continue; // Solo waves can't have conflicts
171
+ const waveConflicts = [];
172
+ // Check every pair of tasks in the wave for file intersection
173
+ for (let i = 0; i < wave.taskIds.length; i++) {
174
+ for (let j = i + 1; j < wave.taskIds.length; j++) {
175
+ const taskA = taskMap.get(wave.taskIds[i]);
176
+ const taskB = taskMap.get(wave.taskIds[j]);
177
+ const aTargets = new Set(taskA.targetFiles);
178
+ const sharedFiles = taskB.targetFiles.filter(f => aTargets.has(f));
179
+ if (sharedFiles.length > 0) {
180
+ waveConflicts.push({
181
+ taskA: taskA.id,
182
+ taskB: taskB.id,
183
+ sharedFiles,
184
+ });
185
+ }
186
+ }
187
+ }
188
+ if (waveConflicts.length > 0) {
189
+ conflicts.set(wave.depth, waveConflicts);
190
+ debugLog(`TaskGraph: Wave ${wave.depth} has ${waveConflicts.length} file conflict(s): ` +
191
+ waveConflicts
192
+ .map(c => `${c.taskA} ∩ ${c.taskB} on [${c.sharedFiles.join(', ')}]`)
193
+ .join('; '));
194
+ }
195
+ }
196
+ return conflicts;
197
+ }
198
+ /**
199
+ * Resolves file conflicts by splitting conflicted tasks out of their parallel
200
+ * wave and serializing them. Returns a new wave schedule where conflicting
201
+ * tasks are placed into sequential sub-waves.
202
+ *
203
+ * @param waves - The original execution waves.
204
+ * @param conflicts - The detected conflicts from `detectFileConflicts`.
205
+ * @returns A new array of execution waves with conflicts resolved.
206
+ */
207
+ export function resolveFileConflicts(waves, conflicts) {
208
+ if (conflicts.size === 0)
209
+ return waves;
210
+ const resolved = [];
211
+ let currentDepth = 0;
212
+ for (const wave of waves) {
213
+ const waveConflicts = conflicts.get(wave.depth);
214
+ if (!waveConflicts || waveConflicts.length === 0) {
215
+ // No conflicts in this wave — keep it as-is but reindex depth
216
+ resolved.push({ depth: currentDepth, taskIds: [...wave.taskIds] });
217
+ currentDepth++;
218
+ continue;
219
+ }
220
+ // Collect all conflicting task IDs
221
+ const conflictingIds = new Set();
222
+ for (const conflict of waveConflicts) {
223
+ conflictingIds.add(conflict.taskA);
224
+ conflictingIds.add(conflict.taskB);
225
+ }
226
+ // Non-conflicting tasks can still run in parallel
227
+ const parallelTasks = wave.taskIds.filter(id => !conflictingIds.has(id));
228
+ const serialTasks = wave.taskIds.filter(id => conflictingIds.has(id));
229
+ if (parallelTasks.length > 0) {
230
+ resolved.push({ depth: currentDepth, taskIds: parallelTasks });
231
+ currentDepth++;
232
+ }
233
+ // Serialize conflicting tasks into individual sub-waves
234
+ for (const taskId of serialTasks) {
235
+ resolved.push({ depth: currentDepth, taskIds: [taskId] });
236
+ currentDepth++;
237
+ }
238
+ }
239
+ debugLog(`TaskGraph: Resolved conflicts. Original ${waves.length} wave(s) → ${resolved.length} wave(s).`);
240
+ return resolved;
241
+ }
242
+ // ─── PM Self-Correction Prompt ───────────────────────────────────────
243
+ /**
244
+ * Generates the system prompt sent to the PM Agent when Kahn's algorithm
245
+ * detects a cycle, requesting it to fix its own task graph.
246
+ */
247
+ export function buildCycleCorrectionPrompt(cycleNodes) {
248
+ return (`[SYSTEM — TASK GRAPH VALIDATION FAILED]\n` +
249
+ `Your generated task graph contains a cyclic dependency:\n` +
250
+ `${cycleNodes.join('\n')}\n\n` +
251
+ `This is impossible to execute — tasks cannot depend on each other circularly.\n` +
252
+ `Please regenerate the task graph with corrected dependencies. Ensure that the ` +
253
+ `dependency chain is strictly acyclic (no task can transitively depend on itself).`);
254
+ }
@@ -34,6 +34,7 @@ export interface ProxyUsageMetadata {
34
34
  */
35
35
  export declare class ProxyClient {
36
36
  private readonly PROXY_URL;
37
+ private readonly EMBED_URL;
37
38
  /**
38
39
  * Generates text, thoughts, or function calls via the secure Gemini proxy URL.
39
40
  * Utilizes Server-Sent Events (SSE) to stream partial token responses back to the client.
@@ -60,4 +61,28 @@ export declare class ProxyClient {
60
61
  usageMetadata?: ProxyUsageMetadata;
61
62
  groundingMetadata?: any;
62
63
  }>;
64
+ /**
65
+ * Embeds one or more text chunks via the secure embedding proxy endpoint.
66
+ * Uses the same Firebase auth pattern as generateFunctionCallViaProxy, but
67
+ * targets a separate Cloud Function optimized for embedding generation.
68
+ *
69
+ * Unlike the generative endpoint, embedding responses are small and atomic,
70
+ * so no SSE streaming is required — a single JSON response is returned.
71
+ *
72
+ * @param idToken - The Firebase ID token for authorization.
73
+ * @param texts - Array of text strings to embed. Batched by caller (max ~25 per call).
74
+ * @param taskType - Embedding task type hint for optimal retrieval quality.
75
+ * - 'RETRIEVAL_DOCUMENT': Used when indexing source code chunks.
76
+ * - 'RETRIEVAL_QUERY': Used when embedding a user's semantic search query.
77
+ * @returns The embedding vectors and usage metadata from the proxy.
78
+ * @throws {Error} If authentication fails (401), credits are insufficient (402), or network errors occur.
79
+ */
80
+ embedTextsViaProxy(idToken: string, texts: string[], taskType?: 'RETRIEVAL_DOCUMENT' | 'RETRIEVAL_QUERY'): Promise<{
81
+ embeddings: number[][];
82
+ usage?: {
83
+ promptTokens: number;
84
+ creditsUsed: number;
85
+ remainingBalance: number;
86
+ };
87
+ }>;
63
88
  }
@@ -5,6 +5,7 @@ import { debugLog } from '../utils/logger.js';
5
5
  */
6
6
  export class ProxyClient {
7
7
  PROXY_URL = 'https://generatecontent-6obg3e4zwa-uc.a.run.app';
8
+ EMBED_URL = 'https://embedcontent-6obg3e4zwa-uc.a.run.app';
8
9
  /**
9
10
  * Generates text, thoughts, or function calls via the secure Gemini proxy URL.
10
11
  * Utilizes Server-Sent Events (SSE) to stream partial token responses back to the client.
@@ -141,4 +142,63 @@ export class ProxyClient {
141
142
  groundingMetadata,
142
143
  };
143
144
  }
145
+ /**
146
+ * Embeds one or more text chunks via the secure embedding proxy endpoint.
147
+ * Uses the same Firebase auth pattern as generateFunctionCallViaProxy, but
148
+ * targets a separate Cloud Function optimized for embedding generation.
149
+ *
150
+ * Unlike the generative endpoint, embedding responses are small and atomic,
151
+ * so no SSE streaming is required — a single JSON response is returned.
152
+ *
153
+ * @param idToken - The Firebase ID token for authorization.
154
+ * @param texts - Array of text strings to embed. Batched by caller (max ~25 per call).
155
+ * @param taskType - Embedding task type hint for optimal retrieval quality.
156
+ * - 'RETRIEVAL_DOCUMENT': Used when indexing source code chunks.
157
+ * - 'RETRIEVAL_QUERY': Used when embedding a user's semantic search query.
158
+ * @returns The embedding vectors and usage metadata from the proxy.
159
+ * @throws {Error} If authentication fails (401), credits are insufficient (402), or network errors occur.
160
+ */
161
+ async embedTextsViaProxy(idToken, texts, taskType = 'RETRIEVAL_DOCUMENT') {
162
+ const response = await fetch(this.EMBED_URL, {
163
+ method: 'POST',
164
+ headers: {
165
+ 'Content-Type': 'application/json',
166
+ 'X-Firebase-Auth': `Bearer ${idToken}`,
167
+ },
168
+ body: JSON.stringify({
169
+ contents: texts,
170
+ taskType,
171
+ }),
172
+ });
173
+ debugLog(`Embed Proxy Request complete. Status: ${response.status} ${response.statusText}`);
174
+ if (response.status === 401) {
175
+ let details = '';
176
+ try {
177
+ const text = await response.text();
178
+ try {
179
+ const errorData = JSON.parse(text);
180
+ details = errorData.details || errorData.error || text;
181
+ }
182
+ catch {
183
+ details = text;
184
+ }
185
+ }
186
+ catch {
187
+ details = 'Unknown error reading body';
188
+ }
189
+ throw new Error(`Authentication failed: ${details}. Please login again.`);
190
+ }
191
+ if (response.status === 402) {
192
+ throw new Error('Insufficient credits. Please visit minovativemind.dev to purchase more credits.');
193
+ }
194
+ if (!response.ok) {
195
+ const errorData = await response.json().catch(() => ({}));
196
+ throw new Error(`Embed proxy error ${response.status}: ${errorData.error || response.statusText}`);
197
+ }
198
+ const data = await response.json();
199
+ return {
200
+ embeddings: data.embeddings?.map((e) => e.values || e) || [],
201
+ usage: data.usage,
202
+ };
203
+ }
144
204
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Runs a callback within the context of a specific agent/thread ID.
3
+ * All downstream async operations will be able to access this ID.
4
+ *
5
+ * @param agentId The ID of the agent/thread to track.
6
+ * @param callback The function to execute.
7
+ * @returns The result of the callback.
8
+ */
9
+ export declare function runWithAgentId<T>(agentId: string, callback: () => T): T;
10
+ /**
11
+ * Retrieves the ID of the currently active agent/thread from the async context.
12
+ * Returns 'main' if no context is active (e.g., fallback for single-agent or orchestrator).
13
+ *
14
+ * @returns The current agent ID, or 'main' as fallback.
15
+ */
16
+ export declare function getCurrentAgentId(): string;
@@ -0,0 +1,25 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ /**
3
+ * AsyncLocalStorage instance to track the current active agent/thread ID.
4
+ */
5
+ const agentContext = new AsyncLocalStorage();
6
+ /**
7
+ * Runs a callback within the context of a specific agent/thread ID.
8
+ * All downstream async operations will be able to access this ID.
9
+ *
10
+ * @param agentId The ID of the agent/thread to track.
11
+ * @param callback The function to execute.
12
+ * @returns The result of the callback.
13
+ */
14
+ export function runWithAgentId(agentId, callback) {
15
+ return agentContext.run(agentId, callback);
16
+ }
17
+ /**
18
+ * Retrieves the ID of the currently active agent/thread from the async context.
19
+ * Returns 'main' if no context is active (e.g., fallback for single-agent or orchestrator).
20
+ *
21
+ * @returns The current agent ID, or 'main' as fallback.
22
+ */
23
+ export function getCurrentAgentId() {
24
+ return agentContext.getStore() || 'main';
25
+ }
@@ -17,6 +17,8 @@ export declare const GEMINI_MODELS: {
17
17
  readonly PRO_3_1: "gemini-3.1-pro-preview";
18
18
  readonly FLASH_3_5: "gemini-3.5-flash";
19
19
  readonly FLASH_LITE_3_1: "gemini-3.1-flash-lite";
20
+ readonly EMBEDDING: "text-embedding-004";
21
+ readonly AUTO: "auto";
20
22
  };
21
23
  /**
22
24
  * Supported Anthropic Claude models.
@@ -26,6 +28,6 @@ export declare const CLAUDE_MODELS: {
26
28
  readonly SONNET: "claude-sonnet-4-6";
27
29
  };
28
30
  /** Default Gemini model for the coding agent. */
29
- export declare const DEFAULT_MODEL: "gemini-3.5-flash";
31
+ export declare const DEFAULT_MODEL: "auto";
30
32
  /** Maximum tokens the model can output per response. */
31
33
  export declare const MAX_OUTPUT_TOKENS = 60000;
@@ -17,6 +17,8 @@ export const GEMINI_MODELS = {
17
17
  PRO_3_1: 'gemini-3.1-pro-preview',
18
18
  FLASH_3_5: 'gemini-3.5-flash',
19
19
  FLASH_LITE_3_1: 'gemini-3.1-flash-lite',
20
+ EMBEDDING: 'text-embedding-004',
21
+ AUTO: 'auto',
20
22
  };
21
23
  /**
22
24
  * Supported Anthropic Claude models.
@@ -26,6 +28,6 @@ export const CLAUDE_MODELS = {
26
28
  SONNET: 'claude-sonnet-4-6',
27
29
  };
28
30
  /** Default Gemini model for the coding agent. */
29
- export const DEFAULT_MODEL = GEMINI_MODELS.FLASH_3_5;
31
+ export const DEFAULT_MODEL = GEMINI_MODELS.AUTO;
30
32
  /** Maximum tokens the model can output per response. */
31
33
  export const MAX_OUTPUT_TOKENS = 60_000;
@@ -41,10 +41,11 @@ ${context.webSearchSummary}
41
41
  }
42
42
  if (context.relevantFiles.size > 0) {
43
43
  injection += `\n## Relevant File Contents\n`;
44
- for (const [filePath, content] of context.relevantFiles.entries()) {
44
+ for (const [filePath, contentObj] of context.relevantFiles.entries()) {
45
+ const contentText = contentObj.text;
45
46
  injection += `<workspace_file path="${filePath}">
46
47
  <content_data><![CDATA[
47
- ${sanitizeForCDATA(content)}
48
+ ${sanitizeForCDATA(contentText)}
48
49
  ]]\\u200B></content_data>
49
50
  </workspace_file>\n`;
50
51
  }
@@ -0,0 +1,9 @@
1
+ export interface FindDependenciesResult {
2
+ filePath: string;
3
+ forwardDeps: string[];
4
+ reverseDeps: string[];
5
+ forwardTree: string[];
6
+ reverseTree: string[];
7
+ }
8
+ export declare function extractImports(content: string, ext: string): string[];
9
+ export declare function resolveImportPath(workspaceRoot: string, sourceFile: string, specifier: string): Promise<string | null>;
@@ -0,0 +1,62 @@
1
+ import { PROFILE_BY_EXT } from './profiles.js';
2
+ import { loadPathAliases } from './resolver.js';
3
+ import { probeFilePath } from './walker.js';
4
+ import path from 'node:path';
5
+ export function extractImports(content, ext) {
6
+ const profiles = PROFILE_BY_EXT.get(ext);
7
+ if (!profiles || profiles.length === 0)
8
+ return [];
9
+ const specifiers = new Set();
10
+ for (const profile of profiles) {
11
+ for (const pattern of profile.patterns) {
12
+ pattern.lastIndex = 0;
13
+ for (const match of content.matchAll(pattern)) {
14
+ const raw = match.groups?.specifier;
15
+ if (raw && raw.trim().length > 0) {
16
+ const normalized = profile.normalizeSpecifier ? profile.normalizeSpecifier(raw.trim(), '') : raw.trim();
17
+ specifiers.add(normalized);
18
+ }
19
+ }
20
+ }
21
+ }
22
+ return Array.from(specifiers);
23
+ }
24
+ export async function resolveImportPath(workspaceRoot, sourceFile, specifier) {
25
+ if (specifier.startsWith('node:') ||
26
+ specifier.startsWith('bun:') ||
27
+ specifier.startsWith('deno:') ||
28
+ specifier.startsWith('package:') ||
29
+ specifier.startsWith('dart:') ||
30
+ specifier.startsWith('http://') ||
31
+ specifier.startsWith('https://')) {
32
+ return null;
33
+ }
34
+ const isRelative = specifier.startsWith('.') || specifier.startsWith('/');
35
+ if (!isRelative) {
36
+ const aliases = await loadPathAliases(workspaceRoot);
37
+ for (const alias of aliases) {
38
+ if (specifier.startsWith(alias.prefix)) {
39
+ const remainder = specifier.slice(alias.prefix.length);
40
+ for (const target of alias.targets) {
41
+ const candidate = path.join(workspaceRoot, target, remainder);
42
+ const resolved = await probeFilePath(candidate);
43
+ if (resolved)
44
+ return path.relative(workspaceRoot, resolved).replace(/\\/g, '/');
45
+ }
46
+ }
47
+ }
48
+ const fromRoot = path.join(workspaceRoot, specifier);
49
+ const rootResolved = await probeFilePath(fromRoot);
50
+ if (rootResolved)
51
+ return path.relative(workspaceRoot, rootResolved).replace(/\\/g, '/');
52
+ return null;
53
+ }
54
+ const sourceDir = path.dirname(path.join(workspaceRoot, sourceFile));
55
+ const absoluteCandidate = path.resolve(sourceDir, specifier);
56
+ if (!absoluteCandidate.startsWith(workspaceRoot))
57
+ return null;
58
+ const resolved = await probeFilePath(absoluteCandidate);
59
+ if (resolved)
60
+ return path.relative(workspaceRoot, resolved).replace(/\\/g, '/');
61
+ return null;
62
+ }
@@ -0,0 +1,9 @@
1
+ import { DependencyNode } from './types.js';
2
+ export interface DependencyGraph {
3
+ getImports(filePath: string): string[];
4
+ getImportedBy(filePath: string): string[];
5
+ getReverseDependencyTree(filePath: string, maxDepth?: number): string[];
6
+ getForwardDependencyTree(filePath: string, maxDepth?: number): string[];
7
+ readonly nodes: ReadonlyMap<string, DependencyNode>;
8
+ }
9
+ export declare function bfsTraverse(nodes: Map<string, DependencyNode>, startFile: string, direction: 'imports' | 'importedBy', maxDepth: number): string[];