dsh-session-handoff
English | 中文
Context window filling up? One /handoff condenses dozens of turns into a one-page handoff document and continues in a clean new session — the old session stays untouched.
Long DSH sessions get slow, eat the context window, and start forgetting. DSH ships compaction and fork, but nothing that says remember what matters, then start fresh. This plugin is that missing step.
When the context window or session size crosses your threshold it asks first. On confirm, it writes a handoff document — goal / approach / done / files / next steps / open questions — creates a new session in the same workspace, injects the document as its first message, and opens it. The new session already knows the job; you don't re-explain anything.
How it differs from compaction and fork
/compact |
fork | this plugin | |
|---|---|---|---|
| Origin session | compacted in place | kept | kept as-is, linked back from its title |
| Load on the new session | same session | full history copied | one page of handoff notes |
| Who continues | yourself | yourself | a fresh session with a clean context |
| When it switches | automatically at the threshold | manually | asks first at the threshold, or fully automatic |
Compaction repacks the old baggage, fork copies it. This plugin writes the baggage down as a one-page brief and travels light.
Features
- Manual —
/handoff [<N>|all]; no argument uses the configuredturns(default 50 user messages) - Automatic — three policies: replace (before native compaction) / hybrid (when the session gets heavy) / native (never switch automatically)
- Asks first — confirm popup by default;
notify/automodes available. After you dismiss a suggestion, the trigger level must grow by another 50% before asking again - Health row — a persistent "context xx% | session xxMB | ~N turns left" line above the composer
- Clean handoff — new session inherits workspace, model route and agent preset (system prompt / tools / skills), with none of the old context
- Structured document — persisted to disk as well; browse past handoffs from the settings panel
- Auto-open — the client half waits for the child session and navigates to it (once per child, no re-jumps on replay)
- Decision log — every decision (action + reason) is recorded and viewable in settings
- Zero core changes — a single
dsh.bundle.patchrow; only public host services are used
Install
Requires DSH desktop or CLI with Node ^22.19.0 || >=24.
Tested on: 0.1.0-rc.5–rc.8, 0.1.1-rc.2, 0.2.0-rc.2, 0.2.1-rc.2.
The plugin is a standard DSH bundle: put it into the node_modules of the profile you are using, then
register it there. Example for the desktop profile:
# 1) put the repository anywhere (or unpack dsh-session-handoff-0.3.1.tgz from the release)
git clone https://github.com/MountBatten718/dsh-session-handoff.git ~/dsh-session-handoff
# 2) link it into the desktop profile's node_modules
ln -s ~/dsh-session-handoff ~/.dsh/profiles/desktop/node_modules/dsh-session-handoff
Then register the dependency in that profile's package.json:
{
"dependencies": {
"dsh-session-handoff": "link:/Users/you/dsh-session-handoff"
}
}
and add dsh-session-handoff to the profile's dsh.profile.bundles. Restart DSH to apply.
The
desktopprofile is owned exclusively by the Electron app, so the CLI refusesplugin add/--dump-configfor that name; point a differently-named symlink at the same directory for a read-only check. For a regular CLI profile, install this directory the same way you install other local plugins.
Usage
Type in any conversation's input box:
/handoff # use the configured turns (default 50 user messages)
/handoff 20 # summarize the last 20 user turns
/handoff all # summarize every user message
What happens: the command card shows "summarizing…" → the LLM produces the handoff document → the new session auto-opens; the origin session stays intact and is linked back.
⚠️ Privacy: the recent conversation text (user + assistant messages in the requested window) is sent to the configured model to produce the summary.
Configuration
Most options live in DSH → Settings → Plugins → session handoff (applied immediately). Full profile patch:
- id: handoff
name: dsh-session-handoff
config:
autoMode: hybrid # replace / hybrid (default) / native
autoPromptLevel: confirm # confirm (default) / notify / auto
sessionVolumeBytes: 8388608 # session size threshold (default 8 MB)
compactionCountTrigger: null # switch after N compactions; null = disabled
confirmTimeoutMs: 120000 # confirm popup timeout (own hard fallback)
provider: '' # fixed provider for the new session
model: '' # fixed model (pair with provider)
childReasoningEffort: '' # reasoning effort of the new session
childPermissionPreset: '' # read-only / workspace-write / danger-full-access
childPlanFirst: true # new session presents a plan first (default on)
subagentDrainTimeoutMin: 10 # minutes to wait for direct subagents to wrap up (1-60)
askRearmPercent: 50 # re-ask after the trigger grows by another N points (10-500)
decisionLogEnabled: true
handoffDocPersist: true # persist handoff documents to disk (default on)
handoffDocDir: '' # absolute dir; empty = DSH data dir
turns: 50 # user-message rounds to extract (1-200)
maxInputChars: 200000 # input byte cap (oldest dropped first)
maxTokens: 8000 # summary output budget
reasoningEffort: inherit # inherit (default) | off | high | max
# ⚠️ do NOT default to 'off': some models reject it outright
timeoutMs: 120000 # summary call timeout
Without provider/model, the current session's latest logged model route is used.
How it works
- Host half (
index.mjs+lib/*.mjs, plain ESM): public services only —commands(/handoff),llm,agents(ctx.agents.create— session and agent together, inheriting the origin's cwd / model route / agent preset viaagentPresets.mount),workspaceRegistry.attachSession,sessionTitle.rename,userQuestions. Automatic handoff hooksagent/pre-step(step boundary = safe point). The client half reads health, edits config and triggers manual handoffs over Typert RPC. - Client half (
src/client/→scripts/build-client.mjs→lib/client.js): health row, settings panel and handoff card; ahandoffconversation node auto-opens the child session. - Black box: every handoff writes stages and outcomes to
<DSH_HOME>/session-handoff-docs/handoff-trace.jsonl(long text redacted, rotates at 5 MiB).
Build
lib/client.js is committed; after editing src/client/**, rebuild it:
node scripts/build-client.mjs # rebuild lib/client.js
node scripts/build-client.mjs --check # verify the artifact matches the sources byte-for-byte
The build script uses only Node's built-in TypeScript type stripping — no dependencies to install.
Known limitations
- Supports DSH
0.1.0-rc.5throughrc.8,0.1.1-rc.2,0.2.0-rc.2, and0.2.1-rc.1/rc.2(verified on 0.2.1-rc.2 on 2026-10-20: plugin load, health row, settings panel and stats RPC all work). DSH0.1.2-alpha.1split the conversation UI/service and needs a separate client-side migration. session.snapshotEvents()is marked@deprecatedby the host (it still uses it internally); a future kernel may need a different read entry point.- Chain state (which handoff in the chain, "already handed off") is in-memory only; after a restart it is recovered from the marker on the old session's title, so the chain index degrades to 1 (display only).
- A failed automatic handoff is not retried automatically; use the manual "hand off" button instead.
- Auto-open relies on client-side polling; verify edge timing on your DSH version.
Credits
Based on WeiYe6/dsh-session-handoff (MIT), which provides the
original /handoff command and handoff-document implementation. This fork adds automatic handoff, the health
row, the settings panel, document persistence, decision logging and subagent drain waiting.
No comments yet. Be the first to write one.