Preface¶
In DeepSeek Harness (DSH), the large blocks of text returned by tool calls enter the model context. Harness truncates by default based on size: it keeps the beginning and end, discarding the middle. Agent hosts like Codex, pi, etc., often adopt similar practices.
This truncation ignores the content’s shape. When test output shows 5,000 passing lines with 3 failures in the middle, head-tail truncation often leaves only the passing records, causing the model to misjudge that all tests passed. unclecode’s toolshrink addresses this issue: it first identifies the output pattern, then retains the informative parts, rather than simply slicing by position.
What is this¶
toolshrink is a DSH plugin that can also be used standalone as a Node.js library. Maintained by unclecode, the repository is located at: github.com/unclecode/toolshrink. The project is categorized as “Model Inference” in the SkillHub community directory, with approximately 10 GitHub stars.
One-line positioning: Cut large agent tool output by what it means, not by where it was cut. (Compress by meaning, not by truncation point.) The plugin includes 13 content-aware reducers; when none match, it falls back to size-based truncation, ensuring results always stay within budget.
Differences from Default Truncation¶
The README provides a vitest example: input of 31,958 characters, 805 lines, with a budget of 2,000 characters.
| Method | Output Size | What the Model Sees |
|---|---|---|
| Head+Tail Truncation | 1,904 characters | Primarily the summary |
| toolshrink | 255 characters | Which tests failed, reasons, line numbers, and summary |
Removed content is marked at the output end, e.g., ... 15,903 characters, 401 lines omitted .... If spill storage is configured, the full original text is written to disk with a locator, preventing silent loss.
Core Feature: 13 Cuts¶
Each cut identifies a text pattern and is tried in order; the first matching cut executes. If none match, the size fallback is used. All cuts follow four rules: no returning half-lines, no splitting UTF-16 surrogate pairs, explicitly marking removed amounts, and ensuring second calls do not change the result.
| Cut | Identifies | Retains | Discards |
|---|---|---|---|
diff |
git diff, patch | Changed lines, file and hunk headers, 1 line of context on both sides | Unchanged context |
json |
Single JSON values | Structure, 3 samples per long array, 5 keys per wide object, counts | Repeated records |
tests |
vitest, jest, pytest, cargo test, go test | Failed items and descriptions, summary | Passing tests |
build |
tsc, cargo, gcc, webpack, esbuild | Errors and warnings with code frames, summary | Build progress |
stacktrace |
Node, Python, Java, Ruby stacks | Messages and user code frames | Dependency library frames (count marked) |
log |
Logs with timestamps | Errors and warnings with preceding lines, ending | Regular lines |
tree |
find, ls -R, file lists | Directory structure, 8 entries per directory, counts | Remaining entries in crowded directories |
repeat |
Retry storms, progress spam | 2 samples per pattern + omission explanation | Consecutive near-duplicate lines |
lint |
eslint, ruff, clippy | Count and sample locations per rule, worst file | Same rule repetitions |
install |
npm, pip, pnpm, cargo installs | Summary, versions, deprecations, vulnerabilities, errors | Download progress |
csv |
CSV, TSV, pipe tables | Header, first 5 rows, last 2 rows, row and column counts | Middle rows |
gitlog |
git log (two formats) | Latest 15 commits, total, authors and counts | Older commits |
size |
Fallback | bash: end; grep/read: start; unknown: head and tail | Rest (count marked) |
Adding a new cut requires only a file implementing the shared interface; no need to fork the entire project.
Installation and Enablement¶
DSH uses an “everything is a plugin” architecture; SkillHub (skillhub.cn) is a community skills directory for Chinese users, with no official affiliation to DeepSeek or High-Flyer. Before installation, it’s recommended to browse the repository source and confirm the MIT license (see package.json). Plugins run with the current dsh process permissions.
Install to the web profile with one command:
dsh plugin --profile web add github:unclecode/toolshrink
The package includes a dsh.bundle manifest, automatically mounting on next start with a default character budget of 50,000. Adjustments can be made in the user-level ~/.dsh/cordis.patch.yml:
- id: toolshrink
config:
maxChars: 20000
log: /tmp/toolshrink.log
For local development, clone the repo, run npm install && npm run build, then mount the adapter file via insert:
- insert:
- id: toolshrink
name: /path/to/toolshrink/adapters/harness/toolshrink.mjs
config:
maxChars: 50000 # Trigger compression if exceeded (default 50000)
maxLines: 2000 # Or if line count exceeded (default 2000)
maxLineChars: 0 # Single-line length limit, 0 disables (default 0)
disable: [json] # Skip specified cuts (default none)
spillDir: ~/.dsh-toolshrink # Directory for full original text
log: /tmp/toolshrink.log # Log one line per cut; omit to suppress
Example log line format: bash 64151 -> 2942 via tree+size.
Typical Usage¶
As a Library¶
Current version is 0.1.0 (ESM, main points to ./dist/index.js).
import { shrink, FileSpillStore } from 'toolshrink'
const out = shrink(bigText, { tool: 'bash', command: 'npm test' }, {
budget: { maxChars: 20_000 },
spill: new FileSpillStore({ dir: '/tmp/spills' }), // Optional
})
out.content // Text to pass to the model
out.reduced // False if input didn't exceed budget
out.strategy // e.g., "tests", "diff+size", "size:tail", "none"
out.note // Human-readable one-line explanation
out.stats // inputChars, outputChars, keptLines, droppedLines, etc.
The second parameter hint is optional: tool affects size truncation direction, command helps identify tests and diffs, path helps identify JSON and logs.
Custom Cuts¶
A cut file exports three members by default, with the filename as the cut name:
// mycut.mjs
export default {
name: 'mycut',
detect(text, hint) {
return hint.command?.startsWith('kubectl') ?? false
},
reduce(text, hint, budget) {
const content = text.slice(0, budget.maxChars)
return {
content,
reduced: true,
strategy: 'mycut',
note: 'kept the part I know matters',
stats: {
inputChars: text.length, inputLines: 0,
outputChars: content.length, outputLines: 0,
},
}
},
}
Load and use:
import { shrink, loadReducers } from 'toolshrink'
const mine = await loadReducers('/path/to/my-cuts')
shrink(text, hint, { extra: mine }) // Tried before built-in cuts
shrink(text, hint, { only: ['tests', 'diff'] }) // Restrict and order
shrink(text, hint, { disable: ['json'] }) // Skip specified cuts
Spill: Full Original Text Recoverable¶
After enabling the spill store, the full original text is written to disk before compression, with a locator appended to the compressed text, e.g.:
[full output saved as spill:bash-d63d2aebb643: directories sampled to 8 entries each]
store.load('spill:bash-d63d2aebb643') can restore the original text at the byte level. The default file store cleans up after 24 hours; the storage backend can be replaced.
Use Cases and Notes¶
Suitable For
- Developers who frequently call shell, test, build, lint, and other tools in DSH or custom agents, with outputs often exceeding context budgets.
- Scenarios requiring auditable truncation (explicit omission amount + optional spill) rather than silent data loss.
Usage Notes
- When no cut matches, it still falls back to
size, behaving similarly to “truncation by position”; improve recognition viahintand custom cuts. - README notes: With a 3,000-character budget, a 60,000-character
findresult truncated by head leaves the model seeing the omission marker and proactively switching to aggregate queries—this is by design, relying on the model to read the marker. - Installation and running permissions match the
dshprocess;spillDirwrites to local disk, so pay attention to path and disk usage. - The repository TODO lists planned cuts (e.g.,
semantic,sql) not yet implemented; do not treat them as existing capabilities.
Conclusion¶
toolshrink changes Agent tool output truncation from “byte-slicing” to “pattern-preserving”: tests keep failures, diffs keep changes, logs keep errors. For DSH users, a single dsh plugin add mounts it; for finer control, adjust YAML or write custom cuts.
- SkillHub directory page: https://www.skillhub.cn/plugins/unclecode/toolshrink
- GitHub repository: https://github.com/unclecode/toolshrink