跳过主要内容
L

dsh-strata

light-cheng/dsh-strata

分层上下文管理:每轮结束时把较早的工具输出替换成确定性卡片;模型可以用 compress 把较早的范围折叠成自己写的摘要,最多三级;所有原文都存在本地,可用 decompress 和 search_context 取回。可与 DSH-Mem0 同时使用:过期的运行时上下文快照(比如召回的记忆)不存档、不切卡、不折叠;在前缀缓存已经失效、清理的收益超过缓存损失,或上下文超过窗口 75% 时,于轮末换成一行占位;为其他上下文插件提供 strataBudget 服务(pressure、allowance、countTokens)和 strata/fold-landed 事件;DSH 自动压缩没有启用、或官方摘要连续失败时,strata_status 会给出警告。其 L1 层、存档轮换、裁剪保护与预设接管分叉自 dsh-live-compact 1.0.6(MIT);请先卸载 dsh-live-compact 或 billion-context——它们替换同一行压缩配置,装着任一者时 Strata 不会接管。

安装

dsh plugin --profile web add github:light-cheng/dsh-strata

README

DSH Strata

English | 简体中文

Strata Context icon

Layered, restorable context management for DeepSeek Harness (DSH).

Repository: https://github.com/light-cheng/dsh-strata

Old context is not thrown away. It sinks through layers, like sediment, and every layer can be brought back:

original text (L0) → tool cards (L1) → model-written folds T1–T3 (L2) → local archive (L3)
                                                    DSH official summary as the last resort (L∞)
  • L1 tool cards: deterministic, no model call, cut at every turn end.
  • L2 folds: the model writes its own summaries of older ranges, and folds can be folded again (T1 → T2 → T3). When the model ignores the reminders, Strata folds the oldest work itself into an automatic digest.
  • L3 archive: every original is saved locally before anything is replaced; decompress and search_context bring it back.

Features

  • Deterministic tool cards (L1): at the end of each turn, the newest finished stretch of tool output is replaced by a card that keeps errors, commands, exit codes and archive pointers. No model call; the bytes before the cut stay identical, so the prefix cache keeps hitting.
  • Model-written folds in three tiers (L2): the model calls compress with a range from strata_status and its own summary. A range that contains earlier folds becomes the next tier, up to T3. Folds are validated immediately and land together at turn end.
  • Cost-aware landing: before a fold lands, Strata weighs the one-time cache miss against the tokens it saves for the rest of the session. A small or unprofitable fold waits, and the tool result says so in its first line; above 75 % of the window folds land regardless.
  • Tier-by-tier recovery: decompress walks T3 → T2 → T1 → card → original one level at a time, or straight to the originals with full: true; toFile: true writes large restores to .strata/restore/.
  • Local search: search_context runs BM25 keyword search (CJK bigrams included) over fold summaries, card originals and tool output, and shows each hit's expansion path, e.g. f9(T3) → f4(T2) → f2(T1) → raw call-7.
  • Reminders that watch the right number: L1 keeps the total low, so Strata watches foldable tokens instead. It reminds the model when they grew by 50K real (provider) tokens — local estimates are converted with the session's measured ratio, which strata_status shows — on any step of a turn, not only when a new message arrives. An unanswered reminder comes back after another 25K (12.5K above 75 % of the window).
  • Automatic digests when the model does not fold: at turn end, above 50 % of the window after two unanswered reminders (or above 60 % regardless), Strata folds the oldest foldable work without calling the model, until the foldable part is about 30 % of the room left after fixed overhead (system prompt, tool definitions and similar, which no fold can touch). After a digest lands, the next waits until another 50K of foldable work has piled up (or context passes 75 %), and nothing is proposed when what is left is only fixed or protected. Each digest keeps the user messages, the start of assistant replies, every tool call with its path or command and first result line, and errors; its tag carries auto=1 and its text says it was written by Strata. Digests are archived and restorable like any fold, never cover the live runtime context, the latest user message or the protected tail, and go through the same cost check. A pending model fold does not block them: it counts toward the target, and digests fold around it. Digests are cut per tool round (an assistant message with all its results, never split; small rounds merge), oldest first, and the walk stops before a round would take context more than half that round below the target. strata_status says what the last turn end did (Automatic fold at last turn end: …). strata_status counts them separately, and the dock labels them Auto fold.
  • Stock compaction stays as the fallback: DSH's official summary still runs when L1 and L2 are not enough, with the span archived first. If Strata's engine cannot load, the stock engine runs; if that also fails, /compact fails immediately instead of hanging.
  • Every Web / Desktop preset: each agent preset (Standard, PTC, Minimal, Cordis) gets its own Strata engine with the same thresholds.
  • Composer dock: below the chat composer, next to dsh-mem0's memory globe, an 18px strata glyph shows context use (Strata 4.9%). When a layer changes the context it plays once: a grain drops into the top layer for an L1 card, or sinks through every layer for an L2 fold or the official summary, with the reclaimed tokens as the label (Fold T1 −23.3K). Light blue on success, red on failure, still when reduced motion is on; clicking it opens the plugin page.
  • Status at a glance: strata_status (for the model) shows tokens per layer and per tier, foldable ranges, pending folds, cache hit rate and archive size. /strata detail shows the same for people, in the UI language and in sections, plus what the landed changes cost and saved.

