dsh-auto-compact
August 16, 2026 · View on GitHub
A DeepSeek Harness plugin that automatically compacts a session once its measured context reaches a configurable absolute token threshold.
It does not invent a new summarizer: it drives the harness's built-in
compaction engine — the exact ctx.compaction service behind the built-in
/compact command — through the same durable, lock-protected surface
replacement path. The only thing this plugin adds is a user-controlled
absolute threshold policy.
Default threshold: 262144 tokens (256K).
Table of contents
- Why it exists
- How it works
- Coverage: every session, every preset
- Features
- Requirements
- Installation
- Configuration
- Behavior and semantics
- Logging
- Uninstallation
- Local development and tests
- Repository layout
- Compatibility
- Troubleshooting
- Security model
- License
Why it exists
The harness's built-in @deepseek-ai/dsh-compaction-basic backend already
compacts automatically, but its trigger is relative: a fraction of the
current model's context window (thresholdRatio, default 0.8).
That is a great default, but some users want a policy that does not move when the routed model changes:
| Policy | Built-in | This plugin |
|---|---|---|
| Trigger | 0.8 × model context window | explicit token count, e.g. 262144 |
| Default | varies with the model | 262144 (256K) |
| Scope | one compaction backend instance | process-wide, per-session backend resolution |
Manual /compact | still available | still available |
When the absolute threshold is lower than the built-in ratio threshold, this plugin fires first. When it is higher, the built-in policy may fire first and this plugin re-measures and stays idle — both share the same engine, lock, and summary format, so they cannot double-compact the same span concurrently.
How it works
agent/pre-step (every session)
│
▼
ctx.tokenMeter.measure(agent.session)
│
│ totalTokens < thresholdTokens ?
├── yes ──▶ do nothing, continue the step
│
▼ no
resolve the agent's own compaction backend:
serviceForAgent(ctx, agent, 'compaction')
│
├── absent ──▶ one warning per agent, skip (preset has no /compact)
│
▼ present
select the older, tool-pair-balanced surface span
(keep a recent tail worth at least retainTokens)
│
▼
ctx.compaction.compactRegion(start, end, agent, signal)
│
▼
re-measure; compact again if still above the threshold
(up to maxCompactions times per check)
│
▼
continue the model step no matter what happened
Key points:
- Measurement uses the platform's own
ctx.tokenMeter, the same replay-aware estimator the built-in compaction backend consumes.totalTokensincludes the latest durable request envelope plus the current conversation surface. - Range selection walks the token-priced surface from the tail, keeps a
recent verbatim budget (
retainTokens), and snaps the cut backwards until no unanswered assistanttool-call/tool-resultpair is split. - Execution is the built-in
ctx.compaction.compactRegion()call: the compaction backend records its durablecompaction/start…compaction/endbracket, asks the model for a summary, and replaces the selected surface span with one user-role checkpoint. All lock, persistence, retry, and summary semantics belong to the harness, not to this plugin. - Failure handling is non-blocking. A failed or impossible compaction is logged and the model step continues unchanged; this plugin never vetoes a turn.
Coverage: every session, every preset
The plugin is installed on the host plane (a profile bundle), not inside a single agent preset:
- It registers one
agent/pre-steplistener process-wide. - For every event it asks the agent itself for its compaction service
(
serviceForAgent(ctx, agent, 'compaction')), so the correct per-preset, per-session isolated backend instance is used. - Consequently it works for:
- every agent preset that mounts a compaction backend
(
standard,code,cordis, localminimal-compact,anchored-standard, …); - fresh sessions, resumed sessions, and sessions loaded after a restart;
- top-level agents and subagents.
- every agent preset that mounts a compaction backend
(
Presets that intentionally mount no compaction backend (for example the shipped
minimal preset, which has no /compact at all) are detected and skipped with
a single warning per agent. There is nothing to compact with there.
Features
- Absolute, user-configurable threshold — default
262144tokens (256K). - Human-friendly values accepted:
262144,"256k","256K","1m". - Configurable verbatim tail (
retainTokens, default32768). - Configurable per-check retry cap (
maxCompactions, default3). - Configurable kill switch (
enabled, defaulttrue). - Tool-call/tool-result aware cuts — never splits an open tool pairing.
- Zero runtime npm dependencies; pure ESM host plugin.
- All compaction work executes inside the harness's built-in backend.
- Presets without a compaction backend (for example the shipped
minimalpreset) get a plugin-ownedcompaction-basicfallback engine withauto: false, so the absolute-threshold check still works there. The preset stays otherwise minimal: no/compactcommand, no pruner, no built-in ratio pressure. - Idle/resume compaction (v0.2.2+): opening or resuming a conversation whose
context is already above the threshold compacts it through the built-in
compactNowmaintenance path — no follow-up message is required. - Idempotent installer and uninstaller.
Requirements
| Requirement | Version / note |
|---|---|
| DeepSeek Harness | 0.1.0-rc.6 (web profile developed and verified against) |
dsh on PATH | launcher for dsh plugin ... |
pnpm on PATH | used internally by dsh plugin |
| Node.js | >= 20 (the plugin itself is dependency-free) |
| Preset compaction backend | sessions should use a preset that mounts @deepseek-ai/dsh-compaction-basic |
Installation
From a local checkout
git clone https://github.com/Zh-U-hB/dsh-auto-compact.git
cd dsh-auto-compact
./install.sh
Or, if the repository is already checked out:
cd /path/to/dsh-auto-compact
./install.sh
What install.sh does
-
Removes any rows a pre-1.0 prototype may have written into
~/.dsh/.agent-presets/*/agent.cordis.yml. -
Runs:
dsh plugin --profile web add /absolute/path/to/dsh-auto-compactBecause
package.jsondeclaresdsh.bundle.patch,dsh pluginappends the bundle to the web profile and inserts the row:- id: auto-compact name: dsh-auto-compact
Activate
Profile bundles are loaded when the process boots, so restart the web surface:
# Ctrl+C in the terminal running dsh web, then:
dsh web
Then hard-refresh the browser once (Cmd+Shift+R / Ctrl+Shift+R).
From that point on the policy is active for every session in the process, including sessions you resume afterwards.
Install into another profile
DSH_PROFILE=tui ./install.sh # or any other profile name
For profiles without agent presets, the plugin still works as long as the
profile composes a ctx.compaction backend and ctx.tokenMeter on the host
plane (the standard dsh-base composition does).
Set the threshold in the web settings UI (v0.2.0+)
Open the harness settings gear, choose Plugins → Configurable, and use the
Auto Compact card. The input accepts a plain token count (262144) or a
human-unit value (256k, 1m; 1024-based). Saving writes the value through
the platform settings service into the profile's settings.yaml, so it
survives restarts and wins over the row config below. Discard resets the
field back to the row config (or the 256K default).
The settings card itself is loaded as part of the plugin's client bundle, so
after upgrading from an earlier version, restart dsh web once.
The row-level configuration below remains the base/default layer:
Configuration
Edit the profile's own patch layer:
~/.dsh/profiles/web/cordis.patch.yml
Default configuration (this block is optional — every key shown is the default):
- id: auto-compact
config:
thresholdTokens: 262144 # 256 × 1024; "256k" / "1m" also accepted
retainTokens: 32768 # minimum recent tail kept verbatim
maxCompactions: 3 # maximum consecutive compactions per check
enabled: true # false pauses the plugin without uninstalling
Examples:
# Compact earlier: at 128K tokens.
- id: auto-compact
config:
thresholdTokens: 131072
# Use human units and keep a larger tail.
- id: auto-compact
config:
thresholdTokens: 256k
retainTokens: 64k
# Pause without uninstalling.
- id: auto-compact
config:
enabled: false
Restart dsh web after changing the profile patch.
Validation rules
thresholdTokensandretainTokensmust be positive integers (or human-unit strings that resolve to one).retainTokens < thresholdTokens.maxCompactionsmust be a positive integer.- Unknown config keys fail plugin load with a descriptive error, so a typo cannot silently fall back to defaults.
Behavior and semantics
tokenMeter replay fallback
If the platform's replay-aware ctx.tokenMeter.measure() throws for a session
(for example, a log interrupted across a step boundary is missing a matching
step/start), the plugin temporarily wraps the tokenMeter instance and falls
back to a surface-plus-request-envelope token estimate (the same fixed-density
heuristic the token meter uses) for compaction decisions. The warning
dsh-auto-compact: tokenMeter replay failed (...) is logged once per session.
This keeps compaction working on damaged-but-usable sessions; healthy sessions
never use the fallback.
The same absolute threshold is also checked on agent/created while the
agent is idle, so a resumed session that was already over the threshold is
compacted as soon as it is opened. compactNow runs as an agent maintenance
job (the same path as the built-in /compact command); if a turn has already
started, the pre-step check handles it instead.
When the check runs
The check runs on the agent/pre-step waterfall — immediately before the model
request for a step is assembled. Because compaction then runs inside the open
turn, the compaction backend's automatic path is used, which is the same
mechanism the built-in ratio policy uses.
What "context reached the threshold" means
ctx.tokenMeter.measure(session).totalTokens is used. It is the harness's own
replay-aware estimate of the latest durable request envelope plus the current
conversation surface. It is an estimate, not a provider-exact token count, and
it is deliberately the same number the built-in compaction backend compares
against.
What happens when compaction is impossible
- No safe cut exists (for example, the tail is one huge unfinished tool unit): one warning is logged per session until the condition clears.
- The threshold is reached but the backend refuses (
busy,changed,summary,commit,persistence, …): the error is logged and the step continues. - The threshold is still exceeded after
maxCompactionsattempts: the plugin logs a warning and continues the turn. A single oversized indivisible node cannot be repaired by surface compaction — the same limitation the built-in backend documents.
Relationship with the built-in /compact command
/compact keeps working exactly as before. The manual command compacts one
useful balanced span below pressure thresholds on an idle agent; this plugin
compacts at step boundaries when the absolute threshold is crossed. Both use
the same ctx.compaction implementation and therefore the same durable lock,
so concurrent or nested runs are impossible.
Threshold policy and model changes
Because the threshold is absolute, switching the routed model does not change when this plugin fires. The built-in ratio policy still runs alongside it and may fire earlier for models with a small context window; that is intentional and safe.
Logging
All messages are prefixed with dsh-auto-compact: and use the harness logger:
| Level | Message pattern | Meaning |
|---|---|---|
info | context at N tokens reached the ... threshold | a compaction attempt starts |
info | idle context at N tokens reached the ... threshold | an idle/resumed session compacts without a new message |
info | idle compaction shadowed ... | an idle compaction completed |
info | compacted N history items (~N tokens shadowed) | an attempt succeeded |
warn | no tool-pair-balanced older span is compactable | threshold exceeded, nothing safe to compact (rate-limited per session) |
warn | context is still at N tokens after N compaction attempt(s) | retry cap exhausted (rate-limited per session) |
warn | agent "..." has no ctx.compaction service and no fallback engine could be mounted | backend unavailable; the turn continues (once per agent) |
warn | automatic compaction failed (...) | backend error; the turn continues |
Uninstallation
./uninstall.sh
The script runs:
dsh plugin --profile web remove dsh-auto-compact
and also removes any legacy preset-local rows. Restart dsh web afterwards.
Local development and tests
No package installation is required for development: the runtime plugin has no dependencies.
npm test # node --test unit + integration-style apply tests
npm run check # syntax-check plugin and scripts, then run tests
Test coverage:
- config parsing/validation (defaults,
128k/1m, rejections); - balanced-cut folding around open tool pairings;
- surface-span selection against token-meter measurements;
apply()behavior: threshold reached, below threshold, backend throwing, missing backend (warning once per agent);- the settings namespace updating the runtime threshold;
- the client settings-card bundle registration.
A test/mount-smoke.mjs helper is included for one-shot headless checks that a
preset composition mounts and exposes ctx.compaction without making any model
request.
Repository layout
dsh-auto-compact/
├── lib/
│ ├── index.js # host plugin: threshold enforcement + settings namespace
│ └── client.js # web settings card (Plugins → Configurable)
├── scripts/
│ └── manage-presets.mjs # legacy preset-row cleanup helper
├── test/
│ ├── unit.test.mjs # config + range-selection unit tests
│ ├── apply.test.mjs # apply() + settings integration tests
│ ├── client.test.mjs # client bundle registration smoke test
│ └── mount-smoke.mjs # headless preset-mount smoke helper
├── cordis.patch.yml # bundle patch: inserts the auto-compact row
├── install.sh # dsh plugin add wrapper
├── uninstall.sh # dsh plugin remove wrapper
├── package.json # package + dsh.bundle.patch + dsh.client metadata
└── README.md / README.zh.md
Compatibility
Developed and verified against DeepSeek Harness 0.1.0-rc.6 (web profile).
The plugin depends on stable harness seams (ctx.tokenMeter,
agent/pre-step, agent.ctx, ctx.compaction.compactRegion) but these are
developer-preview internals: after a harness upgrade, re-run the test suite and
start a fresh session before relying on the plugin.
The installer works with the standard dsh plugin command and keeps the plugin
as a linked local package, so edits to your checkout are visible after the next
dsh web restart.
Troubleshooting
The plugin row is in dsh --profile web --dump-config, but nothing happens
The process must be restarted. Host-plane bundles are loaded at boot; editing
the profile on disk is not hot-reloaded into a running dsh web.
A session never compacts
- Check
enabledis notfalse. - Check the session's preset actually mounts
@deepseek-ai/dsh-compaction-basic; the shippedminimalpreset does not. - Check the harness log for the
dsh-auto-compact:messages above. - Remember the threshold counts the whole measured request envelope plus surface; a mostly-tool-call session can take longer to cross it than the raw transcript size suggests.
A preset shows "has no ctx.compaction service and no fallback engine could be mounted"
Since v0.2.1 the plugin mounts a compaction-basic fallback engine into
presets that have none (including the shipped minimal preset). The warning
above now only appears when that fallback could not be constructed — for
example in a profile that also lacks @deepseek-ai/dsh-compaction-basic or
@deepseek-ai/dsh-llm on the host plane.
I edited cordis.patch.yml and nothing changed
Profile patch changes also require a dsh web restart.
Security model
- The plugin registers no HTTP endpoints, tools, commands, or settings writers.
- It only reads
ctx.tokenMeterand invokes the existingctx.compactionservice that the session's preset already trusts. - It never constructs file paths, performs I/O, or handles user input beyond validating its own YAML config.
- Every mutation of conversation history is performed by the harness's built-in compaction backend under its existing sandbox/durability rules.