dsh-cwl
Structured context eviction for DeepSeek Harness — deterministic, zero-LLM, no summarization lossiness
- Stars
- 0
- Language
- JavaScript
- Created
- Sep 1, 2026
- Updated
- Sep 3, 2026
Introduction
dsh-cwl
CWL — Context Window Lifecycle for DeepSeek Harness: structured context eviction for long-horizon agents.
Paradigm: Beyond Compaction: Structured Context Eviction for Long-Horizon Agents (arXiv:2606.11213, Kiz8)
English | 简体中文
Why not summarization compaction?
Compaction (the standard response to context pressure) summarizes history with an LLM. Four structural problems (per the CWL paper):
- Unpredictable lossiness — the summarizer decides what matters, not the task.
- Structural destruction — causal chains (tool call → output → decision → action) collapse into prose.
- Blocking cost — a full LLM call fires mid-task, under token pressure.
- Compression-induced hallucination — summarization under length pressure is a known failure mode.
CWL treats the transcript as a structured record of work and evicts deterministically:
the agent's trajectory is inferred into a typed episode graph (exploration expl / action act,
with dependency edges), and when context pressure exceeds budget, a zero-LLM, deterministic policy
strips content in graduated levels — exploration episodes first (pure context, safest), then action
episodes whose effects are already persisted. User messages are never evicted.
How it works
- Episode inference (automatic, no agent annotation needed): consecutive same-type tool
batches merge into semantic episodes (
explfor pure read/search — including read-onlybashlike grep/cat —actfor anything with side effects: edit/write/write-style bash). Each user message closes the current episode (a turn boundary), and episodes are capped at a batch limit, so even a single-request long autonomous run (dozens of tool calls) splits into bounded, evictable segments instead of collapsing into one giant episode. Anactthat touches files an earlierexplread gets a dependency edge. - Pressure metering: real context pressure = input + cacheRead + output + reasoning tokens
(accumulated from
assistant/messageusage events —tokenMeter.measure().totalTokensomits cacheRead, which dominates long sessions). - Graduated eviction on the
agent/pre-stepwaterfall (before every LLM call):- evict unexplored-dependent
explepisodes first (keeping a one-line "explored: …" marker) - then oldest completed
actepisodes - never touch the newest tail (preserve-recent) or user messages
- evicted ranges are replaced with a lightweight marker via the official surface-replace seam
(original events stay in the log;
cwl_recallcan restore file paths).
- evict unexplored-dependent
Install
dsh plugin --profile <name> add dsh-cwl # from npm
dsh plugin --profile <name> add github:kalifun/dsh-cwl # or from GitHub
Or vendor the directory and add to your composition:
- id: dsh-cwl
name: ./dsh-cwl/index.js
Usage
No configuration needed. It stays completely inert while context is under budget (default 80% of the model's context window), and starts evicting only when pressure exceeds budget.
# Optional: override the budget (tokens) — for testing pressure behavior
DSH_CWL_BUDGET=30000 dsh web
Eviction policy (deterministic cache-replay validation: eviction −24% cacheRead, strategy-independent; batch best mean −24.7%, consistent across 7 sessions → defaults below; override via env):
| Env var | Default | Values | Effect |
|---|---|---|---|
DSH_CWL_EVICT_ORDER | tail | tail / oldest | oldest evicts oldest episodes first |
DSH_CWL_EVICT_BATCH | on | 0 / false / off to disable | merge adjacent episodes into one surface replace (fewer cache breaks) |
DSH_CWL_EVICT_TAIL_WINDOW | 0 | N | only evict episodes whose end falls within the last N surface nodes |
DSH_CWL_STRIP | on | 0 to disable | fine-grained level: stub large tool-result content in expl episodes before whole-episode eviction (structure preserved) |
DSH_CWL_STRIP_THRESHOLD | 1500 | chars | minimum result text length to be stubbed |
# back to the conservative config (oldest, per-episode replaces)
DSH_CWL_EVICT_ORDER=oldest DSH_CWL_EVICT_BATCH=0 dsh web
Session analysis (per-round token breakdown + "cacheRead of the round after an eviction"):
node tools/analyze-session.mjs <session.jsonl>
Agent-facing tools:
| Tool | Purpose |
|---|---|
cwl_recall | list file paths touched by evicted episodes, to re-read on demand |
Observability:
| Endpoint | Purpose |
|---|---|
GET /api/cwl/evictions | eviction log (session → episodes evicted) |
POST /api/cwl/force | debug: force one eviction on a session |
Verification
node check.js # pure-function unit checks
Long-session pressure test (12 rounds of dialogue, ~200K cacheRead tokens):
baseline vs CWL — see benchmarks/ in the source repo for scripts and reports.
| metric | baseline | CWL | Δ |
|---|---|---|---|
| steps | 30 | 26 | −13% |
| inputTokens | 28,478 | 10,343 | −64% |
| cacheReadTokens | 200,576 | 178,432 | −11% |
| outputTokens | 2,809 | 2,107 | −25% |
All 12 rounds completed correctly; eviction did not degrade task quality.
License
MIT