Introduction¶
When performing long tasks with DSH, you often encounter the need to “switch workspaces to continue.” Manual copying typically drops three things: history, conclusions, and next steps. Copying files cannot transfer the conversation; starting a new empty session forces the model to start from scratch; pasting the entire record into the input box is both too long and distortion-prone.
dsh-handoff is a session handoff plugin for DeepSeek Harness (DSH), maintained by GarinZ, belonging to the Workflow category in the community directory. It allows the Agent to call the handoff_session tool directly within the dialogue to hand off the current session to a new session in another workspace, accompanied by a handoff brief written according to a fixed skeleton. The source session remains unchanged by default.
Core Capabilities¶
The plugin performs three actions, all utilizing DSH’s own capabilities:
- fork —
sessionController.fork()copies the current session’s event prefix into an independent new session (without writingparentSession); - relocate — Moves the new session’s storage to the target workspace: rewriting the
cwdin the log header line, re-binding the active write handle, and switching the workspace ledger, all without restarting; - brief — Appends the handoff brief as the first message in the new session; the brief follows a fixed six-section skeleton.
There are two history strategies for handoffs:
| Mode | history |
Content in New Session | When to Use |
|---|---|---|---|
| Full Copy (Default) | full |
Complete event history + Handoff brief | Need precise context, and the source session is still healthy |
| Summary Only | summary |
Only the handoff brief | Source session is already large, context is about to burst, or plugins that do not support active compression are used |
The reason for “Summary Only” is that fork is a fact copy, not compression. If the source session has been dragged to the edge by context pressure, the copied session will inherit that same pressure. The summary mode directly creates a blank session in the target workspace and writes only the brief; the context pressure of the new session starts from zero, at the cost of losing details from the original dialogue.
Installation¶
Install from npm or GitHub:
# From npm
dsh plugin --profile <profile> add @garinz/dsh-handoff
# Or directly from GitHub
dsh plugin --profile <profile> add github:GarinZ/dsh-handoff
Install from locally checked out source code:
dsh plugin --profile <profile> add /path/to/dsh-handoff
# Or in that directory
dsh plugin --profile desktop add .
The host side performs a hot reload; it is ready to use immediately after installation, without needing to refresh the page. However, the client side consists of client.js (overlay and ask button); after installing or upgrading, you need to refresh the page once for it to load: open developer tools (⌘⌥I) in the built-in Web shell of dsh and run location.reload(). The production version does not have ⌘R.
Typical Usage¶
The plugin registers three commands, visible with /: /handoff <natural language> (Full Copy), /handoff-summary <natural language> (Summary Only), and /btw <question> (Aside). The commands themselves reply instantly with “Aside in progress…” or “Generating brief and handing over…”, freeing up the input box so the answer can replace the command result later, avoiding the appearance of freezing.
For example:
/handoff-summary 交接给 ~/Project/llm/agent-skills。ESP32 那摊活先放下,下一阶段只做 skill 仓库整理:重点写清楚哪些 skill 已经沉淀、哪些还只是草稿,以及"别自动入库"这条约束。
Slash commands prioritize side-path requests: the plugin takes a message snapshot of the current session (including system prompt), appends an instruction “Please write a handoff brief following the six-section skeleton,” makes a single request via the same provider/model, and upon receiving the brief, directly calls handoff_session. This request does not start a round and does not write events into the conversation. It falls back to the path where “the model writes the brief and then calls the tool” only when the target workspace cannot be uniquely determined, or if this session has not yet had a Q&A pair.
Speaking naturally also works; the Agent will call the tool with parameters like:
{
"target": "/Users/me/Project/new-repo",
"summary": "## 目标\n…\n## 下一步\n…",
"history": "full",
"title": "Optional: Give the new session a recognizable title",
"start_turn": false
}
Meanings of each field:
target: Absolute path of the target workspace (or starting with~), workspace ID, or title. If the directory does not exist, it willmkdir -pbefore registering, so “handoff to~/proj/new-project” can directly open a new workspace.summary: The handoff brief, six-section skeleton. It supplements whenhistory: "full"; it is the only context in the new session whenhistory: "summary".history:fullcopies the full history;summarydoes not copy any events, only opens a blank session with the brief.title: Optional, to avoid having two sessions with the same name in the target workspace.start_turn: Whentrue, runs a round immediately in the new session; by default, it only records and does not consume model calls.mode:copy(default) forks a new session, the original session remains unchanged;move(beta) moves the current session itself to the target workspace after rounds end (ID unchanged, full history preserved), and can only be paired withhistory: "full".
The new session in summary mode will be wrapped in a round containing only the brief and no model responses (turn/start → brief → turn/end). This is to satisfy the GUI’s sessionListMetadata.blank judgment: this projection only flips to false when encountering turn/start. Without this round, sessions handed off via summary mode are invisible in the sidebar. There are no steps, no assistant messages, no model calls, and no tools are run.
Brief Skeleton¶
Both modes share a fixed six-section structure:
## 目标 这一段要达成什么
## 决策 决定了什么 + 为什么(理由比结论重要)
## 已完成 带产物:文件路径、跑过的命令、测试结果
## 剩余 还没做的
## 关键上下文 路径、符号、报错原文、配置值
## 下一步 新会话第一条该做什么
The writing rules align with oh-my-pi and pi-handoff: use only facts that appeared in the dialogue, do not fabricate paths and line numbers; if you don’t know a key detail, write “Unknown” and provide the minimal verification steps; use imperative mood when addressing the next person, do not use first person; do not include “writing this brief” itself in progress or next steps.
Aside /btw¶
/btw 刚才那个 403 是哪个开关没开?
It reuses the same side-path request primitive used for the brief: snapshot + a question → one independent model call, without starting a round, writing events, or occupying session memory. The side-path request sends flat text (compressing the session into a conversation record of ## User / ## Assistant / ## Tool Result stuffed into a single user message), because when tools are not declared in the request, the provider will reject raw history containing tool-call blocks and images. The budget is calculated by characters: Aside 200k characters, Brief 400k characters. If exceeded, it discards from the oldest chunk. When maxTokens is left empty, ctx.llm fills it with the defaultMaxTokens declared by the model, avoiding “the chat can finish but the aside gets truncated.”
Progress Panel¶
The fast paths for /btw and /handoff* pop up a fixed progress card in the bottom right: title, status, elapsed seconds, streaming body, and “Stop”. It uses an independent host route GET /handoff/activity?session=<id> (cancel via ?cancel=1) and does not depend on the chat stream. The bottom input box is the follow-up port; pinging POST /handoff/ask passes the question to the host, and the answer streams back to the same card, leaving no command nodes in the conversation. When asking a follow-up, the model can see the last 12 groups of aside Q&A from this session (original text is not truncated, only stored in memory, lost on restart).
Disk Results¶
history: "full":
<目标工作区>/…/session-<new>/session.v4.jsonl.zstd
header.cwd = 目标工作区
事件 = 源会话的完整前缀 + 简报
history: "summary":
<目标工作区>/…/session-<new>/session.v4.jsonl.zstd
header.cwd = 目标工作区
事件 = 标题 + 简报(没有别的)
Applicable Scenarios and Notes¶
Suitable for developers who need to continue long tasks across workspaces in DSH and wish to move context and conclusions together. Before installing, you should check the source code and license (MIT); the plugin runs with the current dsh process permissions.
There are a few points about the behavior you need to know:
- Subagent sessions cannot be handed over; sessions without a recorded
cwdcannot be handed over. When the session has not even had a Q&A pair, the tool will directly reject it (这次会话还没有可交接的对话). - The
header.cwdof the new session is the target workspace path; sandbox root, AGENTS.md, and skills discovery follow along. - Storage migration writes to a temporary directory first, reads back for verification, then updates memory state and the ledger; if any step fails, it rolls back and preserves the original session.
- Full mode has fork semantics: the new session’s
isSeededistrue, and the history is marked as inherited (session/end-seed). Features that depend on “self events” (such as session-based token usage statistics) will count the inherited part as previous history; the summary mode does not have this problem, but it only knows what is written in the brief. - The workspace ledger and logs are written immediately; the sidebar refresh timing is decided by the DSH client. Some interface states may require switching the workspace or refreshing the page to display.
- The plugin depends on several DSH internal interfaces (
sessionPersistence.tracker.writers,workspaceRegistry.indexHeader/attachSession,commands.register). After upgrading DSH, please verify on a temporary session first. - Compatibility: Development and verification environment is DSH
0.2.2-rc.1(macOS Desktop), Node 24. Requires hosting to expose the three servicessessionController,workspaceRegistry, andsessionPersistence(officialdsh-baseprovides these).
Conclusion¶
The value of dsh-handoff lies in turning the act of “continuing across workspaces” from manual labor into a single tool call in the conversation: history, conclusions, and next steps are passed together according to a fixed skeleton, and the source session remains unchanged by default. Repository: https://github.com/GarinZ/dsh-handoff, Directory page: https://www.skillhub.cn/plugins/GarinZ/dsh-handoff.