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)
/compactcommand, UI checkpoint cards (shipped with dsh)
- tool-result pruning (
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
/dcpnow compacts instead of showing status; the status output moved to/dcp status. After upgrading, use/dcp statusto 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/contextrouting 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: falsesilences the compaction rows only; the switch row is controlled byonModelSwitchindependently. 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 withnotice: false./dcp statusstats 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
roundIntervalcounter 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
- Opencode-DCP/opencode-dynamic-context-pruning
- dsh official compaction seam:
docs/subsystems/compaction.md(deepseek-harness)
Development
npm install && npm test # 109 tests: extractor/compaction/command/config/triggers/recover-seam/setup
License
MIT
Links
More in this category
Tencent/WeKnora#dsh-weknora★ 34028
Four read-only tools over a WeKnora knowledge base: list knowledge bases, hybrid passage search, reassemble one document's chunks in order, and WeKnora's own cited RAG or ReAct-agent answer with a resumable session id.
superdesigndev/treg★ 5298
Tool catalog for agents: search ~2,600 external endpoints (SEO and SERP, backlinks, social, people and company enrichment, ad libraries, scraping) by the task you want done, read each one's parameters and per-call price, then call it with the credential injected server-side. Ships the skill plus an MCP row that stays disabled until TREG_TOKEN is set.
TencentCloudBase/CloudBase-AI-Toolkit#dsh-plugin★ 1136
Tencent CloudBase backend for DeepSeek Harness — scaffold and deploy full-stack apps from chat, render query results as table cards with paging, sorting and CSV export, preview a deployment on its domain, and call the CloudBase MCP toolset (`mcp__cloudbase__*`) with device-code login.
gitroomhq/postiz-agent#dsh-postiz★ 517
Connects DeepSeek Harness to Postiz over MCP: list connected social media channels, fetch per-platform posting rules, and schedule, draft, or publish posts to X, LinkedIn, Instagram, Facebook, Threads, TikTok, YouTube, Reddit, Bluesky, Mastodon, Discord, Slack, Telegram and more; adds a postiz workflow skill.
EthanYoQ/Invoice-Downloader#dsh-invoice-downloader★ 507
Local IMAP invoice download, OCR, archive, and Excel reimbursement summaries for DeepSeek Harness.
anysearch-team/anysearch-dsh★ 461
AnySearch-powered real-time web and vertical search provider for DeepSeek Harness.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.