JohnXu22786/context-pruner
Session context triage plugin for DeepSeek Harness (dsh): prunes stale, repeated, failed and oversized context to save token budget.
已收录
0
Memory
Bundle 已验证
功能介绍
面向 DeepSeek Harness 长会话的上下文分诊插件:通过官方 ctx.compaction 接缝,用确定性规则筛查并裁剪过期、重复、失败与超大的上下文内容。
适合
- 旧工具输出、重复调用、失败记录或超大结果大量占用上下文的长 DSH 会话。
- 需要确定性、无模型依赖的压缩,并要求保护近期轮次、支持豁免和结构化节省报告的团队。
不适合
- 已使用其他 ctx.compaction 提供者且不准备禁用其中之一的配置;每个上下文只能启用一个。
- 冗余上下文很少的短会话;裁剪价值有限,还可能使后续提示缓存前缀失效。
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
staleOutput| Tool 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 callrepeatedCall| Call pairs with the same tool and same arguments (JSON key order irrelevant) | Only the most recent is kept; older call pairs are cut | | Failed callfailedCall| Stale error results (isError) | Input arguments replaced with a failure stub (prevents leakage / saves space), error text kept and trimmed | | Oversized blockoversizedBlock| Tool result text exceeds the character cap | Head/tail trimmed, middle annotated with the omitted amount | | Stale reasoningstaleReasoning| Reasoning 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.
常见问题常见问题
在启用了 DSH 的终端中执行已验证命令 dsh plugin --profile default add github:JohnXu22786/context-pruner。命令会解析公开 package 元数据,并保持插件与本页展示的目录身份一致。
兼容性以页面上展示的 bundle 与 profile 状态为准。如果某个 profile 尚未检测到,请先保持禁用,并在生产启用前阅读仓库文档。
GitHub 链接和 activity 元数据是 release 与维护状态的来源。新版本发布后重新查看本页,确认目录已经观察到最新版本。