Skip to content

fix(search): serve remote workspace content search over the remote transport - #3695

Merged
bobleer merged 2 commits into
GCWing:mainfrom
bobleer:bob/remote-search-fallback
Nov 2, 2026
Merged

bobleer merged 2 commits into
GCWing:mainfrom
bobleer:bob/remote-search-fallback

Conversation

@bobleer

@bobleer bobleer commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Content search in a remote workspace now runs on the remote host instead of failing with Flashgrep is not supported for remote workspaces. start_search_file_contents_stream, search_file_contents, and the content branch of search_files use the same workspace-stream search as the agent Grep tool, through the workspace's own file and shell providers. Flashgrep is still never selected for a remote root.

Fixes #3452

Type and Areas

Type: bug fix

Areas: desktop/Tauri, Rust core, tool-execution, docs

Motivation / Impact

These commands refused every remote content search, since workspace_search_unavailable_message always returns a reason for remote workspaces. The agent Grep tool already fell back to workspace IO, so only the file panel search box was broken. Users can now search file contents in SSH and container workspaces.

The existing Grep path was not enough on its own. The search box defaults to case-insensitive, and literal_alternatives rejects case folding and any escaped literal, so a default query would have streamed every file over SFTP. This PR adds, for interactive search only:

  • A proven fixed-string hint (content_search_candidate_literal). k, s, and cased non-ASCII characters split the literal, because they have folds that LC_ALL=C grep -i cannot see. The prefilter stays a conservative superset of the native matcher.
  • Case-folded rg, then a one-shot grep -r candidate step guarded by a behavior probe, then provider streaming if neither is usable.
  • Structured line matches with the whole line text, and an early stop once the result limit is exceeded, including mid-file.
  • Local search parity (InteractiveSearchPolicy): the same default excluded directories, binary suffixes, and 10 MB cap as local tree.rs (now shared constants), BinaryDetection::quit(0) so binary files do not consume the budget, CRLF-aware $, and .git/info/exclude at the workspace root.
  • Symlinked and relative workspace roots resolve through pwd -P in the workspace shell. A symlinked root keeps its logical path in results.

Agent Grep behavior is unchanged: the new GrepOptions fields default to off.

Failures stay explicit. A lost connection, an invalid regex, or the new 120s deadline reaches the UI as an error, and the existing "local filesystem fallback was not attempted" wording is kept. Nothing touches the controller filesystem. A candidate command that exits 2 with no output no longer means "no results": the search logs its stderr and scans through the provider.

Verification

  • cargo test --locked -p tool-runtime --lib search: passed
  • cargo test --locked -p tool-runtime --test tool_io_contracts: 15 passed
  • cargo test --locked -p openbitfun-core --no-default-features --features ssh-remote,workspace-search,agent-runtime --lib service::search::remote_content: 19 passed
  • Same features with --lib grep_tool: 31 passed, so agent Grep has no regression
  • cargo test --locked -p openbitfun-services-core --no-default-features --features filesystem --lib filesystem::tree: 3 passed (shared exclusion constants)
  • cargo check --locked -p openbitfun-core --no-default-features --features workspace-search: passed (disabled-feature stub)
  • cargo test --locked -p openbitfun-desktop --lib api::search_api::tests: 3 passed. This compile used empty placeholders for the mobile-web/dist and extension-host resources.
  • pnpm run check:repo-hygiene: passed
  • pnpm run check:core-boundaries: could not run from this nested worktree (Cargo resolves the installer manifest against the parent checkout's workspace). This PR changes no Cargo manifests, so please rely on CI for it.

Remote scenarios exercised: none against a real host. The new tests use a real local POSIX shell and filesystem behind the workspace provider ports, with rg and grep absence simulated. They do not establish behavior over real SSH, Docker, or a slow link, and the desktop search UI was not driven manually. By code path only:

  • Remote workspace: SSH and container workspaces share these providers.
  • Peer Device Mode: these commands stay Proxied, so a Desktop peer runs them on the peer. A CLI peer still reports them as not implemented.
  • Remote control and Detached Dispatch: not affected, since neither reaches these commands.

Reviewer Notes

  • Slow cases that remain: the walker still lists each directory over SFTP to apply ignore rules, and regex searches or servers with no capable rg/grep read files through the provider. A very large tree can hit the 120s deadline, which shows an error rather than partial results.
  • Unreadable content: per-path permission-denied and not-found failures (files and directories) are skipped, counted, and logged, like local search, with no UI indication. This is classified by error text because the provider port has no typed error, so an unusual server message fails the search visibly rather than being skipped. Anything else, including a dropped connection, stays fatal so it cannot look like "no matches".
  • Global search stays local-only. fileSearchProvider keeps reporting remote projects as unsupported, because each keystroke would hold local file results behind a remote scan. The comment and docs/features/remote-workspaces.md now say so. Enabling it needs per-workspace progressive results.
  • Ignore gaps: global Git excludes and a parent repository's exclude file are not applied, as documented.
  • Binary files: detection only stops at the first NUL, so matches earlier in a file with a late NUL can still be listed.
  • Result order: once the limit is exceeded, results are the first matches in walk order, not a globally sorted prefix.
  • Other behavior: results arrive when the search completes rather than progressively, as with the Flashgrep path.
  • Upgrade compatibility: no persisted shape or wire format changes.
  • Follow-up, not in this PR: running Flashgrep itself on remote hosts for indexed search, as the issue also suggests. It needs per-target binary distribution.

Checklist

  • This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts.
  • Relevant verification is recorded above, or skipped checks are explained.
  • User-facing strings, docs, and locales are updated where applicable. docs/features/remote-workspaces.md is updated; no new UI strings.

…ansport

The file panel, search_files, and search_file_contents refused every remote
content search because Flashgrep only ships for the host platform. Route them
through the workspace-stream search the agent Grep tool already uses, with the
workspace's own file and shell providers.

Interactive search defaults to case folding, which the existing literal
prefilter rejected, so it would have streamed every file over SFTP. Add a
proven fixed-string hint with case-folded rg and a one-shot recursive grep
candidate step, stop scanning once the limit is exceeded, and return structured
line matches. Failures stay explicit and nothing touches the controller
filesystem.

Fixes GCWing#3452
Treat a candidate command that exits 2 with no output as failed rather than as
zero results, and scan through the provider. Skip and count per-path refusals
while keeping transport errors fatal.

Apply the local exclusions, binary detection, and 10 MB cap before results
count against the budget, keep whole lines for matches past the agent column
cap, handle CRLF end anchors, resolve symlinked roots, and read the root
.git/info/exclude. Global search stays local-only, now stated in docs.
@bobleer
bobleer merged commit e37786a into GCWing:main Nov 2, 2026
13 checks passed
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.

[Bug]: 远程服务器搜索失效

1 participant