dsh-session-title
ly6170/dsh-session-title
A DeepSeek Harness plugin that gives the model a tool to set the session title using rules you define. DeepSeek Harness 插件:给模型一个按你自己定义的规则设置会话标题的工具。
설치
dsh plugin --profile web add github:ly6170/dsh-session-titleREADME
dsh-session-title
简体中文 | English
DeepSeek Harness 插件:给模型一个按你自己定义的规则设置会话标题的工具。
装上后,模型可以主动把当前会话标题改成你要的格式(比如 0924 | v0.1.0 | 修复登录跳转),并钉住它——之后不会再被自动生成的标题覆盖。
默认是关闭的。 插件会往模型的工具目录里增加一个工具,属于对模型上下文的改动,所以装上不会立刻生效。安装后在 设置 → 会话标题 里勾选「启用」并保存即可(详见安装)。
安装
dsh plugin --profile web add dsh-session-title
装完重启 dsh 进程——bundle 集合变化不会热加载。
然后:
- 打开 Web 界面 设置 → 会话标题;
- 按需修改「标题正则」(不会写就让 LLM 帮你写);
- 勾选「启用」并保存。
想省掉第 3 步、装上即用,就在 profile 的 config: 里写 enabled: true(见用配置文件预设)。
手动安装
如果 dsh 不在 PATH,或用的是 DSH Desktop / 源码检出,也可以直接编辑 profile 的 ~/.dsh/profiles/web/package.json:
{
"dsh": { "profile": { "bundles": [ /* …, */ "dsh-session-title" ] } },
"dependencies": { "dsh-session-title": "^0.1.0" }
}
然后在 profile 目录执行 pnpm install(或 npm install)、重启。
不需要任何构建步骤:client 侧产物 lib/client.js 已随包提供。
它解决什么
DSH 本体把会话重命名做成了 Host 能力(ctx.sessionTitle 服务 / session/rename Remote),但面向模型的工具目录里没有改名工具,所以过去只能由人在侧栏手动改。本插件补上这一个工具:set_session_title。
命名规则不写死在代码里,由你在设置里配置,因此可以适配任意仓库、任意团队的约定。
行为
| 项 | 说明 |
|---|---|
| 工具名 | set_session_title,参数 title(完整标题) |
| 生效 | 调 ctx.sessionTitle.rename(agent.session, title);校验通过即写入并钉住标题 |
| 副作用 | 钉住后该会话不再自动生成标题 |
| 不做的 | 不读写会话文件、不碰工作区、不做归档/删除 |
不合规的标题会作为工具错误返回明确原因,模型据此改写后重试。
配置规则
配置有两条途径:设置页(推荐,改完即时生效)或 profile 的 config:。两者写的是同一份配置,都无需重启。
| 配置项 | 类型 | 默认 | 说明 |
|---|---|---|---|
enabled | boolean | false | 是否启用;关闭后模型看不到这个工具(运行中切换即时生效) |
pattern | string | ^(\d{4}) | ([^|]+) | ([^|]+)$ | 标题必须匹配的正则(JS 语法,不带动界符) |
patternMode | match / search / none | match | match 全串匹配;search 允许包含;none 关闭正则校验 |
patternHint | string | "" | 正则不匹配时给模型看的提示,留空用默认提示 |
maxTitleBytes | number | 80 | 整标题的 UTF-8 字节上限 |
forbidTrailingPunctuation | boolean | true | 禁止以句末标点结尾 |
forbidControlCharacters | boolean | true | 禁止换行、制表符等控制字符 |
normalizeSeparatorSpacing | boolean | true | 把 | 前后的空白规范化为单空格 |
caseInsensitive | boolean | false | 正则忽略大小写 |
默认规则是 MMDD | 版本或 tag | 描述,只是个开箱即用的示例——改成你自己的约定即可。
未启用时设置页会明确提示「当前未启用:模型看不到 set_session_title 工具」,不会让人误以为插件坏了。正则字段始终会预填当前值,所以你可以先把规则调好、再打开开关。
改动规则后,工具说明会自动重写:模型看到的描述里会带上当前生效的正则和字节上限,因此它不必盲猜格式。
不会写正则?让 LLM 帮你写
设置页说明文字下方有个折叠块「示例提示词:让 LLM 帮你写一条正则」,点开是一段可直接复制的提示词,末尾留了「我的命名需求」让你填。复制后发给任意 LLM,把返回的正则粘进「标题正则」字段即可。
提示词里刻意写死了几条输出约束(不带 / 定界符、不带 flags、不要代码块、不要解释、用 ^/$ 锚定整串、段内不含竖线用 [^|]+),否则模型经常返回没法直接粘贴的东西。它也明确说了「句末标点、字节长度、换行不用你管」——那些是本插件另外的开关,不需要塞进正则。
完整提示词(与插件内一致):
我在用 DeepSeek Harness 的 dsh-session-title 插件管理会话标题,需要你帮我写一条校验标题的正则。
请只输出正则本身:一行、不带 / 定界符、不带 flags、不要代码块、不要解释。并且:
1. 用 ^ 和 $ 锚定整串;
2. 需要单独校验的分段用括号 () 捕获;
3. 某段不允许出现竖线 | 时,用 [^|]+ 表示(| 是分段分隔符);
4. 不要处理句末标点、字节长度、换行 —— 这些插件会另外校验。
我的命名需求:<在这里描述你的规则,例如「以 BUG- 或 FEAT- 开头,接一个数字,然后一个空格加简短中文描述」>
参考:插件当前的默认正则是 ^(\d{4}) \| ([^|]+) \| ([^|]+)$ ,含义是「四位日期 | 版本或 tag | 描述」。
用配置文件预设
想跳过设置页、直接预设初值,就编辑 profile 的 cordis.patch.yml(这是 schema 默认值的覆盖):
- insert:
- id: session-title-tool
name: dsh-session-title
config:
enabled: true
# 用 [BUG]/[FEAT] 前缀
pattern: '^\[(BUG|FEAT)\] .+$'
maxTitleBytes: 60
config:
enabled: true
# 完全不校验格式,只保留字节上限与标点规则
patternMode: none
maxTitleBytes: 120
关于 maxTitleBytes
宿主 session-title 服务的 maxTitleBytes 是硬上限:rename() 会按它做归一化和截断。本插件的 maxTitleBytes 是前置校验,应设置为小于或等于宿主配值,否则模型会拿到一个通过了本插件校验、却被宿主截断的标题。
base 组合里宿主配值是 80(packages/bundle/base/cordis.patch.yml),所以这里默认也取 80。
设置页
插件由两部分组成,设置页注册为 Web 设置面板里的一个独立分区:设置 → 会话标题。
| 侧 | 入口 | 作用 |
|---|---|---|
| host 侧 | index.js | 注册 set_session_title 工具、校验规则、接入 settings 命名空间 |
| client 侧 | lib/client.js | 注册 settings.section 分区,渲染规则表单 |
工作机制:
- 命名空间就是插件在 profile 里的条目 id,即
session-title-tool(见cordis.patch.yml)。client 侧读不到 host 侧的条目 id,所以这个字符串在lib/client.js里也写了一份——改名要同时改两处(有测试锁住这条)。 - 读写通道:
ctx.configForms(ui-settings 提供的 client 服务,背后是 settings Remote),与官方ui-settings-subagent等设置页同款,不需要自建 HTTP 路由。 - 门控:
configForms.whileServed([ns], …),宿主确实提供该命名空间时才注册分区,避免出现空页面。 - 即时生效:写入的是同一份配置引用,无需重启;工具说明也会同步刷新。
client 侧是手写 bundle(零构建)
lib/client.js 是手写的 CJS bundle,刻意不引入 TypeScript / tsdown / JSX 构建链:
- 产物格式与 DSH 内部
packages/client/tsdown.client.ts的 banner/footer 一致:window.__ModuleLoader__.load({ id, factory }); - 运行期只
require('react')(来自平台模块表packages/client/web/src/platform.ts),组件用React.createElement、样式用内联对象; - 所以本地
link:安装时改完即生效,不需要 build。
package.json 里的声明是加载器的发现入口,任何一处不对都会表现为「设置页静默不出现」:
"exports": { "./client": "./lib/client.js" },
"dsh": { "client": { "platform": "web", "inject": [ /* … */ ] } }
卸载
dsh plugin --profile web remove dsh-session-title
或从 profile 的 dependencies 与 dsh.profile.bundles 里移除,重装依赖后重启。已经钉住的标题不受影响。
自测
npm test
host 侧(test/smoke.test.mjs):Config 字段与 volatile 标记、可配置规则(正则/上限/开关)的行为、非法正则的加载期报错、注册形态契约、execute 成功与失败路径、出厂默认关闭、enabled 运行时增删工具、工具说明随规则刷新、插件卸载时注销工具、最小宿主兼容性。
client 侧(test/client.test.mjs):bundle 形态符合 __ModuleLoader__ 契约、dsh.client 声明满足加载器扫描规则、分区注册到正确的命名空间、组件用真实 React 渲染出全部字段、示例提示词与「未启用」提示确实渲染、以及真实 DOM 交互(改字段 → 保存 → 断言写入收到正确的字段与类型)。
几条值得注意的回归测试:
- 用
output.schema校验execute的返回值——这正是不依赖 Harness 包的测试最容易漏、却唯一能捕获「返回值与输出契约不符」的地方; - client 侧表单字段名与 host 侧
Config字段逐一对应——错一个就会被 host 侧校验拒绝; enabled的 schema 默认值与resolveRule()的兜底值必须一致,否则「默认关闭」会有两种答案;- 有一项用 DSH 检出里真实的
volatileForm投影验证设置表单确实能生成;找不到检出时自动跳过(DSH_CHECKOUT可指定路径),因此单独克隆本仓库也能跑通。
实现备注
host 侧(index.js)
- 运行期只依赖三个宿主接口:
ctx.tools.register、ctx.sessionTitle.rename、ctx.on('loader/volatile-update')。唯一依赖是声明 Config 所必需的@deepseek-ai/schemastery。 parameters使用原始 JSON Schema,与 DSH 内置的 MCP 客户端桥接做法一致:ctx.tools.register()只校验output.schema,parameters原样投影给模型。output.schema是{ type: 'string' },execute返回标题字符串(从rename()的 snapshot 上取.title)。注册表会用该 schema 校验返回值,返回 snapshot 对象会直接抛ToolOutputError——副作用已经写入却报失败。- 注册返回的注销函数必须自己挂到
ctx.effect上:ctx.tools.register()内部用的是 ToolRuntime 自己的 context,插件卸载时不会自动清掉本次注册。 - 规则读取走
Volatile.get();enabled和pattern的改动经loader/volatile-update触发同步重注册,所以对运行中的实例立即生效,工具说明也始终反映当前规则。
client 侧(lib/client.js)
- 手写 CJS bundle,零构建;只
require('react'),组件用React.createElement、样式用内联对象。 - 用
useSyncExternalStore订阅ctx.configForms的ConfigForm,并传第三个参数getServerSnapshot,让组件在 SSR / 预渲染路径下同样成立。 - 本地编辑层叠加在 host 侧值之上(
{...hostValue, ...edits}),所以别处的写入不会覆盖用户正在改的内容;保存时逐字段form.set(),由 host 侧的 Config 整份校验把关。