Preface¶
In agent conversations, history grows with each turn and eventually requires compression. DeepSeek Harness (DSH) has a built-in /compact, but this command faces two practical problems when the network is slow or upstream is unstable: when the agent is busy, it simply refuses the request, causing the current compression attempt to be lost; during execution, the composer is locked. If the summarizer gets stuck and doesn’t stream output, you have no button to press and nowhere to input.
The plugin introduced below addresses these two problems: imrcpan/dsh-compact-enhanced. It changes /compact to return immediately, queue when the agent is busy, and execute in the background, provides /compact-cancel for cancellation, and uses a status line in the slash menu to tell you what is happening.
What is this¶
A DSH (DeepSeek Harness) plugin. DSH’s philosophy is “everything is a plugin”, capabilities are assembled via plugins. This plugin targets the “manually trigger context compression” workflow. It does not modify the model, does not modify prompt templates, only changes command scheduling and observability.
Problems Solved:
- The built-in
/compactrejects requests when the agent is not idle, causing requests to be lost in the middle of a running turn. - The built-in
/compactlocks the command-line composer during execution, making it impossible to interrupt if the summarizer gets stuck. - There is no convenient way to tell the user “how much was compressed” or “whether the compression was successful” after completion.
Commands and Behavior¶
The plugin provides two commands, neither accepts arguments:
| Command | Behavior |
|---|---|
/compact |
Starts immediately if the agent is idle; otherwise, queues until the current turn ends before starting |
/compact-cancel |
Discards queued requests, aborts running summarizer requests, or cancels compression started by other command modules |
Both commands do not accept arguments. When there is already a request queued or running, /compact will reject the second request and indicate which command to use to cancel it in the error message.
The resolution of the cancellation line depends on whether the abort signal reaches the summarizer:
Compaction cancelled.— The run has finished.Cancellation requested; the summarizer has not stopped yet.— The summarizer has not responded to the abort withincancelTimeoutMs, and the request is still pending.
What you see in the Session Panel¶
The request’s own lifecycle line appears immediately:
Compaction started in the background.
Compaction queued; it starts when the current turn ends.
Because the plugin passes the command ID as sourceCommandId to the compression engine, compression events are attached to this line. After the summary is finalized, this line upgrades to a standard compression card. After attempting completion, the plugin adds another command/done for the same ID to record the result:
Compacted 42 history items (~12,345 tokens).
No compactable history yet.
Compaction cancelled.
And the documented failure messages for categories like busy, changed, summary, commit, and persistence.
That extra command/done is deliberately added: the command service closes the request immediately after the handler returns (this is exactly why the composer can unlock), so to report background results on the original line, the plugin has to go through the lifecycle event again for the same ID, and the client will fold it into the existing line.
Real-time Status in the Slash Menu¶
/compact is not a fixed line of text in the slash menu, but a status line that changes with the state and sometimes appends the current session’s token estimate:
| State | Menu Line Text |
|---|---|
| Idle | Ready · 12.3k token |
| Waiting for current turn to end | Queued · 12.3k token |
| Summarizer is running | Compacting · 12.3k token |
| Token estimate unavailable | Ready / Queued / Compacting, without the token part |
The token label uses one decimal place plus a fixed k unit (Claude Code style): 3.9k token, 12.0k token, 0.9k token. The segment is omitted entirely when the estimate is zero or token counting is unavailable, so you will not see 0.0k token.
The /compact-cancel line reflects the synchronous state: Cancel the queued compaction, Cancel the running compaction, or No queued or running compaction to cancel.
Implementation-wise, the plugin replaces the compact descriptor in commands.list() at the command directory layer and removes the built-in definition ID to prevent the client from re-localizing the description back to generic text; it re-emits commands/change whenever the state changes, so the next time the menu is opened, it will re-fetch. The trade-off is that the menu line shows the raw command name, losing the built-in localized labels and icons—this is a compromise made to gain real-time status.
Installation and Enablement¶
The package itself is a DSH bundle: package.json declares dsh.bundle.patch, so standard installation will mount the command.
dsh plugin --profile web add github:imrcpan/dsh-compact-enhanced
You need to restart the profile after installation because the combination relationships are read at startup. The cordis.patch.yml released with the package disables the built-in command-compact line and inserts the plugin line, thereby freeing up the /compact name.
When installing from a local checkout, you must install it as a bundle so its manifest takes effect:
dsh plugin --profile web add /absolute/path/to/dsh-compact-enhanced
file: dependencies are also acceptable, but that won’t apply the bundle layer, and you would need to manually copy the line from example/cordis.patch.yml into the profile’s cordis.patch.yml.
Typical Usage¶
Just use it directly in the conversation:
# Trigger a compaction, returns immediately
/compact
# If queued or running, want to cancel
/compact-cancel
Environment Requirements:
- DeepSeek Harness
0.2.1-rc.5(other releases may vary;package.jsonenginesspecifiesdsh >=0.2.1-rc.2) - Node.js
>=20.3(the plugin usesAbortSignal.any)
Configuration Options¶
Configuration is optional; the bundle layer provides no configuration.
| key | Default | Meaning |
|---|---|---|
retryDelayMs |
250 |
How long to wait before retrying after a startup collision with a newly opened turn |
maxQueueAttempts |
40 |
How many times to retry before writing a queue failure to the log |
cancelTimeoutMs |
2000 |
How long to wait for a running job to settle after the cancel command is issued; reports the request is still pending after timeout |
Implementation Details¶
- The command registry does not allow duplicate global registrations, so the bundle layer disables the built-in
command-compactline, and the plugin registers its own/compactto this name. If the profile still has the built-in line mounted, the plugin will catch the duplicate registration and log a warning, keeping/compact-canceland leaving the built-in command in place. - There is no way to access the compression entry point from the profile root: the agent preset mounts it in an isolated per-agent group, and the actual
agent.ctxdoes not expose it. The plugin therefore usescommands.findto resolve thecompactdefinition available to the agent (usually the one mounted by the preset, whose handler closure holds the isolated service). That handler runs in the background with the plugin’s ownAbortSignal. If no other definition is found, it falls back to the engine reachable by the plugin’s own context; if neither is reachable, the command will report that compression is unavailable instead of silently doing nothing. - The agent preset registers a scoped
/compactitself, which shadows the global replacement. The plugin handles the/compactline at the command service layer, so the queuing logic acts on the definition resolved from the directory; the plugin restores the originalexecutemethod upon uninstall. - Cancellation is simply the abort signal passed to the delegated handler (or directly to the engine), so it can affect the summarizer request.
- If a startup collides with a newly opened turn, it will retry after
retryDelayMs, up tomaxQueueAttemptstimes, before writing the failure. Queued state is segmented by agent and exists only in memory. - When the agent is busy, nothing runs: the queue waits for the agent to be idle, then starts in maintenance, exactly where the built-in command would have run.
Suitable Scenarios and Notes¶
Suitable for: DSH developers working in slow network link or upstream unstable environments; those who need to manually compress history and want to keep conversation input unblocked during compression; those who want to cancel operations without waiting for the summarizer to naturally finish.
Things to note:
- This plugin runs with the permissions of the current dsh process; its capability boundaries are the commands and services already in the profile. You should check the source code and license (MIT) before installing.
- There is no client button. Cancellation can only be done via
/compact-cancel, which works because/compactreturns before the summarizer starts. - The queue is in memory. Restarting the host will end the turn and discard queued requests; you will need to run
/compactagain afterwards. - Automatic pressure compression has not been modified, only wrapped by the manual command. Even if an automatic compression has already been done, queued requests will still execute a compression once, because the plugin doesn’t know what the next idle check will find.
- The cancellation report is instant, but the final line must wait for the background result to settle.
Conclusion¶
The value of this plugin is clear: /compact changes from a “request that will be rejected, cannot be interrupted, and results are invisible” to a “background task that returns immediately, is queueable, cancellable, and has visible status”. The code is small, queue and cancellation states are saved in memory per agent, and configuration consists of only three numeric values. If your scenario involves long sessions and unstable upstream, it is worth a try.
Directory Page: https://www.skillhub.cn/plugins/imrcpan/dsh-compact-enhanced
Repository: https://github.com/imrcpan/dsh-compact-enhanced