dsh-compact-and-branch: /compact-and-branch
A focused out-of-tree DeepSeek Harness plugin for treating canonical context
compaction as a clean session branch point.
Motivation
Long-running coding-agent sessions can accumulate a great deal of useful
working context before reaching a natural phase boundary. DSH's /compact
command is useful for reducing that history while preserving the state needed
to continue, but sometimes it is preferable to make that compaction a clean
branch point: continue from the resulting state in a fresh session while
leaving the compacted source session independently resumable.
This is especially useful with relatively limited context windows, as is common
with local LLMs, where long agent trajectories can otherwise require repeated
compactions and make session boundaries more consequential.
/compact-and-branch is intended to have essentially the same model-facing
effect as running /compact and continuing normally in the original session,
except that the continuation occurs in a new root session. It deliberately
reuses DSH's canonical compaction result rather than introducing a separate
handoff summary or another lossy transformation of the session state.
What it does
/compact-and-branch:
- Preflights the source session's workspace, active preset, and current model
selection (the durable
modelSelection projection, falling back to the
latest request header and then the deployment default).
- Calls the same operation as built-in
/compact:
ctx.compaction.compactNow(invocation.agent, invocation.signal, invocation.commandId).
- Only after that succeeds, snapshots
source.session.deriveMessages(). This is
the canonical checkpoint message plus the complete retained recent tail;
the plugin never reads or rewrites CompactionResult.summary.
- Creates a new root session through the Host
sessionController.create API,
with the source workspace, active preset, and no parentSession fork
lineage, then inherits the model route via sessionController.selectModel,
which durably records a model/selection event for the next request.
- Adds one minimal preamble followed by exact copies of every canonical
post-compaction message, preserving message content, ordering, roles,
identities, provenance, and tool pairs. It then advances the new agent's idle
turn cursor past the imported turn so the first live response receives a
distinct turn/step identity, and assigns a pinned handoff title.
- Flushes the target, then publishes a typed session projection. The browser
half waits for the target to appear in the session list, refreshes the list to
replace its provisional blank-session row with the persisted populated
summary, and calls the supported
ctx.sessions.open(targetId) API.
- In the fresh session's chat presentation, hides the synthetic imported
turn's checkpoint and retained recent-tail rows, leaving its native,
selectable DSH notice row: “Compacted context imported from previous
session.” The notice keeps the project's standard context typography, color,
disclosure behavior, and accessibility; durable model-facing messages remain
unchanged.
Built-in /compact is not registered, shadowed, or modified.
Install
dsh plugin is a thin pnpm forwarder: it runs pnpm inside your profile
directory and then reconciles the profile's dsh.profile.bundles stack for
you, so no hand-editing of any config file is needed. Install the plugin
from GitHub:
dsh plugin --profile web add git+https://github.com/zeropointnine/dsh-compact-and-branch
# or pin a release tag
dsh plugin --profile web add github:zeropointnine/dsh-compact-and-branch#v0.3.0
A first dsh plugin run in a profile initializes it automatically. The
profile's package.json gains the dependency, and DSH appends
dsh-compact-and-branch to dsh.profile.bundles on its own (it detects the
package's dsh.bundle declaration). This package has no build or install
scripts, so git-hosted installs need no allowBuilds entry in the profile's
pnpm-workspace.yaml.
After adding, restarting, updating, or removing the plugin, restart DSH. To
remove it later: dsh plugin --profile web remove dsh-compact-and-branch.
Compatibility
This release (0.3.x) targets DSH ^0.1.5-rc.2 and is a clean break from
the 0.1.1 line. DSH 0.1.5 removed the entire Host API-Proxy surface this
plugin's host half was built on (ctx.apiProxy.* moved onto owning business
services, notably ctx.sessionController) and retired the
@deepseek-ai/dsh-client-runtime package the browser half injected, so a
single package cannot serve both lines: the peer sets are disjoint. Users on
DSH 0.1.1-rc.x should pin #v0.2.0.
Within a line the plugin is deliberately resilient where it can be: unknown
ManualCompactionError codes fail open to a raw error instead of a misleading
message, and the DSH surfaces it rides (the compaction seam, the session event
vocabulary, the projection registry) have been stable across releases. Three
documented internal reliances remain and are guarded loudly at runtime: the
idle turn-cursor advance on the created Agent (agent.phase became the public
field after setPhase went private), the maxTokens carry-over onto the
target AgentOptions, and the filtering of system-role messages out of the
imported continuation (DSH 0.1.5 added the system-prompt projection to
deriveMessages(); the fresh session rebuilds its own system context, so the
source's is dropped rather than duplicated). A DSH update that breaks any of
these degrades to a visible initialization error, never silent history
corruption.
Presentation-side, the client half rides three 0.1.5 wire shapes that are
contracts of the DSH client packages rather than this plugin: the session-list
row's projectionValues field (where the navigation projection's view value
arrives — misnamed reads here fail as "no navigation", silently), the
agentPreset session-projection value on the host list summary (0.1.5 moved it
off the summary field; the handoff reads it through the projections so preset
inheritance survives), and the command card's collapsed-summary rendering rule
(one line is clamped; multi-line text becomes an expandable body, which is why
every outcome text this plugin emits is deliberately two lines).
Usage
In any session, type:
/compact-and-branch
No arguments. The current session is compacted through DSH's canonical
/compact machinery, then a fresh root session is created in the same
workspace and preset with the same model selection, carrying the compacted
context; DSH opens it in the browser. The compacted source session stays
independently resumable. The command card and the fresh session both render
the compaction summary through DSH's own MarkdownText renderer — the same
fully-formatted, in-document-flow presentation /compact uses — via the
conversation.chat.commandview and conversation.chat.turnTail UI slots. In
the fresh session, the imported history is collapsed behind a native DSH
notice row, leaving only live conversation visible plus the formatted
summary card at the end of the imported turn. On shells without the
react/@deepseek-ai/dsh-client-ui-primitives module seed words or the
slots service, the plugin falls back to the native bounded context row for
the imported checkpoint.
Development
pnpm install # installs the @deepseek-ai/* peer copies and zod
pnpm test # node --test
The tests load host-side classes (Session, ManualCompactionError, message
creators) from a separate module instance than the plugin's own imports, to
exercise the cross-package error-classification boundary. They resolve that
instance from the DSH_HOME environment variable (point it at a DSH
checkout's node_modules/@deepseek-ai directory, or the checkout root) and
fall back to the package's own pnpm-installed peer copies; if neither is
available, those tests skip with a hint instead of failing.