Skip to content

Add fileaccess harness context provider with shared-folder file tools rooted at a directory - #643

Open
PratikDhanave (PratikDhanave) wants to merge 3 commits into
microsoft:mainfrom
PratikDhanaveFork:fileaccess-harness-provider
Open

Add fileaccess harness context provider with shared-folder file tools rooted at a directory#643
PratikDhanave (PratikDhanave) wants to merge 3 commits into
microsoft:mainfrom
PratikDhanaveFork:fileaccess-harness-provider

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

What

Adds a new self-contained agent/harness/fileaccess package, wired like the sibling agent/harness/todo provider. New(*Options) returns a Provider backed by agent.NewContextProvider, and its Provide hook injects file tools plus instructions on each invocation.

The provider exposes the same six tools as the .NET FileAccessProvider:

  • file_access_read_file
  • file_access_save_file
  • file_access_list_files
  • file_access_list_subdirectories
  • file_access_search_files
  • file_access_delete_file

All operations go through a local-filesystem store rooted at a caller-granted Options.RootDir (not session state). Options.ReadOnly omits the save/delete tools, matching the .NET read-only shipping mode.

Why

The Go harness tree (agent/harness/) had agentmode, loop, todo, toolapproval, and toolautocall, but no file-access counterpart, while the .NET SDK ships FileAccessProvider with exactly this tool set (file_access_read_file / save_file / list_files / list_subdirectories / search_files / delete_file) and a read-only mode. This closes that cross-SDK parity gap so Go agents can be granted a scoped shared folder.

Safety

Every path is interpreted relative to the root. The store resolves paths with filepath.Clean(filepath.Join(root, rel)) and rejects anything that is absolute or escapes the root via a prefix check, so ../outside.txt and absolute paths are refused before touching the filesystem.

Tests

fileaccess_test.go is black-box (package fileaccess_test) and reuses the same harness style as todo_test.go (agenttest.CreateSession, driving tools through the exported Invoking API). It covers: default tool set + instructions present; ReadOnly omitting save/delete; save->read round trip; list_files direct-children-only vs list_subdirectories; search_files regex across nested files; delete; and path-escape rejection (../outside.txt, absolute path, escaping write not creating a file).

go build ./..., go vet ./agent/harness/fileaccess/..., and go test ./agent/harness/fileaccess/... all pass.

Open design questions

  • Scope: The store is a minimal local-filesystem implementation held inside the package. Should it instead be an exported interface so callers can back it with other stores (blob, in-memory), mirroring any abstraction on the .NET side?
  • API shape: Tool argument/return shapes (relative-path strings, search_files returning slash-separated relative paths matched against file contents) are chosen for parity; happy to align field names/semantics exactly with the .NET tool schemas if they differ.
  • Follow-ups: The .NET provider ships read-only mode as an auto-approval rule via the tool-approval harness. This PR keeps the package self-contained (ReadOnly simply omits the mutating tools); wiring an explicit auto-approval rule through toolapproval could be a follow-up.

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added the public-api-change Pull Request changes public APIs label Jul 24, 2026
Introduce a self-contained agent/harness/fileaccess package that mirrors the
.NET FileAccessProvider. It registers a context provider that injects file
tools scoped to a caller-granted root directory: file_access_read_file,
file_access_save_file, file_access_list_files, file_access_list_subdirectories,
file_access_search_files, and file_access_delete_file.

The root comes from Options.RootDir (not session state). A local-filesystem
store constrains every operation to the root, rejecting absolute paths and
".." traversal via filepath.Clean plus a prefix check. Options.ReadOnly omits
the save and delete tools to match the .NET read-only shipping mode.
@github-actions

This comment has been minimized.

@PratikDhanave
PratikDhanave (PratikDhanave) marked this pull request as ready for review August 4, 2026 06:06
@PratikDhanave
PratikDhanave (PratikDhanave) requested a review from a team as a code owner August 4, 2026 06:06
Copilot AI lite review requested due to automatic review settings August 4, 2026 06:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Go harness context provider (agent/harness/fileaccess) that injects shared-folder file tools (read/save/list/search/delete) into agent invocations, intended to mirror the .NET FileAccessProvider and support a read-only mode.

