dsh-handoff:在对话里把当前会话交接到另一个工作区

前言

用 DSH 做长任务时,常会遇到「换个工作区继续」。手工搬通常丢三件事:历史、结论、下一步。复制文件带不走对话;新开一个空会话等于让模型从零开始;把整段记录粘进输入框既超长又失真。

dsh-handoff 是给 DeepSeek Harness(DSH)的会话交接插件,由 GarinZ 维护,属于社区目录里的工作流分类。它让 Agent 在对话里直接调用 handoff_session 工具,把当前会话交接到另一个工作区的新会话,并附上一份按固定骨架写好的交接简报。源会话默认保持不变。

核心能力

插件做了三件事,用的都是 DSH 自身能力:

  1. fork — sessionController.fork() 把当前会话的事件前缀复制成独立新会话(不写 parentSession);
  2. relocate — 把新会话的存储迁到目标工作区:重写日志头行的 cwd、重绑活动写句柄、切换工作区账本,全程不重启;
  3. brief — 把交接简报追加成新会话的第一条消息,简报按固定六节骨架写。

交接有两种历史策略:

模式 history 新会话里有什么 什么时候用
全文复制(默认) full 完整事件历史 + 交接简报 需要精确上下文,且源会话还健康
仅摘要 summary 只有交接简报 源会话已经很大、上下文快撑爆,或用了不支持主动压缩的插件

需要「仅摘要」的原因是:fork 是事实复制,不是压缩。源会话若已被上下文压力拖到边缘,复制出来的会话会继承同样的压力。摘要模式直接在目标工作区新建空白会话、只写简报,新会话的上下文压力从零开始,代价是丢掉原始对话里的细节。

安装

从 npm 或 GitHub 安装:

# 从 npm
dsh plugin --profile <profile> add @garinz/dsh-handoff

# 或直接从 GitHub
dsh plugin --profile <profile> add github:GarinZ/dsh-handoff

从本地检出的源码安装:

dsh plugin --profile <profile> add /path/to/dsh-handoff
# 或在该目录里
dsh plugin --profile desktop add .

宿主半热加载,装完即用,不需要刷新页面。但客户端半是 client.js(浮层与追问按钮),装或升级后需要刷新一次页面才会加载:dsh 自带 Web 壳打开开发者工具(⌘⌥I)执行 location.reload(),生产版没有 ⌘R。

典型用法

插件注册了三个命令,输入 / 可见:/handoff <自然语言>(全文复制)、/handoff-summary <自然语言>(仅摘要)、/btw <问题>(旁问)。命令本身秒回「旁问中…」「正在生成简报并交接…」,把输入框让出来,答案回来后再替换命令结果,避免看起来卡死。

例如:

/handoff-summary 交接给 ~/Project/llm/agent-skills。ESP32 那摊活先放下,下一阶段只做 skill 仓库整理:重点写清楚哪些 skill 已经沉淀、哪些还只是草稿,以及"别自动入库"这条约束。

斜杠命令优先走侧路请求:插件取当前会话的消息快照(含 system prompt),追加「请按六节骨架写交接简报」的指令,用同一个 provider/model 单独请求一次,拿到简报后直接调用 handoff_session。这条请求不开启回合、不往对话里写事件。只有当目标工作区无法唯一确定、或本次会话还没有一问一答时,才退回「模型自己写简报再调工具」的路径。

直接说人话也可以,Agent 会调用工具,参数形如:

{
  "target": "/Users/me/Project/new-repo",
  "summary": "## 目标\n…\n## 下一步\n…",
  "history": "full",
  "title": "可选:给新会话一个能认出来的标题",
  "start_turn": false
}

各字段的含义:

  • target:目标工作区的绝对路径(或 ~ 开头)、工作区 id 或标题。目录不存在时会先 mkdir -p 再注册,所以「交接给 ~/proj/新项目」可以直接开一个新工作区。
  • summary:交接简报,六节骨架。history: "full" 时是补充;history: "summary" 时是新会话唯一的上下文。
  • history:full 复制完整历史;summary 不复制任何事件,只开带简报的空白会话。
  • title:可选,避免目标工作区出现两个同名会话。
  • start_turn:为 true 时立刻在新会话里跑一轮;默认只记录,不消耗模型调用。
  • mode:copy(默认)复制出新会话,原会话不动;move(beta)把当前会话本身在回合结束后迁到目标工作区(id 不变、历史全保留),只能搭配 history: "full"。

