dsh-carryover:DeepSeek Harness 会话交接插件

前言

DeepSeek Harness(dsh)的上下文会满。满了之后常见的做法是「开个新会话」,于是上一轮聊定的东西、还没做完的事、对方提过的细节全没了。如果不清会话,每次都在越来越长的上下文里烧钱;如果清了,万一摘要失败没人发现,话就丢了。

这个插件把这件事变成一个有先后顺序的流程,并且把顺序当成硬规矩:先摘要,后清理。摘要不成功,一条都不许删。

这是什么

dsh-carryover 是一个给 DeepSeek Harness(dsh)用的会话交接插件。

一句话:开新会话之前先把上一段聊过的话写成笔记;笔记真的落到盘上了才允许删掉旧会话轨迹。

属性 说明
作者 JackZo400
类别 记忆
许可证 MIT
仓库 github.com/JackZo400/dsh-carryover

安装

使用以下命令安装:

dsh plugin --profile web add github:JackZo400/dsh-carryover

安装后需确认三件事:
1. sessionsRoot 指向正确的轨迹目录(默认 ~/.dsh/sessions)。
2. summary.apiKey 有值(建议使用环境变量 DSH_CARRYOVER_API_KEY)。
3. 维护好映射文件(session-map.json),插件靠它知道谁该被交接。

配置

在插件配置中启用并设置参数:

- insert:
    - id: carryover
      name: dsh-carryover
      config:
        enabled: true

        sessionsRoot: ~/.dsh/sessions
        transcript: auto           # 自动挑选版本最大的轨迹文件
        workspace: .                # 笔记存储目录(默认 memory/carryover)
        inject: true                # 新会话自动带上笔记
        injectOrder: 40             # 注入顺序
        maxInjectChars: 4000

        summary:
          baseUrl: https://api.deepseek.com
          apiKey: ''
          model: deepseek-chat
          maxTokens: 900

映射文件(session-map.json):
这是插件工作的核心契约,默认路径为 <workspace>/tmp/session-map.json。它定义了通道与会话 ID 的对应关系:

{
  "group:demo": { "sessionId": "3f2a...c1", "carried": "1a9b...7e" },
  "c2c:alice":  { "sessionId": "88d0...4b" }
}
  • sessionId:当前通道的会话 ID。
  • carried:已经交接过的会话 ID。
  • key 可以自定义,但必须用来给笔记文件命名。

工作原理

交接流程(开新会话前)

  1. 从映射文件中查出当前通道的上一个会话 ID。
  2. 打开轨迹目录,解压 session.v*.jsonl.zstd,剔除系统注入内容,只保留正文。
  3. 将正文喂给摘要模型,生成便条。
  4. 将便条追加写入笔记文件,并读回来核对(会话 ID 和摘要内容是否都在)。
  5. 核对通过 → 删除旧轨迹目录;核对失败 → 保留旧轨迹,记录 KEEP 日志。

接上流程(新会话开始)

新会话组装 system prompt 时,插件反查当前会话属于哪条通道,将对应的笔记挂进上下文。全文保留在磁盘上,注入时只带最近的一段。

用法

1. 宿主插件调用

在开新会话之前调用:

const carryover = ctx.get('carryover')
const r = await carryover.carryOver({ key: 'group:demo' })

if (r.status === 'kept') {
  log('没交接成,旧轨迹留着:', r.reason)
}

status 状态包括:
- none:无可交接内容。
- done:交接成功,旧轨迹已删。
- kept:交接失败,旧轨迹未删。

2. Agent 工具调用

Agent 可以直接调用三个工具:
- carryover_status:查看交接状态和笔记位置。
- carryover_note:读取笔记内容。
- carryover_run:执行交接。mode=peek 只预测结果,mode=dry 真摘要但不写不删。

3. 读取笔记

其他插件或脚本读取笔记:

const note = ctx.get('carryover').readNote('group:demo')

4. 独立使用

插件不依赖 dsh 环境,可以在 Node.js 项目中独立调用:

import { createCarryover } from 'dsh-carryover/carryover'

const engine = createCarryover({
  workspace: '/srv/app',
  sessionsRoot: '/srv/app/.dsh-sessions',
  summarize: async (dialogue) => myOwnSummarizer(dialogue),
})

await engine.carryOver({ key: 'group:demo' })

注意与限制

  • 依赖 zstd 命令行:轨迹解压依赖 zstd,因为 node:zlib 只能解压第一帧。
  • 不自己找会话:插件不自动猜测哪个会话是上一个,必须依赖映射文件维护。
  • 不加密:笔记以明文 markdown 存储在工作区,敏感内容需自行注意。
  • 轨迹版本:轨迹文件名带版本号(如 session.v4.jsonl.zstd),默认自动挑选最新版本,认不出文件时不会删除。
  • 护栏机制:路径检查严格,sessionsRoot 之外的路径、包含 .. 的路径均不允许删除,只记账。
羽毛球分组比赛记分
小程序二维码

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

小夜