Repository navigation
Description
Summary
create_harness_agent's file_access_store today expects the caller to supply an AgentFileStore whose root is already the desired working directory. Unlike FileMemoryProvider, FileAccessProvider does not isolate by session_id at run time.
In hosts that compile/create the harness agent before an AgentSession exists (AG-UI / multi-turn entrypoints, workflow executors restored later from checkpoint, etc.), the caller cannot honestly put session_id into the store root at create_harness_agent time. Platforms end up inventing workarounds (provisional root + mutating _root_path on first run), which is fragile and outside the public API.
Please add a first-class option so file access can be session-scoped inside the framework, with the store root derived from session_id when the session becomes available—not by asking the caller to bake session_id into the path at create time.
Current behavior
| Provider | Store root at create | Isolation |
|---|---|---|
FileMemoryProvider (file_memory_store) |
Shared root (default {cwd}/agent-file-memory) |
Framework derives a working folder from session_id / scope at run time |
FileAccessProvider (file_access_store) |
Caller-supplied root is the working area | No session nesting; docs say the store “should already be scoped to the desired folder” |
Relevant docs in FileAccessProvider:
Unlike MemoryContextProvider / FileMemoryProvider … FileAccessProvider operates on a shared, persistent store … The store is passed in by the caller and should already be scoped to the desired folder or storage location.
create_harness_agent wiring:
file_memory_store=None→ default shared FS store; provider isolates per session.file_access_store=...→ opt-in shared tools; no equivalent session isolation.
Problem
Call sites often look like:
- Build / compile harness agent (tools, providers, middleware).
- Later:
agent.run(..., session=AgentSession(...))— this is whensession_idis known (or assigned by the framework).
Requiring FileSystemAgentFileStore(f".../{session_id}") at step 1 forces one of:
- Shared root for all sessions (loses isolation),
- Recreate the whole harness agent once
session_idis known (expensive / awkward with middleware & pools), - Host-side hacks that mutate private store fields after create.
FileMemoryProvider already solved the “unknown session at create time” case for memory; file access needs the same class of solution if hosts want per-session workspaces (downloads, shell-adjacent artifacts, user uploads, etc.).
Proposed direction (API sketch — open to better shapes)
Add an opt-in session-scoped mode for file access that keeps create-time API free of session_id, for example:
create_harness_agent(
client,
# Option A: base directory; framework creates `{base}/{session_key}/` at run time
file_access_base_dir=Path(".../workspaces"),
file_access_session_scoped=True, # default False to preserve today's shared semantics
)
# or Option B: factory invoked when SessionContext has session_id
create_harness_agent(
client,
file_access_store_factory=lambda session_id: FileSystemAgentFileStore(base / session_id),
)Expectations:
- Create time: no
session_idrequired. - Run time: on
before_run/ first use, framework resolvessession_id(same rules as file memory: require a session, treat id as opaque key via_storage_key_segmentor equivalent). - Root for tools: the effective
FileAccessProviderstore root is that session directory (paths relative to it), notbase/with a manual{session_id}/...prefix the model must know about. - Default remains shared: existing
file_access_store=...behavior unchanged for backward compatibility. - Optionally align with
file_memory: allow a shared parent root + framework-owned session subfolder, rather than forcing hosts to pre-bake the session path.
Alternatives considered
- Keep requiring hosts to pass a session-rooted store → doesn't work when session is created inside
run/ AG-UI / checkpoint restore. - Recreate agent per session → works but is heavy and duplicates wiring.
- Host mutates
FileSystemAgentFileStore._root_pathafter create → works today as a private workaround; should not be the recommended pattern.
Environment
- Package:
agent-framework(Python harness:create_harness_agent,FileAccessProvider,FileMemoryProvider) - Motivation: multi-request hosts that compile agents once per HTTP/SSE request (or once per workflow graph) before the framework
AgentSession.session_idis available.
Thanks!
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
[-]Harness: session-scoped file_access_store option when session_id is unknown at create_harness_agent time[/-][+]Python: Harness: session-scoped file_access_store option when session_id is unknown at create_harness_agent time[/+]JHf0912 commentedon Sep 19, 2026
Hi — I'd like to take this on. The direction fits the framework's existing pattern, and I'd like to confirm the API shape before opening a PR.
Proposed shape (primary): mirror
FileMemoryProviderexactly.FileAccessProvider(..., session_scoped: bool = False, scope: str | None = None)— defaultFalsekeeps today's shared-store semantics untouched.before_runderives the session folder with the same recipe as_file_memory.py's_resolve_working_folder:self.scope or context.session_id→ fail-closedValueErrorwhen neither exists →_storage_key_segment(raw, encoded_prefix="~session-"). The existing guidance in_normalize_relative_path(_file_access.py:246-252) already points at_storage_key_segmentfor session-derived namespaces.search/listresults back to session-relative names;before_runalso creates the session directory. The store's existing symlink/traversal guards continue to apply to the prefixed path, soroot_pathstays immutable and caller-owned.create_harness_agent(..., file_access_session_scoped: bool = False)wires it through.Alternative (if prefixing semantics are unwelcome): a
file_access_store_factory: Callable[[str], AgentFileStore] | Nonethat builds the per-session store atbefore_run(the issue's option B). I'd rather not add a new callable parameter unless the prefix approach is rejected.Testing: reuse the injectivity/collision suite already used for file memory isolation (
COLLIDING_IDENTIFIERSattack strings: trailing slashes,.., case/Unicode folding), plus fail-closed coverage for missingsession_idand a regression check thatsession_scoped=Falsepreserves current behavior.Does this direction work for you, or would the core team prefer the factory shape (or something else, e.g. framework-owned
file_access_base_dirfrom the issue's option A)?Python: feat(core): add session-scoped file access isolation (microso…
Python: feat(core): add session-scoped file access isolation (microso…