摘要模式的新会话会被包进一个只含简报、不含模型回复的回合(turn/start → 简报 → turn/end)。这是为了 GUI 的 sessionListMetadata.blank 判断:该投影只在遇到 turn/start 时才翻成 false,不补这个回合,仅摘要交接出来的会话在侧栏里不可见。回合里没有 step、没有 assistant 消息,不会调模型、不跑工具。

简报骨架

两种模式共用固定的六节:

## 目标          这一段要达成什么
## 决策          决定了什么 + 为什么(理由比结论重要)
## 已完成        带产物:文件路径、跑过的命令、测试结果
## 剩余          还没做的
## 关键上下文    路径、符号、报错原文、配置值
## 下一步        新会话第一条该做什么

写作规则对齐了 oh-my-pi 与 pi-handoff:只用对话里出现过的事实、不臆造路径与行号;关键细节不知道就写「未知」并给出最小验证步骤;用祈使句对下一任说话,不用第一人称;不把「写这份简报」本身写进进度或下一步。

旁问 /btw

/btw 刚才那个 403 是哪个开关没开?

它复用简报用的同一个侧路请求原语:快照 + 一句问题 → 一次独立模型调用,不开启回合、不写事件、不占会话记忆。侧路请求发的是扁平文本(把会话压成 ## 用户 / ## 助手 / ## 工具结果 的对话记录塞进一条 user 消息),因为请求里不声明 tools 时,provider 会拒绝带 tool-call 块和图片的原始历史。预算按字符算:旁问 20 万字符、简报 40 万字符,超了从最旧整条丢起,图片一律变 [图片]。留空 maxTokens 时由 ctx.llm 填该模型声明的 defaultMaxTokens,避免「聊天能写完、旁问被截断」。

进度面板

/btw 和 /handoff* 的快路径会在右下角弹出固定进度卡:标题、状态、已跑秒数、流式正文和「停止」。它走独立宿主路由 GET /handoff/activity?session=<id>(?cancel=1 中止),不依赖聊天流。底部输入框是追问口,ping POST /handoff/ask 把问题交给宿主,答案流回同一张卡,不在对话里留命令节点。追问时模型能看到本次会话最近 12 组旁问问答(原文不截断,仅存内存,重启即失)。

落盘结果

history: "full":

<目标工作区>/…/session-<new>/session.v4.jsonl.zstd
  header.cwd = 目标工作区
  事件 = 源会话的完整前缀 + 简报

history: "summary":

<目标工作区>/…/session-<new>/session.v4.jsonl.zstd
  header.cwd = 目标工作区
  事件 = 标题 + 简报(没有别的)

适用场景与注意

适合需要在 DSH 里跨工作区延续长任务、且希望把上下文和结论一起搬走的开发者。安装前应检查源码与许可证(MIT),插件以当前 dsh 进程权限运行。

行为上有几点需要知道:

  • subagent 会话不能交接;没有记录 cwd 的会话不能交接。会话里连一问一答都没有时,工具会直接拒绝(这次会话还没有可交接的对话)。
  • 新会话的 header.cwd 是目标工作区路径,沙箱根、AGENTS.md、skills 发现都跟着走。
  • 存储迁移先写临时目录、读回校验,再改内存态与账本;任一步失败都会回滚并保留原会话。
  • 全文模式是 fork 语义:新会话 isSeeded 为 true,历史标记为继承(session/end-seed)。依赖「自身事件」的功能(例如按会话统计 token 用量)会把继承部分算作前人历史;摘要模式没有这个问题,但它只知道简报里写了的东西。
  • 工作区账本与日志是即时写好的,侧栏刷新时机由 DSH 客户端决定,个别界面状态可能需要切一下工作区或刷新页面才显示。
  • 插件依赖若干 DSH 内部接口(sessionPersistence.tracker.writers、workspaceRegistry.indexHeader/attachSession、commands.register)。DSH 升级后请先在一个临时会话上验证。
  • 兼容性:开发与验证环境为 DSH 0.2.2-rc.1(macOS 桌面版)、Node 24。需要 hosting 暴露 sessionController、workspaceRegistry、sessionPersistence 三个服务(官方 dsh-base 都有)。

结尾

dsh-handoff 的价值在于把「跨工作区继续」这件事从手工搬运变成对话里的一次工具调用:历史、结论、下一步按固定骨架一起过去,源会话默认不动。仓库:https://github.com/GarinZ/dsh-handoff,目录页:https://www.skillhub.cn/plugins/GarinZ/dsh-handoff。

羽毛球分组比赛记分
小程序二维码

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

小夜