Skip to content

feat(agent-server): add include_hidden to file browser endpoints#3740

Open
xingyaoww wants to merge 1 commit into
mainfrom
feature/file-browser-include-hidden
Open

feat(agent-server): add include_hidden to file browser endpoints#3740
xingyaoww wants to merge 1 commit into
mainfrom
feature/file-browser-include-hidden

Conversation

@xingyaoww

@xingyaoww xingyaoww commented Jun 15, 2026

Copy link
Copy Markdown
Member

HUMAN:

  • A human has tested these changes.

AGENT:


Why

The agent-server file browser endpoints GET /api/file/search_subdirs and GET /api/file/home unconditionally skip dot-entries (entry.name.startswith(".")). GUIs built on these endpoints (e.g. agent-canvas' workspace picker) therefore cannot browse or select hidden directories such as ~/.config, ~/.ssh, or a hidden repo. There was no server-side way to opt in to seeing them.

This is the backend half of "Option B" for OpenHands/agent-canvas — letting users add a hidden directory as a workspace via a "Show hidden folders" toggle.

Summary

  • Added an optional include_hidden query param (default False) to GET /api/file/search_subdirs. When true, dot-directories are listed; files and symlinks are still skipped.
  • Added the same include_hidden param to GET /api/file/home so hidden top-level directories can appear in favorites.
  • Default behavior is unchanged (include_hidden=False), so this is fully backward-compatible — no existing client is affected.

Issue Number

Relates to OpenHands/agent-canvas hidden-workspace support (Option B).

How to Test

uv run pytest tests/agent_server/test_file_router.py -q

Manual:

# default: hidden dirs omitted
curl "http://localhost:8000/api/file/search_subdirs?path=$HOME"
# opt in: hidden dirs included
curl "http://localhost:8000/api/file/search_subdirs?path=$HOME&include_hidden=true"

Evidence (local run):

  • uv run pytest tests/agent_server/test_file_router.py -q28 passed (incl. new test_search_subdirs_include_hidden_lists_dot_directories and test_get_home_include_hidden_lists_hidden_favorites).
  • uv run ruff check / ruff format --check → clean.
  • Pre-commit (ruff, pycodestyle, pyright, import rules) → all passed on commit.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

Follow-up wiring (separate PRs):

  1. @openhands/typescript-client: forward include_hidden from FileClient.searchSubdirectories (extend FileSearchSubdirsOptions) and accept it on getHome; release a new version.
  2. agent-canvas: bump the client pin and add a "Show hidden folders" toggle that threads includeHidden through useSearchSubdirs / useHomeDirectory.

This PR was created by an AI agent (OpenHands) on behalf of the user.


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.13-nodejs22-slim Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:ad0f28e-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-ad0f28e-python \
  ghcr.io/openhands/agent-server:ad0f28e-python

All tags pushed for this build

ghcr.io/openhands/agent-server:ad0f28e-golang-amd64
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-golang-amd64
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-golang-amd64
ghcr.io/openhands/agent-server:ad0f28e-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:ad0f28e-golang-arm64
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-golang-arm64
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-golang-arm64
ghcr.io/openhands/agent-server:ad0f28e-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:ad0f28e-java-amd64
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-java-amd64
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-java-amd64
ghcr.io/openhands/agent-server:ad0f28e-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:ad0f28e-java-arm64
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-java-arm64
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-java-arm64
ghcr.io/openhands/agent-server:ad0f28e-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:ad0f28e-python-amd64
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-python-amd64
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-python-amd64
ghcr.io/openhands/agent-server:ad0f28e-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-amd64
ghcr.io/openhands/agent-server:ad0f28e-python-arm64
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-python-arm64
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-python-arm64
ghcr.io/openhands/agent-server:ad0f28e-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-arm64
ghcr.io/openhands/agent-server:ad0f28e-golang
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-golang
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-golang
ghcr.io/openhands/agent-server:ad0f28e-golang_tag_1.21-bookworm
ghcr.io/openhands/agent-server:ad0f28e-java
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-java
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-java
ghcr.io/openhands/agent-server:ad0f28e-eclipse-temurin_tag_17-jdk
ghcr.io/openhands/agent-server:ad0f28e-python
ghcr.io/openhands/agent-server:ad0f28e881ee8e6c8182bb4257f31f2370046357-python
ghcr.io/openhands/agent-server:feature-file-browser-include-hidden-python
ghcr.io/openhands/agent-server:ad0f28e-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim

About Multi-Architecture Support

  • Each variant tag (e.g., ad0f28e-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., ad0f28e-python-amd64) are also available if needed

The folder/file browser endpoints (/api/file/search_subdirs and
/api/file/home) unconditionally skip dot-entries, so GUIs built on
them cannot browse or select hidden directories (e.g. ~/.config).

Add an optional include_hidden query param (default False, so existing
behavior is unchanged) to both endpoints. When true, dot-directories
are listed; files and symlinks are still skipped.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions

Copy link
Copy Markdown
Contributor

Python API breakage checks — ✅ PASSED

Result:PASSED

Action log

@github-actions

Copy link
Copy Markdown
Contributor

REST API breakage checks (OpenAPI) — ✅ PASSED

Result:PASSED

Action log

@github-actions

Copy link
Copy Markdown
Contributor

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-agent-server/openhands/agent_server
   file_router.py1592882%79–81, 117–119, 132–134, 172–173, 177–178, 186, 188–193, 196–198, 284–285, 289–290, 322
TOTAL313451374256% 

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

⚠️ QA Report: PASS WITH ISSUES

Functional QA passed: the running agent-server now exposes hidden directories only when include_hidden=true, while default behavior still omits them.

Does this PR achieve its stated goal?

Yes. I started the actual agent-server and made real HTTP requests against /api/file/search_subdirs and /api/file/home before and after the PR. On origin/main, adding include_hidden=true still omitted dot-directories; on commit ad0f28e8, the same requests returned .hidden_repo and .config only with the opt-in flag, while files and symlinks remained excluded.

Phase Result
Environment Setup make build completed and installed the uv workspace dependencies.
CI Status ⚠️ 16 checks succeeded, 12 were still in progress, and Validate PR description failed.
Functional Verification ✅ Real HTTP requests verified both changed endpoints and unchanged defaults.
Functional Verification

Test 1: /api/file/search_subdirs hidden directory opt-in

Step 1 — Reproduce / establish baseline without the fix:
Checked out origin/main, created a real directory containing projects/, .hidden_repo/, README.md, and a symlink, then started the server with:

HOME=/tmp/file-browser-qa-base/home OPENHANDS_SUPPRESS_BANNER=1   uv run agent-server --host 127.0.0.1 --port 8765

Ran:

curl 'http://127.0.0.1:8765/api/file/search_subdirs?path=%2Ftmp%2Ffile-browser-qa-base%2Fsearch'
curl 'http://127.0.0.1:8765/api/file/search_subdirs?path=%2Ftmp%2Ffile-browser-qa-base%2Fsearch&include_hidden=true'

Observed both responses returned only the visible directory:

{
  "items": [
    {"name": "projects", "path": "/tmp/file-browser-qa-base/search/projects"}
  ],
  "next_page_id": null
}

This establishes the prior behavior: even when a client sent include_hidden=true, hidden directories were not listed.

Step 2 — Apply the PR's changes:
Checked out ad0f28e881ee8e6c8182bb4257f31f2370046357 and recreated the same fixture shape under /tmp/file-browser-qa-pr/search.

Step 3 — Re-run with the fix in place:
Ran the equivalent requests against the PR server. Default behavior still omitted hidden entries:

{
  "items": [
    {"name": "projects", "path": "/tmp/file-browser-qa-pr/search/projects"}
  ],
  "next_page_id": null
}

With include_hidden=true, the hidden directory appeared and the file/symlink stayed excluded:

{
  "items": [
    {"name": ".hidden_repo", "path": "/tmp/file-browser-qa-pr/search/.hidden_repo"},
    {"name": "projects", "path": "/tmp/file-browser-qa-pr/search/projects"}
  ],
  "next_page_id": null
}

This confirms the new opt-in behavior works and the backward-compatible default is preserved.

Test 2: /api/file/home hidden favorite opt-in

Step 1 — Reproduce / establish baseline without the fix:
On origin/main, started the server with HOME=/tmp/file-browser-qa-base/home where the home directory contained Documents/, .config/, readme.txt, and a symlink.

Ran:

curl 'http://127.0.0.1:8765/api/file/home'
curl 'http://127.0.0.1:8765/api/file/home?include_hidden=true'

Observed both responses returned only Documents in favorites:

{
  "home": "/tmp/file-browser-qa-base/home",
  "favorites": [
    {"label": "Documents", "path": "/tmp/file-browser-qa-base/home/Documents"}
  ],
  "locations": [{"label": "/", "path": "/"}]
}

This confirms the old endpoint could not opt into hidden home favorites.

Step 2 — Apply the PR's changes:
Checked out ad0f28e881ee8e6c8182bb4257f31f2370046357 and recreated the same fixture shape under /tmp/file-browser-qa-pr/home.

Step 3 — Re-run with the fix in place:
Default behavior still returned only the visible favorite:

{
  "home": "/tmp/file-browser-qa-pr/home",
  "favorites": [
    {"label": "Documents", "path": "/tmp/file-browser-qa-pr/home/Documents"}
  ],
  "locations": [{"label": "/", "path": "/"}]
}

With include_hidden=true, .config appeared before Documents, while the file and symlink stayed excluded:

{
  "home": "/tmp/file-browser-qa-pr/home",
  "favorites": [
    {"label": ".config", "path": "/tmp/file-browser-qa-pr/home/.config"},
    {"label": "Documents", "path": "/tmp/file-browser-qa-pr/home/Documents"}
  ],
  "locations": [{"label": "/", "path": "/"}]
}

This confirms /api/file/home now supports the same hidden-directory opt-in behavior as the search endpoint.

Issues Found

  • 🟡 Non-functional CI issue: Validate PR description is failing with: A human must check A human has tested these changes. and Add a short human-written note between HUMAN: and the human-tested checkbox. Per repo policy, a human author needs to update those human-only PR description fields.

No functional issues found in the changed behavior.

This QA review was created by an AI agent (OpenHands) on behalf of the user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants