返回目录
其他 待识别

context-pruner

JohnXu22786/context-pruner

Session context triage plugin for DeepSeek Harness (dsh): prunes stale, repeated, failed and oversized context to save token budget.

Stars
0
Forks
0
Issues
0
更新
2 天前

PROJECT TOPICS

项目标签

PROJECT README

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 call repeatedCall | 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 call failedCall | Stale error results (isError) | Input arguments replaced with a failure stub (prevents leakage / saves space), error text kept and trimmed | | Oversized block oversizedBlock | Tool result text exceeds the character cap | Head/tail trimmed, middle annotated with the omitted amount | | Stale reasoning staleReasoning | 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 like task/skill/todowrite/todoread/write/edit/batch by 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.compaction allows 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's disabled: 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):

  1. compaction/start (holds the lock until the paired compaction/end)
  2. compaction/summary (summary content, masked regions, masked seq list, and heuristic token cost; llmStreamCall absent = non-model summary)
  3. user/message + surfaceOp: { op: 'replace', start, end } + sourceEventSeqs (replacement summary message, source is the checkpoint source)
  4. 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.contextTokens per 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.

CLASSIFICATION EVIDENCE

分类依据

项目类型待识别
功能分类其他
规则置信度

系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。