DeepSeek Harness Plugin

fan56/dsh-dcp

Stars ★ 11 Downloads (30d) 3,142 Category Tools & Capabilities Added 2026-08-25 npm @aiwayds/dsh-dcp

Deterministic context compression for dsh — zero LLM calls, rule-based pruning that preserves paths, commands, error traces, and open todos verbatim. Compresses ~80k tokens to a few hundred.

Install

# from npm (prebuilt)

dsh plugin --profile web add @aiwayds/dsh-dcp

# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)

dsh plugin --profile web add github:fan56/dsh-dcp

Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).

README

Deterministic context-compaction backend for dsh (DeepSeek Harness): context compaction without an LLM call, works out of the box.

Requires dsh >= 0.2.0-rc.2 — this plugin targets the dsh RC/stable line only (CI and releases resolve the newest of the latest/next dist-tags at runtime). The alpha line is no longer supported.

简体中文 · English

Why

dsh compacts conversation context by default with compaction-basic, which asks an LLM to re-summarize older messages on every compaction — costly, slow, and non-deterministic. dsh-dcp is a pure-code port of the ideas behind opencode-dcp (dedup, error cleanup, "technical summary instead of prose"):

  • Zero LLM calls: compaction itself costs no extra tokens
  • Deterministic: identical input always yields identical output
  • CJK-friendly: verbatim user text / paths / commands / errors, priced at real CJK density
  • Inherits all official safety: triggers, retained tail, transaction locks, tool-pairing — dsh's own machinery, only the summarizer is replaced

Effects

vs. the official compaction-basic

compaction-basic dsh-dcp
Summarization LLM rewrite per compaction deterministic code extraction
LLM calls per compaction 1 0
Determinism may differ run to run identical input → identical output
Summary content semantic verbatim hard facts (paths/commands/errors/todos/user text)
Chinese model re-transcribes kept verbatim + CJK-aware pricing
Triggers/retention/overflow/safety official inherited, identical
Checkpoint format official compatible (mutually mergeable)

It also borrows the dedup / error-purge / /dcp / technical-summary ideas from opencode-dcp, re-implemented against dsh's compaction seam — that one serves opencode, this one serves dsh.

CJK adaptation

Content is kept verbatim (no re-transcription into English); tokens are priced at real CJK density (~2 chars/token for Chinese/Japanese/Korean/full-width) instead of the host's flat 4 chars/token that underestimates Chinese — so CJK sessions get a budget that reflects real cost, and checkpoints stay information-dense.

Real dsh session

~80k tokens of history → ~700-token checkpoint (~100x), zero LLM calls; cache-hit rate is barely affected (any backend pays one "cold request" right after a compaction).

A checkpoint produced on a real session (Chinese content kept verbatim):

## Primary Request and Intent
- 帮我把登录页的重定向 bug 修掉

## Files and Code
- /app/src/auth/login.ts — W×1 R×1

## Errors and Fixes
- bash: FAIL src/auth.test.ts

## Pending Jobs
- add regression test

## Critical Context
- dsh-dcp 确定性压缩了 12 条消息 / 8 次工具调用(未调用 LLM 摘要)

Not in scope

  • No semantic summarization: it preserves facts that appeared, it does not "understand" code. Need deep semantic checkpoints? Stick with the official compaction-basic
  • Things dsh already does, deliberately not re-implemented:
    • tool-result pruning (compaction-tool-result-pruner, deterministic by size)
    • trigger policy, retained tail, overflow recovery (inherited from official; this plugin only adds the round-interval trigger, see below)
    • /compact command, UI checkpoint cards (shipped with dsh)

Install

Recommended: pair it with our dsh-tui-pi (the TUI already depends on dsh-dcp):

npm i @aiwayds/dsh-tui-pi
dsh plugin add @aiwayds/dsh-dcp     # activates dcp; the bundle auto-mounts

Standalone:

npm i @aiwayds/dsh-dcp
npx dsh-dcp-setup                   # safe: date-stamped backup → append-only → idempotent checks

dsh-dcp plugs into dsh's compaction seam and only affects profiles that mount it. The web profile does not bundle the TUI, so it keeps the official backend and is unaffected.

Uninstall

Bundle path (dsh plugin add, or listed under bundles):

dsh plugin --profile <name> remove @aiwayds/dsh-dcp

The host reconciles automatically: the bundles entry is spliced, the patch layer drops with the package, and the stock LLM summarizer (compaction-basic) re-enables.

Setup-script path (the patch block npx dsh-dcp-setup wrote):

npx dsh-dcp-setup --remove                        # default home patch
npx dsh-dcp-setup --remove --profile tui          # a named profile
npx dsh-dcp-setup --remove /path/to/cordis.patch.yml

--remove strips only the setup-written mount block (including any config you tuned), with the same date-stamped backup before modifying; hand-written mounts are left alone; the file is deleted when nothing else remains. Two WARN lines matter: a leftover compaction-basic disable entry keeps the stock summarizer off (remove it by hand if it was only for dsh-dcp), and another dsh-dcp mount in the same file is untouched.

Removing the package without the reverse step leaves the mount pointing at the vanished absolute entry path, and the profile boot fails with module-not-found.

/dcp command

Command Effect
/dcp compact now (zero LLM); same as /dcp compact
/dcp status status: config, compaction count, tokens saved
/dcp help / --help / -h show the command grammar
/dcp set <k> <v> adjust a knob for this session, with a persist hint

