(fileCount: number)
| 201 | } |
| 202 | |
| 203 | export function getExploreOutputBudget(fileCount: number): ExploreOutputBudget { |
| 204 | // Tiered budget, scaled to project size. The budget is a CEILING (relevance |
| 205 | // still gates WHAT is included), and it MUST stay under the agent's INLINE |
| 206 | // tool-result cap (~25K chars). Above that, the host externalizes the result |
| 207 | // to a file the agent then Reads back — re-introducing a read AND the |
| 208 | // cache-write cost — which is exactly what a 35K vscode explore did in the |
| 209 | // n=4 README A/B. So even large repos cap at ~24K: the answer is the handful |
| 210 | // of ~100-line flow windows the agent would have grep-located and read (it |
| 211 | // natively reads ~6–9 files, median 100-line ranges), NOT a sprawl of 12 |
| 212 | // files. Concentration onto the flow emerges from this cap + the named-file- |
| 213 | // first sort dropping peripheral files. Invariant: a larger tier must never |
| 214 | // get a smaller `maxCharsPerFile` than a smaller tier. |
| 215 | if (fileCount < 150) { |
| 216 | return { |
| 217 | // ITER3: revert iter2's aggressive body shrink (forced Read fallback — |
| 218 | // the per-file 2.5K cap pushed the agent to Read instead of node). |
| 219 | // Back to the iter1 shape (13K/4/3.8K) but keep the test-file |
| 220 | // hard-exclude. The cost lever for this tier lives in steering the |
| 221 | // agent to stop after 1-2 calls, not in this budget. |
| 222 | maxOutputChars: 13000, |
| 223 | defaultMaxFiles: 4, |
| 224 | maxCharsPerFile: 3800, |
| 225 | gapThreshold: 7, |
| 226 | maxSymbolsInFileHeader: 5, |
| 227 | maxEdgesPerRelationshipKind: 4, |
| 228 | includeRelationships: false, |
| 229 | includeAdditionalFiles: false, |
| 230 | includeCompletenessSignal: false, |
| 231 | includeBudgetNote: false, |
| 232 | excludeLowValueFiles: true, |
| 233 | }; |
| 234 | } |
| 235 | if (fileCount < 500) { |
| 236 | return { |
| 237 | // ITER3: same revert/keep-filter pattern as <150. |
| 238 | maxOutputChars: 18000, |
| 239 | defaultMaxFiles: 5, |
| 240 | maxCharsPerFile: 3800, |
| 241 | gapThreshold: 8, |
| 242 | maxSymbolsInFileHeader: 6, |
| 243 | maxEdgesPerRelationshipKind: 6, |
| 244 | includeRelationships: false, |
| 245 | includeAdditionalFiles: false, |
| 246 | includeCompletenessSignal: false, |
| 247 | includeBudgetNote: false, |
| 248 | excludeLowValueFiles: true, |
| 249 | }; |
| 250 | } |
| 251 | if (fileCount < 5000) { |
| 252 | return { |
| 253 | // ~150-line per-file window (the native read unit) × ~6 files, capped at |
| 254 | // the ~24K inline ceiling so the response is never externalized. Per-file |
| 255 | // stays ≥ the <500 tier (3800) — monotonic. |
| 256 | maxOutputChars: 24000, |
| 257 | defaultMaxFiles: 8, |
| 258 | maxCharsPerFile: 6500, |
| 259 | gapThreshold: 12, |
| 260 | maxSymbolsInFileHeader: 10, |
no outgoing calls
no test coverage detected