Summary
Glob 与 Grep 工具在本地 workspace 上偶发长时间无结果(实测约 5 分钟,直到宿主侧取消;同一调用重放可再次失败)。文件系统扫描本身不是瓶颈(src/ 全树 5713 个文件递归扫描实测约 411ms),根因是这两个工具在本地路径优先路由到 Flashgrep 索引服务,而整条 RPC 调用链没有超时保护:当 flashgrep daemon 进程存活但不响应请求(索引重建、内部卡死)时,客户端无限等待,直到 daemon 退出或宿主取消。
- 类型: Bug / 可靠性
- 严重度: 高(工具挂起 5 分钟级,阻塞 agent 工作流)
- 影响面: 桌面端 Glob/Grep 工具(本地 workspace),所有开启 workspace-search 的用户
Area
Desktop app
Reproduction or evidence
复现步骤:
- 开启
app.ai_experience.enable_workspace_search(默认路径即走 Flashgrep)。
- 确认 flashgrep daemon 二进制存在且进程存活(本机
FLASHGREP_DAEMON_BIN 已设置,daemon PID 6020 在运行)。
- 使 daemon 处于忙/不健康状态(例如存在待处理脏文件、索引重建中或内部卡死但进程未退出)。
- 调用 Glob(如
Glob pattern="src/crates/assembly/core/src/agentic/tools/implementations/task*",该目录确实存在)或 Grep。
- 观察工具长时间无返回;daemon 健康时秒回,不健康时挂起,表现为不定时偶发。
复现率: 非确定性,取决于 daemon 健康状态。两次独立失败(用户环境一次 + 原样重放一次)。
代码证据(调用链无超时)
| 位置 |
事实 |
glob_tool.rs:419-426 |
search_service.glob(...).await 无 timeout;同文件 remote shell 分支有 Some(30_000) / Some(5_000) 超时(L339、L364) |
grep_tool.rs:691 |
search_service.search_content(request).await? 无 timeout;remote 分支有 Some(30_000)(L159) |
flashgrep/client.rs:264、310 |
RepoSession::search/glob 均以 None 超时调用 RPC(仅 close 有 2s 超时,L372) |
flashgrep/rpc_client.rs:92-114 |
request_timeout=None 时 receiver.await 无限等待,仅 daemon 关 stdout/退出才解除;daemon 存活但不回包时 pending 请求永不 resolve |
service/search/service.rs:189-191 |
workspace_search_runtime_available() 只检查 feature flag + 二进制存在,不探活 daemon;daemon 已卡死时仍走 Flashgrep 路径,不降级到本地 rg |
根因调用链(本地路径)
GlobTool/GrepTool
→ workspace_search_runtime_available() // 仅查 feature flag + 二进制存在,不探活
→ WorkspaceSearchService::glob/search_content // 无 timeout(service.rs)
→ RepoSession::glob/search/status // timeout = None(client.rs:264,310)
→ ProtocolClient::send_request_with_timeout(None) // receiver.await 无限等待(rpc_client.rs:108)
关键缺陷:
- RPC 层无超时:
RepoSession 的 search/glob/status/task_status 均以 None 超时调用(仅 close 有 2s)。ProtocolClient 在 request_timeout=None 时无限 await oneshot。
- 解除阻塞的唯一路径是 daemon 退出:pending 请求只在 reader_task 读到 EOF/错误(daemon 崩溃或关 stdout)时被 reject。daemon 存活但不响应(单请求卡死、索引任务独占、内部死锁)时请求永不返回。
- 可用性检查不探活:
workspace_search_daemon_available() 只检查二进制文件存在,不 ping daemon,导致「daemon 已卡死」仍走 flashgrep 路径而不是降级到本地 rg。
- 对比异常:同文件里的 remote shell 分支都有显式超时(5s/30s),本地 workspace-search 分支反而裸 await,实现不一致。
影响
- Agent 工作流被 Glob/Grep 挂起阻塞数分钟,需要手动取消。
- 偶发且无明确报错,用户感知为「工具坏了」,排查成本高。
- 挂起期间若并发多个 Glob/Grep(工具声明
is_concurrency_safe=true),多个调用同时堆积在 daemon 上。
Expected behavior
- Glob/Grep 任意路径都有与 remote 分支一致的显式超时上限(30s 级别)。
- daemon 不健康时工具自动降级到本地 rg 并记录 warn 日志,而不是无限挂起。
- 超时错误信息可操作(含 daemon 状态与降级提示)。
建议修复
- 给 RPC 调用加超时(核心):
RepoSession::search/glob/status/task_status/index_build/rebuild 传合理超时(如 10s~30s)。超时后错误返回,并利用已有 send_request_with_restart_timeout 机制触发 daemon 重启(should_restart_daemon 目前只对 Io/closed 重启,需把超时错误纳入重启判定或降级路径)。
- 工具层超时 + 自动降级:
glob_tool.rs / grep_tool.rs 对 workspace-search 调用加 tokio::time::timeout 包装;超时/错误时已有降级逻辑会自动落到 spawn_blocking 本地 rg(该路径无此问题)。
- 探活 daemon:
workspace_search_runtime_available() 或调用前对 daemon 做 ping/health 检查,避免在 daemon 不健康时仍选择 flashgrep 后端。
- 可观测性:为 search/glob RPC 增加耗时与超时计数日志(现有日志已有耗时字段,补超时分支即可)。
Environment, if relevant
BitFun commit: 4d3869859 (2026-08-14)
OS: Windows x64
FLASHGREP_DAEMON_BIN: C:\Users\wfwt\AppData\Local\BitFun\flashgrep\flashgrep-x86_64-pc-windows-msvc.exe
相关代码目录: src/crates/assembly/core/src/agentic/tools/implementations/{glob,grep}_tool.rs,
src/crates/services/services-integrations/src/workspace_search/
Related files
src/crates/assembly/core/src/agentic/tools/implementations/glob_tool.rs
src/crates/assembly/core/src/agentic/tools/implementations/grep_tool.rs
src/crates/assembly/core/src/service/search/service.rs
src/crates/services/services-integrations/src/workspace_search/service.rs
src/crates/services/services-integrations/src/workspace_search/flashgrep/client.rs
src/crates/services/services-integrations/src/workspace_search/flashgrep/rpc_client.rs
src/crates/services/services-integrations/src/workspace_search/flashgrep/repo_session.rs
Environment, if relevant
No response
Summary
Glob 与 Grep 工具在本地 workspace 上偶发长时间无结果(实测约 5 分钟,直到宿主侧取消;同一调用重放可再次失败)。文件系统扫描本身不是瓶颈(
src/全树 5713 个文件递归扫描实测约 411ms),根因是这两个工具在本地路径优先路由到 Flashgrep 索引服务,而整条 RPC 调用链没有超时保护:当 flashgrep daemon 进程存活但不响应请求(索引重建、内部卡死)时,客户端无限等待,直到 daemon 退出或宿主取消。Area
Desktop app
Reproduction or evidence
复现步骤:
app.ai_experience.enable_workspace_search(默认路径即走 Flashgrep)。FLASHGREP_DAEMON_BIN已设置,daemon PID 6020 在运行)。Glob pattern="src/crates/assembly/core/src/agentic/tools/implementations/task*",该目录确实存在)或 Grep。复现率: 非确定性,取决于 daemon 健康状态。两次独立失败(用户环境一次 + 原样重放一次)。
代码证据(调用链无超时)
glob_tool.rs:419-426search_service.glob(...).await无 timeout;同文件 remote shell 分支有Some(30_000)/Some(5_000)超时(L339、L364)grep_tool.rs:691search_service.search_content(request).await?无 timeout;remote 分支有Some(30_000)(L159)flashgrep/client.rs:264、310RepoSession::search/glob均以None超时调用 RPC(仅close有 2s 超时,L372)flashgrep/rpc_client.rs:92-114request_timeout=None时receiver.await无限等待,仅 daemon 关 stdout/退出才解除;daemon 存活但不回包时 pending 请求永不 resolveservice/search/service.rs:189-191workspace_search_runtime_available()只检查 feature flag + 二进制存在,不探活 daemon;daemon 已卡死时仍走 Flashgrep 路径,不降级到本地 rg根因调用链(本地路径)
关键缺陷:
RepoSession的search/glob/status/task_status均以None超时调用(仅close有 2s)。ProtocolClient在request_timeout=None时无限awaitoneshot。workspace_search_daemon_available()只检查二进制文件存在,不 ping daemon,导致「daemon 已卡死」仍走 flashgrep 路径而不是降级到本地 rg。影响
is_concurrency_safe=true),多个调用同时堆积在 daemon 上。Expected behavior
建议修复
RepoSession::search/glob/status/task_status/index_build/rebuild传合理超时(如 10s~30s)。超时后错误返回,并利用已有send_request_with_restart_timeout机制触发 daemon 重启(should_restart_daemon目前只对 Io/closed 重启,需把超时错误纳入重启判定或降级路径)。glob_tool.rs/grep_tool.rs对 workspace-search 调用加tokio::time::timeout包装;超时/错误时已有降级逻辑会自动落到spawn_blocking本地 rg(该路径无此问题)。workspace_search_runtime_available()或调用前对 daemon 做 ping/health 检查,避免在 daemon 不健康时仍选择 flashgrep 后端。Environment, if relevant
Related files
src/crates/assembly/core/src/agentic/tools/implementations/glob_tool.rssrc/crates/assembly/core/src/agentic/tools/implementations/grep_tool.rssrc/crates/assembly/core/src/service/search/service.rssrc/crates/services/services-integrations/src/workspace_search/service.rssrc/crates/services/services-integrations/src/workspace_search/flashgrep/client.rssrc/crates/services/services-integrations/src/workspace_search/flashgrep/rpc_client.rssrc/crates/services/services-integrations/src/workspace_search/flashgrep/repo_session.rsEnvironment, if relevant
No response