Settable: dedup, purgeErrors, maxItems, maxItemChars, maxSummaryTokens, language, tokenEstimate, thresholdRatio, roundInterval, notice, onModelSwitch, modelSwitchMinTokens.

The bare /dcp and /dcp compact hit the same manual compaction seam: the common action takes zero arguments, so nobody has to remember a subcommand, while the read-only and tuning verbs stay behind explicit arguments (/dcp status for the block, /dcp help for usage).

Behavior change in 0.11.0: the bare /dcp now compacts instead of showing status; the status output moved to /dcp status. After upgrading, use /dcp status to inspect — the bare command compacts.

The /dcp status block also lists every session that has compacted (subagents included): per-session: session-1 (2 compactions, ~444 tokens), child (1 compaction, ~22 tokens). Compactions count per session; disposed sessions (one-shot subagents included) fall out of the overview automatically, and the list is capped at the first 10 sessions (+N more for the rest) so the status stays one line.

Triggers

Trigger When Notes
Pressure before every step tokens ≥ thresholdRatio (inherited upstream default 0.8; this plugin's bundle mounts 0.7 — see config table) × context window
Overflow recovery on a provider context-window error inherited
Round interval every roundInterval assistant messages added by this plugin; one round = one LLM roundtrip (each tool-iteration response counts, so one-shot subagents trigger too). Default 50: first compaction after message 50, then every 50 more (100, 150, …); any compaction (pressure included) restarts the clock. Fires at the first idle boundary after the count is reached (below the pressure threshold too). 0 disables; requires the default auto: true
Model switch the session's effective provider/model route changes added by this plugin (onModelSwitch). Default notice: appends one row suggesting /dcp compact first to shadow the old model's history and save tokens; auto compacts at the session's next idle boundary; off disables. Two gates: switches within 10 assistant messages of the last compaction are ignored (nothing stale to shadow), and switches while the context is below modelSwitchMinTokens (default 32768, 0 disables this gate) are ignored (not enough to be worth shadowing); auto requires the default auto: true, otherwise it degrades to notice
Manual /dcp (bare), /dcp compact, /compact anytime
  • Subagents are covered: in-process subagents (including continuable and one-shot children) dispatch through the same events, so pressure/overflow/round/model-switch triggers count and fire per child session independently. The round trigger counts assistant messages, so a one-shot subagent whose whole run is a single turn (many tool iterations) triggers too.
  • How model-switch detection works: the per-request request/context routing snapshot is folded per session (a provider or model change alone counts as a switch), covering every switch entry point — TUI /model, web clients, changed default-model settings. A session's first observed request only seeds the baseline. notice: false silences the compaction rows only; the switch row is controlled by onModelSwitch independently. The size floor is measured by the host token meter (measure().surfaceTokens) and fails open when unavailable.
  • Visibility: after every trigger event a one-line notice row (dcp: compacted N history items (~X tokens, trigger)) is appended to the session; frontends render it as a collapsed row. Note the row also rides the model request context (~15–25 tokens per compaction), and it is on by default since 0.4.0 — disable with notice: false. /dcp status stats count every committed region (a pressure retry loop may commit several).

Configuration

All optional, defaults work out of the box:

Key Default Meaning
thresholdRatio 0.8 pressure trigger (inherited upstream compaction-basic default 0.8; this plugin's bundle patch mounts 0.7, recommended for CJK-heavy sessions)
headroomTokens 65536 inherited from upstream compaction-basic (new in 0.1.7): extra pressure-budget margin (non-negative integer). The pressure ceiling is context window − routed output reservation (maxTokens) − this margin; lower it for small-window models
roundInterval 50 compact every N assistant messages (one LLM roundtrip) (0 disables). Default 50: 50, 100, 150… — the clock restarts after every compaction
onModelSwitch notice after a model switch: notice suggests /dcp compact (default); auto compacts at the next idle boundary; off disables
modelSwitchMinTokens 32768 minimum priced context size (measured by the host token meter) for a model switch to be announced; 0 disables this gate. 32k default ≈ the post-compaction baseline (~16% of the window) plus some real growth
notice true append the one-line compaction notice to the session
language zh summary language; zh also enables Chinese error/"待办:" detection
tokenEstimate cjk CJK (zh/ja/ko/full-width) at ~2 chars/token; ascii matches the host
dedup true annotate repeated tool calls
purgeErrors true collapse stale errors into one note
maxItems / maxItemChars 10 / 200 summary density
maxSummaryTokens 2048 summary token budget

Upgrade note (0.5.0): the roundInterval counter switched from completed turns to assistant messages — the same value now triggers more often (a single turn usually contains several assistant messages).

Bundled skill

The package registers a dsh-dcp-config skill (skills/dsh-dcp-config/SKILL.md, served via ctx.skills.registerProvider): ask the agent to tune compaction, configure dcp, or debug compaction behavior and the guide loads automatically — it carries an interactive ask_user_question tuning wizard (collect the expectation first, then map it to keys) and the persistent recipe for the config: section of the cordis.patch.yml mount block.

Design reference

Development

npm install && npm test     # 109 tests: extractor/compaction/command/config/triggers/recover-seam/setup

License

MIT

Content from the project README on GitHub ↗

Links

More in this category

View the whole category →

Community comments

Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.