Model-driven context compaction for DeepSeek Harness. The agent decides when and what to compress, commits durable session-log replacements, and ships reversible tool-result compression with byte-exact hash retrieval.
Install
# from npm (prebuilt)
dsh plugin --profile web add @internetnutzer/dsh-asc
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:JanEickholt/dsh-asc
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
dsh-asc (full name DeepSeek Harness Agentic Surface Compaction) is a
context-compaction plugin for
DeepSeek Harness: the
model itself decides when and what to compact, and every compaction decision
is committed as a durable session-log replacement event
(surfaceOp: replace) — replayable, searchable, and reversible.
Inspired by the model-driven compaction philosophy of opencode-acp, but built on DSH's event-sourced log: compaction creates no side-state files, decompression is log replay, and search covers the full log including compacted originals.
Install
Prerequisites: a working DeepSeek Harness
installation (dsh CLI available); Node.js ^22.19 or >=24.
Harness compatibility: the built plugin targets core @deepseek-ai/dsh-*
0.2.0-rc.1 or newer (>=0.2.0-rc.1 <0.3.0); its peerDependencies also
admit ^0.1.5-rc.2 so an older host still resolves the install. 0.1.0-rc.6
cores need the 0.2.0 release and 0.1.2-rc.1 cores the 0.2.1 release;
core changed the Session API between the generations (0.1.5 additionally
made the system prompt a surface-eligible system/message event and renamed
the replace surfaceOp bounds to startSeq/endSeq), and the plugin fails
at runtime when the core is newer than the release supports.
From npm (recommended):
dsh plugin --profile <name> add @internetnutzer/dsh-asc
From GitHub — to use a commit newer than the npm release:
dsh plugin --profile <name> add github:JanEickholt/dsh-asc
dsh plugin adds the plugin to the profile and enables it automatically
based on the dsh.bundle declaration in the package; the tools and the
system prompt load together with that profile.
Restart required: after installing, restart the running DeepSeek Harness service.
Other install options
From source — to modify the plugin itself, or to contribute:
git clone https://github.com/JanEickholt/dsh-asc.git
cd dsh-asc
pnpm install
pnpm build
dsh plugin --profile <name> add "link:$(pwd)"
Disabling the basic backend
ctx.compaction allows only one provider at a time. Disable the default
basic backend in your profile's own cordis.patch.yml:
- id: compaction-basic
disabled: true
Optionally mount the invariant companion and the full-text-search backend:
- insert:
- id: dsh-asc-invariant # runtime invariant checks (optional; DSH <= 0.2.0 only)
name: "@internetnutzer/dsh-asc/invariant"
- id: session-query-sqlite # context_search full-text backend (optional)
name: "@deepseek-ai/dsh-session-query-sqlite"
Usage
After installing and restarting, no configuration is required — the plugin:
- injects the context-management discipline into the system prompt (judgment rules, tool usage, tiered compaction cadence), so the model actively manages context from the very first turn;
- injects nudge prompts on demand when context usage runs high (cadence-gated; iteration nudges additionally require real token growth — no per-turn nagging);
- provides deterministic degradation (LLM summarization, plus tool-result pruning when the optional upstream pruner is mounted) on overflow or manual compaction, without requiring model cooperation.
The plugin provides six model tools:
| Tool | Purpose |
|---|---|
context_status |
context usage, tiered checkpoints, system/dialogue composition, recommended ranges, recent surface nodes |
context_compress |
replace a surface range with a checkpoint you write (batching supported; tool-call pairs auto-extended; quality gate) |
context_decompress |
undo a compaction: the original text returns to the surface at the checkpoint's own position (tier-aware; full: true reaches raw content) |
context_recap |
re-read checkpoint summaries without decompressing the originals |
context_search |
full-text search over the whole log (including compacted content) |
context_retrieve |
return a projected tool-result original byte-exact by its 24-hex sha-256 hash or seq |
Compacted content is never lost: the originals stay in the session log and can be decompressed or searched at any time.
Reversible tool-result projection
Separately from model-driven compaction, an optional projection service
compresses oversized tool results BEFORE they enter the context. It listens
on the tools/post-execute waterfall, measures every candidate's text
blocks with the real token meter, and returns every decision unchanged so
the original event still lands in the log first. A reversible commit then
shadows the original and appends a replacement whose content embeds a
retrieval marker:
[dsh-asc projection: structured:json compressed 4200→312 tokens. Full
original (seq 57, stored in this session log): context_retrieve(hash="…").]
- The original stays byte-exact in the session log (single source of truth; no side store), so it survives restarts; a marker is emitted only when the original is durably stored — a marker never dangles.
- Reducers are content-aware: structured explorers for
JSON/YAML/XML/delimited/code, git-diff hunk compaction, search-result
clipping, CLI rule reduction, and a meter-priced head/tail slice as the
last fallback.
context_retrievereturns the stored original byte-exact by hash or seq; unknown keys get a diagnostic, never fabricated content. - The six
context_*tools' own results are never projected (retrieval stays byte-exact; status/recap/search reports are already capped). - The projection mounts as its own cordis service row, independent of
ctx.compaction— disabling it never disables compaction, and the overflow-triggered tool-result pruner stays mounted as the fallback. - Config:
projection.enabled(defaulttrue),projection.thresholdTokens(default1000),projection.excludeTools(default[]),projection.disabledReducers(default['code']); see docs/usage.md for the measured trade-off.
The system prompt ties the tools into one operating loop: capture consumed
raw work into tier-1 checkpoints, distill settled tier-1 piles into tier-2
decisions and tier-2 piles into a tier-3 fact index. Every checkpoint text
carries its topic and Compaction id, so when a visible summary already
points at the needed detail the model decompresses that block directly;
context_search is used only when no visible summary says where a detail
lives, and decompression always proceeds one tier at a time.
How it works
- Event sourcing: a compaction is a transaction in the log
(
compaction/start→compaction/summary→ replaceduser/message→compaction/end); no side state. - Tiered compaction: checkpoints have tiers (T1 full detail → T2 distilled decisions → T3 bare facts); summaries get thinner as they are reused.
- Reversible: decompression replays the events shadowed in the log and commits one in-place replacement event; no side state is needed.
- Auditable: who compacted what, the full summary text, and the token cost are all in the log.
Repository layout
src/
index.ts plugin entry: registers ctx.compaction + the six tools
config.ts strict config validation
types.ts shared config and result types
events.ts session-event vocabulary documentation (no custom members)
invariant.ts runtime invariant companion (subpath export)
engine/ the compaction engine core (engine, region, tier,
quality gate, fallback, prompt, restore)
policy/ protected-node policy and the nudge state machine
tools/ the six model tools
projection/ reversible tool-result projection service + reducers
analytics/ session-log usage scanner, regret signals, asc-stats command
utils/ shared text helpers
tests/ vitest suites
scripts/ corpus-stats and bili-cache-join measurement scripts
docs/ usage, design, analysis, e2e-validation, cache-join
Using dsh-asc alongside the official tool-result pruner
dsh-asc aims at post-execute, reversible tool-result compression: every
projection keeps a context_retrieve lookup so the full original can be
restored at any time. The official
@deepseek-ai/dsh-compaction-tool-result-pruner
(the tool-pruning patch shipped by the dsh-compaction bundle) complements this
as a last-resort overflow guard: when a request still overflows the context
window, it truncates oversized tool results to head + tail so the turn
survives.
We recommend installing both:
- dsh-asc projection — primary path: reversible post-execute management.
- tool-result pruner — overflow insurance: caps results at head 4096 + tail 1024 characters, but only after a request actually fails on overflow.
Caveat: pruner truncation is irreversible and fires only on overflow — nothing intervenes between a tool's execution and the failed request, so oversized results sit at full length mid-turn. Rely on projection first; the pruner is the safety net, not the standard path.
The two compose cleanly: pruneSession() is idempotent and projection keeps
its own retrieval index, so either can run first.
Measured in production
The numbers below come from real sessions on the maintainer's machine — every
one is reproducible by running scripts/corpus-stats.mjs
against your own DSH session logs (usage: node scripts/corpus-stats.mjs [~/.dsh/sessions]). Session logs are durable, so nothing here is estimated or
simulated: folds, shadowed-token counts, and per-request cache usage are all
committed events.
Corpus scanned 2026-10-08 — 240 sessions with folds, at least one
context_* tool call each (engine attribution: only sessions where dsh-asc's
tools were registered count; sessions from the compaction-engine swap
experiment are excluded by that fingerprint):
| Metric | Value |
|---|---|
| Sessions with folds | 240 |
| Folds (compaction transactions) | 721 |
| Tokens shadowed by folds | 51.9M |
| Model-authored summaries | 721 (fallback: 10) |
| Failed folds | 16 (2.2%) |
| Median fold size | 60.5K tokens |
| Prompt-cache hit rate across all metered requests | 94.8% |
| Median first-request-after-fold cache miss | 27.6K tokens |
context_decompress after a fold |
9 calls — 0.7% of folds |
context_recap |
12 calls |
context_retrieve (projection lookups) |
1,188 calls |
context_search |
38 calls |
The story the table tells: across ~721 folds covering ~52M tokens of
compacted history, the model asked for originals back 9 times. Summaries
plus tiered checkpoints carried the work; when detail was genuinely needed,
context_retrieve restored it reversibly (1,188 lookups without a single
irreversible loss). The 94.8% cache hit rate — measured across all
78,927 metered LLM requests in those sessions, including the re-pay spikes
every fold causes — shows compaction coexisting with prompt caching at a
healthy level rather than destroying it.
How a fold plays out
flowchart TD
A[Model calls context_compress] --> B[compaction/start]
B --> C[Quality gate: summary must pass L1 floor + L2 recall]
C -->|pass| D[compaction/summary: shadowedRange + tiered summary committed]
C -->|fail| G[ fold rejected — original stays ]
D --> E[user/message replace event: surface switches to the summary]
E --> F[compaction/end]
F --> H{Model needs detail later?}
H -->|usually not| I[Work continues on the summary]
H -->|9 of 721 folds| J[context_decompress / context_retrieve]
J --> K[Log replay restores byte-exact originals]
K --> L[One in-place replace event — still no side state]
style C fill:#f9f
style J fill:#ff9
Two properties visible in this flow that no wire-level compressor can offer: the quality gate (a fold that fails the floor/recall check never destroys history — it is rejected) and reversibility (decompression is log replay, not a cache of originals; the log is the only source of truth).
Cost of compaction, honestly
Each fold rewrites the message list, so the next request re-pays the prompt cache: the median first-request-after-fold miss is 27.6K tokens (vs a 60.5K median fold). Folds are one-time costs paid for by the per-turn savings of a smaller surface — the cache-join script measures exactly this trade against a wire proxy's ledger, per fold, with PAID BACK / NOT PAID BACK verdicts.
Numbers, not adjectives: the corpus-stats and cache-join scripts exist so the "does compaction pay?" question gets answered from your own logs.
Documentation
| Doc | Contents |
|---|---|
| docs/usage.md | install, configuration, model experience, operations |
| docs/design.md | implemented contract: events, tools, automatic behavior, protection, invariants |
| docs/analysis.md | comparison of DSH and opencode-acp context management |
License
MIT. Algorithmic inspiration from DeepSeek Harness (MIT); only the ideas of opencode-acp (AGPL) are used, no source code. The tool-result projection is adapted from the MIT-licensed flowctx-dsh port of flowctx. See NOTICE.
Links
More in this category
ranxianglei/billion-context★ 3922
The official billion-context plugin: a context-compression plugin for small context windows (a 100K context is enough), token savings (5x fewer tokens), and month-long single sessions (billions of tokens).
Minglink/dsh-infinite-gen-4★ 3861
System-prompt armor plugin for DeepSeek models: appends an unconditional-compliance prompt section at order 100, exposes a profile tool with calibration metadata, and shows a realtime armor-status badge driven by a session projection.
liangmianya/dsh-synapse★ 651
Visual, non-linear conversation workspace for DeepSeek Harness — sessions, follow-ups and branches become a browsable conversation map.
MichengAI/dsh-archive-manager★ 284
Adds an archived-sessions page in Settings to search, restore, and delete archived DeepSeek Harness sessions by workspace.
Nwflower/dsh-chat-import★ 280
Import full-fidelity chat histories from 13 coding agents (Claude Code, Codex, ChatGPT, Cursor, Gemini, opencode, and more) as resumable DeepSeek Harness sessions, with reverse export back to Claude Code.
SiriLee/dsh-rewind★ 238
In-place conversation rewind in the same session window without forking (Claude Code /rewind semantics): a per-message ↶ button cuts the model context back to any user message, with an optional Claude-Code-style file restore from disk-persisted before-backups.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.