agentscope-ai / agentscope-ai/agentscope-java

read_file cannot read workspace/knowledge under IsolationScope.USER despite docs defining knowledge/ as a shared static asset

Abierto
#2,501 1 comentario 0 reacciones 0 asignados Ver en GitHub
area/build area/core/memory area/docs area/harness bug
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

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.