minovative-mind-cli 2.11.5 → 2.13.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 (43) hide show
  1. package/README.md +27 -1
  2. package/dist/commands/chat.js +3 -1
  3. package/dist/commands/eval.d.ts +22 -0
  4. package/dist/commands/eval.js +141 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/services/agent/slashCommands.js +4 -2
  8. package/dist/services/agent/toolLoop.d.ts +4 -0
  9. package/dist/services/agent/toolLoop.js +61 -10
  10. package/dist/services/agent-tools.d.ts +5 -5
  11. package/dist/services/agent-tools.js +150 -11
  12. package/dist/services/contextAgent.d.ts +1 -0
  13. package/dist/services/contextAgent.js +29 -6
  14. package/dist/services/ideOptimization.d.ts +15 -0
  15. package/dist/services/ideOptimization.js +169 -0
  16. package/dist/services/metrics.d.ts +10 -0
  17. package/dist/services/metrics.js +24 -0
  18. package/dist/services/orchestration/messageBus.d.ts +81 -41
  19. package/dist/services/orchestration/messageBus.js +242 -98
  20. package/dist/services/orchestration/orchestrator.d.ts +6 -6
  21. package/dist/services/orchestration/orchestrator.js +32 -21
  22. package/dist/services/orchestration/scopedTools.d.ts +7 -1
  23. package/dist/services/orchestration/scopedTools.js +45 -9
  24. package/dist/services/orchestration/subAgent.d.ts +19 -17
  25. package/dist/services/orchestration/subAgent.js +98 -81
  26. package/dist/services/swebench/gitDiffExtractor.d.ts +57 -0
  27. package/dist/services/swebench/gitDiffExtractor.js +209 -0
  28. package/dist/services/swebench/index.d.ts +4 -0
  29. package/dist/services/swebench/index.js +4 -0
  30. package/dist/services/swebench/instanceLoader.d.ts +21 -0
  31. package/dist/services/swebench/instanceLoader.js +171 -0
  32. package/dist/services/swebench/sweBenchRunnerService.d.ts +38 -0
  33. package/dist/services/swebench/sweBenchRunnerService.js +618 -0
  34. package/dist/services/swebench/types.d.ts +167 -0
  35. package/dist/services/swebench/types.js +7 -0
  36. package/dist/services/verificationService.js +3 -0
  37. package/dist/utils/fuzzyMatch.d.ts +51 -21
  38. package/dist/utils/fuzzyMatch.js +37 -122
  39. package/dist/utils/projectStorage.js +10 -5
  40. package/dist/utils/systemPrompts.d.ts +1 -1
  41. package/dist/utils/systemPrompts.js +10 -4
  42. package/oclif.manifest.json +137 -1
  43. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Two-Layer Message Bus for Sub-Agent Orchestration.
2
+ * @file Two-Layer Message Bus for Sub-Agent Orchestration.
3
3
  *
4
4
  * Provides the core inter-agent communication primitive for the orchestration system.
5
5
  * Two layers of communication:
@@ -19,6 +19,38 @@ import { promises as fs, readFileSync, existsSync, mkdirSync } from 'node:fs';
19
19
  import path from 'node:path';
20
20
  import { atomicWriteFile } from '../../utils/atomicWrite.js';
21
21
  import { debugLog } from '../../utils/logger.js';
22
+ export const MUTATING_TOOLS = new Set([
23
+ 'write_file',
24
+ 'modify_file',
25
+ 'delete_file',
26
+ 'rename_file',
27
+ 'run_command',
28
+ ]);
29
+ export function coalesceActivityEntries(entries) {
30
+ const result = [];
31
+ for (const entry of entries) {
32
+ const last = result[result.length - 1];
33
+ if (last && last.agentId === entry.agentId && last.tool === entry.tool && last.target === entry.target) {
34
+ last.count++;
35
+ last.status = entry.status; // most recent status
36
+ if (entry.resultSummary) {
37
+ last.resultSummary = entry.resultSummary;
38
+ }
39
+ }
40
+ else {
41
+ result.push({
42
+ agentId: entry.agentId,
43
+ tool: entry.tool,
44
+ target: entry.target,
45
+ action: entry.action,
46
+ count: 1,
47
+ status: entry.status,
48
+ resultSummary: entry.resultSummary,
49
+ });
50
+ }
51
+ }
52
+ return result;
53
+ }
22
54
  // ─── Message Bus Implementation ──────────────────────────────────────
