dsh-context-rescue
Manifest validRecovers a session the host already refused for exceeding the context window: it re-checks the gateway message the built-in classifier misses, takes the real window out of that message, and folds the
dsh-context-rescue
中文 · English
给"上下文爆窗"的 DeepSeek Harness 会话叫救护车。
会话里每条消息都回 400,因为某一次请求超过了模型真正能吃的窗口。/compact 也救不回来
—— 它沿着同一条超限的投影重发;如果压缩被代理层接管,还会被直接拒绝。这个插件是急救车:
识别爆窗、把最老的历史折叠成结构化摘要、把同一步重发一次,会话就活了。
400 the request exceeds the model's context window
└─ context-rescue: overflow detected (gw-prose, score 0.95)
├─ T1 裁剪工具输出 → 宿主未提供
├─ T2 宿主压缩引擎 → 代理拒绝,本进程内该车道永久关闭
├─ T3 机械折叠 → 956 节点 → 169 节点,991,512 → 298,595 tokens
└─ T4 超大单条消息 → 仅当历史只有一条巨消息时:保留它的开头与结尾
└─ 重试同一步 → 200 OK
为什么需要它
这条失效链在上游有完整记录(DSH 讨论 #10138),它是三段叠加,不是单点 bug:
- 网关用自然语言拒绝(
payload core text alone is ~N bytes … no upstream can accept it), 宿主的分类器认不出来,于是这次失败没有被打上CONTEXT_WINDOW_EXCEEDED; - 错误码不对,宿主自带的上下文溢出分支就不会执行;
- 没有任何东西缩小投影,重发出去的请求字节级相同,于是再失败一次 —— 无限循环。
现有插件都是事前预防(滚动换窗、裁剪工具输出、代理侧压缩)。没有一个在会话已经卡死之后 动手。本插件平时什么都不做,只在请求失败时介入,因此可以和上面任何一款共存。
安装
# 直接从本仓库装
dsh plugin --profile <profile> add git+https://github.com/Lequait/dsh-context-rescue.git#master
# 本地目录
dsh plugin --profile <profile> add /path/to/dsh-context-rescue
# 从 npm 安装(0.2.4 已发布)
dsh plugin --profile <profile> add dsh-context-rescue
分支形式跟随 master;市场条目则是钉死提交(git+…#<commit>),列表指向的构建不会在脚下挪动。
包内声明了 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } },以 context-rescue
为 id 插入。运行时它不 import 任何 @deepseek-ai/* 包:surfaceOp、compaction/* 事务、
配对规则、模型窗口解析全部按宿主的线上结构在本地构造。宿主升级不会打断它的 import,
它也不声明任何宿主 peer 依赖(也就没有"peer 缺失装不上"这回事)。装完重启一次(新 bundle 需要宿主重载)。
上架条目
market/ 放图标和生成脚本,产出的是格式 1 的插件市场索引条目。校验用的是宿主自己的
parseMarketIndex —— 也就是 Discover 页读索引时跑的那套代码:
# 校验一条以本仓库某个 commit 作为 Git 源的条目
node scripts/make-market-index.mjs \
--repo https://github.com/OWNER/dsh-context-rescue \
--commit "$(git rev-parse HEAD)" \
--out market/index.json
# 或者一步发布:先推送,再把条目钉到真正推上去的那个提交
npm run publish:remote -- --repo OWNER/dsh-context-rescue --dry-run
它会打印解析出的安装 spec(git+https://…git#<commit>;加 --npm 则是 name@version),
任何一条规则不通过就带 JSON 路径非零退出。校验器位置可用 DSH_APP_ASAR 覆盖。
DSH 只读用户自己配置的索引:市场 bundle 是可选的、默认 indexes: [],所以"能被发现"只差一步。
本仓库已经发布,用户有两条路:
# 1. 直接从 git 装 —— 不需要任何市场,钉住你复核过的 commit
dsh plugin --profile <profile> add git+https://github.com/Lequait/dsh-context-rescue.git#<commit>
# 2. 通过 Discover 页
添加索引源 https://github.com/Lequait/dsh-context-rescue/raw/master/market/index.json
(Discover → 索引源 / "Add index source";官方市场 bundle
@deepseek-ai/dsh-plugin-market 是可选的,不是每个 profile 都有)
选中条目 dsh-context-rescue —— 它装的是条目里钉住的 commit,不是分支
仓库同时带了 dsh-plugin topic,按 topic 扫描的列表能自动发现它;条目也已投给两个策展列表
(PR 记录在 docs/PUBLISH.md)。
两个不同的计数
measure() 数的是 surface 节点;真正要上游接受的却是请求,也就是
session.deriveMessages() 生成的那批消息。这两个数并不相同:按前者看还装得下的会话,
按后者看可能照样被拒。/rescue-status 两个都报,宿主闸门断言的也是后者——每次折叠之后,
派生出的请求必须比折叠前小,而且必须在窗口以内。
救不回来的时候
有些会话救不回来:光是系统提示词就已经超过窗口。插件会把每一档都试一次,然后把这条会话记为「已无解」,不再每个请求都重新测量一遍——否则那条会话的每个请求都要付一次完整测量、然后再以同样方式失败。这一步照常继续,请求照常发出,失败原样交回宿主。/rescue(或 context_rescue 的 action: "rescue")总会再试一次;任何一次真正生效的救援都会让正常路径重新打开。
这个「无解」结论属于当时那个窗口和当时那份历史形状,而不是永久属于这个会话:换到窗口更大的模型,或者投影在这之后又长大了,结论都会被丢弃,失败路径与每步预检都会重新测量一次。否则「换个模型试试」——卡住时最自然的动作——反而会让插件继续装看不见。
检查点里留下什么
摘要不调用模型,所以它留的是事实而不是文笔:错误与失败、任务清单、跑过的操作(一行一次, 重复的合并计数)、用户的请求、最近的结论,以及这次工作触及的文件与路径。路径无论是存放在 参数树里还是存放在宿主实际写入的那一条 JSON 文本里,都会被读出来——一次把路径全丢掉的折叠, 会让请求变小、让模型失去线索。
请求发出之前
等到 400 才救,代价是一次白走的重试;而有些会话一次都救不回来(issue #10138)。所以同一套急救
也挂在 agent/pre-step 上,早一步发生:
- 先量循环即将发出的投影;
- 如果它已经超过模型窗口,这个请求根本不可能被接受 —— 就地折叠:先 T1 裁剪,再走零模型检查点;
- 如果只是落在压力带(
fitRatio × 窗口到窗口之间),一律不动:那是普通压缩策略,归宿主管。
窗口依次来自模型注册表(用会话头里的 provider)、会话自身、windowFallback,并按目标缓存,
一个 turn 不会每步都去解析一次。这里故意不走 T2:宿主引擎有自己的 pre-step 钩子,借它会为
同一次超窗压缩两遍。
量一份大投影才是急救里最贵的一步:本机实测整条阶梯中位数 1.1 秒,几乎都花在给节点计价上。pre-step
每一步都会调用,所以它一个 turn 只量一次,之后只数「这之后新追加了什么」——同一 turn 的第二次检查
耗时可忽略,但仍然会发现大段粘贴(任何新消息超过 4,000 字符就强制重新测量;投影节点数与缓存不符也
强制重测,那说明别人折叠过)。node scripts/dev/rescue-bench.mjs 会打出本机最大几份日志的耗时分布。
请求失败之后它做什么
挂在 agent/request-error 瀑布上 —— 这条钩子对任何失败都会触发,跟宿主给的是什么错误码
无关,因此正好补住分类器的漏。
-
判定:强证据(网关原文、
model's context window、no upstream can accept、 预算与窗口的算术关系)+ 结构化错误码;顺带抽出requestTokens/windowTokens。 失败里观测到的窗口优先于适配器声明的窗口。 -
计量:走宿主 token meter,没有就本地估算。
-
规划:用宿主自己的配对规则算安全切点(工具调用是
assistant/message里的tool-callblock,结果是tool/result,镜像事件不动余额)。保留钉住的头部和最近尾部; 遇到未闭合的 compaction 事务直接拒绝出计划。 -
四档阶梯:
- T1 用宿主暴露的 pruner 裁剪工具输出(最便宜,零模型);
- T2 请宿主的压缩引擎做它本该做的事:
compactIfNeeded(agent, 'context-overflow', signal); 借来的引擎只有把投影真的变小才算救活,什么都没做的 no-op 会继续落到 T3; 若被代理拒绝(dsh_compaction_refused),记下来,本进程内不再问第二次; - T3 写检查点:
compaction/start→compaction/prune→user/message(surfaceOp: replace) →compaction/end,正文是折叠区间的结构化摘要。 - T4 当整段超窗的其实只有一条消息(比如刚贴进来的一整个文件)时,没有区间可折:
把那条消息本身换成「开头 + 结尾」的摘录,并在检查点里写明原文长度与被丢弃的中间部分。
只对删掉它仍能保持工具调用配对完整的文本消息生效;孤儿
tool/result之类一律不碰。 当超窗的其实是一次工具输出时,那次调用和它的结果会一起被换成摘录——单独删掉任一半, 另一半就成了孤儿。
系统提示词永远不会被摘录:把指令换成首尾摘录,等于让请求合法地丢掉这一轮该做什么,所以当窗口里 超出的只剩它时,阶梯报告失败而不是假装成功。一次贴进多段内容也能一趟处理完——折叠先吃掉旧区间, 留在尾部的那条由 T4 截断。而在剩下的内容里,旧历史会先被摘录:这一轮刚打进去的那条消息就是这一轮 要做的事,只有当更早的地方没有足够大的东西能把请求压回窗口时,才会动它。
-
只重试一次:返回
{ kind: 'retry' },让循环用变小后的投影重发同一步。全档失败或 超出每轮预算就next()交出控制权,绝不让真实错误被吞掉。
摘要是结构化的,不是复述:错误与失败、涉及的文件与路径、任务清单、关键操作、用户意图、 最近结论。长会话会丢细节,但保留骨架和线索。
工具
| 工具 | 用途 |
|---|---|
context_rescue | action: "status" 报告占用、能力与上次急救;action: "rescue" 立刻走一遍阶梯、不等失败 |
context_rescue_estimate | 分类一段贴进来的报错文本(可选参数 text),给出从中提取的尺寸,外加本会话的占用情况 |
context_rescue_fold | 立刻折叠最旧的一段历史(没有参数,与 /rescue 走同一个机械折叠),并报告 token 变化 |
工具不抛异常,统一返回带 ok、reason 和计数的报告对象。
斜杠命令
只要宿主提供了命令注册表(/compact 用的那个槽),插件就会注册两个命令。它们是可选的:没有该注册表的 profile 仍然有自动急救与工具。
| 命令 | 用途 |
|---|---|
/rescue | 立刻折叠最旧的一段并报告 token 变化(/rescue status 只报告不改写) |
/rescue-status | 报告占用、解析出的窗口、是否可折叠、此刻折叠的代价,以及上一次急救结果 |
状态行会区分急救能做的两种事:能折时说出要折的区间,折不了时说出会被摘录的是哪一条超大消息, 而不是笼统地说「没什么可折」。
命令注册表可能比插件后挂载,插件会在服务出现时补注册。命令结果是一行文本,走的仍是与自动急救完全相同的代码路径,且永不抛异常。
配置
全部可选。
| 键 | 默认 | 含义 |
|---|---|---|
enabled | true | 是否挂载插件;false 时完全不碰宿主 |
mode | "auto" | auto 走完整阶梯(剪枝 → 宿主引擎 → 机械折叠 → 摘录);fold 走同一条阶梯但不问宿主压缩引擎,因此永远不会让模型来总结;diagnose 只检测和报告,不改写任何东西 |
zeroModelOnly | false | 永不调用模型/压缩引擎,直接走机械折叠 |
trimOversizedMessages | true | 单条消息自己就装不下时,用它的开头+结尾替换它 |
excerptChars | 3000 | 替换时首尾各保留多少字符 |
allowModelCompaction | true | 是否允许 T2 |
retainRatio | 0.25 | 作为最近尾部保留的占比 |
retainMinTokens / retainMaxTokens | 4000 / 40000 | 尾部长度的上下限 |
targetRatio | 0.75 | 折叠到窗口的这个比例以内 |
fitRatio | 0.9 | 低于该比例即认为装得下 |
minFoldTokens | 4000 | 小于这个量不值得开一次事务 |
maxAttemptsPerTurn | 2 | 每个 assistant 轮次的急救预算 |
maxDigestChars | 12000 | 检查点正文硬上限 |
pinFirstUserMessage | true | 首条用户消息不进折叠区间——除非它自己就超过了预算 |
windowFallback | 196608 | 失败与模型信息都给不出窗口时使用 |
extraPatterns | [] | 追加的溢出识别正则 |
laneRefusalPatterns | ["dsh_compaction_refused", "bili owns compression"] | 命中即永久关闭 T2 |
registerTools | true | 是否注册上述三个工具 |
registerCommands | true | 宿主有命令注册表时是否注册 /rescue 与 /rescue-status |
preflight | true | 投影已超窗时在 agent/pre-step 就地折叠 |
repairTransactions | true | 关掉崩溃留下的半截压缩事务;悬空的 start 会让之后每一次折叠都被拒绝 |
verbose | true | 把急救决策写进会话日志 |
debug | false | 每个决策打一行结构化日志——检测到的错误码与窗口、折叠前后的度量、每一档以及它为什么放弃、被折叠的区间——这样“没被救的会话”也能自己说明原因 |
配置写错会被告知,而不是被吞掉
插件挂载时会校验选项:拼错的键(如 retainRation)、不认识的 mode、超出可用区间的比例、
以及不低于 fitRatio 的 targetRatio,都会以 config: 开头的 warn 日志报出,而不是悄悄按别的
值运行。对急救插件来说,"静默忽略拼写错误"和"根本没装"是同一件事。
选项是一份真 schema,不是愿望清单
lib/schema.js 把下面每个选项声明成 Cordis/Schemastery schema,宿主会因此做两件事:在插件启动
之前校验你的 patch,并在 dsh --dump-config-schema 里把这些选项写进文档。schema 拒绝的值
(retainRatio: 5、mode: aggressive、preflight: "yes")会让启动直接停下并指出字段与边界,
而不是带着一个被悄悄夹紧的数字继续跑:
$.retainRatio expected number <= 0.6 but got 5
未知键则被 schema 故意放过,好让上面那条 warn 能点出你拼错的名字——两种检查各自负责不同的错误, 谁也不替谁兜底。
安全边界
- 绝不损坏投影:切点按宿主自己的平衡规则计算,会把工具调用和它的结果切开的区间一律不用;
写入中途抛错会补一条
compaction/end{error}闭合事务括号。 - 绝不为空忙花上下文:如果按插件自己的计量,投影本来就在报错所说的窗口之内,就直接原样重试, 不做折叠 —— 网关可能比投影算得更多,那时改写只会白白烧掉历史。
- 绝不循环:每轮最多
maxAttemptsPerTurn次,然后让位。 - 绝不删历史:折叠只做追加 —— 原始事件全部留在会话日志里,
eventAt(seq)仍能取到, 只是投影不再包含它们。急救藏起来的东西并没有从记录里消失;折错了损失的是一次投影, 不是这段对话,日志照样能读、能回放、下次也能换成另一种折法。tests/fold.test.js断言了这一点。 - 绝不假装成功:没有可折叠区间(单条巨消息,或钉住的头部本身超窗)时明确报告并交出控制权。
- 绝不抢位:不注册
compaction服务槽,预防型压缩器照常持有它,并且在 T2 有优先权; 开zeroModelOnly时根本不会碰这条车道。npm run mount-check会先挂一个宿主压缩引擎, 再证明挂载插件后该槽仍然路由到它、且急救仍在零模型档完成。
卸载,以及"能不能撤销一次折叠"
卸载就是回滚,而且不需要任何清理:
dsh plugin --profile <profile> remove dsh-context-rescue # 然后重启应用
折叠写入的全部是宿主自己的事件词汇 —— compaction/start、每个被折节点一条 compaction/prune、
一条带 surfaceOp 的替换 user/message、以及 compaction/end —— 所以一个从未装过本插件的宿主
读到的就是折叠后的样子。npm run host-session 对每一份被折叠的日志都断言过这一点:它用折叠后的
事件新建一个全新的宿主 Session(不经过本插件任何代码),并核对消息、工具调用配对与计量都和
当时活着的会话一致。
但投影不能就地反折叠:@deepseek-ai/dsh-session 只有 compaction/start、compaction/prune、
compaction/end,没有作废(void)事件 —— 没有可以追加的东西把投影丢掉的历史放回来。事件本身
从未被删除,原文仍在 session.v4.jsonl.zstd 里、仍能按序号解析,但模型不会再看到它。想在动手前先
看清代价:/rescue-status 会预览将要折叠的区间,mode: "diagnose" 只报告不写入。
验证
三条命令按人实际敲的顺序跑完闸门,输出照原样流出来,最后给一张表点名失败的那一个:
npm run verify # 核心:单测、类型契约、四个模拟失败形状
npm run verify:host # 需要本机已安装应用的九道闸门
npm run verify:network # 需要 registry 或仓库的四道闸门
本机实测:3 道 8 秒、9 道 3 分 34 秒、4 道 1 分 30 秒 —— 一共十六道闸门,一张表,退出码就是 "有没有失败"。
想只跑其中两道:node scripts/dev/verify.mjs --group host --only install-smoke,schema-check。
要一条条手跑,命令如下:
node --test # 单测 + 回放
npm run typecheck # 用 JSDoc 给 index.js 与 lib/ 做类型检查
node scripts/simulate.js # 四个失效场景跑真实插件代码
DSH_SESSION_LOG=<session.v4.jsonl.zstd> node --test tests/replay-real-log.test.js
# 同一套测试改用 app 自己的 Node 跑,那里才读得到 app.asar 里的宿主校验器
npm run test:host
# 装进本机 app 自己的 cordis 容器里跑一次真实救援
npm run mount-check
# 真正打包、装进临时项目,再让宿主自己的加载器解析 bundle
npm run install-smoke
# 用 app 里真实的 Session 类回放本机日志并折叠
npm run host-session
# 和宿主的真实压缩引擎(compaction-basic)共存跑一次救援
npm run coexist
# 用宿主自己的 CLI 装进一个一次性 profile,再删掉
npm run e2e-install
# 同上,但装的是已发布的 git spec(真实网络路径)
npm run e2e-install -- --git
npm run e2e-install -- --npm # 同上,但用 registry 上的 npm spec 安装(用户最常走的那条)
# 真的启动应用:apply() 被执行吗,且能否与占用压缩车道的插件同一个 profile 共存
npm run boot-check
# 拉线上市场索引,交给宿主解析,再装它给出的 spec(Discover 页走的同一条路)
npm run market-install
# 用临时克隆 + 本地裸仓库把整条发布流程排练一遍
# (同时钉住分支规则:空远端首次推送改成 main,已有 master 的仓库保持 master)
npm run publish:rehearse
# 为当前 checkout 生成并校验上架条目
npm run market -- --repo-slug OWNER/dsh-context-rescue
# 可选的 npm 发布:先查工作树、包内容清单、版本是否被占,最后停在唯一需要账号的那一步
npm run publish:npm -- --dry-run
覆盖判定、配对安全规划、折叠事务、阶梯行为、车道拒绝记忆、每轮预算、no-op 借用引擎、
本来就装得下的投影、同一轮内第二次被拒时强制走阶梯、内部异常交还宿主,以及计量精度。
tests/replay-real-log.test.js 还会把真实会话日志折叠一遍,再复核宿主不变量;未设
DSH_SESSION_LOG 时自动跳过。node scripts/dev/scan-sessions.mjs 会把本机所有会话日志扫一遍,
每个超标会话都在零模型调用下救回来:216 个已捕获会话里有 34 个超过 196,608 窗口,34 个全部
救回,0 失败(见 docs/PLAN.md §5)。这里的数字是本机的一份快照:会话还在不断产生,
scan 与 host-session 会重新计算它们,所以下面引用的也是同一份快照,而不是固定常数。
host-session 是保真度最高的一道闸门,也是上面那两个 bug 的被发现处。它把装好的 app 里那份
Session 类加载出来回放真实日志:追加由宿主自己的 validateSessionEventData 校验,投影由它的
SurfaceManager 构建,断言对象是 deriveMessages() —— 也就是下一次请求真正要发的那串消息。
216 份日志能播种,70 份完成折叠(每份折两次,证明折过的会话还能再折),其中 32 份走的是 pre-flight 路径(请求还没发出就折好),其余是宿主在孤立进程里
自己也没法重建的(别的插件的事件投影、或 v4 之前的旧格式)。
boot-check 再高一层:它真的启动应用。它建五个一次性 profile,其中四个来自出厂 headless 模板 ——
第一个装的是故意在 apply() 里抛错的副本,断言宿主把这个错误挂在它自己插入的那个条目名下
(context-rescue (dsh-context-rescue));这一步是反证,否则"跑绿了、没报错"什么都不能说明。
第二个装真包,断言启动能走过挂载、没有任何指向本插件的错误,并停在模型调用处(没有 API key 的机器就停在那里)。
第三个把 billion-context(某些机器上占着压缩车道的代理)和本插件装进同一个 profile 再启动一次,
于是"能和车道持有者并排工作"是被测出来的,而不是被假设的。第四个对 dsh-smart-compact 做同样的事——它把压缩引擎本身换掉,两个插件同时伸手要一个服务是组合问题,单元测试看不到。
第五个换成 web 模板——那才是桌面端真正组合出来的形状,断言本插件落在和 @deepseek-ai/dsh-client-ui-commands 同一棵树里,也就是用户看到的命令面板那一层。随后这个 profile 会被真的启动一次:宿主必须打印出自己的 URL、把 URL 里的认证 cookie 交给请求、并把 UI 文档吐出来——会让 GUI 起不来的包根本走不到这一步。五个 profile 用完即删。它证明的是"装得上、挂得住",不证明"能推理"。
coexist 把插件挂到宿主真实的压缩引擎旁边跑:它加载 app 里的 @deepseek-ai/dsh-compaction-basic,
让引擎自己占住 compaction 槽位,然后只给假 llm(拿不到模型)。断言的是三件事——插件不抢槽、
"借了引擎却没缩小投影"不算救回来、以及借失败之后引擎与容器都还完好(再调一次也不炸)。
"借"是它敢跟已有压缩器并排装的原因。dsh-smart-compact@0.5.3 继承的是同一个引擎,并重写了
compactIfNeeded(agent, trigger, signal):非压力触发直接交给基础引擎、有待处理的 new_context 请求时
绕过压力档、拒绝时返回 null 而不是抛错。正因为这层重写,我们才用同样三个参数去问 context-overflow,
把 null 当作"什么都没缩小"继续走零模型折叠;另有单测钉住——引擎因自身原因抛错(预算测量失败、基础引擎
报错)既不算"车道拒绝",也拦不住折叠。
mount-check 是集成闸门:它把插件装进本机已安装 app 自带的 cordis 4 容器,因此验证的是
真实的 inject/Service 接线和真实的 ctx.waterfall('agent/request-error', payload, next)
派发,而不是手搓的假 context。它断言工具确实注册到真实 tools 服务、瀑布返回
{kind:'retry'}、投影被改写成检查点、每轮预算拦住第三次、工具能通过服务响应,以及在先挂了
宿主压缩引擎时服务槽仍归宿主、急救仍在零模型档完成。
install-smoke 是打包闸门:npm pack 决定最终发出去的形状(受 files 白名单约束),
tarball 被当作依赖装进一次性项目,evaluatePluginCompatibility 断言宿主接受这个 manifest
(同时用一个故意写成不可能满足的 engines.dsh 做反例,保证这道闸门不会空过),再由宿主自己的
bundlePatchPaths/loadOverlayPatches 从"安装后的路径"解析出 patch。少一个文件、或 patch 相对目录解析错,只会在这一关暴露 —— 其余闸门
读的都是源码树。
模拟不是摆设:它抓到了一个真 bug —— 计量路径复用了摘要渲染的 2000 字符截断,把一条 480,000 字符的消息算成 1,992 tokens,插件因此误判"装得下"。
publish:rehearse 补上源码树测试碰不到的最后一段:publish-remote 是唯一需要账号的步骤,
所以排练会把本 checkout 克隆到临时目录、建一个裸仓库当远端、对它跑真实的发布脚本,再断言
宿主会看到什么 —— 推上去的分支、能被宿主 parseMarketIndex 接受的 market/index.json、
条目 pin 住的 commit 确实是"带代码的那个"而不是索引 commit 自己、commit 钉死的
git+…#<commit> 安装 spec,以及写进推送后 package.json 的仓库元信息。
它没救回来时
不要猜,直接读插件自己的状态:context_rescue 的 action: "status",或输入框里的
/rescue-status,会报出当前会话的 measuredTokens、window.windowTokens、fits、
foldSupported、hostCompaction、toolResultPruner 与 laneBlocked。
当会话确实超出窗口时,这份报告还会预览它会怎么折:替换几个节点、序列区间、这段历史有多少
token、摘要多长、保留多少近期历史。查状态不会写任何东西——预览就是 /rescue 会执行的那份计划,
算出来看一眼就丢掉。
| 现象 | 含义 | 处理 |
|---|---|---|
启动时没有 armed: context-overflow rescue active 这一行 | 宿主跳过了这个 bundle | 核对 engines.dsh 与 app 版本,并确认 patch 文件在安装目录里能解析到 |
fits: true 但 provider 仍然拒绝 | 窗口估计比网关真正执行的上限大 | 失败文案里通常带着真实数字;没有数字时把 windowFallback 设成实际窗口 |
laneBlocked: true | billion-context 拒绝了宿主自己的压缩 | 插件会留在零模型路径上;可设 zeroModelOnly: true 把这点写明,不再尝试借用 |
foldSupported.ok: false | 这个会话形状没有 append/surface.nodes/eventAt | 宿主版本不匹配 —— 插件拒绝改写自己读不回来的投影 |
hostCompaction: absent | 没有挂载压缩引擎 | 无害:第二档跳过,零模型阶梯照常跑 |
| 预览说 a rescue would find nothing to fold | 超出的部分全在保留的尾部里,且没有单条消息能说明问题 | 设 pinFirstUserMessage: false,或调高 retainRatio 让更少的尾部被保护 |
| 救过了但请求仍然失败 | 折叠后仍然超窗 | 调低 retainRatio/retainMaxTokens,或开新的一轮让检查点接住要点 |
局限
- 它在请求失败之后动手。想完全不看到失败,请搭配一款事前预防插件,两者不冲突。
- 摘要保留骨架与线索,不保留每一个 token。
- "车道被拒"只在进程内记忆;重启宿主会再试一次,这是有意的(代理策略可能已改)。
许可证
MIT。
Comments
Loading…
From the same category
by ranxianglei
基本稳定可用 100K tokens is enough. Universal context-compression proxy for ALL AI coding agents,10w上下文足矣
★ 3.6k
↓ 788.3k/wk
MIT
TypeScript
Nov 2, 2026
dsh plugin --profile web add billion-contextA DeepSeek coding agent harness: persistent shell, Ultra subagents, auto approval, Chrome MCP and session telemetry
★ 1k
MIT
JavaScript
by Han-1413141
DeepSeek Harness session cost meter plugin: session/daily cost, budget, history, OpenCode Go quota, official & custom-provider balance, Codex-like token heatmap, peak/off-peak pricing with pre-switch
★ 597
↓ 26k/wk
MIT
JavaScript
Nov 1, 2026
dsh plugin --profile web add dsh-cost-meterby Yuuyuko-uu
DSH 鱼的记忆:给会话一份会淡忘的长期记忆,每条都带时间
★ 453
MIT
JavaScript
Nov 1, 2026
dsh plugin --profile web add dsh-fish-memoryby MichengAI
DSH Archive Manager — 在 DeepSeek Harness 中安全查看、恢复和管理已归档会话 · Safely view, restore, and manage archived sessions in DSH
★ 277
↓ 30.5k/wk
Apache-2.0
JavaScript
Nov 2, 2026
dsh plugin --profile web add @michengai/dsh-archive-managerby Nwflower
Import 14+ external agent chat histories (Claude Code, Codex, ChatGPT, Cursor, Gemini, Reasonix, opencode, ZCode, Grok Build, OpenClaw, Pi, Hermes, Kimi CLI, DSH) into DeepSeek Harness as resumable se
★ 275
↓ 5k/wk
MIT
JavaScript
Oct 31, 2026
dsh plugin --profile web add dsh-chat-import