agentscope-ai / agentscope-ai/agentscope-java
read_file cannot read workspace/knowledge under IsolationScope.USER despite docs defining knowledge/ as a shared static asset
- Lenguaje dominante
- Java
- Estrellas
- 5.6k
- Forks
- 1.3k
- Merge medio
- 4 d 12 h
- PR fusionados (30 d)
- 77
Descripción
## Summary
Under the default `IsolationScope.USER`, the `read_file` tool cannot read files under `workspace/knowledge/`, even though the docs classify `knowledge/` as a **shared static asset** that is "not auto-partitioned by userId" and is meant to be accessed via `read_file`. `WorkspaceManager` reads it fine (via its local-disk fallback), but the `read_file` tool path does not, so within a single session `KNOWLEDGE.md` is injected into the system prompt while `read_file("knowledge/")` returns "not found" for the same directory.
## Environment
- AgentScope Java: `2.0.0` (Maven Central `io.agentscope:agentscope-harness:2.0.0`)
- Filesystem: default local filesystem (`LocalFilesystemSpec`), `IsolationScope.USER` (the default)
- Verified identical behavior between v2.0.0 GA (`44c304e`) and `main` (`5700e5f`) — no post-release fix exists.
## Reproduction
1. Build a `HarnessAgent` with the default local filesystem (so `IsolationScope.USER` applies) and a real `userId` in the `RuntimeContext`:
```java
HarnessAgent.builder()
.workspace(Path.of("./workspace")) // contains knowledge/KNOWLEDGE.md + knowledge/std-doc/x.md
// default filesystem = LocalFilesystemSpec
.build();
```
2. Populate `./workspace/knowledge/KNOWLEDGE.md` and `./workspace/knowledge/std-doc/example.md`.
3. Call the agent with a `RuntimeContext` whose `userId` is set (e.g. `1871380447893483522`), and have the model invoke the `read_file` tool: `read_file(path="knowledge/std-doc/example.md")`.
## Expected Behavior
Per the docs, `knowledge/` is a shared static asset:
> "Static assets (AGENTS.md, tools.json, knowledge/) are shared across all users and are not auto-partitioned by userId." — https://java.agentscope.io/v2/en/docs/harness/filesystem.html
> knowledge/ files are "listed for agent access via read_file". — https://java.agentscope.io/v2/zh/docs/harness/workspace.html
So `read_file("knowledge/std-doc/example.md")` should read `workspace/knowledge/std-doc/example.md` (with an optional per-user override at `workspace//knowledge/...` taking precedence if present).
## Actual Behavior
`read_file("knowledge/std-doc/example.md")` returns `"Error: File 'knowledge/std-doc/example.md' not found"`, while `KNOWLEDGE.md` *is* successfully injected into the system prompt in the same session.
Runtime proof (executed):
- Shared file exists: `workspace/knowledge/std-doc/...md` — 51094 bytes ✓
- Per-user dir does NOT exist: `workspace//knowledge/` — absent ✗
- Session log shows `read_file("knowledge/...")` → "File not found", and `list_files("knowledge")` → "Empty or not a directory", across relative, backslash-escaped, and absolute path variants.
## Root Cause
Two read paths in the same process resolve `knowledge/` differently:
| Path | What it does | Resolves `knowledge/x` to | Result |
|------|--------------|---------------------------|--------|
| `WorkspaceManager.readKnowledgeMd` → `readWithOverride` | filesystem first, then **local-disk fallback (no namespace)** | `workspace/knowledge/x` | ✓ reads |
| `FilesystemTool.read` → `LocalFilesystem.read` → `resolvePath` → `applyNamespacePrefix` | **filesystem only (namespaced)** | `workspace//knowledge/x` | ✗ not found |
`LocalFilesystem.applyNamespacePrefix` applies the namespace prefix to **every** relative path, with no carve-out for the shared static-asset prefixes the docs define:
- `agentscope-harness/.../filesystem/local/LocalFilesystem.java` — `applyNamespacePrefix` (~L681-699) prefixes all relative paths with the namespace; `resolvePath` (~L593-604) and `read` (~L272) route through it.
- `agentscope-harness/.../workspace/WorkspaceManager.java` — `readKnowledgeMd` (~L252) and `readWithOverride` (~L265-278) do a filesystem-first read then fall back to local disk at `workspace.resolve(relativePath)` **without** the namespace, which is why prompt injection works.
- `agentscope-harness/.../filesystem/ProjectAwareOverlay.java` — `WORKSPACE_PREFIXES` (~L44-58) already enumerates the shared set (`knowledge`, `AGENTS.md`, `skills`, `MEMORY.md`, ...), but that classification is used only for write routing, not for read-path namespace exemption.
The net effect: the documented "shared static asset" semantics is honored by `WorkspaceManager` but **not** by the `read_file` tool path, so the model is told (via the prompt) that these files exist and then fails to read them — burning ReAct iterations retrying variants and, in our case, persisting a false "knowledge dir is empty" memory that poisons later sessions.
## Proposed Fix
Make `read_file` honor the shared-static-asset semantics. Two options:
1. **Namespace exemption (preferred):** In `LocalFilesystem.applyNamespacePrefix`, skip prefixing for relative paths that start with a shared static-asset prefix (the same `WORKSPACE_PREFIXES` set `ProjectAwareOverlay` already defines). Resolution should then follow the documented override model: check `workspace//knowledge/x` first (per-user override), fall back to `workspace/knowledge/x` (shared base).
2. **Overlay fallback at the tool layer:** Have the `FilesystemTool`/`LocalFilesystem` read path fall back to the un-namespaced workspace path when the namespaced path is absent, mirroring `WorkspaceManager.readWithOverride`.
Either way, a per-user override at `workspace//knowledge/...` should take precedence over the shared `workspace/knowledge/...` base, matching the docs' "per-user directory, shared `knowledge/` as the base" description.
## Verification Plan
- New test: with `IsolationScope.USER` and a real `userId`, `read_file("knowledge/std-doc/example.md")` reads `workspace/knowledge/std-doc/example.md`.
- Override test: when `workspace//knowledge/std-doc/example.md` exists, it shadows the shared file for that user only.
- Regression: per-user runtime data (`memory/`, sessions, tasks) still resolves under `workspace//...` and is NOT shared.
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.