Skip to content

feat: add workspace file management for agents#2093

Open
BingqingLyu wants to merge 22 commits into
mainfrom
fork-pr-62417-feature-workspace-file-manager
Open

feat: add workspace file management for agents#2093
BingqingLyu wants to merge 22 commits into
mainfrom
fork-pr-62417-feature-workspace-file-manager

Conversation

@BingqingLyu

@BingqingLyu BingqingLyu commented Apr 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • Problem: Agents have workspace directories but no way to browse or manage files through the Control UI.
  • Why it matters: Users need to inspect, edit, and organize agent workspace files without SSH or CLI access.
  • What changed: Added agents.workspace.* gateway RPC methods (list, get, set, delete, mkdir, move, stat) and a Workspace tab in the agents panel with file browser, editor, and file operations.
  • What did NOT change (scope boundary): No changes to agent runtime behavior, plugin SDK, or existing file/config panels.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

N/A

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/gateway/server-methods/agents-workspace.test.ts
  • Scenario the test should lock in: Path traversal rejection, CRUD operations, file size limits, error handling
  • Why this is the smallest reliable guardrail: Unit tests cover all API methods with mocked fs, validating security and correctness without requiring a running gateway
  • Existing test that already covers this (if any): None (new feature)
  • If no new test is added, why not: Tests are included

User-visible / Behavior Changes

  • New "Workspace" tab in the agents panel for browsing and managing agent workspace files
  • File browser with directory navigation, breadcrumbs, and sorting (directories first)
  • Inline file editor with dirty state tracking and save/delete actions
  • File upload, download, and mkdir operations
  • 10MB file size limit enforced server-side

Diagram (if applicable)

Before:
[user] -> [agents panel] -> [overview | files | tools | skills | channels | cron]

After:
[user] -> [agents panel] -> [overview | files | workspace | tools | skills | channels | cron]
                                          |
                                    [file tree] + [file editor]
                                          |
                              agents.workspace.* RPC -> fs-safe

Security Impact (required)

  • New permissions/capabilities? Yes - workspace file read/write through gateway RPC
  • Secrets/tokens handling changed? No
  • New/changed network calls? Yes - new agents.workspace.* RPC methods
  • Command/tool execution surface changed? No
  • Data access scope changed? Yes - workspace directory access scoped per agent
  • Risk + mitigation: All file operations use fs-safe helpers with path traversal protection. Relative paths are validated to reject .. and absolute paths. File size capped at 10MB. Operations are scoped to the resolved agent workspace directory.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Docker (openclaw-workspace-test:latest)
  • Model/provider: N/A (UI feature)
  • Integration/channel: N/A
  • Relevant config: Default gateway config with --allow-unconfigured --bind lan

Steps

  1. Start gateway with openclaw gateway --allow-unconfigured
  2. Open Control UI and navigate to an agent
  3. Click the "Workspace" tab
  4. Browse files, select a file to edit, modify content, click Save

Expected

  • File tree shows workspace contents with correct icons and sizes
  • Editing a file enables the Save button (dirty state)
  • Clicking Save persists changes and disables the button
  • Navigating away resets editor state

Actual

  • All operations work as expected

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Unit tests: 40+ test cases covering security (path traversal), CRUD, edge cases, and error handling all pass.

Human Verification (required)

  • Verified scenarios: File browse, edit, save, delete, mkdir, navigate, upload via Docker container with mounted ~/.openclaw
  • Edge cases checked: Path traversal attempts, empty directories, large file rejection, file selection reset on navigate
  • What I did not verify: Base64 encoding upload through UI, symlink display

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: New RPC surface increases attack surface for path traversal
    • Mitigation: All paths validated with validateRelativePath() rejecting .. and absolute paths, plus fs-safe helpers enforce root directory containment

WilShi added 22 commits April 10, 2026 15:50
Add RPC methods for agent workspace file operations:
- agents.workspace.list: browse directory contents with optional recursion
- agents.workspace.get: read file content (utf8/base64)
- agents.workspace.set: write file content with optional directory creation
- agents.workspace.delete: remove files/directories with optional recursion
- agents.workspace.mkdir: create directories
- agents.workspace.move: rename/move files with overwrite control
- agents.workspace.stat: retrieve file metadata

All operations enforce path traversal protection and 10MB file size limits.
Includes comprehensive test coverage for security, CRUD, and edge cases.

Closes openclaw#61368
Add a Workspace tab to the agents panel with:
- File tree browser with directory navigation and breadcrumbs
- File editor with textarea for viewing and editing file content
- Dirty state tracking with separate edited content state
- Save button that only enables when content has changed
- File upload, download, delete, and mkdir operations
- Responsive layout with toolbar, file tree, and editor panes
- Workspace controller with state management helpers
- Type definitions for workspace API results
- Fix overwrite=false never being enforced in workspaceMove: the
  throw was inside the try block and swallowed by the catch. Use a
  separate destExists flag so the error propagates correctly.
- Fix file size limit bypass for multi-byte UTF-8 content: use
  Buffer.byteLength() instead of string.length to count actual
  bytes, preventing content with emoji/CJK from exceeding 10MB.
- Add comment clarifying that mkdirPathWithinRoot always creates
  intermediate directories (recursive: true) regardless of the
  parents flag.
