dsh-session-titler
手动触发的「先确认再改名」标题生成器:顶栏按钮与会话行菜单各一个入口,让模型总结整段会话给出标题,在可编辑弹窗里确认后才重命名会话;不注册标题 provider,不影响自带的自动标题。
安装
dsh plugin --profile web add dsh-session-titler
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
dsh-session-titler
官网 / Website: <https://dsh.zlmix.com/session-titler/>
Summarize a whole DeepSeek Harness session into one title on demand — confirm it before it is applied. A Host + Web Client plugin: its only entries are a header button and a session-row menu item, and nothing is written until you press Confirm. It registers no title provider, so automatic titling keeps whatever else you have installed.
把整个会话总结成一条新标题,确认后才写入。DeepSeek Harness 的 Host + Web Client 双半插件。
Quick start (EN) — Desktop app: install from the in-app Plugins page (thedesktopprofile is app-managed, sodsh plugin --profile desktop …refuses). CLI-managed profiles:dsh plugin --profile <profile> add dsh-session-titler. Then hit ✦ Generate title in the session header or Generate title in a session row's “…” menu, edit the proposal, and press Apply — nothing is written before that.
快速开始
1. 安装
桌面版(DeepSeek Harness.app) —— desktop profile 由应用独占,命令行不接管它
(dsh plugin --profile desktop … 会直接报 profile "desktop" is managed exclusively by the Electron application)。
请在应用内左侧「插件」页安装;插件市场收录后,也可以直接在里面搜 titler 一键装(收录状态见 PR,通常一天内生效)。
命令行管理的 profile(web / main / 自建 profile 等)—— dsh plugin 是 pnpm 透传,一条命令即可:
# 从 npm 安装(推荐) dsh plugin --profile <你的profile> add dsh-session-titler # 从 GitHub 源码安装(想跟 main 分支时) dsh plugin --profile <你的profile> add github:ZilongYang/dsh-session-titler#main # 本地开发:把本仓库直接挂进去 dsh plugin --profile <你的profile> add link:/绝对路径/dsh-session-titler
2. 使用
1. 打开任意一个已经有内容的会话;
2. 点标题栏里的 ✦ 生成标题 —— 它在「创造模式」右边、「费用 ¥0」左边;
或点左侧会话行 「...」菜单 → 生成标题(0.2.1 及更早紧接「重命名」下一行;DSH 0.2.2 起在「复制会话 ID」之后、「归档」之前);
3. 等 2–4 秒,弹窗给出标题提案,可以直接改;
4. 点 确认修改 —— 左侧标题立即更新,刷新页面也还在。
点「取消 / Esc / 点遮罩」都不会写任何数据;空白会话没有可总结的内容,按钮会置灰。
3. 卸载
dsh plugin --profile <你的profile> remove dsh-session-titler
桌面版在应用内「插件」页停用或卸载。
装完没看到按钮?Host 半是进程内模块,重启一次 dsh/应用再刷新页面即可(Client 半刷新页面就生效)。
入口在哪(两处,都在现有 Web UI 上,不新增页面)
入口 · 位置
顶栏按钮「✦ 生成标题」 · 会话标题栏里,conversation.session.header.actions 的 order: -8 —— 紧跟「创造模式」芯片 agent-preset(-10)右侧,排在费用芯片 cost-meter(-5)之前,从而让 费用 ¥0 与 费用明细(1)保持相邻
会话行菜单项「生成标题」 · 会话行「...」菜单 sidebar.workspaces.session.menu.item 的 order: 250。DSH ≤ 0.2.1:内置动作也是这个 Slot 的条目,本项在「重命名」(200)下面一行、「分叉会话」(300)上面;DSH 0.2.2 起:内置动作迁到 ctx.contextMenus,本 Slot 成为 legacy 通道,整块排在「复制会话 ID」(450)之后、「归档」(600,afterContent)之前
点击后:生成 → 居中确认弹窗(标题可编辑,另有「重新生成 / 取消」)→ 点「确认修改」才真正改标题。取消、Esc、点遮罩都不会写任何数据。
它是怎么工作的
顶栏按钮 / 会话菜单项
│ store.open({ sessionId, currentTitle })
▼
Client store ──POST /session-titler/propose { sessionId }──► Host 路由(只读)
▲ ├─ ctx.sessionQuery.readSurface(sessionId)
│ │ (当前「模型表面」,已考虑压缩/替换)
│ ├─ 组装 transcript(32 KiB 预算,条目级截断)
│ ├─ 路由候选(新→旧):活会话 requestHeader().config
│ │ → 日志每条 request/header → agentDefaultModel 默认模型
│ │ 每个候选都要过 listProviders() 与 resolveModelInfo()
│ │ (探测不可用就不过滤,绝不把能成功的调用弄坏)
│ │ 强制推理的端点:被拒一次后带档位重试 + 放宽输出预算
│ └─ ctx.llm.stream({ …, purpose:'session-title' })
│ { ok:true, value:{ title, provider, model, fallback, messages, truncated } }
▼
确认弹窗(可编辑)
│ 点「确认修改」
▼
ctx.sessions.using(id, { source:'workspaceOperation' }, ref =>
ref.binding.session.rename(title)) ← 与官方「重命名」菜单同一条写入路径
▼
Host 追加 session/title 事件(source: user)→ 列表标题刷新、刷新页面仍保持
关键取舍:不注册 ctx.sessionTitle 的 provider。那是独占的,会顶掉 DSH 自带的自动标题 provider,而且 automatic 只有 first-prompt / all-prompts,没有「只在用户点击时才生成」的档位。所以本插件走自己的显式调用,并保证生成阶段完全不写会话日志。
目录
文件 · 作用
package.json · bundle 补丁声明 + dsh.client(web 平台、客户端模块依赖)
cordis.patch.yml · 往 profile 插一行 host 插件(id: session-titler)
index.js · Host 半:注册 /session-titler/propose(幂等挂载:重复挂载时由最新一次接管路由),做请求围栏、体积上限、错误映射
title.js · Host 半:读会话 → 选模型路由(每个候选都校验已注册 adapter 且当前 profile 仍配置该模型)→ 组装/裁剪 transcript → 经 streamTitle() 调模型(优先 ctx.llm.stream,兜底 ctx.llm.prepareCall)→ 标题归一化
test/title.test.js · 路由解析、失败映射与调用降级的单元测试(npm test = node --test,不进 npm 包)
test/host.test.js · 挂载幂等性单元测试:重复挂载不抛、最新 handler 生效、旧 fiber 不误删新路由(同样不进 npm 包)
fence.js · Host 半:loopback / 同源请求校验(DNS-rebinding、CSRF 防护,不是鉴权)
client.js · Client 半:手写模块产物,注册 3 个座位 + 状态 store + 确认弹窗
locale/{zh,en}.json · 插件卡片显示用的 meta.title / meta.description
HTTP 契约
POST /session-titler/propose,请求体 ≤ 8 KiB:{ "sessionId": "session-…" }
成功:
{ "ok": true, "value": { "title": "…", "provider": "…", "model": "…", "fallback": false, "messages": 14, "truncated": false } }
fallback: true 表示会话自己记录的路由在当前 profile 里已经不能派发——该 provider 没有已注册 adapter,或者 provider 还在但那个模型 id 已被删除/改名——本次是回退到日志里更早的可用路由或 llm-pi-ai/agentDefaultModel 的默认模型生成的;弹窗会显示「使用模型:X(…已回退)」。
失败:{ "ok": false, "error": { "code": "…", "message": "…", … } }(no-adapter / no-model 额外带 route)
code · HTTP · 触发
forbidden · 403 · 围栏未通过(非 loopback/可信 Host、跨站标记、异源 Origin)
method-error · 405 · 非 POST
bad-request · 400 · JSON 坏 / sessionId 缺失
too-large · 413 · 请求体超限
not-found · 404 · 会话不存在或读不出来
no-content · 409 · 表面里没有 user / assistant 文本
timeout · 504 · 30 s 未回
no-adapter · 502 · 会话记录的路由 + 默认模型都没有已注册的 adapter(响应带 route,弹窗给中文可操作提示)
no-model · 502 · 会话记录的 provider 还在、但那个模型 id 在当前 profile 里已不存在,默认模型也不可用(响应带 route)
`l
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:7,最后更新 2026-11-05