(args: z.infer<typeof GatherArgs>, ctx: ToolContext)
| 102 | argsSchema = GatherArgs; |
| 103 | |
| 104 | async execute(args: z.infer<typeof GatherArgs>, ctx: ToolContext): Promise<ToolResult> { |
| 105 | const runner = getSubAgentRunner(); |
| 106 | if (!runner) { |
| 107 | return { |
| 108 | content: '[SUBAGENT_DISABLED] gather needs sub-agents enabled. Run `qx setup` and pick a sub-agent mode, ' + |
| 109 | 'or set subagents.mode: sequential in ~/.qodex/config.yaml.', |
| 110 | isError: true, |
| 111 | }; |
| 112 | } |
| 113 | |
| 114 | const probes = args.probes; |
| 115 | const maxConc = args.max_concurrency ?? 3; |
| 116 | const maxIter = args.max_iterations_each ?? 6; |
| 117 | ctx.emit({ type: 'progress', message: `Gathering: dispatching ${probes.length} scout(s), up to ${maxConc} at once…` }); |
| 118 | logger.info('gather: dispatching scouts', { count: probes.length, maxConc, maxIter }); |
| 119 | |
| 120 | const start = Date.now(); |
| 121 | const results = await runWithConcurrency(probes, maxConc, async (probe, i) => { |
| 122 | ctx.emit({ type: 'progress', message: `Scout ${i + 1}/${probes.length}: ${probe.focus.slice(0, 60)}` }); |
| 123 | try { |
| 124 | const r = await runner(buildScoutPrompt(probe.focus, probe.hint), { |
| 125 | maxIterations: maxIter, |
| 126 | signal: ctx.signal, |
| 127 | sessionId: `${ctx.sessionId}/scout-${i}-${Date.now()}`, |
| 128 | role: 'scout', |
| 129 | }); |
| 130 | return { focus: probe.focus, ok: r.ok, findings: r.finalText, error: r.error }; |
| 131 | } catch (e: any) { |
| 132 | return { focus: probe.focus, ok: false, findings: '', error: e?.message ?? String(e) }; |
| 133 | } |
| 134 | }); |
| 135 | |
| 136 | const elapsedSec = Math.round((Date.now() - start) / 1000); |
| 137 | const okCount = results.filter(r => r.ok).length; |
| 138 | return { |
| 139 | content: consolidateFindings(results), |
| 140 | metadata: { scouts: probes.length, succeeded: okCount, elapsedSec }, |
| 141 | }; |
| 142 | } |
| 143 | } |
nothing calls this directly
no test coverage detected