fix(web): bound search provider work (round 6)

This commit is contained in:
Hypatia May
2026-07-27 13:18:05 +08:00
parent 30503c3b03
commit a8c28be1ba
8 changed files with 151 additions and 14 deletions
@@ -12,7 +12,7 @@ Web 侧边栏会展示会话标题及其 Workspace 归属,但无法根据只
Web 与 headless 共用的组合会使用内存数据库挂载 [`@deepseek-ai/dsh-session-query-sqlite`](../../../../packages/session-query/session-query-sqlite/README.md)。每个服务实例都独占一个连接私有索引,因此并行 CLI 或 Web 调用可维持 SQLite 后端的单一所有者契约,又不会留下进程级派生文件。数据库从空状态启动;每次调用的首次内容查询会惰性对齐实时会话与持久化会话。它仍是与规范 JSONL 持久化相互独立的可丢弃派生索引。
宿主网关通过现有的类型化 RPC 栈公开 `session.search`。它根据 `session.list` 使用的同一组可见摘要推导授权集合,向 `ctx.sessionQuery.searchSessions` 请求全局排序后的当前 surface `user/message`、`assistant/message` 和 `steering/message` 匹配项,并持续消费提供方分页,直到获得 20 个已授权会话及一个前瞻项,或结果流耗尽。每个命中的会话 id、最佳匹配会话 id、surface 和事件类型都会经过重新校验,其 snippet 才能离开宿主。将可能很大的授权集合排除在 SQLite 绑定之外,可避开可移植变量上限,同时保持全局排序。响应仍只有一个有界页面;`hasMore` 会指示 UI 提示用户缩小查询范围,而不是公开分页能力。载体信号会取消已被取代的工作,包括持久化列表枚举、分批受限执行的冷会话元数据 stat,以及每一页提供方搜索。查询服务缺失或索引、查询失败仍作为业务错误处理,不会修改规范会话存储。
宿主网关通过现有的类型化 RPC 栈公开 `session.search`。它根据 `session.list` 使用的同一组可见摘要推导授权集合,向 `ctx.sessionQuery.searchSessions` 请求全局排序后的当前 surface `user/message`、`assistant/message` 和 `steering/message` 匹配项(每页最多 20 个命中),并持续消费提供方分页,直到获得 20 个已授权会话及一个前瞻项,或结果流耗尽。每个命中的会话 id、最佳匹配会话 id、surface 和事件类型都会经过重新校验,其 snippet 才能离开宿主。将可能很大的授权集合排除在 SQLite 绑定之外,可避开可移植变量上限,同时保持全局排序。响应仍只有一个有界页面;`hasMore` 会指示 UI 提示用户缩小查询范围,而不是公开分页能力。宿主最多调用提供方 100 次(检查 2,000 个命中);如果单页命中数超限、续传游标重复,或用尽该预算后结果流仍未耗尽,都会直接返回 `internal` 业务错误,不返回部分结果。载体信号会取消已被取代的工作,包括持久化列表枚举、分批受限执行的冷会话元数据 stat,以及每一页提供方搜索。查询服务缺失或索引、查询失败仍作为业务错误处理,不会修改规范会话存储。
[`WorkspaceBrowser`](../../../../packages/client/ui-workspace/README.md) 有意将元数据搜索与内容搜索保持独立。非空白查询会立即从会话列表中计算不区分大小写的标题和 Workspace 子串匹配,在 250 ms 防抖后发起内容请求,在查询变化时中止前一请求,并忽略陈旧的完成结果。它先按新近程度排列本地匹配,再合并由后端排序且仅匹配内容的结果,按会话 id 去重;无论常规分组模式如何,最终都渲染为扁平列表。每一行显示标题、Workspace,并在存在时显示一行摘要片段。选择某一行只会打开对应会话,并保留查询条件;不会跳转至确切事件。
@@ -20,7 +20,7 @@ Web 与 headless 共用的组合会使用内存数据库挂载 [`@deepseek-ai/ds
## 故障与可见性契约
搜索绝不会扩大会话可见范围:没有可供服务的 cwd 的冷会话会被排除,原因与它们不出现在 `session.list` 中相同;查询只会接收这条基线提供的 id。被遮蔽事件和纯日志事件、消息内容之外的工具事件、错误、待办事项及其他追踪记录都不会产生 UI 命中结果。
搜索绝不会扩大会话可见范围:没有可供服务的 cwd 的冷会话会被排除,原因与它们不出现在 `session.list` 中相同;只有 id 位于这条基线中的提供方命中才能离开宿主。被遮蔽事件和纯日志事件、消息内容之外的工具事件、错误、待办事项及其他追踪记录都不会产生 UI 命中结果。
首个或后续内容请求仍在处理期间,UI 会保留即时元数据匹配结果,并显示历史搜索状态。如果后端失败,这些行会保持不变,并显示警告说明内容搜索不可用。合并后没有任何行时,界面会显示明确的空状态。候选行超过 20 条时,界面会提示用户缩小查询范围。
@@ -35,8 +35,8 @@ Web 与 headless 共用的组合会使用内存数据库挂载 [`@deepseek-ai/ds
无需预先打开,即可检索到历史持久化对话,同时宿主仍只保留一条可见性边界和一套语义索引实现。即时本地结果掩盖了大部分请求延迟,取消机制可防止已作废查询重新渲染列表,后端故障则会降级为内容搜索尚不可用时已有的行为。
首次内容查询可能耗时更长,因为它要承担惰性对齐的开销。搜索质量采用 token/短语召回,而不是模糊召回或任意子串召回,并受上述连续中文限制。结果粒度为会话,最多 20 条,不支持分页,也不能跳转到具体消息。
首次内容查询可能耗时更长,因为它要承担惰性对齐的开销。搜索质量采用 token/短语召回,而不是模糊召回或任意子串召回,并受上述连续中文限制。结果粒度为会话,最多 20 条,不支持分页,也不能跳转到具体消息。如果有效但选择性极差的提供方结果流在前 2,000 个命中内仍未产生足够多的已授权结果,系统会进入仅保留元数据匹配的故障路径,而不是无限制地继续处理。
## 测试
宿主测试将请求校验、可见会话过滤、事件和 surface 过滤、结果边界、取消与故障映射固定为契约。运行时与 UI 测试将无状态委托、防抖/中止/陈旧响应行为、本地回退、合并顺序、去重、行渲染与导航语义固定为契约。无密钥的组装层 Web 测试会播种一段尚未打开的持久化对话,通过惰性 SQLite 索引按消息内容找到它,捕获侧边栏结果,打开该会话,并验证查询条件仍然保留。
宿主测试将请求校验、可见会话过滤、事件和 surface 过滤、结果边界、提供方页数和单页命中数预算、游标与跨页去重行为、后续页取消及故障映射固定为契约。运行时与 UI 测试将无状态委托、防抖/中止/陈旧响应行为、本地回退、合并顺序、去重、行渲染与导航语义固定为契约。无密钥的组装层 Web 测试会播种一段尚未打开的持久化对话,通过惰性 SQLite 索引按消息内容找到它,捕获侧边栏结果,打开该会话,并验证查询条件仍然保留。