Introduction to the dsh-recall Plugin: Offloading Large Tool Results from Context and Retrieving on Demand with recall(id)

Introduction

The way DSH (DeepSeek Harness) runs dictates that the context will continuously expand: every step’s tool result stays in the session, and every subsequent step has to resend it. What truly consumes the context isn’t all results, but a few very large ones that are never read again. dsh-recall is a plugin designed for this: the heads and tails of large tool results stay in the conversation, while the middle parts are moved locally to SQLite. The model retrieves the needed fragments via recall("<id>").

It is maintained by sh-222, categorized under “Memory” in the community directory, currently starred by 1 (the directory page also shows 1 fork). It is a personal maintenance project, not an official DeepSeek product. Below is an introduction to its mechanism, installation, and configuration.

What is it

One-sentence positioning: A context-reclaim plugin. It does not delete or modify tool outputs; it only moves the middle paragraphs of results exceeding a threshold out of the conversation, replacing them with a short object ID and a line of description. The full text remains in the session logs and local storage.

Its difference from existing approaches: DSH’s built-in spill strategy and 15% compression only keep the head, discarding tail retrieval prompts and summaries; dsh-recall keeps both heads and tails, and supports the model retrieving content by character range or keywords. It is not full-text compression, nor does it delete old messages.

Core Functions

Trigger condition: On the next tool call, any tool result that is “older than the latest keep count” and “longer than thresholdChars” will be rewritten as head + [recall] description + tail; the middle part is stored as an object named md5(session:seq:chars)[:8], for example #4d3661d2. The model sees a placeholder like this in the dialogue:

XXXX… (600 chars of head) …

[recall] read call_A — 4000 chars total. The head and tail are above and below; the middle 3160
chars are stored as object #4d3661d2. Fetch any part with recall("4d3661d2") (optionally
recall("4d3661d2", start, count)), or search it with recall("4d3661d2", query).

XXXX… (240 chars of tail, or the whole stock spill notice when there is one)

Supporting capabilities:

  • The entire tail is preserved, especially the stock spill notice (Full formatted result stored at: …) including the opening parenthesis, so the model doesn’t lose positioning clues.
  • Optional token budget gate (budgetTokens), only moves content if the current surface truly exceeds the budget, avoiding premature compression in short sessions.
  • The recall tool supports three types of calls: retrieve the first 8,000 characters, retrieve by character range (single call limit 24,000), or search by keyword returning up to 20 matching lines and their character offsets. The unit of offset matches the reference in the description and can be directly fed back into range calls.
  • Storage is a SQLite file, payloads are compressed with deflate and keyed by the original SHA-256 hash; the same output read twice is stored only once. Uses PRAGMA application_id to reject foreign database files.
  • Version retention policy exists: maxAgeDays and maxBytes evict by oldest priority.
  • Accounting uses DSH’s own token meter, not chars / 4 estimation.
  • Rewriting follows DSH’s built-in compaction/prune + surfaceOp: {op:'replace'} path, with the original text preserved in persistent logs, and replaying results consistent after session recovery.
  • Storage checks (session, seq) to determine if it has already been moved, so printing [recall] text in the tool won’t skip the actual move.
  • Image blocks in tool results are unaffected except for the shortened text.
  • Below the composer is a strip displaying the amount moved, tokens left outside the context, recall hit count, and disk usage. Requires web profile (dsh.client.platform: web).

Statistics based on 8,504 real-world tool results in the README: results over 6 KB account for 4.6%, but these results account for 40.7% of all tool result character volume, 76% of which were never opened again. The author states the limit bluntly: tool results account for about 46% of the context; even if all are moved, the limit is only around 18% of the context. On the cache pricing path close to input pricing, there is 20–25% actual benefit; on the cheap cache path, it’s single-digit.

Installation and Enablement

Install using the official command under Web / TUI profile:

dsh plugin --profile web add github:sh-222/dsh-recall

After installation, the harness needs to be restarted; the plugin is only loaded at startup.

Electron’s desktop profile does not support CLI management by design. You need to manually edit ~/.dsh/profiles/desktop/package.json, adding dependencies and bundle lines, then install:

{
  "dependencies": { "dsh-recall": "github:sh-222/dsh-recall" },
  "dsh": { "profile": { "bundles": ["…", "dsh-recall"] } }
}
cd ~/.dsh/profiles/desktop && pnpm install --ignore-scripts

Upgrade and uninstall:

dsh plugin --profile web remove dsh-recall   # or directly change the bundle line then pnpm install

Uninstalling does not affect storage in ~/.local/share/dsh-recall/; simply delete it if no longer needed.

Runtime environment requirements: dsh >= 0.2.0-0 (declared in package.json); object storage requires Node >= 22.5 (uses standard library node:sqlite); strip requires web profile, no host-side requirements for the profile. When Node is older, the plugin can still load, and the recall tool can still respond, but it reports storage as unavailable.

Configuration

The following configuration items come from the Config validation definition in the plugin source code. They are validated one by one on load; unknown configuration keys result in a direct error rather than being silently ignored:

Configuration Default Description
thresholdChars 2500 Tool results exceeding this length are moved
headChars 600 Number of characters kept at the start of the conversation
tailChars 240 Number of characters kept at the end of the conversation
keep 2 The latest N tool results are never processed
budgetTokens 0 Only move if the current surface exceeds this token budget; 0 means only the threshold is considered
spillNoticeChars 600 Additional upper limit for tail characters to fully preserve the stock spill notice; 0 means disabled
maxAgeDays 0 Delete objects older than this many days; 0 means permanent retention
maxBytes 0 Evict by oldest priority when exceeding this byte count; 0 means no limit
enabled true Set to false to only disable the moving action; recall(id) is still usable
stateDir ~/.local/share/dsh-recall Directory where object storage is located

There is a very practical validation rule: headChars + tailChars must be less than thresholdChars; otherwise, the rewritten text is longer than the original, and the plugin simply refuses to start. Source code comments indicate that the default values of 2,500/600 are the empirical inflection points—the threshold of 6,000 only covers 23.5% of tool result volume, while 2,500 covers 53.0%.

Typical Usage

After installation and restart, the plugin works automatically in the background; no extra commands are needed. The model retrieves content via three recall calls:

recall("4d3661d2")                  # retrieve first 8,000 characters
recall("4d3661d2", 8000, 1600)      # retrieve 1600 characters starting from 8000, single call limit 24,000
recall("4d3661d2", query: "FATAL")  # return up to 20 matching lines and their character offsets

Offsets from search hits can be directly used as the start of a range call; they use the same Unicode code point unit and surrogate pairs won’t be split.

To confirm if this trade-off is worth it, look at the strip below the composer: move count, tokens left outside context, recall hit count, and disk usage. If the recall hit count is zero for a long time, it means nothing is being retrieved from what was moved; at this point, you can raise thresholdChars or set a tighter value for budgetTokens.

Applicable Scenarios and Notes

Suitable for: Long sessions, large amounts of tool output, scenarios where pricing is by token or cache retrieval cost is not low; teams that want to preserve complete session logs without deleting any original outputs.

Points to note:

  • The plugin runs with the permissions of the current dsh process and can directly read and write local directories. Read the source code before installing to confirm index.js, store.js, client.js, and cordis.patch.yml match expectations, and check the repository’s license.
  • This is a personal maintenance project with low star and fork counts; upgrades and issue response depend on the maintainer. You must verify the match between the repository version (0.5.0 in package.json) and the local dsh version yourself.
  • If storage is unavailable, the plugin won’t crash the profile, it will just log a line and have recall report storage as unavailable; this is a degradation behavior, not a failure.
  • The strip requires a web profile; on other profiles, the plugin can still move and retrieve, just without that status bar.
  • Once the eviction strategy (maxAgeDays / maxBytes) takes effect, pointers to deleted objects become invalid. This is an inherent cost of any capacity limit, so the default is not to delete anything.
  • Benefits are capped, around 18% of the context volume; on the cheap cache path, it’s more cost-effective to prioritize reducing new inputs and steps.
  • Directory Page: https://www.skillhub.cn/plugins/sh-222/dsh-recall
  • GitHub: https://github.com/sh-222/dsh-recall

The community directory is an independent site with no official affiliation to DeepSeek / Synthflow; dsh-recall is just a community plugin, not a product listed in the official app store.

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye