Session context triage for long DeepSeek Harness sessions: deterministic screeners trim stale, duplicate, failed, and oversized content through the official ctx.compaction seam.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:JohnXu22786/context-pruner
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. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
dsh-context-triage
A session context triage plugin for DeepSeek Harness (dsh) that automatically manages context volume during long sessions: it identifies and handles stale, duplicate, failed, oversized, and low-value message content to save token budget and curb context bloat.
Features
- Deterministic, zero model dependency: all verdicts and rewrites are produced by heuristic rules—no LLM calls, no extra services; behavior is predictable and testable offline.
- Five Screeners, each independently switchable:
Screener Verdict rule Handling action Stale output staleOutputTool call pairs more than N turns after the latest user message, with results still complete Call block rewritten to an archive summary, result block removed Repeated call repeatedCallCall pairs with the same tool and same arguments (JSON key order irrelevant) Only the most recent is kept; older call pairs are cut Failed call failedCallStale error results ( isError)Input arguments replaced with a failure stub (prevents leakage / saves space), error text kept and trimmed Oversized block oversizedBlockTool result text exceeds the character cap Head/tail trimmed, middle annotated with the omitted amount Stale reasoning staleReasoningReasoning blocks beyond the retention turns; kept blocks exceeding the length cap Removed; or trimmed (within the reserve region only trim, never delete) - Reserve area (
reserve): content within the most recent N user turns is never touched, avoiding disruption of context the model is currently using. - Exemption list (
exempt): two-dimensional tool-name and file-path glob exemption (protects stateful tools liketask/skill/todowrite/todoread/write/edit/batchby default). - "Worth it" check: any archive/stub action requires the rewrite to be strictly smaller than the original, ruling out changes that cost more; call-pair-level actions (archive = rewrite + cut) take effect or are abandoned as atomic groups.
- Audit report: every triage outputs structured statistics—counts and savings per reason, per-item details, and suggested compression ranges; visible through the tool, the command, and the CLI.
- Native integration with the dsh compression seam: implements
ctx.compaction(CompactionEngine); auto-pressure, context overflow, manual, and forced region compression all go through the official harness mechanism. Replacement summary messages use a checkpoint source recognized by any backend-agnostic consumer.
Installation and mounting
The plugin is distributed as a bundle: dsh.bundle.patch in package.json points to cordis.patch.yml (the patch inserts the plugin line; config can be overridden on the line).
Installing in DSH
dsh plugin --profile demo add github:JohnXu22786/context-pruner
Option 1 (mount from a local directory, recommended):
dsh plugin --profile web add link:/absolute/path/context-pruner
Option 2 (manual patch): merge the entry in cordis.patch.yml into the profile's cordis.patch.yml, or launch it directly as an overlay:
dsh web --patch ./cordis.patch.yml
Option 3 (git source):
dsh plugin --profile web add "github:your-repo/context-pruner#main"
After mounting, use dsh --profile web --dump-config to check whether the plugin line reaches the startup tree.
Note:
ctx.compactionallows only one provider per context. If your profile already loads another compression implementation (e.g., the built-in base compression backend), disable one of them via the patch'sdisabled: true—they cannot coexist.
Interface
Entry point (manifest)
| Item | Value |
|---|---|
| Package name | dsh-context-triage (dsh.bundle.patch in package.json declares the bundle) |
| Main entry | lib/index.js (main/exports; ./core and ./dsh subpaths available for embedders) |
| Plugin name | context-triage |
| Dependency injection | tools (required); the command registry is optionally probed via ctx.get('commands'), silently skipped if absent |
| Config | Exports Config (Schemastery Schema); defaults in the config table below |
The plugin is a functional plugin: it exports name / inject / Config / apply(ctx, config), called by cordis after validating config and filling defaults.
Provided extension points
| Extension point | Description |
|---|---|
Service ctx.compaction |
TriageCompactionEngine extends CompactionEngine, implementing compactIfNeeded (auto-pressure / overflow), compactNow (manual idle compression), compactRegion (forced region compression) |
Tool triage_history |
Model-visible; parameter dryRun?: boolean. Runs one triage and returns the audit report to the model; with dryRun=false and a worth-handling region present, applies it directly |
Command /triage |
Human command, bypasses the model; outputs the audit report and applies worthwhile handling |
How a compression transaction lands
Triage results are written to the session log as a standard compression transaction (append-only; history is not rewritten):
compaction/start(holds the lock until the pairedcompaction/end)compaction/summary(summary content, masked regions, masked seq list, and heuristic token cost;llmStreamCallabsent = non-model summary)user/message+surfaceOp: { op: 'replace', start, end }+sourceEventSeqs(replacement summary message,sourceis the checkpoint source)compaction/end
The model-visible history is derived from the session log; after the replacement, deriveMessages() naturally yields [summary message, ...retained content]—no in-place modification of historical records is needed.
Configuration
All fields have defaults; only override what you want to adjust. Invalid values (negative turns, out-of-range ratios, etc.) throw at load time. Example:
# cordis.patch.yml
- insert:
id: context-triage
name: dsh-context-triage
config:
budget:
contextTokens: 200000 # lower for smaller-context models
softRatio: 0.6
screeners:
staleOutput: { turns: 5 }
staleReasoning: { enabled: false }
exempt:
tools: [task, skill, write, edit]
| Field | Default | Description |
|---|---|---|
enabled |
true |
Master switch |
reserve.turns |
3 |
Reserve area: content within the most recent N user turns is not processed |
budget.contextTokens |
1000000 |
Estimated context window (tokens); determines the pressure ratio |
budget.softRatio |
0.7 |
Usage above this ratio → auto-compression triggers (pressure) |
budget.hardRatio |
0.9 |
Usage above this ratio → pressure report marked forced (hard) grade |
budget.minSavingsTokens |
2000 |
No action when estimated savings are below this |
screeners.staleOutput.turns |
8 |
Call pairs more than N turns after the latest user message are archived |
screeners.repeatedCall.enabled |
true |
Repeated calls cut |
screeners.failedCall.turns |
4 |
Failed-call handling threshold (turns) |
screeners.failedCall.errorKeepChars |
400 |
Error text kept for failed calls (chars) |
screeners.oversizedBlock.capChars |
6000 |
Tool result text cap (chars); beyond it, head/tail trimmed |
screeners.oversizedBlock.headChars / tailChars |
800 / 400 |
Head/tail length kept during trimming |
screeners.staleReasoning.keepTurns |
3 |
Keep reasoning blocks within the most recent N user turns |
screeners.staleReasoning.maxBlockChars |
2000 |
Length cap for kept reasoning blocks; beyond it, trimmed |
exempt.tools |
[task, skill, todowrite, todoread, write, edit, batch] |
Exempt tool names |
exempt.filePatterns |
[] |
Exempt path globs (match call arguments filePath/path), e.g. ['**/*.lock'] |
summary.capChars |
20000 |
Compression summary char cap |
summary.headRatio |
0.4 |
Head retention ratio when trimming the summary |
Local experience (no dsh required)
src/core is a framework-agnostic engine with an offline replay CLI; it runs directly against a JSONL session file:
npm install
npm run build
node lib/cli/replay.js examples/session.sample.jsonl --config examples/demo.config.json
node lib/cli/replay.js examples/session.sample.jsonl --config examples/demo.config.json --show-transcript
Replay format (one JSON event per line; seq determined by line order):
{"type":"user/message","text":"项目构建失败了"}
{"type":"assistant/message","reasoning":"…","calls":[{"id":"c1","name":"bash","arguments":"{\"cmd\":\"npm run build\"}"}]}
{"type":"tool/result","callId":"c1","text":"…","isError":false}
Sample output in examples/report.example.md; tests cover all screeners, merge priorities, atomic groups, audit consistency, and end-to-end replay (npm test).
Design trade-offs
- Token estimation is heuristic: CJK ≈ 1 token/char, others ≈ 4 chars/token; used only for pressure verdicts and audit statistics, not for billing. Configure
budget.contextTokensper your actual model window. - Rewrites always yield net savings: archive summaries, failure stubs, and trimming all compare the sizes before and after the action; unattractive findings are automatically abandoned. Call-pair actions are judged as atomic groups, so there is no half-done state like "summary kept, result split."
- Prompt caching: compression changes the message sequence, invalidating the prompt cache prefix after that point. In long sessions, saved tokens usually vastly outweigh cache recomputation cost; for pay-per-request providers (no cache billing) it is pure gain.
- Never touches user input: the oversized-block screener only acts on tool results; user messages are never rewritten unless the whole reserve region covers them.
License
Released under the MIT License.
Links
More in this category
volcengine/OpenViking#examples/dsh-memory-plugin★ 28649
OpenViking memory and context bundle for DeepSeek Harness: pre-step auto-recall and profile injection, session capture, `viking://` URI guarding, and recall/write memory tools backed by an OpenViking server.
vectorize-io/hindsight#coding-agents★ 20034
Hindsight, agent memory that learns: long-term project memory with auto recall and retain, knowledge pages, deep reflection, and per-repo memory banks.
dsh-engramory★ 154
The Engramory curated-memory discipline as an installable plugin ([npm: dsh-engramory](https://www.npmjs.com/package/dsh-engramory)): a deterministic 200-line / 25 KB cap on the `MEMORY.md` index via `ctx.tools.guard()` — growth denied, a shrinking rewrite always passes — plus the protocol registered as a runtime skill. The store is plain markdown, one file per fact, shared with Claude Code, Codex, Kiro, and OpenClaw.
bowenliang123/dsh-context★ 107
Context insight panel: see what the model's context window is made of and how it evolves — composition vs. window size, per-request history, compression/injection events, and per-message token stats.
Co-Engram/Co-Engram★ 68
Self-evolving team memory as plain Markdown in git: a native Cordis plugin that registers 38 bare-name memory tools plus a prompt-signals section re-evaluated at every assembly, with RPE reinforcement, decay and sleep consolidation; shares one data repo with its Claude Code (MCP) and OpenClaw hosts; verified against DSH 0.1.0-rc.6.
omdsh-dev/dsh-mnemon★ 55
Cross-agent, local-first persistent memory plugin for DeepSeek Harness (DSH), powered by Mnemon. It shares long-term memory across Mnemon-enabled agents and adds runtime memory, searchable project documents, semantic recall, knowledge graph, and a Sidebar UI.