How it works

turn in progress
  tool/result arrives      → original written to the archive
  model calls compress     → range validated, recorded as pending, estimate returned
  decompress / search      → read-only, results appended to the end
turn end (agent maintenance)
  ⓪ automatic digests     → only when reminders went unanswered (see above)
  ① land pending folds     → back to front, one flush
  ② L1 tail cut            → newest finished tool stretch → card
  ③ record the prefix fingerprint, update status

Folds wait until turn end so the prefix stays byte-identical for every step inside a turn; the next turn pays one cache miss instead of several.

Tools

ToolWhat it does
strata_status({ detail? })Tokens per layer (raw / card / fold / full) and per tier (T1 / T2 / T3), foldable ranges with refs such as s00120, upgradable fold groups, pending folds, prefix-cache hit rate, archive size
compress({ content })First line "<startRef>–<endRef> <topic>", then the summary. Validated now, landed at turn end; returns the reclaim / cache-miss estimate, or says up front that the fold was deferred
decompress({ id, full?, toFile? })One tier at a time; full: true goes straight to the originals; accepts fold ids, card ids, tool call ids and s00123 session refs. Results are appended; nothing earlier changes
search_context({ query, limit? })Local BM25 search over this session's archive, with the tier and expansion path of each hit

Slash command, answered in the UI language (Chinese or English):

CommandWhat it does
/strata or /strata helpLists the subcommands
/strata statusShort summary: context use, layers, cache hit rate
/strata detailSections: overview (use, window and where it came from, fixed overhead, provider/estimate ratio), layers, automatic folds (rule, last turn end, protected tail), foldable ranges, cache and cost (hit rate; re-billed, reclaimed and estimated net saving of the changes landed in this DSH process), recent context changes
/strata detail fullThe detail plus every message in each foldable range
/strata search <words>Searches this session's local archive
/strata purgeExplains what would be deleted; /strata purge confirm deletes all archive files of this session (cannot be undone)