23
55
  /**
24
56
  * Two-layer, disk-backed message bus for sub-agent coordination.
@@ -31,15 +63,15 @@ import { debugLog } from '../../utils/logger.js';
31
63
  * a CLI crash and resume from the last known state.
32
64
  */
33
65
  export class MessageBus {
34
- activityLog = [];
35
- signals = [];
66
+ /** Maximum semantic signals any single agent can post */
67
+ static MAX_SIGNALS_PER_AGENT = 50;
36
68
  activityCursors = new Map();
37
- signalCursors = new Map();
38
- persistQueue = Promise.resolve();
69
+ activityLog = [];
39
70
  persistPath;
71
+ persistQueue = Promise.resolve();
72
+ signalCursors = new Map();
73
+ signals = [];
40
74
  workspaceRoot;
41
- /** Maximum semantic signals any single agent can post */
42
- static MAX_SIGNALS_PER_AGENT = 50;
43
75
  constructor(workspaceRoot, conversationId) {
44
76
  this.workspaceRoot = workspaceRoot;
45
77
  const orchestrationDir = path.join(workspaceRoot, '.minovativemind', 'orchestration');
@@ -48,50 +80,70 @@ export class MessageBus {
48
80
  }
49
81
  // ─── Layer 1: Automatic Activity Log ─────────────────────────────
50
82
  /**
51
- * Records a tool execution into the activity log. Called by the scoped tool
52
- * wrapper in `scopedTools.ts` zero cost to the agent.
83
+ * Formats a batch of activity entries into a compact, human-readable string
84
+ * without token-wasteful column whitespace padding. Coalesces repeated actions.
53
85
  */
54
- logActivity(entry) {
55
- this.activityLog.push(entry);
56
- this.persistToDiskAsync();
86
+ static formatActivityEntries(entries) {
87
+ if (entries.length === 0)
88
+ return '';
89
+ const coalesced = coalesceActivityEntries(entries);
90
+ return coalesced
91
+ .map((e) => {
92
+ const countSuffix = e.count > 1 ? ` (${e.count} ops)` : '';
93
+ const statusPrefix = e.status === 'error' ? '[FAILED] ' : '';
94
+ const summary = e.resultSummary ? ` | ${e.resultSummary}` : '';
95
+ return ` [${e.agentId}] ${statusPrefix}${e.tool} ${e.target}: ${e.action}${countSuffix}${summary}`;
96
+ })
97
+ .join('\n');
57
98
  }
58
99
  // ─── Layer 2: Semantic Signals ───────────────────────────────────
59
100
  /**
60
- * Posts a semantic signal from an agent. These cost tokens (the agent calls
61
- * `post_message`) but carry intent that raw tool logs cannot express.
62
- *
63
- * Enforces per-agent signal cap to prevent runaway agents from flooding the bus.
64
- *
65
- * @returns `true` if the signal was accepted, `false` if the agent hit the cap.
101
+ * Formats semantic signals into a readable string for agent context injection.
66
102
  */
67
- postSignal(signal) {
68
- const agentSignalCount = this.signals.filter((s) => s.fromAgent === signal.fromAgent).length;
69
- if (agentSignalCount >= MessageBus.MAX_SIGNALS_PER_AGENT) {
70
- debugLog(`MessageBus: Agent ${signal.fromAgent} hit signal cap (${MessageBus.MAX_SIGNALS_PER_AGENT}). ` +
71
- `Dropping signal of type "${signal.type}".`);
72
- return false;
73
- }
74
- this.signals.push(signal);
75
- this.persistToDiskAsync();
76
- return true;
103
+ static formatSignals(signals) {
104
+ if (signals.length === 0)
105
+ return '';
106
+ return signals
107
+ .map((s) => {
108
+ const tag = s.type.toUpperCase();
109
+ switch (s.type) {
110
+ case 'discovery':
111
+ return ` [${tag} from ${s.fromAgent}]: "${s.content}" (affects: ${s.affectedFiles.join(', ')})`;
112
+ case 'warning':
113
+ return ` [${tag} from ${s.fromAgent}]: "${s.content}"${s.changedSignature ? ` (signature: ${s.changedSignature})` : ''}`;
114
+ case 'request':
115
+ return ` [${tag} from ${s.fromAgent} → ${s.toAgent}]: "${s.content}"`;
116
+ case 'completion':
117
+ return ` [${tag} from ${s.fromAgent}]: "${s.summary}"`;
118
+ default:
119
+ return ` [SIGNAL from ${s.fromAgent}]: ${JSON.stringify(s)}`;
120
+ }
121
+ })
122
+ .join('\n');
77
123
  }
78
124
  // ─── Reading ─────────────────────────────────────────────────────
79
125
  /**
80
- * Retrieves all unread activity entries and semantic signals for a specific agent.
81
- * Advances the agent's cursors so subsequent calls return only new entries.
82
- *
83
- * Filters out the requesting agent's own entries (an agent doesn't need to
84
- * re-read its own tool logs or signals).
126
+ * Clears all bus state and removes the persistence file.
127
+ * Called when orchestration completes successfully (no crash recovery needed).
85
128
  */
86
- getUnread(agentId) {
87
- const actCursor = this.activityCursors.get(agentId) ?? 0;
88
- const sigCursor = this.signalCursors.get(agentId) ?? 0;
89
- const activities = this.activityLog.slice(actCursor).filter((e) => e.agentId !== agentId);
90
- const signals = this.signals.slice(sigCursor).filter((s) => s.fromAgent !== agentId);
91
- // Advance cursors to current end
92
- this.activityCursors.set(agentId, this.activityLog.length);
93
- this.signalCursors.set(agentId, this.signals.length);
94
- return { activities, signals };
129
+ async cleanup() {
130
+ this.activityLog = [];
131
+ this.signals = [];
132
+ this.activityCursors = new Map();
133
+ this.signalCursors = new Map();
134
+ try {
135
+ await fs.unlink(this.persistPath);
136
+ }
137
+ catch {
138
+ // File may not exist — non-fatal
139
+ }
140
+ }
141
+ /**
142
+ * Returns all activity entries for a specific agent (used for dead agent
143
+ * recovery — collecting partial progress before re-dispatch).
144
+ */
145
+ getAgentActivity(agentId) {
146
+ return this.activityLog.filter((e) => e.agentId === agentId);
95
147
  }
96
148
  /**
97
149
  * Returns the complete, unfiltered bus state for PM reconciliation.
@@ -103,13 +155,6 @@ export class MessageBus {
103
155
  signals: [...this.signals],
104
156
  };
105
157
  }
106
- /**
107
- * Returns all activity entries for a specific agent (used for dead agent
108
- * recovery — collecting partial progress before re-dispatch).
109
- */
110
- getAgentActivity(agentId) {
111
- return this.activityLog.filter((e) => e.agentId === agentId);
112
- }
113
158
  /**
114
159
  * Returns the total number of activity entries and signals in the bus.
115
160
  * Used for terminal display and diagnostics.
@@ -120,47 +165,162 @@ export class MessageBus {
120
165
  signalCount: this.signals.length,
121
166
  };
122
167
  }
123
- // ─── Formatting Helpers ──────────────────────────────────────────
124
168
  /**
125
- * Formats a batch of activity entries into a compact, human-readable string
126
- * suitable for injection into an agent's context window.
169
+ * Retrieves all unread activity entries and semantic signals for a specific agent.
170
+ * Defaults to state-mutating events and unicast signal routing to eliminate token noise.
127
171
  */
128
- static formatActivityEntries(entries) {
129
- if (entries.length === 0)
130
- return '';
131
- return entries
132
- .map((e) => {
133
- const statusIcon = e.status === 'success' ? '✓' : '✗';
134
- const summary = e.resultSummary ? ` | ${e.resultSummary}` : '';
135
- return ` ${e.agentId} | ${e.tool.padEnd(14)} → ${e.target.padEnd(40)} | ${statusIcon} ${e.action}${summary}`;
136
- })
137
- .join('\n');
172
+ getUnread(agentId, options) {
173
+ return this.queryBus({
174
+ agentId,
175
+ onlyMutations: options?.onlyMutations ?? true,
176
+ advanceCursor: options?.advanceCursor ?? true,
177
+ });
138
178
  }
139
179
  /**
140
- * Formats semantic signals into a readable string for agent context injection.
180
+ * Records a tool execution into the activity log. Called by the scoped tool
181
+ * wrapper in `scopedTools.ts` — zero cost to the agent.
141
182
  */
142
- static formatSignals(signals) {
143
- if (signals.length === 0)
144
- return '';
145
- return signals
146
- .map((s) => {
147
- const tag = s.type.toUpperCase();
148
- switch (s.type) {
149
- case 'discovery':
150
- return ` [${tag} from ${s.fromAgent}]: "${s.content}" (affects: ${s.affectedFiles.join(', ')})`;
151
- case 'warning':
152
- return ` [${tag} from ${s.fromAgent}]: "${s.content}"${s.changedSignature ? ` (signature: ${s.changedSignature})` : ''}`;
153
- case 'request':
154
- return ` [${tag} from ${s.fromAgent} ${s.toAgent}]: "${s.content}"`;
155
- case 'completion':
156
- return ` [${tag} from ${s.fromAgent}]: "${s.summary}"`;
157
- default:
158
- return ` [SIGNAL from ${s.fromAgent}]: ${JSON.stringify(s)}`;
183
+ logActivity(entry) {
184
+ this.activityLog.push(entry);
185
+ this.persistToDiskAsync();
186
+ }
187
+ // ─── Formatting Helpers ──────────────────────────────────────────
188
+ /**
189
+ * Peeks urgent signals (breaking warnings or direct requests) that are relevant
190
+ * to a sub-agent's task scope without advancing its cursor. Used for in-band notice
191
+ * delivery in scopedTools.
192
+ */
193
+ peekUrgentSignals(agentId, scope) {
194
+ const sigCursor = this.signalCursors.get(agentId) ?? 0;
195
+ const unreadSignals = this.signals.slice(sigCursor).filter((s) => s.fromAgent !== agentId);
196
+ const urgent = [];
197
+ for (const signal of unreadSignals) {
198
+ // 1. Direct targeted request to this agent
199
+ if (signal.type === 'request' && (signal.toAgent === agentId || signal.toAgent === 'all')) {
200
+ urgent.push(signal);
201
+ continue;
159
202
  }
160
- })
161
- .join('\n');
203
+ // 2. Warnings from upstream dependencies or affecting target files
204
+ if (signal.type === 'warning') {
205
+ if (!scope || !scope.dependsOn || scope.dependsOn.includes(signal.fromAgent)) {
206
+ urgent.push(signal);
207
+ continue;
208
+ }
209
+ }
210
+ // 3. Upstream completion signals from direct dependencies
211
+ if (signal.type === 'completion' && scope?.dependsOn && scope.dependsOn.includes(signal.fromAgent)) {
212
+ urgent.push(signal);
213
+ continue;
214
+ }
215
+ }
216
+ return urgent;
217
+ }
218
+ /**
219
+ * Posts a semantic signal from an agent. These cost tokens (the agent calls
220
+ * `post_message`) but carry intent that raw tool logs cannot express.
221
+ *
222
+ * Enforces per-agent signal cap to prevent runaway agents from flooding the bus.
223
+ *
224
+ * @returns `true` if the signal was accepted, `false` if the agent hit the cap.
225
+ */
226
+ postSignal(signal) {
227
+ const agentSignalCount = this.signals.filter((s) => s.fromAgent === signal.fromAgent).length;
228
+ if (agentSignalCount >= MessageBus.MAX_SIGNALS_PER_AGENT) {
229
+ debugLog(`MessageBus: Agent ${signal.fromAgent} hit signal cap (${MessageBus.MAX_SIGNALS_PER_AGENT}). ` +
230
+ `Dropping signal of type "${signal.type}".`);
231
+ return false;
232
+ }
233
+ this.signals.push(signal);
234
+ this.persistToDiskAsync();
235
+ return true;
162
236
  }
163
237
  // ─── Persistence ─────────────────────────────────────────────────
238
+ /**
239
+ * Performs an intelligent, scoped query against unread bus activity and signals.
240
+ * Filters out read-only noise for peer sub-agents, enforces unicast delivery for targeted
241
+ * requests, and supports selective querying by file, agent, or signal type.
242
+ */
243
+ queryBus(options) {
244
+ const actCursor = this.activityCursors.get(options.agentId) ?? 0;
245
+ const sigCursor = this.signalCursors.get(options.agentId) ?? 0;
246
+ const onlyMutations = options.onlyMutations ?? true;
247
+ const normalizedFile = options.file ? options.file.replace(/\\/g, '/').toLowerCase() : undefined;
248
+ const activities = this.activityLog.slice(actCursor).filter((e) => {
249
+ // Exclude own actions
250
+ if (e.agentId === options.agentId)
251
+ return false;
252
+ // Filter read-only tools if onlyMutations is enabled
253
+ if (onlyMutations && !MUTATING_TOOLS.has(e.tool))
254
+ return false;
255
+ // Optional file filter
256
+ if (normalizedFile) {
257
+ const entryTarget = e.target.replace(/\\/g, '/').toLowerCase();
258
+ if (!entryTarget.includes(normalizedFile) && !normalizedFile.includes(entryTarget)) {
259
+ return false;
260
+ }
261
+ }
262
+ // Optional fromAgent filter
263
+ if (options.fromAgent && e.agentId !== options.fromAgent)
264
+ return false;
265
+ return true;
266
+ });
267
+ const signals = this.signals.slice(sigCursor).filter((s) => {
268
+ // Exclude own signals
269
+ if (s.fromAgent === options.agentId)
270
+ return false;
271
+ // Unicast request routing: only deliver if targeted to this agent or broadcast to 'all'
272
+ if (s.type === 'request') {
273
+ const req = s;
274
+ if (req.toAgent && req.toAgent !== 'all' && req.toAgent !== options.agentId) {
275
+ return false;
276
+ }
277
+ }
278
+ // Optional type filter
279
+ if (options.type && s.type !== options.type)
280
+ return false;
281
+ // Optional fromAgent filter
282
+ if (options.fromAgent && s.fromAgent !== options.fromAgent)
283
+ return false;
284
+ // Optional file filter for signals
285
+ if (normalizedFile) {
286
+ if (s.type === 'discovery') {
287
+ const disc = s;
288
+ const touches = (disc.affectedFiles || []).some((f) => {
289
+ const norm = f.replace(/\\/g, '/').toLowerCase();
290
+ return norm.includes(normalizedFile) || normalizedFile.includes(norm);
291
+ });
292
+ if (!touches && !disc.content.toLowerCase().includes(normalizedFile))
293
+ return false;
294
+ }
295
+ else if (s.type === 'warning') {
296
+ const warn = s;
297
+ const touches = warn.content.toLowerCase().includes(normalizedFile) ||
298
+ Boolean(warn.changedSignature && warn.changedSignature.toLowerCase().includes(normalizedFile));
299
+ if (!touches)
300
+ return false;
301
+ }
302
+ else if (s.type === 'request') {
303
+ const req = s;
304
+ if (!req.content.toLowerCase().includes(normalizedFile))
305
+ return false;
306
+ }
307
+ else if (s.type === 'completion') {
308
+ const comp = s;
309
+ const touches = comp.summary.toLowerCase().includes(normalizedFile) ||
310
+ Object.keys(comp.exports || {}).some((k) => k.toLowerCase().includes(normalizedFile));
311
+ if (!touches)
312
+ return false;
313
+ }
314
+ }
315
+ return true;
316
+ });
317
+ // Advance cursors if requested (default: true)
318
+ if (options.advanceCursor !== false) {
319
+ this.activityCursors.set(options.agentId, this.activityLog.length);
320
+ this.signalCursors.set(options.agentId, this.signals.length);
321
+ }
322
+ return { activities, signals };
323
+ }
164
324
  /**
165
325
  * Writes the full bus state to disk atomically. Called after every mutation
166
326
  * to ensure crash resilience. Uses fire-and-forget to avoid blocking the
@@ -209,20 +369,4 @@ export class MessageBus {
209
369
  this.signalCursors = new Map();
210
370
  }
211
371
  }
212
- /**
213
- * Clears all bus state and removes the persistence file.
214
- * Called when orchestration completes successfully (no crash recovery needed).
215
- */
216
- async cleanup() {
217
- this.activityLog = [];
218
- this.signals = [];
219
- this.activityCursors = new Map();
220
- this.signalCursors = new Map();
221
- try {
222
- await fs.unlink(this.persistPath);
223
- }
224
- catch {
225
- // File may not exist — non-fatal
226
- }
227
- }
228
372
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Main Orchestrator for Sub-Agent Dispatch and Coordination.
2
+ * @file Main Orchestrator for Sub-Agent Dispatch and Coordination.
3
3
  *
4
4
  * The orchestrator acts as the "PM Kernel", responsible for:
5
5
  * 1. Task Decomposition (using gemini-3.7-flash)
@@ -13,10 +13,10 @@ export declare class Orchestrator {
13
13
  private readonly workspaceRoot;
14
14
  private readonly conversationId;
15
15
  private readonly inputHandler;
16
+ private agentResults;
16
17
  private bus;
17
18
  private locks;
18
19
  private pmChat;
19
- private agentResults;
20
20
  constructor(workspaceRoot: string, conversationId: string, inputHandler: AsyncInputHandler);
21
21
  /**
22
22
  * Main entry point for orchestration. Decomposes the task, validates the graph,
@@ -30,10 +30,6 @@ export declare class Orchestrator {
30
30
  * Calls the PM Agent to decompose the task into a TaskGraph.
31
31
  */
32
32
  private decomposeTask;
33
- /**
34
- * Computes execution waves and resolves file lock conflicts via pre-allocation.
35
- */
36
- private scheduleWaves;
37
33
  /**
38
34
  * Dispatches a single sub-agent and records its result.
39
35
  */
@@ -42,4 +38,8 @@ export declare class Orchestrator {
42
38
  * Final reconciliation phase after all waves complete.
43
39
  */
44
40
  private reconcile;
41
+ /**
42
+ * Computes execution waves and resolves file lock conflicts via pre-allocation.
43
+ */
44
+ private scheduleWaves;
45
45
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Main Orchestrator for Sub-Agent Dispatch and Coordination.
2
+ * @file Main Orchestrator for Sub-Agent Dispatch and Coordination.
3
3
  *
4
4
  * The orchestrator acts as the "PM Kernel", responsible for:
5
5
  * 1. Task Decomposition (using gemini-3.7-flash)
@@ -57,10 +57,10 @@ export class Orchestrator {
57
57
  workspaceRoot;
58
58
  conversationId;
59
59
  inputHandler;
60
+ agentResults = new Map();
60
61
  bus;
61
62
  locks;
62
63
  pmChat;
63
- agentResults = new Map();
64
64
  constructor(workspaceRoot, conversationId, inputHandler) {
65
65
  this.workspaceRoot = workspaceRoot;
66
66
  this.conversationId = conversationId;
@@ -103,7 +103,9 @@ export class Orchestrator {
103
103
  if (signal.aborted)
104
104
  break;
105
105
  // Cooling-off pause before dispatching each execution wave
106
- await new Promise((resolve) => setTimeout(resolve, TPM_COOLING_DELAYS.ORCHESTRATION_WAVE_MS));
106
+ await new Promise((resolve) => {
107
+ setTimeout(resolve, TPM_COOLING_DELAYS.ORCHESTRATION_WAVE_MS);
108
+ });
107
109
  if (signal.aborted)
108
110
  break;
109
111
  const taskDescriptions = wave.taskIds
@@ -131,7 +133,9 @@ export class Orchestrator {
131
133
  if (signal.aborted)
132
134
  break;
133
135
  if (i > 0) {
134
- await new Promise((resolve) => setTimeout(resolve, TPM_COOLING_DELAYS.PARALLEL_CHUNK_MS));
136
+ await new Promise((resolve) => {
137
+ setTimeout(resolve, TPM_COOLING_DELAYS.PARALLEL_CHUNK_MS);
138
+ });
135
139
  if (signal.aborted)
136
140
  break;
137
141
  }
@@ -163,7 +167,8 @@ export class Orchestrator {
163
167
  const successful = results.filter((r) => r.success).length;
164
168
  s.stop(`Wave ${wave.depth + 1} completed: ${successful}/${wave.taskIds.length} tasks succeeded`);
165
169
  if (toolLogs.length > 0) {
166
- toolLogs.forEach((log) => p.log.step(log));
170
+ for (const log of toolLogs)
171
+ p.log.step(log);
167
172
  }
168
173
  // Post-wave evaluation
169
174
  const failedCount = results.filter((r) => !r.success).length;
@@ -179,7 +184,9 @@ export class Orchestrator {
179
184
  }
180
185
  // 4. Reconciliation
181
186
  if (!signal.aborted) {
182
- await new Promise((resolve) => setTimeout(resolve, TPM_COOLING_DELAYS.ORCHESTRATION_RECONCILE_MS));
187
+ await new Promise((resolve) => {
188
+ setTimeout(resolve, TPM_COOLING_DELAYS.ORCHESTRATION_RECONCILE_MS);
189
+ });
183
190
  }
184
191
  const finalSummary = await this.reconcile(graph, signal);
185
192
  return finalSummary;
@@ -228,26 +235,16 @@ export class Orchestrator {
228
235
  return null;
229
236
  });
230
237
  }
231
- /**
232
- * Computes execution waves and resolves file lock conflicts via pre-allocation.
233
- */
234
- scheduleWaves(graph) {
235
- try {
236
- const rawWaves = computeExecutionWaves(graph);
237
- const conflicts = detectFileConflicts(graph, rawWaves);
238
- return resolveFileConflicts(rawWaves, conflicts);
239
- }
240
- catch (err) {
241
- debugLog(`Orchestrator: Failed to schedule waves: ${err.message}`);
242
- return null;
243
- }
244
- }
245
238
  /**
246
239
  * Dispatches a single sub-agent and records its result.
247
240
  */
248
241
  async dispatchAgent(taskDef, globalContext, signal, onProgress, onTool) {
249
242
  return trackTask(`Sub-Agent: ${taskDef.id}`, async () => {
250
- const runner = new SubAgentRunner(taskDef.id, taskDef.intent, this.workspaceRoot, this.bus, this.locks, globalContext, onProgress, onTool);
243
+ const runner = new SubAgentRunner(taskDef.id, taskDef.intent, this.workspaceRoot, this.bus, this.locks, globalContext, onProgress, onTool, {
244
+ targetFiles: taskDef.targetFiles,
245
+ readOnlyFiles: taskDef.readOnlyFiles,
246
+ dependsOn: taskDef.dependsOn,
247
+ });
251
248
  const result = await runner.execute(signal);
252
249
  // Clean up any stray locks if the agent crashed or stalled
253
250
  if (result.crashed) {
@@ -310,4 +307,18 @@ export class Orchestrator {
310
307
  this.locks.shutdown();
311
308
  return finalSummary.trim();
312
309
  }
310
+ /**
311
+ * Computes execution waves and resolves file lock conflicts via pre-allocation.
312
+ */
313
+ scheduleWaves(graph) {
314
+ try {
315
+ const rawWaves = computeExecutionWaves(graph);
316
+ const conflicts = detectFileConflicts(graph, rawWaves);
317
+ return resolveFileConflicts(rawWaves, conflicts);
318
+ }
319
+ catch (err) {
320
+ debugLog(`Orchestrator: Failed to schedule waves: ${err.message}`);
321
+ return null;
322
+ }
323
+ }
313
324
  }
@@ -11,6 +11,11 @@ import { type PathResolutionOptions } from '../../utils/pathSecurity.js';
11
11
  * @returns Absolute canonical file path
12
12
  */
13
13
  export declare function resolveCanonicalLockPath(workspaceRoot: string, filePath: string, options?: PathResolutionOptions): string;
14
+ export interface TaskScope {
15
+ targetFiles?: string[];
16
+ readOnlyFiles?: string[];
17
+ dependsOn?: string[];
18
+ }
14
19
  /**
15
20
  * Returns Gemini tool declarations available to sub-agents during orchestration.
16
21
  * In addition to standard agent tools (read/write/search/command/etc.), includes
@@ -30,5 +35,6 @@ export declare function getScopedToolDeclarations(): any[];
30
35
  * @param locks - Shared file lock registry instance
31
36
  * @param onProgress - Callback to notify parent of sub-agent progress
32
37
  * @param options - Optional sub-path auto-focus and override configuration
38
+ * @param taskScope - Optional task scope metadata (targetFiles, dependsOn)
33
39
  */
34
- export declare function executeScopedTool(name: string, args: Record<string, any>, workspaceRoot: string, agentId: string, bus: MessageBus, locks: FileLockRegistry, onProgress: (msg?: string) => void, options?: PathResolutionOptions): Promise<any>;
40
+ export declare function executeScopedTool(name: string, args: Record<string, any>, workspaceRoot: string, agentId: string, bus: MessageBus, locks: FileLockRegistry, onProgress: (msg?: string) => void, options?: PathResolutionOptions, taskScope?: TaskScope): Promise<any>;