dsh-session-search-pro:在当前 DSH 会话里搜回历史与进行中的会话

前言

用 DeepSeek Harness 久了,一段对话结束后,想找回「上周那个关于 session search 的结论是怎么得出来的」,往往只能靠记忆硬翻。原生做法是手工翻 ~/.dsh 下的会话文件,再用命令行工具全文捞。本次介绍的插件换条路:直接复用 harness 运行时自带的 sessionQuery 服务,给 agent 挂上搜索、列表、读取三个工具,让模型在自己的会话里就能顺着记忆找内容。

dsh-session-search-pro 是一个 DSH 插件,归属「记忆」分类,由 YaoleWu 维护,许可证是 MIT。下面介绍它的能力边界、安装方式和实际用法。

是什么

它给 agent 提供三个工具:agent_session_search、agent_session_list、agent_session_read,底层全部走 harness 的 sessionQuery 服务,插件自身不做文本解析,不建数据库,也没有运行时依赖(没有 ripgrep,没有 zstd 解析)。它读的是运行时本来就索引好的数据,对会话文件只读不写。

和同生态的 dsh-session-search 相比,两者取舍不同:前者直接读会话文件、自己解压扫描 zstd 帧,因此能覆盖 Codex、Claude Code、PI、OpenCode 等其他运行时的会话;本插件只走 harness API,代价是只能搜 DSH 自己的会话,换来的是能搜到正在进行中的当前会话、不依赖文件格式假设、也不依赖外部二进制。

三个工具

分节之前先说一句:工具是插件注册给 agent 的,实际调用通常由模型发起,你直接用自然语言问就行。

跨全部 DSH 会话做全文搜索,每条结果带匹配度最高的片段。参数:

  • query(必填,string):要搜索的文本。不区分大小写、空白宽松,按字面匹配,正则元字符没有特殊含义。
  • limit(number):最多返回的会话数,1–50,默认取插件配置 maxResults(未覆盖时为 10)。
  • maxScan(number):退回扫描路径时最多打开多少个会话,1–500,默认取插件配置 maxScan(200)。走索引时忽略。

返回值带 engine: "index" | "scan",据此可知这次是哪条路径给出的结果;扫描路径还会带 scanned 和 truncated。

agent_session_list

列出会话,进行中的和已结束的都算,可选按工作目录过滤并排序。

  • limit(number):最多返回的会话数,1–100,默认 20。
  • cwd(string):按会话工作目录做子串过滤。
  • sort("newest" | "oldest"):排序方式,默认 newest。

agent_session_read

按 id 读取单个会话:标题、元数据,以及按顺序排列的事件。

  • sessionId(必填,string):要读取的会话 id,例如 "a4d75296-fc89-44b1"。
  • maxEvents(number):最多返回多少个事件(取最近的若干条),1–200,默认 50。

安装与启用

这个包目前还没有发布到 npm,所以直接从 GitHub 安装,写进 profile 的 package.json:

// ~/.dsh/profiles/<profile>/package.json
{
  "dependencies": {
    "dsh-session-search-pro": "github:LeslieWylie/dsh-session-search-pro"
  },
  "dsh": {
    "profile": {
      "bundles": ["dsh-session-search-pro"]
    }
  }
}

然后重新安装依赖并重启 profile:

cd ~/.dsh/profiles/<profile> && pnpm install
dsh --profile <profile>

想固定版本而不是跟随默认分支,可以用 github:LeslieWylie/dsh-session-search-pro#v0.1.0。

这里有一个必须说明的来源改动:上面的安装命令,以及下文涉及的包内路径,都是插件 README 原文里的写法(README 中的署名是 LeslieWylie)。现在这个仓库的实际归属是 YaoleWu——GitHub 上把 LeslieWylie/dsh-session-search-pro 重定向到 YaoleWu/dsh-session-search-pro,package.json 的 repository 字段也仍写着 LeslieWylie。package.json 的版本为 0.2.0,仓库当前有 5 stars、1 fork。按 README 安装即可,但如果在意不跟随默认分支,建议自行确认包的来源与版本。

不想改 profile 的话,可以先临时挂载一次。这个包自带 cordis.patch.yml,只要它已经装进 profile 的 node_modules,就能用启动器的 --patch 参数挂载:

cd ~/.dsh/profiles/<profile> && pnpm add github:LeslieWylie/dsh-session-search-pro
dsh --profile <profile> --patch ./node_modules/dsh-session-search-pro/cordis.patch.yml

包对运行环境的约束写在 engines 里:Node ^22.19.0 || >=24.0.0;对等依赖是 @deepseek-ai/cordis ^4.0.1 和 @deepseek-ai/dsh-tools ^0.1.0-rc.6。

典型用法

插件被打包进 profile 之后,agent 会自动拿到这三个工具,直接口语化提问即可,例如:

