Files
Corey Zumar 07afe65150 fix(files): browse outside the workspace behind a slash-merging proxy (#5174)
* fix(files): browse outside the workspace behind a slash-merging proxy

The file panel showed "No files in workspace" for any directory above or
outside the working folder when the app is served through the Databricks
Apps front door, even though the picker dropdown listed those very
directories.

Absolute browse locations were marked by a leading %2F in the filesystem
route's {path} segment. That %2F decodes to a boundary // which the front
door merges back to a single / (//health 301-redirects to /health), so
/Users/me arrived at the server as a workspace-relative Users/me and listed
a nonexistent path under the workspace. The picker uses the host filesystem
endpoint, which already sends bare slashes, so it was unaffected -- hence
paths in the dropdown but an empty tree.

Follow the host endpoint's own convention instead: send the path with
literal slashes and no leading %2F (nothing for a proxy to merge) and name
an absolute location out of band with ?base=host. The server re-adds the
leading slash, applies the same owner gate (an absolute path is owner-only,
so base=host is not a way around it), and re-encodes %2F only on the
internal server->runner leg, which is not proxied. A genuine leading slash
still works for a direct, unproxied client.

Covers list, lazy directory expand, search, file read, and write; the
filesystem root (base=host with an empty segment) is handled on the no-path
route.

Test plan:
- e2e_ui test_absolute_browse_survives_a_slash_merging_proxy reproduces the
  front door's %2F-decode + //-merge on the session's filesystem/search
  requests and asserts the tree lists an outside directory. Reverting the
  web change makes it fail with "No files in workspace".
- collaboration test asserts the owner gate applies to the base=host form
  (collaborator 403, owner 200), so it is not an escape hatch.
- route tests pin base=host -> absolute (%2F) forwarding, a bare path
  without base staying workspace-relative, and base=host root -> "/".

Co-authored-by: Isaac
Signed-off-by: dbczumar <corey.zumar@databricks.com>

* refactor(files): reuse the shared browse-location helpers for file read/write

Address Polly's non-blocking notes on PR #5174. fetchFileContent and
writeFileContent reimplemented the "strip leading slash + ?base=host" wire
rule inline; route them through the exported browseLocationSegment /
browseLocationBase so the slash-merge-safe contract lives in one place.
Behavior is identical (verified: same URL for relative and absolute paths).

Also note in the list_environment_root base=host branch that the delegate
(read_or_list_environment_path) runs its own owner-gated _validate_session,
so the delegation is authorized there.

Co-authored-by: Isaac
Signed-off-by: dbczumar <corey.zumar@databricks.com>

* fix(files): open a file at an absolute browse location by its absolute path

A follow-on to the browse-outside-workspace fix: navigating to a directory
outside the workspace (e.g. /tmp) now lists files, but OPENING one 404'd.
The tree names files by their bare name relative to the browsed location, and
openTreeFile re-attached that name only for workspace-relative locations --
for an absolute location it handed the viewer the bare name, which the viewer
then looked up under the workspace root (nonexistent) → 404.

Always re-attach the browsed location: for an in-workspace location that
yields a workspace-relative path, and for one outside the workspace the file's
absolute path, which fetchFileContent already requests host-absolutely
(base=host, per the earlier consolidation).

Tests: FilesPanel unit test (open a file at an absolute location → the
absolute path reaches onFileSelect; mutation-verified against the old
bare-name form) and a useFileContent test pinning the absolute wire form
(bare path + base=host).

Co-authored-by: Isaac
Signed-off-by: dbczumar <corey.zumar@databricks.com>

* fix(files): keep the browsed directory when the file viewer closes

Opening a file swaps the files panel for the FileViewer in the rail's
content slot, unmounting the panel -- and the browse location was plain
component state, so closing the viewer remounted the panel at the
workspace root instead of the directory the file was opened from
(browse to /tmp, open a log, go back -> dumped at the workspace root).

Remember the location in a module-level per-conversation cache, the same
pattern FolderTree already uses for its expanded-folders set across the
identical unmount/remount. Seeded at mount, written on every navigation
(cleared when returning to the workspace root), and re-read on an
in-place conversation switch so another session still opens at its own
root -- the cache is keyed by conversation, so no location ever leaks
across sessions.

Tests: FilesPanel unit tests for the unmount/remount round trip and the
per-conversation scoping (round trip mutation-verified), and the
outside-workspace e2e now opens a file at the browsed directory, closes
the viewer via its tab, and asserts the header still names that
directory (also mutation-verified: against a pre-fix build it fails
with the header back at the workspace path).

Co-authored-by: Isaac
Signed-off-by: dbczumar <corey.zumar@databricks.com>

---------

Signed-off-by: dbczumar <corey.zumar@databricks.com>
2026-08-21 23:07:02 -07:00
..
2026-06-13 00:37:34 +00:00
2026-06-13 00:37:34 +00:00
2026-06-13 00:37:34 +00:00
2026-06-13 00:37:34 +00:00
2026-06-13 00:37:34 +00:00
2026-06-13 00:37:34 +00:00
2026-06-13 00:37:34 +00:00
2026-06-13 00:37:34 +00:00
2026-06-13 00:37:34 +00:00

server

Hosts agents as a service. Accepts agent specs, stores them durably, and exposes APIs for managing and (eventually) invoking agents over HTTP.

The server is one way to deploy agents — the managed, multi-tenant, always-on way.