前言¶
用 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 的,实际调用通常由模型发起,你直接用自然语言问就行。
agent_session_search¶
跨全部 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-basebundle 里的@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 无官方从属关系,使用时自行核对来源。