microsoft / microsoft/agent-framework
Python: [Feature]: Better FileAccessProvider for python harness
Open
@westey-m is already working on this.
Since Jun 29, 2026.
harness
python
- Dominant language
- Python
- Stars
- 13.6k
- Forks
- 2.3k
- Avg merge
- 2d 45m
- Merged PRs (30d)
- 358
Description
Description
The existing preview feature is lacking some key tools for editing files, and the tooling name / conventions differs from existing frameworks that popular model providers train on.
Description
- No surgical editing. To change three lines, the model must re-emit the entire file via
save_file. That burns tokens, races with concurrent edits, and frequently corrupts large files when the model "rewrites from memory." - No read-only/read-write split. There's no first-class way to give a sub-agent read access only, so "safe to delegate investigation" agents have to be hand-rolled.
- No per-tool approval gating. Destructive operations (delete/overwrite) can't be individually gated behind an approval step.
- Fixed workdir. The store's root is set at construction, so one agent instance can't safely serve multiple working directories (parallel tasks/sessions) without rebuilding the agent.
Proposal: ship richer file capabilities as ContextProviders (so they inject per-run), split into a read tier and an edit tier:
- Read tier —
read,list_files,grep(recursive,grep -rn-style regex withincludeglob filtering),glob(**-aware path matching). - Edit tier (extends read) —
write,edit(exact-string replace, withreplace_all),apply_patch(unified diff: modify/add/delete/rename),delete_file.
Cross-cutting:
- Per-run workdir scoping: resolve the workdir from
session.state["workdir"]atbefore_run, falling back to a construction default, so a single agent instance serves many workdirs concurrently. - Path safety: workdir-relative paths only, with traversal/symlink protection.
- Approval modes per tool: gate
delete_file(and optionallyedit/apply_patch) behindalways_require.
Code Sample
from agent_framework import (
Agent,
FileReadAccessProvider,
FileEditAccessProvider # subclasses Read
)
# Editing agent: read + write/patch tools, workdir-scoped, delete gated.
editor = Agent(
client=client,
instructions=...,
context_providers=[
FileEditToolsProvider(
default_workdir="/work/project-a",
require_delete_approval=True, # delete_file -> always_require
require_edit_approval=False, # edit/apply_patch auto-run
),
],
)
# Read-only sub-agent: gets read/list_files/grep/glob ONLY — safe to delegate.
investigator = Agent(
client=client,
instructions=...,
context_providers=[FileReadToolsProvider(default_workdir="/work/project-a")],
)
session_a.state["workdir"] = "/work/task-a"
session_b.state["workdir"] = "/work/task-b"
await asyncio.gather(
editor.run("refactor module X", session=session_a),
editor.run("fix failing test", session=session_b),
) # each run's file tools are scoped to its own directory
**Surgical edits instead of whole-file rewrites:**
# exact-string edit
edit(file_name="src/app.py", old_string="DEBUG = True", new_string="DEBUG = False", replace_all=True)
# unified-diff patch (add/modify/delete/rename), the format models produce best
apply_patch(file_name="src/app.py", patch="--- a/src/app.py\n+++ b/src/app.py\n@@ ...")
**Search the tree without reading every file:**
grep(pattern=r"def create_agent", include="*.py") # recursive, like grep -rn
glob(pattern="**/*.sysml") # recursive path match
**Approval loop for gated destructive ops** (when `require_delete_approval=True`):
result = await editor.run("remove the obsolete config", session=session_a)
for req in result.user_input_requests: # delete_file surfaces here for approval
...
Language/SDK
Both
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.