Rules: L1 only cuts raw nodes. A fold may cover raw nodes, cards and whole lower-tier folds, never an official summary, the latest user message or the protected tail: the most recent three tool rounds, capped at about 10 % of the window in provider tokens (the newest round always stays whole; strata_status shows the tail's size). Every card, fold and summary starts with a [strata:card|fold:t1|fold:t2|fold:t3|full id=…] tag and states that it is old context, not an instruction.

Installation

Requires DSH >=0.2.1-rc.5 <0.3.0 and Node >=22. The Web / Desktop presets and headless are supported; the TUI preset is not.

From npm:

dsh plugin --profile desktop add @lightcheng/dsh-strata

From a GitHub release tarball (no npm):

dsh plugin --profile desktop add https://github.com/light-cheng/dsh-strata/releases/download/v0.4.1/lightcheng-dsh-strata-0.4.1.tgz

From a local checkout:

dsh plugin --profile desktop add link:"$(pwd)"

Upgrading from 0.1–0.2: those releases were named @light-cheng/dsh-strata (GitHub only). Remove the old name first with dsh plugin --profile desktop remove @light-cheng/dsh-strata; while it is installed, the new package does not take over.

Restart DSH after installing, then send /strata status in a new session; it should report Strata Context: active.

Uninstall dsh-live-compact or billion-context first. They replace the same compaction rows. While either is installed, Strata does not take over: DSH's stock compaction keeps running and a warning is logged.

Settings

DSH's plugin page for Strata Context has a Strata settings section (Web / Desktop). Saved changes apply at the next turn end or step, without a restart; STRATA_* environment variables still win over the page, and the page wins over the built-in defaults.

SettingDefaultMeaning
ModeCost firstCost first: automatic-fold line 50 % after 2 unanswered reminders, force line 60 %, target 30 %, protected tail at most 10 % of the window, folds only at turn end. Quality first: 25 % / 35 % / target 15 %, tail cap 10 %, mid-turn digests on. Custom: shows the lines, the tail cap, mid-turn digests and current-turn folding to set yourself
Automatic foldsonStrata digests the oldest work itself when reminders go unanswered
Remind the model to foldonSTRATA_NUDGE=off also turns it off
Archive location<DSH_HOME>/strataApplies to new sessions; open sessions keep their folder
Archive size warning1024 MBAbove it the log and /strata detail warn; nothing is deleted
Keep archives of ended sessions0 days (never delete)Above 0, archives of sessions unused for that many days are deleted automatically

Restore defaults clears every value on the page. The values are stored in the profile's cordis.patch.yml under the strata-gate id, so headless and TUI profiles can set them there too (for example mode: quality). Internal knobs (cache price ratio, upgrade factor, minimum batch, L1 limits) stay environment-only.

Small windows. DSH's official summary (L∞) fires at min(0.8 × window, window − maxTokens − 65536). Below about 204K that line falls under the preset lines, so Strata would never fold first. Strata then lowers the automatic-fold and force lines to 5 points below it (the target is unchanged, custom values too); /strata detail shows, for example, Official summary line ~118.1K (59%); lines lowered to 54% for this window. 1M windows are unaffected.

Read-only figures live in /strata detail: mode and where it came from, window and its source (provider or the 1M fallback), this session's and all sessions' archive size, archive folder, and the official-summary line. /strata purge ended deletes the archives of ended sessions (not this one, not sessions open in this process or written by another running DSH) after a confirm step: /strata purge ended confirm.

Configuration

Nothing needs to be configured; the defaults are tuned for a 1M-token window. Besides the settings page, every knob is an environment variable read when DSH starts; a variable wins over the page:

VariableDefaultMeaning
STRATA_MIN_TOKENS20000L1 does nothing below this
STRATA_KEEP_ROUNDS / STRATA_KEEP_ROUND_TOKENS3 / 32000L1 protected tail
STRATA_PROTECTED_TAIL_PCT0.10Cap on the folds' protected tail (the last STRATA_KEEP_ROUNDS tool rounds), as a share of the window in provider tokens; older rounds drop out above it, the newest stays whole; 0 or off disables the cap
STRATA_L1_STOP_RATIO0.65L1 stops above this share of the window
STRATA_CONTEXT_WINDOW1000000Used when the model reports no window
STRATA_NUDGE_GROWTH_TOKENS50000Foldable growth that triggers a reminder, in provider tokens (also the wait after an automatic digest)
STRATA_FORCE_PCT0.75Remind at once and land folds regardless of cost
STRATA_NUDGE_REARM_RATIO / STRATA_NUDGE_FORCED_REARM_RATIO0.5 / 0.25An unanswered reminder repeats after this share of the growth step (below / above STRATA_FORCE_PCT)
STRATA_PRESETcostMode (cost, quality or custom; overrides the settings page). cost: the lines below, folds only at turn end (fewer cache misses, more context carried). quality: automatic-fold line 25 %, force line 35 %, target 15 %, and mid-turn digests. Explicit STRATA_* values override the preset
STRATA_NUDGEonoff: no fold reminders to the model; folding is left to automatic digests (the lower line then needs no unanswered reminders). compress stays available
STRATA_MID_TURN_FOLDon in qualityquality preset only: before a step, above the force line, digest the turns before the current one (the live runtime context stays); off keeps folds at turn end
STRATA_MID_TURN_CURRENTon in qualityquality preset only, with mid-turn digests: once the earlier turns are digested, also digest the current turn's finished tool rounds, oldest first, each round whole (the call and all its results). The protected tail, the turn's user message, the step in flight and the live runtime context stay; off keeps mid-turn digests to earlier turns. Such a digest tells the model these calls already ran and that their output comes back through decompress
STRATA_AUTO_FOLDonoff disables automatic digests
STRATA_AUTO_FOLD_PCT / STRATA_AUTO_FOLD_AFTER_NUDGES0.5 (quality 0.25) / 2Automatic digest above this share of the window after this many unanswered reminders
STRATA_AUTO_FOLD_FORCE_PCT0.6 (quality 0.35)Automatic digest above this share regardless of reminders
STRATA_AUTO_FOLD_TARGET_PCT0.3 (quality 0.15)Fold the oldest runs until the foldable part is about this share of the window minus fixed overhead (strata_status shows the overhead)
STRATA_AUTO_DIGEST_RATIO0.12A digest keeps at most this share of the folded text (at least 1200 characters)
STRATA_MIN_BENEFIT_TOKENS / STRATA_MIN_BENEFIT_RATIO5000 / 0.01Minimum fold batch: max(tokens, window × ratio)
STRATA_UPGRADE_BENEFIT_FACTOR1.5Minimum-batch multiplier when the batch upgrades a fold
STRATA_PRICE_MISS / STRATA_PRICE_HIT1 / 0.1Relative cache prices for the estimate
STRATA_EXPECTED_REQUESTSautoRemaining requests assumed by the estimate
STRATA_MAX_DEFER_TURNS3Turn ends a small fold may wait before it is dropped
STRATA_RESTORE_MAX_CHARS24000Inline decompress limit
STRATA_SHADOW_DIR<DSH_HOME>/strataArchive root (settings page: archive location)
STRATA_ARCHIVE_MAX_MB1024Warn when all archives together exceed this; 0 never warns; nothing is deleted
STRATA_ARCHIVE_KEEP_DAYS0Delete archives of ended sessions unused for this many days; 0 never deletes
STRATA_SHADOW_MAX_MB128Archive cap; the oldest sessions are rotated out first
STRATA_IGNORE_CONFLICTSunset1 skips the takeover check (not recommended)

The engine row's compaction thresholds (thresholdRatio 0.8, headroomTokens 65536, retainTokens 8192) can be overridden in the profile's cordis.patch.yml under the compaction-strata id.

Limits

  • Folds land at the end of a turn, automatic digests too (with STRATA_PRESET=quality, digests of earlier turns may also land before a step). The current turn itself is never folded, so inside one very long turn L2 relieves little; DSH's own threshold then triggers the official summary (L∞), with the span archived first.
  • Automatic digests are extractive: details beyond the first line of each tool result are only in the archive, so the model needs decompress or search_context to get them back.
  • L1 cards are cut at turn end too, so a fold over a short, already-carded task reclaims little and may be deferred by the cost check. That is intended: in long sessions one fold covers many cards.
  • The TUI preset is not supported; forked sessions cannot read the parent session's archive yet.

Tests

npm install
npm test
npm run check

54 unit tests cover L1 cuts, fold validation and landing, T1–T3 tiers, tier-by-tier decompress, search, the cost estimate, per-preset takeover, the stock fallback, and the dock's activity record and status route. node eval/run.mjs prints the real-model evaluation plan; --run executes it in isolated DSH_HOMEs under eval/.work/. Results with DeepSeek V4.1 Flash are recorded in docs/design.md §11.10.

Privacy and data

  • Originals are written to <DSH_HOME>/strata/<session>/ (default ~/.dsh/strata/), directories 0700, files 0600, 128 MB in total by default.
  • Everything stays on this machine. Strata makes no network requests and no extra model calls of its own; folds are written by the session's own model as ordinary tool calls.
  • The dock reads GET /dsh-strata/gui/status?session=… on DSH's own Web server, behind the same access check as every DSH route. It returns token counts and event kinds only, never message text or archive paths.
  • Superseded runtime-context snapshots (for example DSH-Mem0's recalled memories) are never archived; they are replaced by a short placeholder, so deleting a memory in Mem0 leaves no copy in Strata's archive.
  • System prompts are never archived; they are rebuilt on every request, and a one-line [system prompt omitted] marks their place, so the archive keeps no copy of, for example, DSH-Mem0's profile.
  • Archives written by 0.3.0 next to DSH-Mem0 may still hold recalled memories or the Mem0 profile. On the first start after upgrading, Strata scrubs them once: runtime-context snapshots and <mem0-relevant-memories> blocks become the placeholder, <mem0-user-profile> blocks and system-prompt sections of official-summary archives are replaced too, everything else stays as it was, and a .scrub-runtime-context-v2 marker in the archive root keeps it from running again.
  • Cards and folds carry archive ids, not absolute paths. /strata purge confirm deletes the current session's archive.

Acknowledgements

  • dsh-live-compact by Merkyor (MIT). Strata's L1 layer, archive rotation, prune guard and preset gate are a fork of version 1.0.6 (commit 768eff9); its copyright notice is kept in LICENSE.
  • billion-context by ranxianglei. Strata borrows its ideas: model-driven restorable folds, the compress / decompress / search_context tool shape, growth and pressure reminders, minimum-benefit batching. No billion-context code or prompt text is included.

Release notes

Release notes are bilingual with English first: RELEASE.md.

License

MIT. See LICENSE.

相关插件