.NET: [BREAKING] Issue 7571 file access read lines - #7671
Open
Anton Sokolovskyi (antsok) wants to merge 2 commits into
Open
.NET: [BREAKING] Issue 7571 file access read lines#7671Anton Sokolovskyi (antsok) wants to merge 2 commits into
Anton Sokolovskyi (antsok) wants to merge 2 commits into
Conversation
…ine editor
The harness file tools were line-precise when editing (file_access_replace_lines takes
1-based line numbers and file_access_grep reports them) but all-or-nothing when reading,
so there was no way to see the lines around a match without reading the whole file.
Adds file_access_read_lines, rendering each row as `<n>\t<line>` with everything after
the tab verbatim, including the line's own terminator, so a row feeds straight back into
file_access_replace_lines.
That contract only holds if grep and the line editor agree on what a line is, and they
did not. The stores split on '\n' and stripped '\r'; FileEditor split on '\n', '\r\n'
and a lone '\r' and kept terminators. So on a file using lone '\r' terminators, grep's
line 1 addressed only part of what the model was shown and editing by that number
silently changed the wrong text, and on a newline-terminated file grep could report a
trailing line number the editor rejected as out of range. Both stores now use
FileEditor.SplitLinesKeepEnds and report the matching line verbatim.
BREAKING: FileSearchMatch.Line now includes the line's terminator, and line numbers
change on content containing a lone '\r' or a trailing newline. This affects
file_access_grep and file_memory_grep. The whole surface is [Experimental("MAAI001")].
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Anton Sokolovskyi (antsok)
requested review from
SergeyMenshykh,
chetantoshniwal,
Peter Ibekwe (peibekwe),
Roger Barreto (rogerbarreto) and
westey (westey-m)
as code owners
August 14, 2026 20:17
Anton Sokolovskyi (antsok)
deployed
to
github-app-auth
August 14, 2026 20:17 — with
GitHub Actions
Active
Anton Sokolovskyi (antsok)
deployed
to
github-app-auth
August 14, 2026 20:17 — with
GitHub Actions
Active
Anton Sokolovskyi (antsok)
deployed
to
github-app-auth
August 14, 2026 20:17 — with
GitHub Actions
Active
Anton Sokolovskyi (antsok)
deployed
to
github-app-auth
August 14, 2026 20:17 — with
GitHub Actions
Active
Copilot started reviewing on behalf of
Anton Sokolovskyi (antsok)
August 14, 2026 20:17
View session
Anton Sokolovskyi (antsok)
deployed
to
github-app-auth
August 14, 2026 20:19 — with
GitHub Actions
Active
Contributor
There was a problem hiding this comment.
Pull request overview
Adds line-range reading and aligns .NET grep results with line-editing semantics.
Changes:
- Adds
file_access_read_lines. - Preserves line terminators across grep/read/edit workflows.
- Updates approvals, documentation, and tests.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
InMemoryAgentFileStoreTests.cs |
Tests updated grep semantics. |
FileEditorTests.cs |
Tests splitting and slicing. |
FileAccessProviderTests.cs |
Tests the new tool and approvals. |
HarnessAgentTests.cs |
Verifies tool exposure. |
InMemoryAgentFileStore.cs |
Aligns grep line handling. |
FileSystemAgentFileStore.cs |
Aligns filesystem grep behavior. |
FileSearchMatch.cs |
Documents verbatim lines. |
FileEditor.cs |
Adds shared splitting and slicing. |
FileAccessProviderOptions.cs |
Documents read-only tool behavior. |
FileAccessProvider.cs |
Implements file_access_read_lines. |
Harness_Step03_DataProcessing/README.md |
Updates security guidance. |
Claw_Step02_WorkingWithData/README.md |
Updates security guidance. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
…as the schema does Addresses both review comments on microsoft#7671. TrimTrailingNewline removed only "\n", so grep matched against text such as "match\r" on CRLF and lone-CR lines and an end-anchored pattern like "match$" failed even though the line's text was exactly "match". Renamed to TrimLineTerminator and it now strips "\r\n", "\n", or a lone "\r". The file_access_read_lines description and the SliceLines failure messages referred to end_line/start_line, but the generated schema exposes the arguments as endLine/startLine, so the model could be prompted to emit an invalid argument name. Both now use the schema's names. (new_line is left as-is: FileLineEdit sets it explicitly via JsonPropertyName.) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Anton Sokolovskyi (antsok)
deployed
to
github-app-auth
August 14, 2026 20:37 — with
GitHub Actions
Active
Anton Sokolovskyi (antsok)
added a commit
to antsok/agent-framework
that referenced
this pull request
Aug 14, 2026
…s_grep Ports the fix for the same defect found by review on the .NET side (microsoft#7671). _search_file_content removed only the trailing "\n" before matching, so on a CRLF file the pattern was applied to text such as "beta match\r" and an end-anchored pattern like "match$" failed even though the line's text is exactly "beta match". The terminator is not part of the line's text, so it is stripped in full now. The per-line offset had to move with it: it advanced by len(scanned) + 1, which was only correct while scanned still carried the "\r". It now advances by len(line), whose terminator is already included, keeping the snippet anchored at the match. Also drops a stale claim in _split_lines_keepends' docstring, which still said it reproduced _search_file_content's content.split("\n") — that dependency now runs the other way round. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot started reviewing on behalf of
Anton Sokolovskyi (antsok)
August 14, 2026 20:56
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (2)
dotnet/src/Microsoft.Agents.AI/Harness/FileStore/FileSystemAgentFileStore.cs:209
- The new newline, line-number, and snippet-offset behavior is tested only against
InMemoryAgentFileStore.FileSystemAgentFileStorehas its own copied search loop and an existing comprehensive search test suite, so a store-specific regression here would pass. Add equivalent CRLF, lone-CR, trailing-newline, anchored-pattern, and snippet-offset coverage for this implementation.
// Lines keep their terminators, so these line numbers address the same lines that
// replace_lines edits and each reported line can be reused as a literal new_line.
List<string> lines = FileEditor.SplitLinesKeepEnds(fileContent);
dotnet/src/Microsoft.Agents.AI/Harness/FileAccess/FileAccessProvider.cs:326
- The advertised line-number parity is not guaranteed for custom
AgentFileStoreimplementations.file_access_grepdelegates to the publicAgentFileStore.SearchAsync, whose contract does not define splitting or terminator retention, while this method andreplace_linessplit independently through an internal-only helper. An existing custom store can therefore return a grep number that reads or edits a different line. Define the required semantics on the public store contract and make a shared implementation available (or centralize line matching above the store) before promising parity.
[Description("Read part of a file by 1-based inclusive line number; omit endLine to read to the end of the file, and an endLine past the last line is clamped. Line numbers match file_access_grep and file_access_replace_lines. Each line is prefixed with its number and a tab; everything after that tab is verbatim, including the line's own terminator, so it can be reused as a file_access_replace_lines new_line.")]
private async Task<string> ReadLinesAsync(string fileName, int startLine, int? endLine = null, CancellationToken cancellationToken = default)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation & Context
The harness file tools are line-precise when editing —
file_access_replace_linestakes 1-based line numbers andfile_access_grepreports them — but all-or-nothing when reading. There is no way to see the lines around a match without reading the whole file, so agents either re-read entire files or edit by a line number they never looked at.Reading a range only works if grep and the line editor agree on what a line is, and they did not. The stores split on
'\n'and stripped'\r';FileEditorsplit on'\n','\r\n'and a lone'\r'and kept terminators. Consequences today:'\r'terminators, grep's line 1 covers only part of what the model was shown, so editing by that number silently changes the wrong text.NewLineis written verbatim, feeding a grepped line back intoreplace_linesjoins it to the next line.Description & Review Guide
What are the major changes?
FileEditor.SplitLinesKeepEndsbecomesinternaland is used by both stores'SearchAsync, so grep line numbers address the same linesreplace_linesedits, by construction.FileSearchMatch.Lineis reported verbatim, terminator included.file_access_read_linestool, rendering<n>\t<line>with everything after the tab verbatim — so a row is already a validreplace_linesnew_line.What is the impact of these changes?
Breaking for
file_access_grepandfile_memory_grep:Linevalues now include terminators, and line numbers change on content with a lone'\r'or a trailing newline. The whole surface is[Experimental("MAAI001")]. ApiCompat does not flag this —FileEditoris internal andLinekeeps its type — so the Release build passing is not evidence of compatibility.ReadLinesToolNameis a pure addition and needs no suppression.file_access_read_linesjoins the read-only tool set, so it is exposed underDisableWriteToolsand covered byReadOnlyToolsAutoApprovalRule— the auto-approval doc lists and sample security notes are updated accordingly.What do you want reviewers to focus on?
Whether unifying the splitter is the right call versus leaving grep and the editor divergent, and the per-line snippet offset arithmetic in both stores now that terminators are part of each line.
Note for parity: Python addresses a trailing empty line on
"a\nb\n"; .NET has two lines there, because .NET's line editor never had that phantom line. Each language stays self-consistent, which is what the grep → read → edit round trip depends on.Related Issue
#7571 — linked without a closing keyword on purpose: the Python half ships in a separate PR, and the issue should stay open until both land. Will change to Closes in the last one.
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.