前言¶
用 DSH 做长任务时,常会遇到「换个工作区继续」。手工搬通常丢三件事:历史、结论、下一步。复制文件带不走对话;新开一个空会话等于让模型从零开始;把整段记录粘进输入框既超长又失真。
dsh-handoff 是给 DeepSeek Harness(DSH)的会话交接插件,由 GarinZ 维护,属于社区目录里的工作流分类。它让 Agent 在对话里直接调用 handoff_session 工具,把当前会话交接到另一个工作区的新会话,并附上一份按固定骨架写好的交接简报。源会话默认保持不变。
核心能力¶
插件做了三件事,用的都是 DSH 自身能力:
- fork —
sessionController.fork()把当前会话的事件前缀复制成独立新会话(不写parentSession); - relocate — 把新会话的存储迁到目标工作区:重写日志头行的
cwd、重绑活动写句柄、切换工作区账本,全程不重启; - 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。