Bundle
@xiaobanli/dsh-compact-after-task
Auto-compact a DSH conversation after the current task finishes, at a user-set context threshold. Settings-page switch + slider included.
- Source
- xiaoliang2
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 13 days ago
Readme
# dsh-compact-after-task
A [DSH](https://github.com/deepseek-ai/deepseek-harness) (Cordis) plugin that **auto-compacts a conversation after the current task finishes**, whenever context pressure crosses a threshold you set (default **50%** of the routed model's context window). Install it and stop manually clicking "compress" — and stop letting the context (and the bill) grow unchecked.
<img width="1188" height="978" alt="图片" src="https://github.com/user-attachments/assets/8bad7d73-2913-4589-80a6-51857e10a177" />
The switch and the threshold are adjustable directly in **Settings → Plugins** (toggle + slider); changes apply **immediately, no restart needed**.
## The problem it solves
In a long conversation the model re-receives the **entire history on every turn**. As the conversation grows, this bites harder and harder:
- **No compaction → exploding cost.** The bigger the context, the more tokens each turn re-sends. Even with a provider KV cache (cheaper than fresh input, still not free), the history is re-transmitted and re-billed every single turn. Rolling from tens of thousands to hundreds of thousands of tokens quietly multiplies per-turn cost — **the longer you talk, the more each reply costs, without you noticing.**
- **Manual compaction is tedious and easy to forget.** `/compact` or the UI compress button works, but you have to remember to use it — and people only remember once things are already slow and expensive, after several huge turns have been paid for.
- **The built-in auto-compaction is a late, mid-task safety net.** `dsh-compaction-basic` only fires at **80%** of the context window, and only at a step boundary *while a task is running* — by then you've already paid for several enormous requests, and the compaction interrupts work in progress.
- **Slower responses.** Longer prefixes mean more processing per request and a noticeably sluggish feel.
This plugin automates the "remember to compress" chore: **after each task finishes (the agent returns to idle) it checks the pressure and compacts at your chosen threshold** — dealing with cost *before* it hurts, not after.
## How it relates to the built-in compaction (complementary, not a replacement)
| | Built-in `dsh-compaction-basic` | This plugin |
|---|---|---|
| When | At each step boundary (`agent/pre-step`), mid-task | After the task fully finishes (agent returns to idle) |
| Threshold | `thresholdRatio` 0.8 (fixed) | `thresholdRatio` user-set, default 0.5 |
| Role | Last-resort mid-task safety net | Routine "slim down between tasks" |
The compaction itself is identical to `/compact`: it calls `ctx.compaction.compactNow()`, produces one `<compacted-summary>` checkpoint, and the conversation UI shows the usual "compacted N history items" card.
## Requirements
- DSH Desktop / DSH profile with the base bundle (`dsh-base`), which provides `dsh-compaction-basic`, `dsh-token-meter`, `dsh-agent` and `dsh-llm`.
- **Core auto-compaction works on any version.** The **settings-page card (toggle + slider) needs DSH ≥ `0.1.0-rc.7`** (released 2026-08-17): since rc.7 the Host serves every registered settings namespace to the browser automatically, so a third-party plugin's settings card works out of the box — this is the [official mechanism](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/cookbook/adding-a-settings-card.md). On rc.6 and earlier the namespace list was hardcoded and a third-party namespace was refused (`settings-not-exposed`) — **upgrade**, or apply the two-line patch below only if you cannot upgrade.
- **On rc.7+ this plugin reaches the compaction service via `agentPresets.serviceFor()`, not `inject: ["compaction"]`.** Since rc.7 introduced agent presets, the `compaction` service is no longer provided on the host plane — each session's preset (`standard`/`cordis`/…) mounts it inside an `isolate` realm, so a host-plane plugin that `inject`s `compaction` pends forever (`waiting for service: compaction`). This plugin instead calls the official read API `ctx.agentPresets.serviceFor(agent, "compaction")` when the agent goes idle (that API exists precisely for callers whose request is *about* a session but arrives from outside it); on rc.6 and earlier it falls back to `ctx.get("compaction")`.
- **For the settings card to appear, the package `exports` must expose `"./package.json": "./package.json"`.** `dsh-client-modules` discovers client bundles by resolving `<package>/package.json` to read the `dsh.client` metadata; if a package declares `exports` without that key, the lookup throws `ERR_PACKAGE_PATH_NOT_EXPORTED` and the package is silently cached as "not a client package" — the host half runs fine but the settings card never shows. All official packages ship this line in `exports`.
## Install
Two ways — pick **one** (do not mix them: the loader rejects a duplicate entry id).
### A. Standard: plugin market / `dsh plugin add` (recommended)
The package ships a `dsh.bundle.patch` pointing at its own `cordis.patch.yml`, so it is a **first-class bundle**: installing it via the market or the CLI adds it to the profile's `dsh.profile.bundles`, and on the next boot the loader applies the package's own patch layer automatically — **no manual editing needed**.
```sh
# from npm (after publishing) — or from GitHub directly:
dsh plugin --profile desktop add @xiaobanli/dsh-compact-after-task
dsh plugin --profile desktop add github:xiaoliang2/dsh-compact-after-task
```
Or, in the desktop app, open **Settings → Plugin Market** and one-click install. Restart DSH Desktop, done.
### B. Manual, profile-local (no market, no publishing)
1. Copy this package into the profile and make it resolvable from the profile's `node_modules` (same way `dshmarket` is installed):
```powershell
# from the DSH home, e.g. C:\Users\you\.dsh\profiles\desktop
Copy-Item -Recurse <path-to>\dsh-compact-after-task .\plugins\dsh-compact-after-task
New-Item -ItemType Junction -Path .\node_modules\@xiaobanli\dsh-compact-after-task -Target .\plugins\dsh-compact-after-task
```
2. Add the plugin row to the profile's `cordis.patch.yml` (your user patch layer, applied after every bundle layer):
```yaml
- insert:
- id: compact-after-task
name: '@xiaobanli/dsh-compact-after-task'
config:
enabled: true
thresholdRatio: 0.5
```
3. Declare the dependency in the profile's `package.json` so a later `pnpm install` does not prune it:
```json
"dependencies": {
"@xiaobanli/dsh-compact-after-task": "file:plugins/dsh-compact-after-task"
}
```
4. **Restart DSH Desktop** for the profile change to take effect.
> If you already installed it manually (method B), uninstall/remove it before installing via the market, otherwise the same entry id `compact-after-task` is inserted twice and the loader refuses to boot.
### Only on DSH ≤ 0.1.0-rc.6 (if you cannot upgrade)
Before rc.7 the Host exposed only a hardcoded namespace whitelist, so the settings card cannot read/write until you add this plugin's namespace to it (two files in the app install, under `resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh-host-apiproxy`):
- `lib/index.js` — in `const WEB_SETTINGS_NAMESPACES = [ ... ]` (~line 888) add `"compact-after-task",`
- `lib/types/api-proxy.js` — in the single-line `const WEB_SETTINGS_NAMESPACES = [...]` (~line 50) add `"compact-after-task"`
Then restart. Note DSH updates overwrite these files, so re-apply after each update. On rc.7+ this patch is unnecessary and these files no longer exist.
## Configuration
| Key | Type | Default | Meaning |
|---|---|---|---|
| `enabled` | boolean | `true` | Master switch. |
| `thresholdRatio` | number (0.05–1) | `0.5` | Compress when pressure reaches this fraction of the routed model's context window (`0.5` = 50%). |
| `onlyRoots` | boolean | `false` | Only compress top-level conversation agents (skip background subagents). |
### Adjust from the settings page (toggle + slider)
After install, an **Auto-compact** card appears under **Settings → Plugins → Configurable plugins**:
- **Toggle**: auto-compact after each task finishes;
- **Slider**: auto-compact threshold (5%–100%, 5% steps).
Releasing the slider saves immediately; the `settings/updated` event applies it live, **no restart needed**. The value persists in the settings document and takes precedence over the loader config in `cordis.patch.yml` (which acts as the default/base layer).
### Picking a threshold
Set it from "the max per-turn re-send you can tolerate ÷ the model's context window":
- DeepSeek's default 1M window: **0.3–0.6 is the sweet spot**; 0.5 is a conservative default.
- Want each turn to re-send ≤ 400K tokens → 0.4; ≤ 250K → 0.25.
- Don't go ≥ 0.8: the built-in compaction already fires at 0.8 at a step boundary, so a higher threshold here is meaningless.
- Small-window models (128K/64K): raise to 0.6–0.8, or you'll squeeze out useful working context.
- Too low (below ~0.1) means compressing after every small task, and the summary swallows details that were still useful.
Tune by feel after a couple of long tasks: compressing after every little task / summaries getting coarse → raise it; still re-sending hundreds of thousands of tokens per turn → lower it. Edit `thresholdRatio` in `cordis.patch.yml` and restart.
## Behavior details
- Listens to the `agent/status` event and acts only on the **`idle`** transition — i.e. the current task's drive has fully drained. This is the "compress after finishing the current task" moment; it never interrupts a running task.
- Uses the same pressure measurement as the built-in engine: `tokenMeter.measure(session).totalTokens` vs `contextWindow × thresholdRatio` (context window resolved via `llm.resolveModelInfo`).
- Compaction is a single `ctx.compaction.compactNow(agent, signal)` call — the same operation `/compact` runs; `compactNow` reserves next-turn admission, so queued messages are correctly deferred until the compaction finishes. On rc.7+ `ctx.compaction` is the instance returned by `agentPresets.serviceFor(agent, "compaction")`; the operation is unchanged.
- Expected failures are silent: `busy` (agent busy / a compaction already running) and `cancelled` are skipped; anything else is logged as a warning without disturbing agent lifecycle events.
- If the agent's preset mounts no compaction group (e.g. `minimal`), `serviceFor` returns `undefined` and that agent is skipped.
## Test
```bash
npm install
node test-compact-after-task.mjs
```
The unit test stubs the services and covers: idle + above threshold compacts, below threshold does not, `running` does not, `busy` is silent, `disabled` does nothing, `onlyRoots` filters subagents.
## License
[MIT](LICENSE)
Install
dsh plugin --profile web add github:xiaoliang2/dsh-compact-after-task#647b88dc93900be096b6882cfbab178aa36d5457
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install xiaobanli-dsh-compact-after-task from the hub