# 会话标题整理师(dsh-title-curator) 让 DeepSeek Harness 的会话标题跟着工作主线走:统一成 **「类别 emoji + 对象|目标」**,并且**永远不覆盖你手动改过的标题**。 命名规则移植自 [oil-oil/oil-codex-title](https://github.com/oil-oil/oil-codex-title)(MIT),状态机按 DSH 的会话 API 重写。 ![侧边栏效果](assets/sidebar.png) *侧边栏实拍(默认宽度,超出部分由宿主截断):每行都是「类别 emoji + 对象|目标」。* ## 和现有两个版本的区别 | | oil-codex-title(Codex) | oil-dsh-title(第三方移植) | 本插件 | |---|---|---|---| | 触发 | Stop Hook | 每条用户消息 | 会话事件 + 回合结束去抖 + 启动补齐 | | 模型 | 独立 `codex exec` 子进程 | 复用会话路由 | 复用会话路由 | | 幂等 | 内容指纹 | 无 | 内容指纹(同内容不重复调模型) | | 手动改名保护 | 状态文件 + 锁 | 无 | 事件序号 + 锁 | | 重名冲突 | 同 cwd 作用域检查 | 仅比对标题字符串 | 比对 + 二次确认 + 放弃 | | 纯确认跳过 | 有 | 无 | 有(最多连跳 2 轮) | | 审计 | audit.jsonl + 用量账本 | 无 | audit.jsonl + 用量累计 | | 调用频率 | Hook 触发 | 每条消息一次 | 去抖 + 最小间隔 90s | ## 标题格式 ```text <类别 emoji> <对象>|<持续目标> ``` - 恰好一个全角 `|`,两侧不留空格;半角 `|` 会被规范化。 - 对象在前:模块、内容主题、模型名、具名工具;不以「制作/对比/优化」开头。 - 同一对象的措辞尽量不变;主线没实质变化就保留原标题。 - 语言跟随最近几轮用户消息的主导语言,产品名和代码标识符保留原文。 - 八个固定类别:🎬 内容制作、🧩 工具开发、🔎 对比调研、📝 方法整理、📅 日程安排、🎨 页面设计、⚙️ 环境配置、💬 一般讨论。 示例: | 原标题 | 整理后 | |---|---| | 回应中文问候 | 🧩 邮箱验证码|过期排查 | | 确认注册功能 | 🧩 支付回调|重复发货修复 | | 讨论视频标题 | 🎬 图像模型评测|视频策划 | | 继续修改 | 🎨 登录表单|布局优化 | ## 安装 从 GitHub 安装: ```sh dsh plugin --profile desktop add github:chancecc99-gif/dsh-title-curator ``` 从本地目录安装: ```sh dsh plugin --profile desktop add /绝对路径/dsh-title-curator ``` 或在 Harness 的插件管理页选择本地目录安装,然后重启应用(新包需要重新加载模块)。安装后在新会话里正常聊天,回合结束后几秒内标题会更新。 卸载: ```sh dsh plugin --profile desktop remove dsh-title-curator ``` 已经写入的标题是普通的 `session/title` 事件,卸载后保留,不会回滚。 ## 配置 在 profile 的 `cordis.patch.yml` 里覆盖(插件行 id 为 `title-curator`): ```yaml - id: title-curator config: enabled: true debounceMs: 6000 minIntervalMs: 90000 maxConcurrent: 1 timeoutMs: 45000 maxTokens: 700 sweepDelayMs: 4000 headerDelayMs: 1500 recentMessages: 5 messageChars: 600 ``` | 字段 | 默认 | 说明 | |---|---|---| | `enabled` | `true` | 总开关;关闭后不再评估 | | `debounceMs` | `6000` | 最后一条用户消息后等待多久再评估 | | `minIntervalMs` | `90000` | 同一会话两次评估的最小间隔 | | `maxConcurrent` | `1` | 全局并发命名调用数 | | `timeoutMs` | `45000` | 单次模型调用超时,超时保留原标题 | | `maxTokens` | `700` | 标题输出上限(推理模型会先花 token 思考,留下余量) | | `sweepDelayMs` | `4000` | 启动后多久补齐已打开的会话 | | `headerDelayMs` | `1500` | 首次见到某会话的请求头后多久评估 | | `recentMessages` | `5` | 送入模型的最近用户消息条数(1~8) | | `messageChars` | `600` | 每条消息的字符上限 | | `maxInputChars` | `6000` | 整个输入的字符上限 | ## 行为细节 1. **只改元数据**:调用 `sessionTitle.rename()`,不向会话追加任何消息,不进入模型上下文。 2. **指纹幂等**:策略版本 + cwd + 当前标题 + 最近消息哈希 → 相同指纹直接跳过,不花 token。 3. **去抖与限频**:用户消息触发定时器,`turn/end` 立刻提前评估;同会话 90 秒内不重复调用。 4. **补齐既有会话**:启动 4 秒后对当时已打开的会话各评估一次;之后**点开**任何一个老会话(`session/created`)或它发出第一次请求(`request/header`),都会补一次。 5. **纯确认跳过**:最后几轮只是「好/收到/继续/ok」时不调模型。 6. **手动改名即锁定**:第一次运行前发现标题是 `user` 来源,或写入后标题事件序号被别人改过 → 永久锁定该会话。 7. **重名保护**:候选标题与其它在线会话重复时,带冲突列表重问一次;仍然重复就保持原样,不编造「任务 2」。 8. **写后校验**:写前复核标题与指纹未变,写后回读确认一致。 9. **格式不达标必须改**:模型返回 `keep` 但当前标题不符合「emoji + 对象|目标」时,带明确反馈重问一次;仍不改就记为 `kept-noncompliant`,不写指纹,下一轮继续尝试。 10. **失败静默**:模型超时、输出非法、进程错误都只记录审计,保留原标题。 ## 数据与隐私 数据目录:`$DSH_HOME/plugin-data/dsh-title-curator/`(默认 `~/.dsh/...`),可用环境变量 `DSH_TITLE_CURATOR_DATA` 覆盖。 - `state.json`:每个会话的 `lastSeq`、指纹、锁定原因、重命名次数、token 累计。 - `audit.jsonl`:每次评估的时间、状态、决策、耗时、用量。 送给模型的内容只有:当前标题、cwd 末级目录名、其它会话标题(最多 16 条)、最近 5 条**用户**消息(每条截断 600 字符)。不发助手回答,不发文件路径,不发工具输出。 ## 已验证 2026-10-28 在 DSH 0.2.1-rc.3(macOS 桌面端)实测,`audit.jsonl` 记录: | 会话 | 结果 | |---|---| | 一个历史对话(后台未打开) | `renamed` → `🧩 Codex桌面端|配置浏览器与审核排错` | | 当前对话 | `renamed` → `🧩 oil-codex-title|开发多Agent话题命名版` | | 打开即命名的历史会话 | `renamed` → `🎬 评测视频|标题重规划为三集` | 单次调用约 0.9k token(含缓存命中),耗时 1.1~2.1 秒。 ## 已知限制 - 只改标题字符串,不做真正的分类目录:宿主只提供标题写入接口。 - 首次调用 `rename()` 后,DSH 内置标题提供方不会再自动改这个会话(`user` 来源会钉住标题),这是预期行为:此后由本插件负责。 - 评估时扫描一次会话事件读取消息;超长会话(数千事件)会有可感知开销。 - 未被打开的会话不会被改名:DSH 只允许对**在内存中活跃**的会话写标题,离线会话的日志没有公开写入口。打开它(或发一条消息)即可触发。 - 子代理会话(`header.parentSession` 存在)不会被命名。 - 插件每完成一次命名都会把标题锁定状态写进 sidecar;删除 sidecar 会退化为「不碰任何 `user` 来源标题」,这是安全的降级。 ## 开发 ```sh npm test ``` 22 个用例覆盖:格式校验、规范化、JSON 解析、流式组装、确认过滤、指纹稳定性、决策表、端到端改名、手动锁、非法输出、重名放弃、截断重试、推理档位、启动补齐、老会话首次请求。 ## 发布到 npm 之前 包里已经带 `dsh.bundle.patch`,`dsh plugin add` 支持 GitHub 源,所以不发布 npm 也能用。若要发布,先确认名字可用(`dsh-session-title` 已被占用),再执行 `npm publish`。 ## 许可 MIT,见 [LICENSE](LICENSE)。命名规则与原始 Codex 实现来自 [oil-oil/oil-codex-title](https://github.com/oil-oil/oil-codex-title)(MIT),版权归原作者。