| 376 | // Metrics and completedAgents are updated incrementally inside runVulnExploitPipeline |
| 377 | // so that getProgress queries reflect real-time status during execution. |
| 378 | function aggregatePipelineResults(results: PromiseSettledResult<VulnExploitPipelineResult>[]): void { |
| 379 | const failedPipelines: string[] = []; |
| 380 | |
| 381 | for (const result of results) { |
| 382 | if (result.status === 'rejected') { |
| 383 | const errorMsg = result.reason instanceof Error ? result.reason.message : String(result.reason); |
| 384 | failedPipelines.push(errorMsg); |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | if (failedPipelines.length > 0) { |
| 389 | log.warn(`${failedPipelines.length} pipeline(s) failed`, { |
| 390 | failures: failedPipelines, |
| 391 | }); |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | // Run thunks with a concurrency limit, returning PromiseSettledResult for each. |
| 396 | // When limit >= thunks.length (default), all launch concurrently — identical to Promise.allSettled. |