…y uploads

- Enforce workspace root boundary in workspaceList and workspaceMove
  by resolving symlinks via realpath and verifying the resolved path
  stays within the agent workspace root directory.
- Fix stale file content responses: gate the file content update on
  the current workspaceSelectedFile so a slow response from a
  previously selected file does not overwrite the editor.
- Fix binary file upload corruption: use arrayBuffer() + base64
  encoding instead of file.text() so non-text files (images,
  archives) are preserved correctly through the RPC transport.
- Enforce workspace root boundary in workspaceStat via realpath
  containment check, preventing symlink-based metadata leaks.
- Add 10MB file size guard on workspaceGet reads so large workspace
  artifacts cannot force the gateway to allocate oversized buffers.
- Fix binary file downloads: request base64 encoding and decode to
  Uint8Array before creating the download blob, preserving original
  bytes for non-text files.
- Validate agent ID against configured agents list using
  listAgentIds before resolving workspace path, rejecting
  unknown agent IDs with an explicit error instead of silently
  creating fallback workspace directories.
- Clear workspaceFileContent immediately when selecting a new
  file so a failed fetch does not leave stale content from the
  previous file in the editor.
…tate

- Normalize entry paths to POSIX separators in workspaceList so
  breadcrumbs and navigation work correctly on Windows hosts.
- Clear workspaceError on navigation start and on successful list
  response so transient errors do not persist over fresh results.
- Clear editor selection, content, and edited state when the
  currently open file is deleted, preventing accidental re-creation
  on save.
- Implement actual recursive directory removal: use fs.rm with
  recursive flag after verifying the path stays within workspace
  root, since removePathWithinRoot does not support recursive mode.
- Use lstat instead of stat for symlink entries in workspaceList
  to avoid following symlinks that point outside the workspace,
  preventing external target metadata from leaking through the
  listing RPC.
- Walk up to the nearest existing ancestor when validating the move
  destination path, so symlinked ancestors that point outside the
  workspace root are caught even when intermediate directories do
  not exist yet.
- Guard workspace navigation list responses against stale delivery
  by checking the current workspacePath before applying results,
  preventing a slow response from overwriting a newer navigation.
…pty downloads

- Block recursive deletion when the resolved path equals the
  workspace root, preventing callers from erasing all contents.
- Capture the requested agent ID when loading workspace entries
  on tab switch and ignore stale responses if the user switched
  agents before the response arrived.
- Use result.content != null instead of truthiness check so empty
  files (zero-byte, empty base64 string) can still be downloaded.
- Use lstat instead of stat in workspaceDelete so symlinks are
  checked as entries rather than following to their targets,
  fixing incorrect 'not empty' errors for symlinked directories
  and ENOENT for dangling symlinks.
- Honor parents=false in workspaceMkdir by using non-recursive
  fs.mkdir with root boundary validation, so callers can detect
  missing parent directories instead of silently creating them.
Run protocol:gen:swift to update GatewayModels.swift with the
new agents.workspace.* request/response types.
Reset workspace state and fetch the new agent's file listing
when the user switches agents while already on the Workspace
tab, preventing stale content from the previous agent.
Capture the selected file, agent ID, and workspace path at save
time and verify they still match before updating editor content
or refreshing the file list, preventing stale save responses from
overwriting a different file or agent's view.
- Add agent/path staleness guards to the post-delete list refresh
  callback so late responses do not overwrite a different context.
- Replace quadratic per-byte string concatenation in upload base64
  encoding with chunked String.fromCharCode to avoid UI stalls on
  larger files.
- Add agentsSelectedId check to the file-load stale guard so
  same-named files across different agents cannot cross-pollinate.
- Add agent/path staleness guards to the post-mkdir list refresh
  callback to prevent late responses from overwriting a different
  workspace context.
- Use lstat instead of access for destination existence checks in
  workspaceMove so dangling symlinks and permission-denied entries
  are correctly detected, only treating ENOENT as 'not exists'.
- Add agent/path staleness guards to the post-upload list refresh
  callback to prevent late responses from overwriting a different
  workspace context.
…hemas

The Swift codegen does not emit enum types for string unions, so
exporting WorkspaceEntryType as a named schema produced a reference
to an undefined type. Remove it from ProtocolSchemas so the codegen
inlines the field type instead of generating a broken reference.
- Auto-create workspace directory and return empty listing when
  the workspace root does not exist yet, instead of failing with
  ENOENT on first use.
- Freeze the upload destination path at the start of the batch
  so navigating during a multi-file upload does not scatter files
  across different folders.
Reset workspaceError when the post-save list refresh succeeds so
a previous transient error does not persist and hide fresh results.
…g symlinks

- Only treat ENOENT as empty listing in workspaceList; propagate
  EACCES and other errors so real access issues are not hidden.
- Use lstat as the authoritative existence check in workspaceStat
  so dangling symlinks are reported as type symlink instead of
  throwing 'File not found'. Fall back to lstat metadata when
  stat fails on broken symlink targets.
Pass maxBytes to readFileWithinRoot so the size check happens
before the full buffer is allocated, preventing OOM on large
workspace files. The Refresh button already uses a dedicated
onRefresh handler that preserves editor state.
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.

[Feature]: Full Workspace File Management API for Remote/Container Deployments

2 participants