Introduction¶
Sessions in DeepSeek Harness (hereinafter referred to as DSH) tend to get longer. When you open a session that has accumulated for a long time, a common phenomenon is: the interface freezes for a few seconds, followed by a white screen, and then DSH disappears completely. Switching to another session works fine, and reinstalling or clearing the cache doesn’t help—because the freeze occurs in the few seconds after the data has been retrieved and is just about to be drawn to the screen; it’s neither in the network nor in the model.
dsh-lag-guard is designed for this purpose. It is a client plugin written by community maintainer 456erddgr that does one thing: make very long sessions openable normally. Below is an introduction to the causes it detected, how it fixes them, and what you will see after installing it.
Evidence First: How Much Data is Actually Moved When Opening a Session¶
The author’s own session DSH 安装与快速启动 is 5.3MB after unzipping, containing a total of 1087 events and 176 user/assistant messages. However, the data fetching parameters DSH uses when opening a session are hardcoded:
// dsh-api-session-controller/lib/client.js:2177
const HISTORY_PAGE_OPTIONS = { maxMessages: 500, turnWindow: { minMessages: 50, minTurns: 2 } };
176 messages didn’t reach 500, so the “Home Page” equals “All,” moving 484 events / 3290.8KB at once, accounting for 61.7% of the entire session. Among them, the 4 request/header events account for 2510.9KB, or about 47.1%; a single header.tools contains the complete JSON schemas for 201 tools, totaling 627KB. In other words, nearly half of the transferred data was a full-text copy of the manuals for all available tools at that time, and such events accumulate a copy for every round of request.
Layering two more factors on top of this:
// dsh-client-ui-chat/lib/client.js:290-300
useVirtualizer({
getItemKey: (i) => items[i]?.turn ?? i,
estimateSize: () => TURN_SPACING_PX,
measureElement: () => TURN_SPACING_PX, // Dynamic measurement is disabled
});
Virtualization switches by turn, and this session only has 7 turns with an extremely uneven distribution (turn 1 has 375 events, 1.2MB; turn 5 has 309, 1.6MB). There are only a few blocks in the window, which means the entire chunk is drawn at once.
// dsh-client-ui-conversation/lib/client.js:1641
const toolsChanged = previous !== undefined && JSON.stringify(previous.tools) !== JSON.stringify(prompt.tools);
Every view rebuild performs a pure JSON.stringify comparison on the 3 headers in the window and hundreds of schemas in total, taking 13.4ms and producing 3.6MB of temporary strings. The problem is not in the number of renderings (notifier batches microtasks), nor in dsh-chunked-list (that’s just a persistent linked list), but rather that the 3.3MB of data, non-virtualized turns, and repeated 13.4ms serialization all land on the first-frame sync path, occupying the main thread until it freezes.
How It Fixes It¶
You can’t change the Host side; the typert registry throws an error directly when encountering a duplicate name. The fix lies on the browser side: the client package of the session controller exports SessionEventStream, and its follow() is the only way to fetch all history (opening a windowed home page, repairing gaps, and real-time appending all use this async generator), while readPage() uses the adjacent pagination interface. The plugin wraps these two methods and does two things.
-
Tighten the requests. Default
maxMessagesis tightened from 500 to 120, andturnWindowfrom{50, 2}to{20, 1}. Taking the smaller value ensures that manually set smaller windows are not expanded and guaranteesminMessages <= maxMessages—violating this inequality causes the Host’svalidatePageRequestto reject the entire page asgateway/bad-request. -
Trim the frame. If
request/header.toolsexceeds 200,000 characters after serialization, replace it with an empty array, keepingtoolsDropped(the original count) andtoolsChars(the original size); if a singletool/resultexceeds 128KB, truncate it and mark it ascontentTruncated(a fallback; 0 hits in the author’s session).
Comparison of the same session after the fix:
| Window Parameters | Events Moved | Size | Dropped Tools | Compared to Full Session |
|---|---|---|---|---|
| 500 / 50,2 (Original) | 484 | 3290.8KB | — | 61.7% |
| 120 / 20,1 (Default) | 175 | 1604.9KB | 350.5KB | −78% |
| 50 / 10,1 (More Aggressive) | 67 | 770.2KB | 143.0KB | −81% |
Installation and Activation¶
You can find it by searching for lag-guard (or 卡/卡顿) in the plugin market and clicking install. The manual install command comes from the README:
dsh plugin --profile desktop add github:456erddgr/dsh-lag-guard
You must restart DSH after installing. After restarting, open that session that has been constantly lagging you, and the console should show an additional line:
[lag-guard] session-77a714c5-... opened with 175 events, dropped 627KB of tool schemas in 29ms
If this line does not appear, the plugin did not load. Check dsh.profile.bundles in the profile’s package.json to see if dsh-lag-guard is present.
Typical Usage: Adjusting the Window and Seeing Savings¶
To adjust parameters yourself, edit the profile’s cordis.patch.yml. Note that the patch replaces the entire config, so keys not written will revert to default values:
- id: dsh-lag-guard
config:
maxMessages: 80 # More frugal, shorter first screen
minMessages: 10
minTurns: 1
maxHeaderToolsChars: 50000
To temporarily revert changes without saving to disk, execute the following in the rendering process console; it takes effect immediately:
globalThis.__DSH_LAG_GUARD__ = { maxMessages: 500, minMessages: 50, minTurns: 2, maxHeaderToolsChars: Infinity };
To quantify how much it saves, run the built-in metrics collector:
node tools/sink.mjs # Listens on local port 17891, writes to tools/metrics.jsonl
node tools/sink.mjs --flush # Aggregation: how many times opened, how many events moved, how many KB schemas cut, longest lag
The data is sent only to 127.0.0.1; it does not connect to the internet or report. To uninstall, remove dsh-lag-guard from dependencies and dsh.profile.bundles, run pnpm install, and restart.
What Gets Dropped and What Doesn’t¶
Nothing that is the main body of the conversation is dropped; not a single piece of data is deleted:
- The first-screen history becomes shorter; click “Load Earlier” to retrieve content from earlier.
- For those requests with extremely large schemas, the small badge “Toolset Changed” in the Chat will become invalid (
toolsbecomes an empty array, the comparison result is always “unchanged”). - The “Tool List / Tool Diff” view in the Trajectory tab for those few requests will be empty, for the same reason.
- Single tool outputs exceeding 128KB will be truncated in display and marked.
Not affected: Session log files, Host pagination and validation, model context, actual tool execution, projections, real-time streams, and other sessions.
Verification and Known Limitations¶
The tests built into the plugin don’t fool themselves with mocks: it puts DSH’s real dsh-api-session-controller/lib/client.js into node:vm to run (only stubbing window.__ModuleLoader__ and three dependencies), then loads the plugin, using real session events to drive follow and readPage:
node test/verify.mjs --asar <path to @deepseek-ai> --session <path to session.v4.jsonl>
The output shows all 21 contract items as green, including “open request clamps maxMessages 500 -> 120”, “request/header tools are dropped before publication”, and “a second module load does not stack wrappers”.
A few limitations that need to be clarified:
- The volume numbers are precise, but the feel is not. The author did not perform an end-to-end frame rate comparison; whether it still lags after restarting depends on your perception; if it still lags, try adjusting
maxMessagesto 50. - It fixes the “Transfer + First Frame” layer. If the lag comes from elsewhere (for example, a third-party plugin doing heavy work when a session opens), it can at most alleviate it and cannot save you.
- It breaks when upstream changes occur. It relies on the exports of
SessionEventStreamand the shape offollow/readPage. If DSH changes its name one day,install()will returnfalseand quietly do nothing—it won’t break DSH, but it won’t help you either.
Version information given in the README: package.json is 0.2.0, changelog only goes up to 0.1.1 and 0.1.0. License is MIT. The project does not modify any files in the DSH installation directory; all logic is in a single 253-line file.
Use Cases and Reminders¶
Suitable for DSH users who frequently open long, heavy history sessions and are already troubleshooting first-frame lag. It runs on the browser side with the permissions of the current DSH process. It is recommended to read the source code and license before installing to confirm compatibility with your environment.
This is a third-party plugin listed in the community directory, maintained by 456erddgr, with no official affiliation with DeepSeek / Huansuan. The directory page is at https://www.skillhub.cn/plugins/456erddgr/dsh-lag-guard, and the source code is at https://github.com/456erddgr/dsh-lag-guard.