“帮我搜一下之前关于 session search 的会话”
“列出我在 ~/Desktop 下的最近会话”
“读一下 a4d75296-fc89-44b1 这个会话”

模型会自己去调 agent_session_search、agent_session_list 或 agent_session_read。

默认 profile 上的搜索路径

这一步值得单独讲,因为它是这套插件最容易踩空的地方。agent_session_search 有两套引擎,调用时自动选择:

  • 索引:dsh-base bundle 里的 @deepseek-ai/dsh-session-query-sqlite 提供基于 SQLite FTS5 的 searchSessions(),可用时插件走它,返回 engine: "index"。
  • 有界扫描:索引不可用时,退回用 listSessions() + filterEvents() 按最新优先逐个会话扫描,返回 engine: "scan"。

问题在于索引默认是关的。dsh-base 这样接线:

- id: session-query-sqlite
  name: '@deepseek-ai/dsh-session-query-sqlite'
  config:
    path: ':memory:'
    openAt: never

引擎自身的守卫在 openAt 为 never 时会抛 SESSION_QUERY_SEARCH_DISABLED,内容搜索是选择性开启的:部署方需要在后续 patch 层把 openAt 覆盖成 first-search 或 startup,通常还要配一个持久化的 path。所以在默认 profile 上,索引调用一定失败。

本插件只捕获「索引不可用」这一类错误并退回扫描;如果失败是后端真的坏了,它就如实报错——用更慢的扫描去回答一个已坏掉的存储没有意义。

README 给出了一份本机 24 会话语料的实测对比:能命中的词,索引和扫描都是 3 条结果;命中不到的词,索引 17 毫秒,扫描 3,042 毫秒。索引值得开,但它不该是唯一路径。插件在 0.1.0 及更早版本里正是把它当成唯一路径,结果在每个默认安装上都对任何查询返回 {"error": "session search is disabled…"},且没有抛异常,看上去像一个正常但永远搜不到东西的工具。

要打开索引,在 profile 的 cordis.patch.yml 里覆盖那一行 bundle:

- update:
    id: session-query-sqlite
    config:
      path: '~/.dsh/session-index.db'
      openAt: first-search

插件自身的配置项只有两个,写在 cordis.patch.yml 的 bundle 行里:

键 默认值 说明
maxResults 10 调用方不传 limit 时,agent_session_search 使用的默认值。
maxScan 200 退回扫描时最多打开多少个会话。

适用场景与注意

适合这样的场景:只跑 DSH 一个运行时,希望 agent 在当前会话里就能检索自己过去和正在写入的会话内容,并且不想额外维护索引文件格式或外部命令行工具。

需要注意的几点:

  • 插件以当前 dsh 进程的权限运行,能读到 profile 里能访问的会话数据。安装前应检查 lib/ 下的源码与 MIT 许可证,不要看到 bundle 名就直接装。
  • 仅支持 DSH,不搜索 Codex、Claude Code、PI 或 OpenCode 的会话。跨多运行时的检索需求应选 dsh-session-search。
  • 依赖 sessionQuery:三个工具都依赖它,没有降级模式。如果该服务未被注入,插件会记一条警告日志然后不注册任何工具,而不是注册一批一调用就报错的工具。
  • 扫描退路是一个会话一个会话打开的,filterEvents() 要读完整个会话日志,一次未命中的搜索会走遍语料;maxScan 用来兜底,truncated: true 表示触到了上限。语料大就把索引打开。
  • agent_session_read 的事件读取不支持取消:底层 filterEvents() 不接受 abort signal,调用被中止时读取本身仍会跑完,只是结果被丢弃。

纯 JavaScript 实现,没有构建步骤,源码和发布用同一个文件 lib/index.js。测试有两套:tests/tools.test.mjs 针对一个打桩的 sessionQuery 驱动 apply();tests/boot.test.mjs 启动真实 cordis Context、加载 harness 的会话服务、按 profile 方式装载本包,并经过真实工具注册表执行这些工具。第二套存在的理由正是 0.1.0 当初坏掉的方式——单元测试的桩自己定义了一个 searchSessions,测试全绿,但真实后端抛出的 SESSION_QUERY_SEARCH_DISABLED 却被当成了每次查询的答案。在 profile 的 node_modules 里跑:

cd ~/.dsh/profiles/<profile>/node_modules/dsh-session-search-pro && node tests/boot.test.mjs

结尾

dsh-session-search-pro 做的事情很窄:把 sessionQuery 的检索能力包成 agent 可用的三个工具,让模型在自己的会话里就能找历史和进行中的内容。它的价值在于零运行时依赖、会话文件只读不碰、索引路径与扫描退路并存,从而在默认 profile 上也能给出结果。社区目录页在 https://www.skillhub.cn/plugins/YaoleWu/dsh-session-search-pro ,仓库在 https://github.com/YaoleWu/dsh-session-search-pro ;目录是独立站点,与 DeepSeek 无官方从属关系,使用时自行核对来源。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