dsh-lag-guard: Limit the long session history window to 120 entries to prevent a multi-megabyte tool schema from bloating the first frame.

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.

  1. Tighten the requests. Default maxMessages is tightened from 500 to 120, and turnWindow from {50, 2} to {20, 1}. Taking the smaller value ensures that manually set smaller windows are not expanded and guarantees minMessages <= maxMessages—violating this inequality causes the Host’s validatePageRequest to reject the entire page as gateway/bad-request.

  2. Trim the frame. If request/header.tools exceeds 200,000 characters after serialization, replace it with an empty array, keeping toolsDropped (the original count) and toolsChars (the original size); if a single tool/result exceeds 128KB, truncate it and mark it as contentTruncated (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 (tools becomes 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 maxMessages to 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 SessionEventStream and the shape of follow / readPage. If DSH changes its name one day, install() will return false and 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.

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

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

Xiaoye