dsh-infinite-context
DeepSeek Harness plugin: multi-tier memory management, semantic retrieval, structured memory, and model-context awareness for infinite context.
- Stars
- 2
- Language
- TypeScript
- Created
- Aug 29, 2026
- Updated
- Oct 3, 2026
Introduction
dsh-infinite-context
简介
一个 DeepSeek Harness (DSH) 插件,通过多层记忆管理让长对话拥有「无限上下文」体验:
- 渐进式压缩 — token 压力驱动,最老消息优先摘要,近期对话原样保留
- 动态压缩阈值 — 按路由模型的真实 CTX 推导触发水位;单轮超长 think 造成的一次性增长可越过轮次间隔立即介入
- 深度思考介入 — 包装 agent 的 LLM 流:
input + output逼近当前模型真实窗口时注入溢出信号 → 持久压缩 → 带余量重试,模型思考中也能被介入 - 三层记忆金字塔 — short(近期原文)→ mid(LLM 摘要)→ long(合并摘要)
- 持久化存储 — SQLite(
node:sqlite),重启不丢记忆 - 语义检索 — 记忆嵌入、索引,每轮注入最相关的 top-K 记忆
- 三层去重 — 精确 + 归一化模糊 + 语义余弦,防止重复入库
- 表面去重检索 — 命中记忆若已存在于当前活动上下文(尚未被压缩掉的工具结果),本轮不再重复注入;文本离开表面后自动恢复可注入
- 本地简洁模式 — 路由到本地 provider 时注入一条短指令,禁止工具调用间的叙述、要求调用合并,把啰嗦的开销压下去
- 结构化记忆 — 四分类(user/feedback/project/reference)+ 索引 + 审计 + 忘得可见
- 模型上下文感知 — 自动采纳 DSH 解析的真实模型 CTX,本地小模型提前压缩
- 高价值过滤 — 只入库高价值工具结果,低价值工具自动过滤
- 手动工具 — 10 个:search / status / index / maintain / model_probe / forget / consolidate / reset / force_compress / ingest
核心特性
| 特性 | 说明 |
|---|---|
| 渐进式压缩 | compress_trigger_ratio: 0.75 — 上下文 >75% 就压缩(本地模型长上下文 TPS 骤降,提前介入);compress_target_ratio: 0.6 — 只摘要溢出部分 |
| 动态压缩阈值 | compaction_dynamic_threshold: true — 真实窗口 < 声明窗口时按真实窗口强制压缩(探测/modelWindows 驱动);thresholdRatio: 0.7 + compaction_dynamic_floor: 0.5 — 触发比例随窗口填充从 0.7 滑向 0.5(~60% 触发,本地模型留在高速区);单轮激增(≥20% 窗口)越过轮次间隔;同一会话两次强制压缩间隔 ≥10s |
| 深度思考介入 | thinking_guard_enabled: true — 包装 llm/stream:input + output 逼近 窗口 − (system/tools + 摘要估算 + 余量) 动态线时注入 CONTEXT_WINDOW_EXCEEDED → 持久压缩 → 重试;输入单独超线则生成前先压缩;thinking_guard_ratio: 0.9 为触发上限 |
| 三层去重 | 精确(hasText)+ 归一化(normalizeForDedup)+ 语义(cosine ≥ 0.92) |
| 表面去重检索 | rag_surface_dedupe: true — 注入前把 Top-K 记忆与当前活动上下文的文本比对(归一化小写 + 折叠空白,按段落 70% 覆盖判定),已在表面上的记忆本轮跳过;抑制不写入任何排除集,压缩把文本移出表面后即可再次注入 |
| 本地简洁模式 | concise_local_mode: true — isLocalRoute(provider) 为真时,在 agent/pre-step 决策消息里、最新用户消息之前注入 <runtime_directive scope="dsh-infinite-context:concise">;每个表面至多一份(检测到即不再注入),压缩丢弃后自动补回 |
| 工具结果归档(CCR) | tool_archive: true — 超过 6000 字符的工具结果先按内容寻址入库(tr_<sha256 前 12 位>),表面节点换成带 ref 的短桩;新节点入库后与压缩前各改写一次,把内置 pruner 的「有损中段剪枝」变成「可按 ref 取回」(memory_expand);只改写尚未发送过的节点,绝不失效已建立的前缀缓存 |
| 即时蒸馏(absorb) | tool_absorb: true — 超长工具结果先蒸馏出「信号行」(错误/失败/退出码/测试计数/diff stat/文件路径 + 首尾各 3 行,≤1200 字符)作为桩的主体;全文仍按 ref 入库、可 memory_expand 取回,蒸馏结果同时作为记忆入库(importance 低,遗忘优先退休) |
| 压缩调度 nudge | compress_nudge: true — 用表面增长的 EMA 预判下一轮是否越线,会越线就把压缩提前一步执行(同一轮不重复、冷却期内不触发),避免「压缩跑起来时已经付过一轮超量输入」 |
| 可逆压缩(fold ranges) | fold_ranges: true — 压缩折叠前把被折叠的原始消息整体存入 folded_ranges 表,折叠桩标头标注条数/字符数与 fl_… 引用,memory_expand ref=fl_… 可整段取回原文(等价上游 decompress);memory_search 会附折叠区关键词命中。保留上限:fold_range_retention_days(30 天)、fold_range_max_entries(300 条) |
| 上下文计量账本 | usage_ledger: true — 逐请求记录注入 tokens/条数、表面跳过、归档/蒸馏字符、nudge 次数、压缩节省 tokens 与 LLM 的 input/output/cacheRead/cacheWrite(DSH 计数互斥,不预先求和);每条 LLM 记录带 provider/model 归属,memory_status 的 usage.models 输出逐模型(MODEL SWITCHES 式)用量与缓存命中率,汇总窗口为最近 7 天 |
| 结构化记忆 | memory_index(MEMORY.md 索引)+ memory_maintain(审计)+ 忘得可见 |
| 模型 CTX 感知 | 自动读取 DSH 模型目录的 contextWindow;本地模型主动探测真实运行窗口(llama/ollama/openai,含 llama-server meta.n_ctx);per-model 注册表按模型隔离 |
| 高价值过滤 | denylist 过滤 24 个低价值工具;importance 分级(short=0.3/mid=0.6/long=0.6,long 继承批次 max) |
架构
src/
├── types.ts 核心类型(无依赖)
├── embedder.ts 轻量级特征哈希嵌入器(无依赖)
├── vector-index.ts 内存向量索引(无依赖)
├── memory-store.ts SQLite 持久化存储(无依赖)
├── token-budget.ts CJK-aware token 估算 + 内容块计量(无依赖)
├── forgetting.ts 遗忘策略(无依赖)
├── memory-engine.ts 记忆引擎核心(无依赖)
├── model-context.ts 模型上下文跟踪器:探测时机 + per-model 注册表(无依赖)
├── model-probe.ts 主动探测:llama/ollama/openai + 本地/在线判定(无依赖)
├── compaction-policy.ts 压缩触发决策:动态比例、skip/force/delegate(无依赖)
├── summarization-target.ts 摘要目标路由(跟随会话模型,无依赖)
├── config.ts schemastery 配置解析
├── memory-context.ts Cordis 服务(动态 CTX 感知 + 探测接线)
├── memory-compaction.ts 压缩引擎(渐进式 + RAG + 清理 + thinking guard 接线)
├── thinking-guard.ts 深度思考介入:llm/stream 包装 + 动态触发线
├── OutputSanitizer.ts 工具结果清理
├── memory-compaction.ts 压缩后端 + 每轮治理钩子
├── conciseness-mode.ts 本地模型简洁指令(构造/幂等检测)
├── VectorRetriever.ts RAG 检索/入库
├── strings.ts 共享字符串工具
├── core.ts 公共导出桶
├── index.ts 完整导出桶
├── tool-archive.ts 超长工具结果外置归档(CCR 式桩 + ref 取回)
├── absorb.ts 工具结果即时蒸馏(信号行提取,无依赖)
├── fold-archive.ts 可逆压缩归档:折叠原文存取 + `fl_` 引用(无依赖)
├── usage-ledger.ts 上下文计量账本(注入/归档/压缩/缓存命中,无依赖)
└── tools.ts 11 个手动工具
tests/ 248 个单元测试
手动工具
| 工具 | 说明 |
|---|---|
memory_search(query?, k?) | 语义检索持久化记忆 |
memory_status | 报告分层计数、预算、嵌入器、遗忘策略、模型 CTX + per-model 窗口,最近 7 天上下文计量(注入/跳过/归档/蒸馏/压缩节省/缓存命中率)、逐模型用量 usage.models,以及可逆压缩区 foldedRanges(条数/字符/最近条目) |
memory_index(limit?) | MEMORY.md 风格结构化索引 |
memory_maintain | 只读审计:重复/冲突/过时 |
memory_model_probe(forceProbe?, model?) | 报告模型 CTX 来源,可强制探测 |
memory_forget | 执行遗忘扫描 |
memory_consolidate | 强制金字塔合并 |
memory_reset | 清空所有记忆 |
memory_force_compress(sessionId?) | 强制压缩指定会话 |
memory_ingest(text, source) | 手动入库一条文本(自动触发见 tools/result 回调) |
memory_expand(ref?, query?, offset?, limit?) | 取回被归档的工具结果全文:不带 ref 列出/搜索归档;带 ref 可整段分页读取,或按 query 定位命中窗口(内置 pruner 剪掉的中段也能取回) |
配置参考
memory-context 配置
| 键 | 默认值 | 说明 |
|---|---|---|
storePath | dsh-infinite-context.db | SQLite 路径;:memory: 禁用持久化。相对路径解析到 <DSH_HOME>/storages/(不是进程 cwd),绝对路径与 ~ 原样/按用户目录展开 |
contextWindow | 94000 | 模型上下文窗口(回退值;插件自动采纳 DSH 解析的真实窗口,并会以实时探测结果为上限) |
headroomRatio | 0.25 | 系统/工具/输入/输出预留比例 |
modelWindows | [] | 逐模型显式窗口表([{model, contextWindow}])——声名值缺失或虚高/虚低时的权威真值;探测结果仍可进一步收窄 |
modelProbe.enabled/kind/baseURL | false | 主动探测本地服务器的真实上下文窗口(llama/ollama/openai) |
embedder.kind | lightweight | lightweight(无依赖)或 transformers |
budget.short/mid/long/retrieved | 10000/20000/5000/15000 | 分层 token 预算 |
forgetting.minScore | 0.25 | 低于此分数的记忆被遗忘 |
forgetting.maxMemories | 500 | 记忆总数上限 |
记忆库位置(
storePath):相对路径解析到<DSH_HOME>/storages/,而不是 DSH 进程的 cwd。cwd 只是「dsh从哪里被启动」的偶然结果(源码 checkout / 安装目录 / 临时目录),用它解析插件数据会让记忆库在换目录后「看起来全部丢失」,并把库留在 一个重装即删的目录里。绝对路径与~按原意处理,:memory:仍为纯内存。$DSH_HOME的判定与 DSH 一致(非空白环境变量优先,否则~/.dsh)。若解析后的路径 尚不存在、但 cwd 下存在旧库,启动时会warn打印两个路径而不是静默从空库开始。
本地模型(LM Studio / llama-server / Ollama):
settings.yaml里声明的contextWindow可能远大于服务器实际运行的上下文(例如声明 100000、实际只有 8k)。 开启modelProbe(kind: openai同时兼容 LM Studio 的/api/v0/models与 llama-server 的meta.n_ctx,或llama/ollama)后,插件会在首次观测到该模型时 读取服务器的真实运行上下文,并取min(声明值, 探测值)作为生效窗口——压缩因此会在 真实上限之前触发,而不是等到溢出。插件自身的每轮 RAG 注入(rag_token_budget) 也会从压缩触发水位中预留,避免「插件自己吃掉的上下文」被漏算。远程模型(无法探测的) 用modelWindows直接钉住真实窗口,例如[{model: glm-5.3-flash, contextWindow: 1000000}]; 本地模型也可用它兜底(如 llama.cpp--ctx-size固定值),探测结果仍会进一步收窄。
逐模型窗口注册表:同一运行时里多个会话可能路由到不同模型(1M 的远程对话 + 8K 的本地模型)。窗口按模型 id 分别记录(探测/声明/覆盖取最小值), 压缩预算按「当前会话路由到的模型」取值——上一个请求属于别的模型不会再污染本会话的水位。
memory_status/memory_model_probe会输出perModelWindows全表便于核对。
深度思考介入(mid-thinking guard):DSH 只在 API 报
CONTEXT_WINDOW_EXCEEDED后才做"溢出→压缩→重试"。本插件把介入点提前到生成流里:包装llm/streamwaterfall(仅守护 agent 请求,isAgentLoopRequest精确区分,插件自己的摘要调用永不误伤), 逐块计量input + output,逼近当前模型真实窗口(本地探测/在线声明的动态值)时注入 溢出终止块 → 复用 DSH 现成的agent/request-error→ 持久压缩 → 带余量重试, 模型"被叫停→压缩→重想"。触发线是动态的:触发线 = min( 窗口 − reserve, 窗口 × thinking_guard_ratio ) reserve = system/tools 占用 + 摘要输出估算 + 余量因为压缩会把可压缩 surface 整段重放进摘要器,
window − reserve保证触发时当前模型的 剩余 CTX 足够跑完本插件的压缩;接管大项目时若 input 单独就已超线,会在生成前先压缩, 不浪费一次注定失败的生成。文件读取(tool-result 嵌套内容)已被准确计入估算(见token-budget.ts)。
memory-compaction 配置
| 键 | 默认值 | 说明 |
|---|---|---|
thresholdRatio | 0.7 | 压缩触发比例:context ≥ 窗口×0.7 触发基础压缩(原 0.8)。本地模型长上下文 TPS 骤降,提前到 ~70% 让模型留在高速区(qwen3 167936 → ~117K 触发) |
compaction_dynamic_threshold | true | 动态压缩阈值:当路由模型的真实窗口(探测 / modelWindows)小于声明窗口时,按真实窗口推导阈值强制持久化历史压缩(复用 compaction-basic 的溢出式均衡压缩),不再等一个模型永远到不了的声明窗口阈值——短上下文本地模型的「续杯」能力;同一会话两次强制压缩间隔 ≥10s |
compaction_dynamic_floor | 0.5 | 动态触发比例下限:窗口填充过 ~50% 后,触发比例从 thresholdRatio(0.7) 滑向此值(~60% 触发,本地模型尽早离开慢速长上下文区) |
thinking_guard_enabled | true | 深度思考介入:包装 agent 的 LLM 流,input + output 逼近当前模型真实窗口的动态线时注入 CONTEXT_WINDOW_EXCEEDED → 持久压缩 → 带余量重试;输入单独超线则生成前先压缩 |
thinking_guard_ratio | 0.9 | 触发线上限(窗口占比);实际触发通常更早:窗口 − (system/tools + 摘要估算 + 余量) |
compress_trigger_ratio | 0.75 | 上下文 >75% 预算时就压缩(原 0.85;本地模型尽早介入避免慢速长上下文) |
compress_target_ratio | 0.6 | 压缩目标水位(只处理溢出部分) |
retainRatio | 0.3 | 保留尾部比例:压缩时最近 ~30% 窗口原样保留,更老的头部被摘要替换(0.4 时实测 137K→95K;0.3 可压到 ~60K,配合下方 maxTokens 提升保证摘要质量) |
maxTokens | 10000 | 摘要输出上限:调高到 10000 让更大的遮蔽跨度仍能生成完整、细节保留的检查点(原 8192) |
retain_recent_messages | 4 | 最近 N 条消息永不压缩 |
rag_top_k | 3 | 每轮注入的记忆数 |
rag_min_score | 0.3 | 注入的最低相似度 |
rag_ingest_denylist | 内置 24 个 | 低价值工具过滤列表 |
rag_ingest_importance | 0.3 | 工具结果重要性(遗忘优先淘汰) |
rag_surface_dedupe | true | 表面去重检索:命中记忆的文本若已存在于当前活动上下文(典型:刚入库、尚未被压缩掉的工具结果),则本轮不注入——避免把模型已经能读到的文本再花 token 送一遍。抑制是状态相关的:一旦压缩把该文本移出表面,记忆立刻恢复可注入(旧的 lastInjectedIds 排除语义不变,只记录真正注入过的 id) |
concise_local_mode | true | 本地模型简洁模式:会话路由到本地(loopback/私网 baseURL)provider 时,注入一条短运行时指令,禁止工具调用之间的叙述文字、要求独立调用合并到同一步、把解释集中到最终答复。本地模型的叙述会作为后续每个请求的输入反复重发(并在缓存过期后重新付费),一条指令通常在同一轮内回本。每个表面至多注入一次(agent/pre-step 决策消息,与 RAG 注入同一机制;llm/stream 的请求是深度冻结的,监听器只能读不能改) |
concise_local_directive | 内置文本 | 简洁模式的指令正文(可覆盖为自定义措辞) |
tool_archive | true | 工具结果归档(CCR 式):超过阈值的工具结果原文按内容哈希入库,表面只留「头部 + 带 ref 的标记 + 尾部」的桩。内置 tool-result-pruner 只会把中段永久替换成固定 marker(有损、且只在压缩时运行);本插件在压缩前与新节点写入后各改写一次,于是被剪掉的中段随时能用 memory_expand 原样取回 |
tool_archive_threshold_chars | 6000 | 超过此字符数的工具结果才归档。默认刻意低于内置 pruner 的 8192 阈值,保证桩先于 pruner 生效 |
tool_archive_head_chars | 2048 | 桩保留的头部字符数(含工具名、callId 与 ref) |
tool_archive_tail_chars | 1024 | 桩保留的尾部字符数(错误信息通常在这里) |
tool_archive_max_entries | 500 | 归档表最大条数,超出的按 created_at 最旧裁剪 |
tool_archive_retention_days | 30 | 归档保留天数,更旧的条目在下一次写入时删除(0 = 不按时间淘汰) |
tool_absorb | true | 工具结果即时蒸馏:归档前先把超长结果蒸馏成信号行(错误/失败/退出码/测试通过-失败计数/diff stat/文件路径,加首尾各 3 行,每行截到 240 字符,总量 ≤1200 字符)作为桩的主体,全文仍按 ref 入库、memory_expand 原样取回。蒸馏结果同时作为一条记忆入库(importance 低,遗忘优先退休),所以「这一轮报了什么错」在表面桩和记忆都被压缩后仍然可检索。蒸馏不出信息(纯空白、空心结果)时回退头+尾桩 |
tool_absorb_min_chars | 4000 | 触发蒸馏的最小字符数。刻意低于归档阈值 6000:不大但很吵的结果也值得蒸馏 |
tool_absorb_max_digest_chars | 1200 | 蒸馏摘要的字符上限(同时被归档阈值兜底,保证桩一定比原文小) |
compress_nudge | true | 压缩调度 nudge:用表面增长的 EMA(nextGrowthEma,权重 0.5)预判「下一个请求会不会越线」,会越线就把这一轮压缩提前执行(shouldNudgeCompaction:同轮不重复、失败冷却期内不触发、增长未知不触发)。压缩原本每 N 轮才评估一次,等触发时往往已经付过一轮超量输入 |
usage_ledger | true | 上下文计量账本:逐请求记录注入 tokens/条数、表面跳过数、归档与蒸馏字符数、nudge 次数、压缩节省 tokens,以及 LLM 的 input/output/cacheRead/cacheWrite(DSH 的计数是互斥的:inputTokens 只是未缓存部分,合并求和会抹掉缓存命中率)。memory_status 输出最近 7 天汇总与缓存命中率 |
usage_retention_days | 30 | 计量事件保留天数(0 = 不淘汰);每 256 次写入顺带清理一次 |
fold_ranges | true | 可逆压缩归档:压缩折叠前把被折叠的原始消息(序列化全文 + 字符数 + token 估算)存入 folded_ranges,折叠桩标头条数/字符数与 fl_… 引用,memory_expand ref=fl_… 整段取回(等价上游 decompress)。压缩产出被拒时归档行可能留存但不被引用(只占存储,受下面两条上限约束) |
fold_range_retention_days | 30 | 折叠归档保留天数(0 = 不淘汰);每次归档顺带清理超期条目 |
fold_range_max_entries | 300 | 折叠归档条数上限;每次归档顺带按时间倒序裁剪 |
部署
发布就绪说明:npm/GitHub/tarball 安装走
dist/编译产物(prepare/prepack自动构建)。 Node 24 不允许 node_modules 下的.ts类型剥离(ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), 因此 bundle 必须发布编译后 JS;bundle 补丁里的入口用裸子路径说明符 (如dsh-infinite-context/memory-context)——相对路径会按 profile 目录解析而失效。
⚠️
dsh plugin add的临时路径陷阱:dsh plugin --profile <name> add <tgz>会把 tarball 暂存到D:\Temp\dsh-plugin-install-<随机>\并在 profile 的package.json里记成file:D:/Temp/...—— 临时目录随时会被清理,之后任何一次 profile 级npm install都会因解析不到该路径而失败。可靠做法:把 tarball 放到持久化目录(如~/.dsh/packages/),并让 profile 依赖指向它:
# 方式一:通过 --patch 临时加载(.ts 源码直载,适合本机开发)
dsh web --patch ./cordis.yml
# 方式二(推荐):持久化 tarball 安装 —— npm pack 后把 tgz 放进 ~/.dsh/packages/,
# 并把 profile package.json 的依赖改为 file:C:/Users/<you>/.dsh/packages/<pkg>.tgz
npm pack
mkdir -p ~/.dsh/packages 2>/dev/null; cp dsh-infinite-context-0.1.0.tgz ~/.dsh/packages/
# 方式三:dsh plugin add(注意上面的临时路径陷阱;装完后建议把依赖重定向到持久化路径)
# 方式四:手动复制到 DSH plugins 目录 + 编辑 cordis.patch.yml(.ts 直载)
更新已安装插件:
npm pack→ 覆盖~/.dsh/packages/里的 tgz → 把新dist/*与bundle.patch.yml直接复制进<profile>/node_modules/dsh-infinite-context/(立即可用,无需 npm install)→ 重启 DSH。
一键安装脚本(scripts/,通用工具,可用于任何 DSH 插件):
# 交互式菜单(校验 dsh.bundle 清单 → 自动编译 → tarball 安装 →
# 自动清理 profile 补丁层同 id 旧条目 → 可选联动重启)
scripts\install-dsh-plugin.bat
# 或直接调用 PowerShell 版
scripts\install-dsh-plugin.ps1 <目录|tgz|npm:包名|github:owner/repo> -Profile <name>
# 只验证自动探测结果(node / harness / DSH_HOME / profile / 来源),不打包不安装不重启
scripts\install-dsh-plugin.ps1 -DetectOnly
自动探测与持久化(2026-09-03):脚本自动定位 harness 根目录——优先级为 运行中的 DSH 进程工作目录(PEB 读取,最权威)→ cfg 记忆值 → 常见安装位置 → 文件系统浅层检索,
$DSH_HOME取$env:DSH_HOME或~/.dsh;解析结果持久化到scripts/install-dsh-plugin.cfg(GBK,与 bat 共用)。tarball 固定输出到~/.dsh/packages/(规避dsh plugin add临时路径陷阱); 安装前自动移除目标 profile 里指向临时/已失效路径的旧依赖(否则 pnpm add 直接 ENOENT 失败), 安装后校验依赖已指向持久化 tarball。注意:scripts/install-dsh-plugin.ps1必须保存为 带 BOM 的 UTF-8(powershell.exe 5.1 对无 BOM 文件按 ANSI/GBK 误读中文会解析错乱),install-dsh-plugin.bat必须是 GBK + CRLF(cmd 对 LF-only 批处理解析错位)。
测试
# 单元测试(248 个,无 DSH 依赖)
vitest run --config vitest.config.ts
# 类型检查
tsc -p tsconfig.typecheck.json --noEmit
致谢
本插件的压缩/上下文治理方向长期对齐 billion-context(MIT 外加一条附加条款:任何终端用户可见或可交互、且使用了该软件的产品或服务,须在首页、文档或「关于/致谢」页面注明使用了 billion-context 并附指向该仓库的链接)。据此条款在此致谢并链接该仓库。我们仅在引擎层等价重实现其能力,未复制其源码。上游文档快照、长期更新目标与吸纳对照见 docs/billion-context/:UPSTREAM.md(紧随更新目标与刷新流程)、ABSORPTION.md(功能吸纳对照与冲突取舍)。
Introduction
A DeepSeek Harness (DSH) plugin that gives long sessions an "infinite context" feel via multi-tier memory management:
- Progressive compression — token-pressure driven, oldest-first summarization, recent context preserved verbatim
- Dynamic compaction threshold — trigger water level derived from the routed model's REAL CTX; a single oversized thinking turn bypasses the round-interval rate limit
- Mid-thinking guard — wraps the agent's LLM stream: when
input + outputapproaches the current model's real window, injects an overflow signal → durable compaction → retry with room; the plugin intervenes even while the model is deep-thinking - Three-tier memory pyramid — short (recent turns) → mid (LLM summaries) → long (consolidated summaries)
- Persistent store — SQLite (
node:sqlite), memories survive restarts - Semantic retrieval — memories embedded, indexed, and top-K spliced into context per turn
- Three-layer dedup — exact + normalized fuzzy + semantic cosine, prevents duplicate ingestion
- Surface-aware retrieval dedup — a hit whose text is still on the active context (an ingested tool result that has not been compacted away) is not injected again; it becomes eligible again as soon as compaction removes the text from the surface
- Local conciseness mode — on a local route the plugin injects one short directive that forbids inter-tool narration, requires batching, and pushes explanation into the final answer
- Structured memory — four classifications (user/feedback/project/reference) + index + audit + visible forgetting
- Model-context awareness — auto-adopts DSH-resolved real model CTX, small local models compress early
- High-value filtering — only high-value tool results ingested, low-value tools filtered
- Manual tools — 10: search / status / index / maintain / model_probe / forget / consolidate / reset / force_compress / ingest
Key Features
| Feature | Description |
|---|---|
| Progressive compression | compress_trigger_ratio: 0.75 — compress already when >75% full (local long-context TPS collapses, so intervene early); compress_target_ratio: 0.6 — only summarize overflow; retainRatio: 0.3 — newest ~30% of the window stays verbatim; maxTokens: 10000 — summarizer output cap raised to preserve detail |
| Dynamic compaction threshold | compaction_dynamic_threshold: true — when the REAL window (probe / modelWindows) is below the declared one, force compaction at a REAL-window threshold; thresholdRatio: 0.7 + compaction_dynamic_floor: 0.5 — the trigger ratio slides from 0.7 toward the floor as the window fills (~60% trigger, keeping slow local models in their fast zone); a single-round surge (≥20% of window) bypasses the interval; forced compactions ≥10s apart |
| Mid-thinking guard | thinking_guard_enabled: true — wraps llm/stream: when input + output nears the dynamic line window − (system/tools + summary estimate + margin), injects CONTEXT_WINDOW_EXCEEDED → durable compaction → retry; input already over the line is compacted BEFORE generation; thinking_guard_ratio: 0.9 is the ceiling |
| Three-layer dedup | Exact (hasText) + normalized (normalizeForDedup) + semantic (cosine ≥ 0.92) |
| Surface-aware retrieval dedup | rag_surface_dedupe: true — before injecting, Top-K memories are compared against the text of the ACTIVE context (lowercased + whitespace-collapsed, 70% segment coverage); memories already visible are skipped this turn. Suppression is recorded nowhere, so a memory returns as soon as compaction moves its text off the surface |
| Local conciseness mode | concise_local_mode: true — when isLocalRoute(provider) holds, a <runtime_directive scope="dsh-infinite-context:concise"> message is spliced into the agent/pre-step decision right before the latest user message; at most one copy per surface (detected, never duplicated), re-injected after compaction drops it |
| Tool-result archive (CCR) | tool_archive: true — a tool result above the threshold is archived by content hash (tr_<sha256 12>), and the surface node becomes a short stub carrying that ref. Rewritten once when a node is appended and once before compaction, so the lossy middle-cut performed by the built-in pruner becomes recoverable via memory_expand. Only nodes that were never sent are rewritten, so an established prefix cache is never invalidated |
| Immediate distillation (absorb) | tool_absorb: true — an oversized result is distilled into signal lines first (errors/failures/exit codes/test counts/diff stats/paths plus 3 head and 3 tail lines, ≤1200 chars) and that digest becomes the stub body; the full text still archives under its ref for memory_expand, and the digest is also ingested as a memory (low importance, retired first) |
| Compression scheduling nudge | compress_nudge: true — a surface-growth EMA predicts whether the NEXT request crosses the trigger and compresses one step early when it would (shouldNudgeCompaction; never twice in a turn, never during a cooldown) |
| Reversible compaction (fold ranges) | fold_ranges: true — before folding, the exact folded messages are archived in folded_ranges; the fold header carries the message/char counts and a fl_… ref, and memory_expand ref=fl_… restores the full text (upstream decompress parity); memory_search also surfaces keyword hits inside folded history. Retention: fold_range_retention_days (30 days) and fold_range_max_entries (300) |
| Context accounting ledger | usage_ledger: true — per-request accounting of injected tokens/memories, surface skips, archived/distilled chars, nudges, compaction tokens saved, and the LLM's input/output/cacheRead/cacheWrite (DSH counts are DISJOINT, so they are stored separately and never pre-summed); every LLM record carries a provider/model attribution, and memory_status reports per-model usage (usage.models, MODEL SWITCHES style) for the last 7 days plus the cache hit rate |
| Structured memory | memory_index (MEMORY.md style) + memory_maintain (audit) + visible forgetting |
| Model CTX awareness | Auto-reads DSH model catalog contextWindow; local models are actively probed for their REAL runtime window (llama/ollama/openai incl. llama-server meta.n_ctx); per-model registry isolates concurrent sessions |
| High-value filtering | denylist filters 24 low-value tools; importance tiers (short=0.3/mid=0.6/long=0.6, long inherits batch max) |
Manual Tools
| Tool | Description |
|---|---|
memory_search(query?, k?) | Semantic search over persisted memories |
memory_status | Report tier counts, budgets, embedder, forgetting policy, model CTX + per-model windows, the last 7 days of context accounting (injection, surface skips, archived/distilled chars, compaction tokens saved, cache hit rate), per-model usage (usage.models), and reversible fold ranges (foldedRanges: counts, chars, recent entries) |
memory_index(limit?) | MEMORY.md-style structured index |
memory_maintain | Read-only audit: duplicates/conflicts/stale |
memory_model_probe(forceProbe?, model?) | Report model CTX source, force probe, list per-model windows |
memory_forget | Run a forgetting sweep |
memory_consolidate | Force pyramid consolidation |
memory_reset | Erase all memories |
memory_force_compress(sessionId?) | Force compress a session |
memory_ingest(text, source) | Manually ingest a text (auto-triggered via the tools/result callback) |
memory_expand(ref?, query?, offset?, limit?) | Read back an archived tool result: without ref it lists or searches the archive; with ref it pages through the full text, or returns match windows for query (the middle cut by the built-in pruner included) |
Deployment
Publish-readiness note: npm/GitHub/tarball installs consume the compiled
dist/(prepare/prepackbuild automatically). Node 24 refuses TypeScript stripping under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so bundles must ship compiled JS, and bundle-patch entries must use bare subpath specifiers (e.g.dsh-infinite-context/memory-context) — relative paths resolve against the profile directory and break.
# Option 1: Temporary load via --patch (direct .ts loading, for local dev)
dsh web --patch ./cordis.yml
# Option 2: Install into a profile (tarball, verified end-to-end)
npm pack && dsh plugin --profile <name> add ./dsh-infinite-context-0.1.0.tgz
# Option 3: Manual copy to DSH plugins dir + edit cordis.patch.yml (direct .ts)
One-click installer (scripts/, generic tool for any DSH plugin):
# Interactive menu (validates the dsh.bundle manifest → auto-builds →
# tarball install → auto-cleans legacy same-id patch entries → optional restart)
scripts\install-dsh-plugin.bat
# Or call the PowerShell version directly
scripts\install-dsh-plugin.ps1 <dir|tgz|npm:pkg|github:owner/repo> -Profile <name>
Testing
# Unit tests (248, no DSH dependency)
vitest run --config vitest.config.ts
# Type check
tsc -p tsconfig.typecheck.json --noEmit
Acknowledgements
The compression/context-governance direction of this plugin is aligned long-term with billion-context (MIT plus one additional attribution term: any end-user-visible or interactive product or service using that software must state that it uses billion-context on its home page, docs, or an About/Credits page, with a link to that repository). Per that clause we credit and link the repository here. We only re-implement its capabilities equivalently at the engine layer; no upstream source code was copied. The pinned upstream docs snapshot, the long-term follow-up target, and the absorption/conflict record live in docs/billion-context/: UPSTREAM.md and ABSORPTION.md.
See ARCHITECTURE.md for the full design.