Skip to content

Python: [Feature]: line-range reads in the harness file tools (file_access_read_lines or offset/limit on file_access_read) #7571

Description

Description

Problem

The harness file tools are asymmetric about lines. On the edit side they are
line-precise: file_access_replace_lines targets 1-based line numbers, and
file_access_grep reports matching_lines with those same numbers (the two share the
\n-only, keepends split of _split_lines_keepends, so a grep number always addresses
the same line the editor touches). On the read side there is no line access at all:
file_access_read takes only file_name and returns the entire content
(_ReadFileInput, _file_access.py), and the AgentFileStore.read(path) contract
underneath is likewise all-or-nothing.

That leaves a hole in exactly the workflow the line-precise edit tools exist for. The
natural point-edit loop is:

  1. file_access_grep — find the location (line numbers),
  2. see the surrounding lines to write a correct edit,
  3. file_access_replace_lines / file_access_replace — make the point edit.

Step 2 has no tool. The model's options are:

  • Re-read the whole file — token-expensive on large artifacts, and self-defeating
    under any tool-result cap: an application that truncates or spools oversized tool
    results (ours caps them and spills to the store) truncates precisely the large files
    where partial reads matter most, so the model may never see the region it needs.
  • Grep again with a broader patternfile_access_grep's snippet and
    matching_lines are anchored to the pattern, not to a requested range; "show me
    lines 120–160" is not expressible.

Observed effect in production (harness + AG-UI, advisor + headless sub-agents): models
told to point-edit fall back to whole-file file_access_read before nearly every
replace_lines, and smaller models then skip the read and guess — producing the
wrong-line edits the line-precise editor was added to prevent.

Requested

Either shape works; the essential property is numbering parity with grep/replace_lines
(same _split_lines_keepends semantics, including the trailing-newline final empty line):

  1. New tool file_access_read_lines(file_name, start_line, end_line=None) — 1-based
    inclusive range, end_line=None → end of file, out-of-range end_line clamps.
    Returning line-numbered text lets the model feed numbers straight back into
    file_access_replace_lines.
  2. Optional params on the existing readfile_access_read(file_name, start_line=None, end_line=None) (or offset/limit), defaulting to today's whole-file behavior.

Implementation stays inside FileAccessProvider — read the full content via the existing
AgentFileStore.read, slice with _split_lines_keepends, no store-protocol change
needed. (A store-level partial read would be a separate optimization; the context-window
win comes from not returning the whole file to the model.)

A new tool (option 1) composes with the existing surface: disable_write_tools children
keep it (it is a read), disable_readonly_tool_approval should govern it exactly like
file_access_read, and read_only_tools_auto_approval_rule should match it.

Workaround (application-side, today)

A hand-rolled @tool bound to the same store the provider holds
(ats.tools.read_lines.make_read_lines_tool): normalizes paths with the provider's own
_normalize_relative_path, splits with _split_lines_keepends (both imported from
agent_framework._harness._file_access with fallbacks — private imports this FR would
remove), mirrors the provider's read-approval mode, and is wired separately into the
advisor and each headless sub-agent. It works, but it reaches into two private helpers to
guarantee numbering parity, and every consumer has to remember to wire it beside the
provider — exactly the kind of duplication a provider-native range read would remove.

Code Sample

Language/SDK

Python

Metadata

Metadata

Assignees

No one assigned

    Labels

    pythonUsage: [Issues, PRs], Target: PythontriageUsage: [Issues], Target: All issues that still need to be triaged

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions