(
{ file_path, offset = 1, limit = undefined, pages },
context,
_canUseTool?,
parentMessage?,
)
| 494 | return { result: true } |
| 495 | }, |
| 496 | async call( |
| 497 | { file_path, offset = 1, limit = undefined, pages }, |
| 498 | context, |
| 499 | _canUseTool?, |
| 500 | parentMessage?, |
| 501 | ) { |
| 502 | const { readFileState, fileReadingLimits } = context |
| 503 | |
| 504 | const defaults = getDefaultFileReadingLimits() |
| 505 | const maxSizeBytes = |
| 506 | fileReadingLimits?.maxSizeBytes ?? defaults.maxSizeBytes |
| 507 | const maxTokens = fileReadingLimits?.maxTokens ?? defaults.maxTokens |
| 508 | |
| 509 | // Telemetry: track when callers override default read limits. |
| 510 | // Only fires on override (low volume) — event count = override frequency. |
| 511 | if (fileReadingLimits !== undefined) { |
| 512 | logEvent('tengu_file_read_limits_override', { |
| 513 | hasMaxTokens: fileReadingLimits.maxTokens !== undefined, |
| 514 | hasMaxSizeBytes: fileReadingLimits.maxSizeBytes !== undefined, |
| 515 | }) |
| 516 | } |
| 517 | |
| 518 | const ext = path.extname(file_path).toLowerCase().slice(1) |
| 519 | // Use expandPath for consistent path normalization with FileEditTool/FileWriteTool |
| 520 | // (especially handles whitespace trimming and Windows path separators) |
| 521 | const fullFilePath = expandPath(file_path) |
| 522 | |
| 523 | // Dedup: if we've already read this exact range and the file hasn't |
| 524 | // changed on disk, return a stub instead of re-sending the full content. |
| 525 | // The earlier Read tool_result is still in context — two full copies |
| 526 | // waste cache_creation tokens on every subsequent turn. BQ proxy shows |
| 527 | // ~18% of Read calls are same-file collisions (up to 2.64% of fleet |
| 528 | // cache_creation). Only applies to text/notebook reads — images/PDFs |
| 529 | // aren't cached in readFileState so won't match here. |
| 530 | // |
| 531 | // Ant soak: 1,734 dedup hits in 2h, no Read error regression. |
| 532 | // Killswitch pattern: GB can disable if the stub message confuses |
| 533 | // the model externally. |
| 534 | // 3P default: killswitch off = dedup enabled. Client-side only — no |
| 535 | // server support needed, safe for Bedrock/Vertex/Foundry. |
| 536 | const dedupKillswitch = getFeatureValue_CACHED_MAY_BE_STALE( |
| 537 | 'tengu_read_dedup_killswitch', |
| 538 | false, |
| 539 | ) |
| 540 | const existingState = dedupKillswitch |
| 541 | ? undefined |
| 542 | : readFileState.get(fullFilePath) |
| 543 | // Only dedup entries that came from a prior Read (offset is always set |
| 544 | // by Read). Edit/Write store offset=undefined — their readFileState |
| 545 | // entry reflects post-edit mtime, so deduping against it would wrongly |
| 546 | // point the model at the pre-edit Read content. |
| 547 | if ( |
| 548 | existingState && |
| 549 | !existingState.isPartialView && |
| 550 | existingState.offset !== undefined |
| 551 | ) { |
| 552 | const rangeMatch = |
| 553 | existingState.offset === offset && existingState.limit === limit |
nothing calls this directly
no test coverage detected