Changes:

  • Introduces fileaccess.Provider with tool injection + default/read-only instructions and a local filesystem-backed store rooted at Options.RootDir.
  • Implements six file tools (file_access_*) with path resolution intended to constrain access to the configured root directory, plus read-only mode by omitting mutating tools.
  • Adds black-box tests validating tool exposure, read-only behavior, round-trip save/read, list/search semantics, delete, and basic ../absolute-path escape rejection.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
agent/harness/fileaccess/fileaccess.go New file-access provider and local filesystem store for shared-folder tool operations.
agent/harness/fileaccess/fileaccess_test.go New black-box tests covering tool presence/behavior, basic path escape rejection, and core operations.
Suppressed comments (1)

agent/harness/fileaccess/fileaccess.go:283

  • Path containment checks do not account for symlinks inside the root. For example, if the shared folder contains a symlink directory like "link" -> "/tmp", calling save_file with path "link/outside.txt" will pass resolve() (it stays under root textually) but os.MkdirAll/os.WriteFile will follow the symlink and write outside the root. The same issue applies to read/delete/search for symlink files.
func (s *store) SaveFile(rel, content string) error {
	full, err := s.resolve(rel)
	if err != nil {
		return err
	}

Comment on lines +10 to +13
// All operations are constrained to the configured root directory. Paths are
// resolved relative to the root and any attempt to escape it (via "..", an
// absolute path, or symlink-style traversal in the supplied name) is rejected.
//
Comment on lines +258 to +262
full := filepath.Clean(filepath.Join(s.root, rel))
if full != s.root && !strings.HasPrefix(full, s.root+string(os.PathSeparator)) {
return "", fmt.Errorf("path %q escapes the shared folder root", rel)
}
return full, nil
@github-actions

This comment has been minimized.

@gdams

Copy link
Copy Markdown
Member

PratikDhanave (@PratikDhanave) can you resolve the parity gaps?

@github-actions github-actions Bot added area:agent Changes files in the agent area size:xlarge More than 300 changed lines or 10 files pending-auto-risk Automatic risk classification is in progress labels Aug 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Cross-repo parity review — PR #643

Upstream reference: dotnet/src/Microsoft.Agents.AI/Harness/FileAccess/FileAccessProvider.cs and FileAccessProviderOptions.cs.

This PR ports the .NET FileAccessProvider to Go. The intent and the high-level structure (context provider + scoped root dir + read-only mode) are well-aligned, but several concrete divergences from the upstream public contract need to be resolved before this is a clean port:

Issues found

  1. Tool names differ (inline comment, line 6) — Go uses verbose suffixed names (file_access_read_file, file_access_save_file, etc.) while .NET uses shorter canonical names (file_access_read, file_access_write, file_access_ls, file_access_grep, file_access_delete). Mismatched names break cross-SDK portability of prompts and agent instructions.

  2. Two tools are missing (inline comment, line 75) — .NET ships file_access_replace and file_access_replace_lines for partial file edits. Go has neither. These are first-class write-mode tools in the upstream contract.

  3. Options field names diverge (inline comment, line 62) — ReadOnly should be DisableWriteTools to match .NET. The .NET options also include DisableReadOnlyToolApproval and DisableWriteToolApproval, which Go omits.

  4. Tool-approval integration absent (inline comment, line 86) — .NET exports ReadOnlyToolsAutoApprovalRule and AllToolsAutoApprovalRule for wiring into the approval harness. Go ships no equivalent, leaving callers with no idiomatic way to auto-approve file tools.

What is aligned

  • The context-provider pattern (Invoking / Invoked hooks) matches the sibling harness packages.
  • Path-escape safety (root-relative, prefix check) is functionally correct.
  • The SourceID / instructions injection approach is consistent with todo provider style.
  • RootDir scoping (caller-supplied, not session state) matches the .NET design intent.

Because public exported APIs have changed, the public-api-change label is appropriate and already present. Please resolve the tool-name, missing-tools, options, and approval-rule gaps before merging.

Generated by Go API Consistency Review Agent · sonnet46 · 39.6 AIC · ⌖ 5 AIC · ⊞ 6K ·

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by Go API Consistency Review Agent · sonnet46 · 39.6 AIC · ⌖ 5 AIC · ⊞ 6K

// Package fileaccess provides a context provider that gives agents file tools
// for reading and writing files inside a single caller-granted directory (a
// "shared folder"). It mirrors the .NET FileAccessProvider, exposing the same
// set of tools: file_access_read_file, file_access_save_file,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parity issue: Tool names diverge from .NET FileAccessProvider

The Go package doc comments (and the actual tool registrations) use different tool names than the .NET FileAccessProvider ships:

Go name .NET name
file_access_read_file file_access_read
file_access_save_file file_access_write
file_access_list_files file_access_ls
file_access_list_subdirectories (no separate tool; .NET's file_access_ls returns both files and dirs)
file_access_search_files file_access_grep
file_access_delete_file file_access_delete

Ref: dotnet/src/Microsoft.Agents.AI/Harness/FileAccess/FileAccessProvider.csWriteToolName, ReadFileToolName, LsToolName, GrepToolName, DeleteFileToolName.

Using different tool names means prompts, instructions, and tests written for one SDK will not transfer to the other. Please align with the upstream .NET names (or document a justified divergence).

// file_access_delete_file tools so the agent can only read.
ReadOnly bool

// Instructions overrides the default instructions provided to the agent.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parity issue: Missing file_access_replace and file_access_replace_lines tools

The .NET FileAccessProvider ships two additional tools not present in this Go package:

  • file_access_replace — replaces occurrences of a substring within a file (avoids full rewrites)
  • file_access_replace_lines — replaces whole lines within a file

These are write-mode tools omitted when DisableWriteTools is set.

Ref: dotnet/src/Microsoft.Agents.AI/Harness/FileAccess/FileAccessProvider.csReplaceToolName, ReplaceLinesToolName.

If these are intentionally deferred, please note that in the PR description and/or add a TODO; otherwise the Go provider is functionally incomplete relative to .NET.

- Use file_access_list_files to list the files directly inside a folder.
- Use file_access_list_subdirectories to list the sub-folders directly inside a folder.
- Use file_access_search_files to find files whose contents match a regular expression.`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parity issue: Options diverges from .NET FileAccessProviderOptions

The Go Options struct uses ReadOnly bool while the .NET equivalent uses DisableWriteTools bool. These are semantically equivalent, but the name divergence means documentation and cross-SDK guidance will be inconsistent. Please use DisableWriteTools (or add it as an alias) to match the upstream name.

Also, the .NET options expose two additional fields that Go omits:

  • DisableReadOnlyToolApproval bool — disables the approval requirement for read-only tools
  • DisableWriteToolApproval bool — disables the approval requirement for write tools

And the upstream type is marked [Experimental]; Go may want an equivalent //go:build constraint or a package-level note.

Ref: dotnet/src/Microsoft.Agents.AI/Harness/FileAccess/FileAccessProviderOptions.cs.

store *store
readOnly bool
instructions string
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parity issue: Tool-approval integration is absent

The .NET FileAccessProvider ships two static auto-approval rules as first-class public API:

  • FileAccessProvider.ReadOnlyToolsAutoApprovalRule — auto-approves file_access_read, file_access_ls, file_access_grep
  • FileAccessProvider.AllToolsAutoApprovalRule — auto-approves all seven tools

These are intended to be registered with the ToolApprovalAgent harness, and the PR description acknowledges the .NET read-only mode uses this pattern. Go's sibling toolapproval harness package presumably supports the same hook. Please either:

  1. Expose equivalent exported AutoApprovalRule values in this package so callers can wire them into the approval harness, mirroring the .NET public contract, or
  2. Document explicitly why this is deferred and what callers should do instead.

Ref: dotnet/src/Microsoft.Agents.AI/Harness/FileAccess/FileAccessProvider.csReadOnlyToolsAutoApprovalRule, AllToolsAutoApprovalRule.

@github-actions github-actions Bot added failed-auto-risk Automatic risk classification was inconclusive or failed and removed pending-auto-risk Automatic risk classification is in progress labels Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:agent Changes files in the agent area failed-auto-risk Automatic risk classification was inconclusive or failed public-api-change Pull Request changes public APIs size:xlarge More than 300 changed lines or 10 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants