(fileCount: number)
| 159 | } |
| 160 | |
| 161 | export function getExploreOutputBudget(fileCount: number): ExploreOutputBudget { |
| 162 | // Tiered budget, scaled to project size. The budget is a CEILING (relevance |
| 163 | // still gates WHAT is included), and it MUST stay under the agent's INLINE |
| 164 | // tool-result cap (~25K chars). Above that, the host externalizes the result |
| 165 | // to a file the agent then Reads back — re-introducing a read AND the |
| 166 | // cache-write cost — which is exactly what a 35K vscode explore did in the |
| 167 | // n=4 README A/B. So even large repos cap at ~24K: the answer is the handful |
| 168 | // of ~100-line flow windows the agent would have grep-located and read (it |
| 169 | // natively reads ~6–9 files, median 100-line ranges), NOT a sprawl of 12 |
| 170 | // files. Concentration onto the flow emerges from this cap + the named-file- |
| 171 | // first sort dropping peripheral files. Invariant: a larger tier must never |
| 172 | // get a smaller `maxCharsPerFile` than a smaller tier. |
| 173 | if (fileCount < 150) { |
| 174 | return { |
| 175 | // ITER3: revert iter2's aggressive body shrink (forced Read fallback — |
| 176 | // the per-file 2.5K cap pushed the agent to Read instead of node). |
| 177 | // Back to the iter1 shape (13K/4/3.8K) but keep the test-file |
| 178 | // hard-exclude. The cost lever for this tier lives in steering the |
| 179 | // agent to stop after 1-2 calls, not in this budget. |
| 180 | maxOutputChars: 13000, |
| 181 | defaultMaxFiles: 4, |
| 182 | maxCharsPerFile: 3800, |
| 183 | gapThreshold: 7, |
| 184 | maxSymbolsInFileHeader: 5, |
| 185 | maxEdgesPerRelationshipKind: 4, |
| 186 | includeRelationships: false, |
| 187 | includeAdditionalFiles: false, |
| 188 | includeCompletenessSignal: false, |
| 189 | includeBudgetNote: false, |
| 190 | excludeLowValueFiles: true, |
| 191 | }; |
| 192 | } |
| 193 | if (fileCount < 500) { |
| 194 | return { |
| 195 | // ITER3: same revert/keep-filter pattern as <150. |
| 196 | maxOutputChars: 18000, |
| 197 | defaultMaxFiles: 5, |
| 198 | maxCharsPerFile: 3800, |
| 199 | gapThreshold: 8, |
| 200 | maxSymbolsInFileHeader: 6, |
| 201 | maxEdgesPerRelationshipKind: 6, |
| 202 | includeRelationships: false, |
| 203 | includeAdditionalFiles: false, |
| 204 | includeCompletenessSignal: false, |
| 205 | includeBudgetNote: false, |
| 206 | excludeLowValueFiles: true, |
| 207 | }; |
| 208 | } |
| 209 | if (fileCount < 5000) { |
| 210 | return { |
| 211 | // ~150-line per-file window (the native read unit) × ~6 files, capped at |
| 212 | // the ~24K inline ceiling so the response is never externalized. Per-file |
| 213 | // stays ≥ the <500 tier (3800) — monotonic. |
| 214 | maxOutputChars: 24000, |
| 215 | defaultMaxFiles: 8, |
| 216 | maxCharsPerFile: 6500, |
| 217 | gapThreshold: 12, |
| 218 | maxSymbolsInFileHeader: 10, |
no outgoing calls
no test coverage detected